aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/Kconfig11
-rw-r--r--drivers/gpu/drm/Makefile13
-rw-r--r--drivers/gpu/drm/armada/armada_510.c23
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.c208
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.h11
-rw-r--r--drivers/gpu/drm/armada/armada_drm.h13
-rw-r--r--drivers/gpu/drm/armada/armada_drv.c245
-rw-r--r--drivers/gpu/drm/armada/armada_fbdev.c4
-rw-r--r--drivers/gpu/drm/armada/armada_gem.h2
-rw-r--r--drivers/gpu/drm/armada/armada_output.c4
-rw-r--r--drivers/gpu/drm/ast/ast_dp501.c38
-rw-r--r--drivers/gpu/drm/ast/ast_drv.c3
-rw-r--r--drivers/gpu/drm/ast/ast_drv.h14
-rw-r--r--drivers/gpu/drm/ast/ast_fb.c9
-rw-r--r--drivers/gpu/drm/ast/ast_main.c82
-rw-r--r--drivers/gpu/drm/ast/ast_mode.c62
-rw-r--r--drivers/gpu/drm/ast/ast_post.c23
-rw-r--r--drivers/gpu/drm/ast/ast_tables.h39
-rw-r--r--drivers/gpu/drm/ast/ast_ttm.c24
-rw-r--r--drivers/gpu/drm/ati_pcigart.c2
-rw-r--r--drivers/gpu/drm/bochs/bochs.h4
-rw-r--r--drivers/gpu/drm/bochs/bochs_drv.c5
-rw-r--r--drivers/gpu/drm/bochs/bochs_fbdev.c7
-rw-r--r--drivers/gpu/drm/bochs/bochs_kms.c18
-rw-r--r--drivers/gpu/drm/bochs/bochs_mm.c26
-rw-r--r--drivers/gpu/drm/bridge/ptn3460.c2
-rw-r--r--drivers/gpu/drm/cirrus/cirrus_drv.c7
-rw-r--r--drivers/gpu/drm/cirrus/cirrus_drv.h6
-rw-r--r--drivers/gpu/drm/cirrus/cirrus_fbdev.c9
-rw-r--r--drivers/gpu/drm/cirrus/cirrus_mode.c15
-rw-r--r--drivers/gpu/drm/cirrus/cirrus_ttm.c21
-rw-r--r--drivers/gpu/drm/drm_agpsupport.c1
-rw-r--r--drivers/gpu/drm/drm_auth.c7
-rw-r--r--drivers/gpu/drm/drm_bufs.c111
-rw-r--r--drivers/gpu/drm/drm_context.c102
-rw-r--r--drivers/gpu/drm/drm_crtc.c969
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c1
-rw-r--r--drivers/gpu/drm/drm_debugfs.c185
-rw-r--r--drivers/gpu/drm/drm_dma.c11
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c2753
-rw-r--r--drivers/gpu/drm/drm_drv.c1170
-rw-r--r--drivers/gpu/drm/drm_edid.c134
-rw-r--r--drivers/gpu/drm/drm_fb_cma_helper.c5
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c176
-rw-r--r--drivers/gpu/drm/drm_fops.c115
-rw-r--r--drivers/gpu/drm/drm_gem.c41
-rw-r--r--drivers/gpu/drm/drm_gem_cma_helper.c9
-rw-r--r--drivers/gpu/drm/drm_hashtab.c2
-rw-r--r--drivers/gpu/drm/drm_info.c91
-rw-r--r--drivers/gpu/drm/drm_internal.h132
-rw-r--r--drivers/gpu/drm/drm_ioctl.c396
-rw-r--r--drivers/gpu/drm/drm_irq.c463
-rw-r--r--drivers/gpu/drm/drm_legacy.h113
-rw-r--r--drivers/gpu/drm/drm_lock.c37
-rw-r--r--drivers/gpu/drm/drm_memory.c24
-rw-r--r--drivers/gpu/drm/drm_mipi_dsi.c20
-rw-r--r--drivers/gpu/drm/drm_modes.c1
-rw-r--r--drivers/gpu/drm/drm_modeset_lock.c215
-rw-r--r--drivers/gpu/drm/drm_of.c67
-rw-r--r--drivers/gpu/drm/drm_pci.c46
-rw-r--r--drivers/gpu/drm/drm_plane_helper.c7
-rw-r--r--drivers/gpu/drm/drm_platform.c38
-rw-r--r--drivers/gpu/drm/drm_prime.c5
-rw-r--r--drivers/gpu/drm/drm_probe_helper.c26
-rw-r--r--drivers/gpu/drm/drm_rect.c140
-rw-r--r--drivers/gpu/drm/drm_scatter.c9
-rw-r--r--drivers/gpu/drm/drm_stub.c805
-rw-r--r--drivers/gpu/drm/drm_sysfs.c93
-rw-r--r--drivers/gpu/drm/drm_usb.c88
-rw-r--r--drivers/gpu/drm/drm_vm.c89
-rw-r--r--drivers/gpu/drm/exynos/Kconfig2
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.c128
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.h3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_connector.c21
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c82
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.h7
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dpi.c18
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c170
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h13
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dsi.c323
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c9
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimc.c91
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c332
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_g2d.c38
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.c109
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.h16
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gsc.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.c682
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.h4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c19
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.h3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_rotator.c4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c25
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c46
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c60
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_crt.c4
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_dp.c8
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_hdmi.c4
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_lvds.c4
-rw-r--r--drivers/gpu/drm/gma500/framebuffer.c8
-rw-r--r--drivers/gpu/drm/gma500/gtt.c2
-rw-r--r--drivers/gpu/drm/gma500/gtt.h1
-rw-r--r--drivers/gpu/drm/gma500/intel_bios.c16
-rw-r--r--drivers/gpu/drm/gma500/mdfld_dsi_output.c4
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_hdmi.c4
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_lvds.c2
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.c3
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_lvds.c4
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_sdvo.c4
-rw-r--r--drivers/gpu/drm/i2c/tda998x_drv.c390
-rw-r--r--drivers/gpu/drm/i810/i810_dma.c21
-rw-r--r--drivers/gpu/drm/i810/i810_drv.c3
-rw-r--r--drivers/gpu/drm/i810/i810_drv.h2
-rw-r--r--drivers/gpu/drm/i915/Kconfig12
-rw-r--r--drivers/gpu/drm/i915/Makefile2
-rw-r--r--drivers/gpu/drm/i915/dvo_ns2501.c560
-rw-r--r--drivers/gpu/drm/i915/i915_cmd_parser.c19
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c791
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c109
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c299
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h417
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c472
-rw-r--r--drivers/gpu/drm/i915/i915_gem_context.c319
-rw-r--r--drivers/gpu/drm/i915/i915_gem_evict.c4
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c515
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.c385
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.h37
-rw-r--r--drivers/gpu/drm/i915/i915_gem_render_state.c179
-rw-r--r--drivers/gpu/drm/i915/i915_gem_render_state.h47
-rw-r--r--drivers/gpu/drm/i915/i915_gem_stolen.c117
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c30
-rw-r--r--drivers/gpu/drm/i915/i915_gem_userptr.c521
-rw-r--r--drivers/gpu/drm/i915/i915_gpu_error.c318
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c898
-rw-r--r--drivers/gpu/drm/i915/i915_params.c25
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h783
-rw-r--r--drivers/gpu/drm/i915/i915_sysfs.c59
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c36
-rw-r--r--drivers/gpu/drm/i915/intel_bios.h3
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c47
-rw-r--r--drivers/gpu/drm/i915/intel_ddi.c782
-rw-r--r--drivers/gpu/drm/i915/intel_display.c2775
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c2066
-rw-r--r--drivers/gpu/drm/i915/intel_dp_mst.c548
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h205
-rw-r--r--drivers/gpu/drm/i915/intel_dsi.c95
-rw-r--r--drivers/gpu/drm/i915/intel_dsi.h3
-rw-r--r--drivers/gpu/drm/i915/intel_dsi_cmd.c16
-rw-r--r--drivers/gpu/drm/i915/intel_dsi_cmd.h1
-rw-r--r--drivers/gpu/drm/i915/intel_dsi_panel_vbt.c46
-rw-r--r--drivers/gpu/drm/i915/intel_dsi_pll.c94
-rw-r--r--drivers/gpu/drm/i915/intel_dvo.c21
-rw-r--r--drivers/gpu/drm/i915/intel_fbdev.c120
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c279
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c54
-rw-r--r--drivers/gpu/drm/i915/intel_lrc.c1766
-rw-r--r--drivers/gpu/drm/i915/intel_lrc.h114
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c25
-rw-r--r--drivers/gpu/drm/i915/intel_opregion.c19
-rw-r--r--drivers/gpu/drm/i915/intel_overlay.c22
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c209
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c1672
-rw-r--r--drivers/gpu/drm/i915/intel_renderstate.h10
-rw-r--r--drivers/gpu/drm/i915/intel_renderstate_gen6.c1
-rw-r--r--drivers/gpu/drm/i915/intel_renderstate_gen7.c1
-rw-r--r--drivers/gpu/drm/i915/intel_renderstate_gen8.c1
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c777
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.h138
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c10
-rw-r--r--drivers/gpu/drm/i915/intel_sprite.c145
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c23
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.c218
-rw-r--r--drivers/gpu/drm/mga/mga_dma.c77
-rw-r--r--drivers/gpu/drm/mga/mga_drv.c3
-rw-r--r--drivers/gpu/drm/mga/mga_drv.h2
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.c3
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.h8
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_fb.c8
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_mode.c37
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_ttm.c24
-rw-r--r--drivers/gpu/drm/msm/Kconfig2
-rw-r--r--drivers/gpu/drm/msm/Makefile4
-rw-r--r--drivers/gpu/drm/msm/adreno/a2xx.xml.h58
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx.xml.h302
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.c217
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.h5
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_common.xml.h56
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_device.c285
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c161
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.h17
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h243
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi.xml.h10
-rw-r--r--drivers/gpu/drm/msm/dsi/mmss_cc.xml.h18
-rw-r--r--drivers/gpu/drm/msm/dsi/sfpb.xml.h10
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.c93
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.h1
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.xml.h115
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_connector.c27
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c386
-rw-r--r--drivers/gpu/drm/msm/hdmi/qfprom.xml.h10
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h107
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c23
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c2
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c114
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h64
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c506
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c151
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c172
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h431
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c159
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h25
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp_common.xml.h4
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c124
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h3
-rw-r--r--drivers/gpu/drm/msm/msm_fbdev.c49
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c17
-rw-r--r--drivers/gpu/drm/msm/msm_gem_prime.c5
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c12
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h6
-rw-r--r--drivers/gpu/drm/msm/msm_iommu.c35
-rw-r--r--drivers/gpu/drm/msm/msm_mmu.h8
-rw-r--r--drivers/gpu/drm/nouveau/Kconfig12
-rw-r--r--drivers/gpu/drm/nouveau/Makefile37
-rw-r--r--drivers/gpu/drm/nouveau/core/core/client.c163
-rw-r--r--drivers/gpu/drm/nouveau/core/core/event.c178
-rw-r--r--drivers/gpu/drm/nouveau/core/core/gpuobj.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/core/handle.c115
-rw-r--r--drivers/gpu/drm/nouveau/core/core/ioctl.c530
-rw-r--r--drivers/gpu/drm/nouveau/core/core/mm.c97
-rw-r--r--drivers/gpu/drm/nouveau/core/core/notify.c168
-rw-r--r--drivers/gpu/drm/nouveau/core/core/object.c154
-rw-r--r--drivers/gpu/drm/nouveau/core/core/parent.c33
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/copy/nva3.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/copy/nve0.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/acpi.c59
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/acpi.h9
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/base.c397
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/ctrl.c153
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/gm100.c17
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv04.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv10.c16
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv20.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv30.c10
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv40.c32
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv50.c73
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nvc0.c78
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nve0.c58
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/base.c98
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/conn.c63
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/conn.h5
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c85
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/dport.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/gm107.c22
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c41
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c46
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/hdminv84.c31
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c33
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c33
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/hdminve0.c83
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv04.c137
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv50.c609
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv50.h141
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv84.c34
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv94.c35
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nva0.c20
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nva3.c36
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c460
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nve0.c22
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c22
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/outp.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/outp.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c100
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h5
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c56
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/priv.h6
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c88
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c157
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c160
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c210
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c170
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c157
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/dmaobj/priv.h30
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/base.c115
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c32
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c30
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c30
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c30
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c58
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c83
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c58
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c64
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxgk110b.c104
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c12
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c121
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c56
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c10
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c123
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h65
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c64
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc4.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c81
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c10
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c85
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c74
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/gk110b.c117
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c7
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/gm107.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv04.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv10.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv108.c3
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv20.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv25.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv2a.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv30.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv34.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv35.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv40.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv50.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c271
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h40
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c6
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nve4.c39
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c17
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/mpeg/nv40.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/mpeg/nv44.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/mpeg/nv84.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/perfmon/base.c128
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/software/nv04.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/software/nv10.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/software/nv50.c40
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/software/nv50.h5
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/software/nvc0.c10
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/class.h470
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/client.h11
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/device.h19
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/event.h54
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/handle.h8
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/ioctl.h6
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/mm.h12
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/notify.h37
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/object.h29
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/parent.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/printk.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/include/engine/disp.h11
-rw-r--r--drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h27
-rw-r--r--drivers/gpu/drm/nouveau/core/include/engine/fifo.h12
-rw-r--r--drivers/gpu/drm/nouveau/core/include/engine/graph.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/include/engine/perfmon.h1
l---------drivers/gpu/drm/nouveau/core/include/nvif/class.h1
l---------drivers/gpu/drm/nouveau/core/include/nvif/event.h1
l---------drivers/gpu/drm/nouveau/core/include/nvif/ioctl.h1
l---------drivers/gpu/drm/nouveau/core/include/nvif/unpack.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bar.h5
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/M0205.h32
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/M0209.h30
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/fan.h8
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h160
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h5
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h10
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/clock.h31
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/fb.h7
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/fb/regsnv04.h21
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/fuse.h30
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/gpio.h18
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/i2c.h23
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/ltc.h35
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h41
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/mc.h31
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/pwr.h46
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/therm.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/os.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bar/base.c32
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bar/gk20a.c54
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c7
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bar/priv.h6
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/M0205.c136
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/M0209.c137
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/fan.c93
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c74
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/timing.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/base.c197
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/gk20a.c665
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c307
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h10
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c41
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h18
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c16
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c11
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c10
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/priv.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h82
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c17
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c24
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c96
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c23
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c490
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/sddr2.c94
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c47
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fuse/base.c54
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fuse/g80.c81
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fuse/gf100.c83
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fuse/gm107.c66
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fuse/priv.h9
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/base.c51
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/nv94.c (renamed from drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c)12
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/base.c47
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/ltc/base.c126
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/ltc/gf100.c (renamed from drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.c)204
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/ltc/gk104.c60
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/ltc/gm107.c (renamed from drivers/gpu/drm/nouveau/core/subdev/ltcg/gm107.c)128
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/ltc/priv.h71
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.h21
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mc/base.c12
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mc/gk20a.c38
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mc/nv04.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c9
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mc/nvc3.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mc/priv.h38
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/base.c35
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/arith.fuc94
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc122
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc22
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc133
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h1275
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h1276
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h1302
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h877
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h11
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/gk104.c69
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c61
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/nv108.c43
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/nva3.c51
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/nvc0.c43
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/nvd0.c43
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/pwr/priv.h44
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/fan.c9
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/gm107.c93
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/nv84.c38
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/priv.h3
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/vm/base.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c8
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/arb.c12
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/crtc.c44
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/cursor.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/dac.c54
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/dfp.c14
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/disp.c12
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/disp.h8
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/hw.c114
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/hw.h84
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/overlay.c137
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/tvnv04.c6
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/tvnv17.c27
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/tvnv17.h8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.c248
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.h3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.c16
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_agp.c33
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_backlight.c57
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c41
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c272
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.h9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_chan.c269
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_chan.h23
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c86
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.h14
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_crtc.h5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c341
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.h15
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.c13
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.h21
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c396
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.h70
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c162
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.h15
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.c546
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.h29
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c260
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_hwmon.c58
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_nvif.c136
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_platform.c183
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_platform.h49
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_prime.c10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sgdma.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sysfs.c88
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sysfs.h4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ttm.c46
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_usif.c384
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_usif.h9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vga.c23
-rw-r--r--drivers/gpu/drm/nouveau/nv04_fbcon.c59
-rw-r--r--drivers/gpu/drm/nouveau/nv04_fence.c12
-rw-r--r--drivers/gpu/drm/nouveau/nv10_fence.c17
-rw-r--r--drivers/gpu/drm/nouveau/nv10_fence.h3
-rw-r--r--drivers/gpu/drm/nouveau/nv17_fence.c34
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c965
-rw-r--r--drivers/gpu/drm/nouveau/nv50_fbcon.c13
-rw-r--r--drivers/gpu/drm/nouveau/nv50_fence.c37
-rw-r--r--drivers/gpu/drm/nouveau/nv84_fence.c61
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fbcon.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fence.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvif/class.h567
-rw-r--r--drivers/gpu/drm/nouveau/nvif/client.c129
-rw-r--r--drivers/gpu/drm/nouveau/nvif/client.h39
-rw-r--r--drivers/gpu/drm/nouveau/nvif/device.c78
-rw-r--r--drivers/gpu/drm/nouveau/nvif/device.h62
-rw-r--r--drivers/gpu/drm/nouveau/nvif/driver.h21
-rw-r--r--drivers/gpu/drm/nouveau/nvif/event.h62
-rw-r--r--drivers/gpu/drm/nouveau/nvif/ioctl.h128
-rw-r--r--drivers/gpu/drm/nouveau/nvif/list.h353
-rw-r--r--drivers/gpu/drm/nouveau/nvif/notify.c248
-rw-r--r--drivers/gpu/drm/nouveau/nvif/notify.h39
-rw-r--r--drivers/gpu/drm/nouveau/nvif/object.c304
-rw-r--r--drivers/gpu/drm/nouveau/nvif/object.h75
l---------drivers/gpu/drm/nouveau/nvif/os.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvif/unpack.h24
-rw-r--r--drivers/gpu/drm/omapdrm/omap_connector.c16
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_tiler.c6
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c1
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.h9
-rw-r--r--drivers/gpu/drm/omapdrm/omap_encoder.c27
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fbdev.c4
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem.c33
-rw-r--r--drivers/gpu/drm/omapdrm/omap_plane.c21
-rw-r--r--drivers/gpu/drm/panel/Kconfig7
-rw-r--r--drivers/gpu/drm/panel/panel-ld9040.c21
-rw-r--r--drivers/gpu/drm/panel/panel-s6e8aa0.c29
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c230
-rw-r--r--drivers/gpu/drm/qxl/Makefile2
-rw-r--r--drivers/gpu/drm/qxl/qxl_cmd.c7
-rw-r--r--drivers/gpu/drm/qxl/qxl_debugfs.c14
-rw-r--r--drivers/gpu/drm/qxl/qxl_display.c69
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.c35
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.h36
-rw-r--r--drivers/gpu/drm/qxl/qxl_fb.c10
-rw-r--r--drivers/gpu/drm/qxl/qxl_fence.c91
-rw-r--r--drivers/gpu/drm/qxl/qxl_kms.c17
-rw-r--r--drivers/gpu/drm/qxl/qxl_object.c21
-rw-r--r--drivers/gpu/drm/qxl/qxl_object.h10
-rw-r--r--drivers/gpu/drm/qxl/qxl_prime.c72
-rw-r--r--drivers/gpu/drm/qxl/qxl_release.c174
-rw-r--r--drivers/gpu/drm/qxl/qxl_ttm.c103
-rw-r--r--drivers/gpu/drm/r128/r128_cce.c24
-rw-r--r--drivers/gpu/drm/r128/r128_drv.c3
-rw-r--r--drivers/gpu/drm/r128/r128_drv.h3
-rw-r--r--drivers/gpu/drm/radeon/Makefile8
-rw-r--r--drivers/gpu/drm/radeon/atom.c11
-rw-r--r--drivers/gpu/drm/radeon/atom.h2
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c15
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c39
-rw-r--r--drivers/gpu/drm/radeon/atombios_i2c.c4
-rw-r--r--drivers/gpu/drm/radeon/btc_dpm.c35
-rw-r--r--drivers/gpu/drm/radeon/ci_dpm.c47
-rw-r--r--drivers/gpu/drm/radeon/ci_smc.c39
-rw-r--r--drivers/gpu/drm/radeon/cik.c850
-rw-r--r--drivers/gpu/drm/radeon/cik_sdma.c327
-rw-r--r--drivers/gpu/drm/radeon/cypress_dpm.c1
-rw-r--r--drivers/gpu/drm/radeon/dce3_1_afmt.c10
-rw-r--r--drivers/gpu/drm/radeon/dce6_afmt.c30
-rw-r--r--drivers/gpu/drm/radeon/drm_buffer.c (renamed from drivers/gpu/drm/drm_buffer.c)12
-rw-r--r--drivers/gpu/drm/radeon/drm_buffer.h148
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c39
-rw-r--r--drivers/gpu/drm/radeon/evergreen_dma.c26
-rw-r--r--drivers/gpu/drm/radeon/evergreen_hdmi.c61
-rw-r--r--drivers/gpu/drm/radeon/kv_dpm.c56
-rw-r--r--drivers/gpu/drm/radeon/ni.c14
-rw-r--r--drivers/gpu/drm/radeon/ni_dma.c184
-rw-r--r--drivers/gpu/drm/radeon/ni_dpm.c25
-rw-r--r--drivers/gpu/drm/radeon/r100.c94
-rw-r--r--drivers/gpu/drm/radeon/r200.c23
-rw-r--r--drivers/gpu/drm/radeon/r300.c15
-rw-r--r--drivers/gpu/drm/radeon/r300_cmdbuf.c2
-rw-r--r--drivers/gpu/drm/radeon/r420.c4
-rw-r--r--drivers/gpu/drm/radeon/r600.c241
-rw-r--r--drivers/gpu/drm/radeon/r600_audio.c207
-rw-r--r--drivers/gpu/drm/radeon/r600_cp.c26
-rw-r--r--drivers/gpu/drm/radeon/r600_cs.c6
-rw-r--r--drivers/gpu/drm/radeon/r600_dma.c81
-rw-r--r--drivers/gpu/drm/radeon/r600_dpm.c1
-rw-r--r--drivers/gpu/drm/radeon/r600_hdmi.c172
-rw-r--r--drivers/gpu/drm/radeon/r600d.h66
-rw-r--r--drivers/gpu/drm/radeon/radeon.h266
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c102
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h152
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c122
-rw-r--r--drivers/gpu/drm/radeon/radeon_atpx_handler.c16
-rw-r--r--drivers/gpu/drm/radeon/radeon_benchmark.c34
-rw-r--r--drivers/gpu/drm/radeon/radeon_bios.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_combios.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c388
-rw-r--r--drivers/gpu/drm/radeon/radeon_cp.c46
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c91
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c190
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c107
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c36
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_encoders.c69
-rw-r--r--drivers/gpu/drm/radeon/radeon_fb.c12
-rw-r--r--drivers/gpu/drm/radeon/radeon_fence.c479
-rw-r--r--drivers/gpu/drm/radeon/radeon_gart.c38
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c175
-rw-r--r--drivers/gpu/drm/radeon/radeon_ib.c321
-rw-r--r--drivers/gpu/drm/radeon/radeon_irq_kms.c33
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c14
-rw-r--r--drivers/gpu/drm/radeon/radeon_mn.c274
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h5
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c157
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.h7
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c34
-rw-r--r--drivers/gpu/drm/radeon/radeon_prime.c20
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c324
-rw-r--r--drivers/gpu/drm/radeon/radeon_sa.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_semaphore.c57
-rw-r--r--drivers/gpu/drm/radeon/radeon_state.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_test.c55
-rw-r--r--drivers/gpu/drm/radeon/radeon_trace.h22
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c245
-rw-r--r--drivers/gpu/drm/radeon/radeon_ucode.c167
-rw-r--r--drivers/gpu/drm/radeon/radeon_ucode.h71
-rw-r--r--drivers/gpu/drm/radeon/radeon_uvd.c161
-rw-r--r--drivers/gpu/drm/radeon/radeon_vce.c9
-rw-r--r--drivers/gpu/drm/radeon/radeon_vm.c314
-rw-r--r--drivers/gpu/drm/radeon/rs400.c14
-rw-r--r--drivers/gpu/drm/radeon/rs600.c20
-rw-r--r--drivers/gpu/drm/radeon/rs690.c3
-rw-r--r--drivers/gpu/drm/radeon/rs780_dpm.c1
-rw-r--r--drivers/gpu/drm/radeon/rv515.c5
-rw-r--r--drivers/gpu/drm/radeon/rv6xx_dpm.c1
-rw-r--r--drivers/gpu/drm/radeon/rv770.c25
-rw-r--r--drivers/gpu/drm/radeon/rv770_dma.c27
-rw-r--r--drivers/gpu/drm/radeon/rv770_dpm.c1
-rw-r--r--drivers/gpu/drm/radeon/si.c519
-rw-r--r--drivers/gpu/drm/radeon/si_dma.c199
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c155
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.h5
-rw-r--r--drivers/gpu/drm/radeon/si_smc.c62
-rw-r--r--drivers/gpu/drm/radeon/sid.h2
-rw-r--r--drivers/gpu/drm/radeon/sislands_smc.h3
-rw-r--r--drivers/gpu/drm/radeon/sumo_dpm.c1
-rw-r--r--drivers/gpu/drm/radeon/trinity_dpm.c25
-rw-r--r--drivers/gpu/drm/radeon/uvd_v1_0.c111
-rw-r--r--drivers/gpu/drm/radeon/uvd_v2_2.c4
-rw-r--r--drivers/gpu/drm/rcar-du/Kconfig1
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.c2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.h2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.c175
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.h4
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_encoder.c13
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_encoder.h5
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_group.c2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_group.h2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.c246
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.h2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c49
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h5
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_plane.c2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_plane.h2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_vgacon.c6
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_vgacon.h2
-rw-r--r--drivers/gpu/drm/savage/savage_bci.c39
-rw-r--r--drivers/gpu/drm/savage/savage_drv.c3
-rw-r--r--drivers/gpu/drm/savage/savage_drv.h2
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_backlight.c2
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_backlight.h2
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_crtc.c8
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_crtc.h2
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_drv.c5
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_drv.h2
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_kms.c2
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_kms.h2
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_plane.c2
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_plane.h2
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_regs.h2
-rw-r--r--drivers/gpu/drm/sis/sis_drv.c3
-rw-r--r--drivers/gpu/drm/sis/sis_drv.h2
-rw-r--r--drivers/gpu/drm/sis/sis_mm.c6
-rw-r--r--drivers/gpu/drm/sti/Kconfig15
-rw-r--r--drivers/gpu/drm/sti/Makefile21
-rw-r--r--drivers/gpu/drm/sti/NOTES58
-rw-r--r--drivers/gpu/drm/sti/sti_compositor.c281
-rw-r--r--drivers/gpu/drm/sti/sti_compositor.h90
-rw-r--r--drivers/gpu/drm/sti/sti_drm_crtc.c421
-rw-r--r--drivers/gpu/drm/sti/sti_drm_crtc.h22
-rw-r--r--drivers/gpu/drm/sti/sti_drm_drv.c241
-rw-r--r--drivers/gpu/drm/sti/sti_drm_drv.h29
-rw-r--r--drivers/gpu/drm/sti/sti_drm_plane.c195
-rw-r--r--drivers/gpu/drm/sti/sti_drm_plane.h18
-rw-r--r--drivers/gpu/drm/sti/sti_gdp.c549
-rw-r--r--drivers/gpu/drm/sti/sti_gdp.h16
-rw-r--r--drivers/gpu/drm/sti/sti_hda.c794
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi.c809
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi.h88
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c336
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h14
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c211
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h14
-rw-r--r--drivers/gpu/drm/sti/sti_layer.c197
-rw-r--r--drivers/gpu/drm/sti/sti_layer.h123
-rw-r--r--drivers/gpu/drm/sti/sti_mixer.c249
-rw-r--r--drivers/gpu/drm/sti/sti_mixer.h54
-rw-r--r--drivers/gpu/drm/sti/sti_tvout.c648
-rw-r--r--drivers/gpu/drm/sti/sti_vid.c138
-rw-r--r--drivers/gpu/drm/sti/sti_vid.h12
-rw-r--r--drivers/gpu/drm/sti/sti_vtac.c223
-rw-r--r--drivers/gpu/drm/sti/sti_vtg.c366
-rw-r--r--drivers/gpu/drm/sti/sti_vtg.h28
-rw-r--r--drivers/gpu/drm/tdfx/tdfx_drv.c4
-rw-r--r--drivers/gpu/drm/tegra/dc.c132
-rw-r--r--drivers/gpu/drm/tegra/dc.h5
-rw-r--r--drivers/gpu/drm/tegra/dpaux.c5
-rw-r--r--drivers/gpu/drm/tegra/drm.c216
-rw-r--r--drivers/gpu/drm/tegra/drm.h11
-rw-r--r--drivers/gpu/drm/tegra/dsi.c4
-rw-r--r--drivers/gpu/drm/tegra/fb.c66
-rw-r--r--drivers/gpu/drm/tegra/gem.c5
-rw-r--r--drivers/gpu/drm/tegra/gem.h17
-rw-r--r--drivers/gpu/drm/tegra/gr2d.c1
-rw-r--r--drivers/gpu/drm/tegra/gr3d.c4
-rw-r--r--drivers/gpu/drm/tegra/hdmi.c1
-rw-r--r--drivers/gpu/drm/tegra/output.c6
-rw-r--r--drivers/gpu/drm/tegra/sor.c24
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_drv.c76
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_drv.h1
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_panel.c111
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_slave.c29
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_tfp410.c37
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c338
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_manager.c8
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c48
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_vm.c8
-rw-r--r--drivers/gpu/drm/ttm/ttm_execbuf_util.c160
-rw-r--r--drivers/gpu/drm/ttm/ttm_memory.c3
-rw-r--r--drivers/gpu/drm/ttm/ttm_module.c2
-rw-r--r--drivers/gpu/drm/ttm/ttm_page_alloc.c31
-rw-r--r--drivers/gpu/drm/ttm/ttm_page_alloc_dma.c36
-rw-r--r--drivers/gpu/drm/ttm/ttm_tt.c13
-rw-r--r--drivers/gpu/drm/udl/Kconfig3
-rw-r--r--drivers/gpu/drm/udl/udl_connector.c17
-rw-r--r--drivers/gpu/drm/udl/udl_drv.c102
-rw-r--r--drivers/gpu/drm/udl/udl_drv.h2
-rw-r--r--drivers/gpu/drm/udl/udl_fb.c8
-rw-r--r--drivers/gpu/drm/udl/udl_gem.c8
-rw-r--r--drivers/gpu/drm/udl/udl_main.c23
-rw-r--r--drivers/gpu/drm/udl/udl_modeset.c21
-rw-r--r--drivers/gpu/drm/via/via_dma.c4
-rw-r--r--drivers/gpu/drm/via/via_drv.c3
-rw-r--r--drivers/gpu/drm/via/via_drv.h2
-rw-r--r--drivers/gpu/drm/via/via_map.c6
-rw-r--r--drivers/gpu/drm/via/via_mm.c6
-rw-r--r--drivers/gpu/drm/via/via_verifier.c1
-rw-r--r--drivers/gpu/drm/vmwgfx/Makefile3
-rw-r--r--drivers/gpu/drm/vmwgfx/svga_reg.h1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c183
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c342
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_context.c38
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c28
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c16
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h76
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c253
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fb.c10
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.c346
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.h35
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c14
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c36
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c52
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_shader.c396
-rw-r--r--drivers/gpu/host1x/job.c22
-rw-r--r--drivers/gpu/ipu-v3/Kconfig3
-rw-r--r--drivers/gpu/ipu-v3/Makefile3
-rw-r--r--drivers/gpu/ipu-v3/ipu-common.c920
-rw-r--r--drivers/gpu/ipu-v3/ipu-cpmem.c764
-rw-r--r--drivers/gpu/ipu-v3/ipu-csi.c741
-rw-r--r--drivers/gpu/ipu-v3/ipu-ic.c778
-rw-r--r--drivers/gpu/ipu-v3/ipu-prv.h44
-rw-r--r--drivers/gpu/ipu-v3/ipu-smfc.c157
-rw-r--r--drivers/gpu/vga/vga_switcheroo.c6
-rw-r--r--drivers/gpu/vga/vgaarb.c87
818 files changed, 62349 insertions, 23359 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f5120046ff80..e3b4b0f02b3d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -8,6 +8,7 @@ menuconfig DRM
tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)"
depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && MMU && HAS_DMA
select HDMI
+ select FB_CMDLINE
select I2C
select I2C_ALGOBIT
select DMA_SHARED_BUFFER
@@ -24,12 +25,6 @@ config DRM_MIPI_DSI
bool
depends on DRM
-config DRM_USB
- tristate
- depends on DRM
- depends on USB_SUPPORT && USB_ARCH_HAS_HCD
- select USB
-
config DRM_KMS_HELPER
tristate
depends on DRM
@@ -114,6 +109,8 @@ config DRM_RADEON
select POWER_SUPPLY
select HWMON
select BACKLIGHT_CLASS_DEVICE
+ select INTERVAL_TREE
+ select MMU_NOTIFIER
help
Choose this option if you have an ATI Radeon graphics card. There
are both PCI and AGP versions. You don't need to choose this to
@@ -201,3 +198,5 @@ source "drivers/gpu/drm/msm/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig"
source "drivers/gpu/drm/panel/Kconfig"
+
+source "drivers/gpu/drm/sti/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index dd2ba4269740..9292a761ea6d 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -4,10 +4,10 @@
ccflags-y := -Iinclude/drm
-drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
+drm-y := drm_auth.o drm_bufs.o drm_cache.o \
drm_context.o drm_dma.o \
- drm_drv.o drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \
- drm_lock.o drm_memory.o drm_stub.o drm_vm.o \
+ drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \
+ drm_lock.o drm_memory.o drm_drv.o drm_vm.o \
drm_agpsupport.o drm_scatter.o drm_pci.o \
drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \
drm_crtc.o drm_modes.o drm_edid.o \
@@ -20,11 +20,10 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
drm-$(CONFIG_PCI) += ati_pcigart.o
drm-$(CONFIG_DRM_PANEL) += drm_panel.o
-
-drm-usb-y := drm_usb.o
+drm-$(CONFIG_OF) += drm_of.o
drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
- drm_plane_helper.o
+ drm_plane_helper.o drm_dp_mst_topology.o
drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o
drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
@@ -35,7 +34,6 @@ CFLAGS_drm_trace_points.o := -I$(src)
obj-$(CONFIG_DRM) += drm.o
obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o
-obj-$(CONFIG_DRM_USB) += drm_usb.o
obj-$(CONFIG_DRM_TTM) += ttm/
obj-$(CONFIG_DRM_TDFX) += tdfx/
obj-$(CONFIG_DRM_R128) += r128/
@@ -63,6 +61,7 @@ obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_BOCHS) += bochs/
obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_DRM_TEGRA) += tegra/
+obj-$(CONFIG_DRM_STI) += sti/
obj-y += i2c/
obj-y += panel/
obj-y += bridge/
diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c
index 59948eff6095..ad3d2ebf95c9 100644
--- a/drivers/gpu/drm/armada/armada_510.c
+++ b/drivers/gpu/drm/armada/armada_510.c
@@ -15,20 +15,19 @@
#include "armada_drm.h"
#include "armada_hw.h"
-static int armada510_init(struct armada_private *priv, struct device *dev)
+static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev)
{
- priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1");
+ struct clk *clk;
- if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
- priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
+ clk = devm_clk_get(dev, "ext_ref_clk1");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : PTR_ERR(clk);
- return PTR_RET(priv->extclk[0]);
-}
+ dcrtc->extclk[0] = clk;
-static int armada510_crtc_init(struct armada_crtc *dcrtc)
-{
/* Lower the watermark so to eliminate jitter at higher bandwidths */
armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
+
return 0;
}
@@ -45,8 +44,7 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc)
static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
const struct drm_display_mode *mode, uint32_t *sclk)
{
- struct armada_private *priv = dcrtc->crtc.dev->dev_private;
- struct clk *clk = priv->extclk[0];
+ struct clk *clk = dcrtc->extclk[0];
int ret;
if (dcrtc->num == 1)
@@ -81,7 +79,6 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
const struct armada_variant armada510_ops = {
.has_spu_adv_reg = true,
.spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,
- .init = armada510_init,
- .crtc_init = armada510_crtc_init,
- .crtc_compute_clock = armada510_crtc_compute_clock,
+ .init = armada510_crtc_init,
+ .compute_clock = armada510_crtc_compute_clock,
};
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 3aedf9e993e6..e4a1490b42c2 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -7,6 +7,9 @@
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include "armada_crtc.h"
@@ -257,7 +260,7 @@ static void armada_drm_vblank_off(struct armada_crtc *dcrtc)
* Tell the DRM core that vblank IRQs aren't going to happen for
* a while. This cleans up any pending vblank events for us.
*/
- drm_vblank_off(dev, dcrtc->num);
+ drm_crtc_vblank_off(&dcrtc->crtc);
/* Handle any pending flip event. */
spin_lock_irq(&dev->event_lock);
@@ -286,6 +289,8 @@ static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms)
armada_drm_crtc_update(dcrtc);
if (dpms_blanked(dpms))
armada_drm_vblank_off(dcrtc);
+ else
+ drm_crtc_vblank_on(&dcrtc->crtc);
}
}
@@ -332,24 +337,23 @@ static void armada_drm_crtc_commit(struct drm_crtc *crtc)
static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode, struct drm_display_mode *adj)
{
- struct armada_private *priv = crtc->dev->dev_private;
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
int ret;
/* We can't do interlaced modes if we don't have the SPU_ADV_REG */
- if (!priv->variant->has_spu_adv_reg &&
+ if (!dcrtc->variant->has_spu_adv_reg &&
adj->flags & DRM_MODE_FLAG_INTERLACE)
return false;
/* Check whether the display mode is possible */
- ret = priv->variant->crtc_compute_clock(dcrtc, adj, NULL);
+ ret = dcrtc->variant->compute_clock(dcrtc, adj, NULL);
if (ret)
return false;
return true;
}
-void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
+static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
{
struct armada_vbl_event *e, *n;
void __iomem *base = dcrtc->base;
@@ -410,6 +414,27 @@ void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
}
}
+static irqreturn_t armada_drm_irq(int irq, void *arg)
+{
+ struct armada_crtc *dcrtc = arg;
+ u32 v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
+
+ /*
+ * This is rediculous - rather than writing bits to clear, we
+ * have to set the actual status register value. This is racy.
+ */
+ writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
+
+ /* Mask out those interrupts we haven't enabled */
+ v = stat & dcrtc->irq_ena;
+
+ if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
+ armada_drm_crtc_irq(dcrtc, stat);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
/* These are locked by dev->vbl_lock */
void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask)
{
@@ -470,7 +495,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode, struct drm_display_mode *adj,
int x, int y, struct drm_framebuffer *old_fb)
{
- struct armada_private *priv = crtc->dev->dev_private;
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
struct armada_regs regs[17];
uint32_t lm, rm, tm, bm, val, sclk;
@@ -504,7 +528,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
/* Wait for pending flips to complete */
wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
- drm_vblank_pre_modeset(crtc->dev, dcrtc->num);
+ drm_crtc_vblank_off(crtc);
crtc->mode = *adj;
@@ -515,7 +539,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
}
/* Now compute the divider for real */
- priv->variant->crtc_compute_clock(dcrtc, adj, &sclk);
+ dcrtc->variant->compute_clock(dcrtc, adj, &sclk);
/* Ensure graphic fifo is enabled */
armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
@@ -537,7 +561,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
dcrtc->v[1].spu_v_porch = tm << 16 | bm;
val = adj->crtc_hsync_start;
dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
- priv->variant->spu_adv_reg;
+ dcrtc->variant->spu_adv_reg;
if (interlaced) {
/* Odd interlaced frame */
@@ -546,7 +570,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1;
val = adj->crtc_hsync_start - adj->crtc_htotal / 2;
dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
- priv->variant->spu_adv_reg;
+ dcrtc->variant->spu_adv_reg;
} else {
dcrtc->v[0] = dcrtc->v[1];
}
@@ -561,7 +585,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
LCD_SPUT_V_H_TOTAL);
- if (priv->variant->has_spu_adv_reg) {
+ if (dcrtc->variant->has_spu_adv_reg) {
armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
@@ -595,7 +619,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
armada_drm_crtc_update(dcrtc);
- drm_vblank_post_modeset(crtc->dev, dcrtc->num);
+ drm_crtc_vblank_on(crtc);
armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms));
return 0;
@@ -805,12 +829,11 @@ static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc,
{
struct drm_device *dev = crtc->dev;
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
- struct armada_private *priv = crtc->dev->dev_private;
struct armada_gem_object *obj = NULL;
int ret;
/* If no cursor support, replicate drm's return value */
- if (!priv->variant->has_spu_adv_reg)
+ if (!dcrtc->variant->has_spu_adv_reg)
return -ENXIO;
if (handle && w > 0 && h > 0) {
@@ -858,11 +881,10 @@ static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
{
struct drm_device *dev = crtc->dev;
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
- struct armada_private *priv = crtc->dev->dev_private;
int ret;
/* If no cursor support, replicate drm's return value */
- if (!priv->variant->has_spu_adv_reg)
+ if (!dcrtc->variant->has_spu_adv_reg)
return -EFAULT;
mutex_lock(&dev->struct_mutex);
@@ -888,6 +910,10 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
if (!IS_ERR(dcrtc->clk))
clk_disable_unprepare(dcrtc->clk);
+ writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA);
+
+ of_node_put(dcrtc->crtc.port);
+
kfree(dcrtc);
}
@@ -921,18 +947,15 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
armada_reg_queue_end(work->regs, i);
/*
- * Hold the old framebuffer for the work - DRM appears to drop our
- * reference to the old framebuffer in drm_mode_page_flip_ioctl().
+ * Ensure that we hold a reference on the new framebuffer.
+ * This has to match the behaviour in mode_set.
*/
- drm_framebuffer_reference(work->old_fb);
+ drm_framebuffer_reference(fb);
ret = armada_drm_crtc_queue_frame_work(dcrtc, work);
if (ret) {
- /*
- * Undo our reference above; DRM does not drop the reference
- * to this object on error, so that's okay.
- */
- drm_framebuffer_unreference(work->old_fb);
+ /* Undo our reference above */
+ drm_framebuffer_unreference(fb);
kfree(work);
return ret;
}
@@ -1027,19 +1050,20 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev)
return 0;
}
-int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
- struct resource *res)
+int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
+ struct resource *res, int irq, const struct armada_variant *variant,
+ struct device_node *port)
{
- struct armada_private *priv = dev->dev_private;
+ struct armada_private *priv = drm->dev_private;
struct armada_crtc *dcrtc;
void __iomem *base;
int ret;
- ret = armada_drm_crtc_create_properties(dev);
+ ret = armada_drm_crtc_create_properties(drm);
if (ret)
return ret;
- base = devm_ioremap_resource(dev->dev, res);
+ base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -1049,8 +1073,12 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
return -ENOMEM;
}
+ if (dev != drm->dev)
+ dev_set_drvdata(dev, dcrtc);
+
+ dcrtc->variant = variant;
dcrtc->base = base;
- dcrtc->num = num;
+ dcrtc->num = drm->mode_config.num_crtc;
dcrtc->clk = ERR_PTR(-EINVAL);
dcrtc->csc_yuv_mode = CSC_AUTO;
dcrtc->csc_rgb_mode = CSC_AUTO;
@@ -1072,9 +1100,18 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1);
writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN);
+ writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
+ writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
- if (priv->variant->crtc_init) {
- ret = priv->variant->crtc_init(dcrtc);
+ ret = devm_request_irq(dev, irq, armada_drm_irq, 0, "armada_drm_crtc",
+ dcrtc);
+ if (ret < 0) {
+ kfree(dcrtc);
+ return ret;
+ }
+
+ if (dcrtc->variant->init) {
+ ret = dcrtc->variant->init(dcrtc, dev);
if (ret) {
kfree(dcrtc);
return ret;
@@ -1086,7 +1123,8 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
priv->dcrtc[dcrtc->num] = dcrtc;
- drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs);
+ dcrtc->crtc.port = port;
+ drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs);
drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
@@ -1094,5 +1132,107 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop,
dcrtc->csc_rgb_mode);
- return armada_overlay_plane_create(dev, 1 << dcrtc->num);
+ return armada_overlay_plane_create(drm, 1 << dcrtc->num);
+}
+
+static int
+armada_lcd_bind(struct device *dev, struct device *master, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int irq = platform_get_irq(pdev, 0);
+ const struct armada_variant *variant;
+ struct device_node *port = NULL;
+
+ if (irq < 0)
+ return irq;
+
+ if (!dev->of_node) {
+ const struct platform_device_id *id;
+
+ id = platform_get_device_id(pdev);
+ if (!id)
+ return -ENXIO;
+
+ variant = (const struct armada_variant *)id->driver_data;
+ } else {
+ const struct of_device_id *match;
+ struct device_node *np, *parent = dev->of_node;
+
+ match = of_match_device(dev->driver->of_match_table, dev);
+ if (!match)
+ return -ENXIO;
+
+ np = of_get_child_by_name(parent, "ports");
+ if (np)
+ parent = np;
+ port = of_get_child_by_name(parent, "port");
+ of_node_put(np);
+ if (!port) {
+ dev_err(dev, "no port node found in %s\n",
+ parent->full_name);
+ return -ENXIO;
+ }
+
+ variant = match->data;
+ }
+
+ return armada_drm_crtc_create(drm, dev, res, irq, variant, port);
+}
+
+static void
+armada_lcd_unbind(struct device *dev, struct device *master, void *data)
+{
+ struct armada_crtc *dcrtc = dev_get_drvdata(dev);
+
+ armada_drm_crtc_destroy(&dcrtc->crtc);
}
+
+static const struct component_ops armada_lcd_ops = {
+ .bind = armada_lcd_bind,
+ .unbind = armada_lcd_unbind,
+};
+
+static int armada_lcd_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &armada_lcd_ops);
+}
+
+static int armada_lcd_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &armada_lcd_ops);
+ return 0;
+}
+
+static struct of_device_id armada_lcd_of_match[] = {
+ {
+ .compatible = "marvell,dove-lcd",
+ .data = &armada510_ops,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, armada_lcd_of_match);
+
+static const struct platform_device_id armada_lcd_platform_ids[] = {
+ {
+ .name = "armada-lcd",
+ .driver_data = (unsigned long)&armada510_ops,
+ }, {
+ .name = "armada-510-lcd",
+ .driver_data = (unsigned long)&armada510_ops,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, armada_lcd_platform_ids);
+
+struct platform_driver armada_lcd_platform_driver = {
+ .probe = armada_lcd_probe,
+ .remove = armada_lcd_remove,
+ .driver = {
+ .name = "armada-lcd",
+ .owner = THIS_MODULE,
+ .of_match_table = armada_lcd_of_match,
+ },
+ .id_table = armada_lcd_platform_ids,
+};
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h
index 9c10a07e7492..98102a5a9af5 100644
--- a/drivers/gpu/drm/armada/armada_crtc.h
+++ b/drivers/gpu/drm/armada/armada_crtc.h
@@ -32,12 +32,15 @@ struct armada_regs {
armada_reg_queue_mod(_r, _i, 0, 0, ~0)
struct armada_frame_work;
+struct armada_variant;
struct armada_crtc {
struct drm_crtc crtc;
+ const struct armada_variant *variant;
unsigned num;
void __iomem *base;
struct clk *clk;
+ struct clk *extclk[2];
struct {
uint32_t spu_v_h_total;
uint32_t spu_v_porch;
@@ -72,12 +75,16 @@ struct armada_crtc {
};
#define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
-int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *);
+struct device_node;
+int armada_drm_crtc_create(struct drm_device *, struct device *,
+ struct resource *, int, const struct armada_variant *,
+ struct device_node *);
void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
-void armada_drm_crtc_irq(struct armada_crtc *, u32);
void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
void armada_drm_crtc_enable_irq(struct armada_crtc *, u32);
void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
+extern struct platform_driver armada_lcd_platform_driver;
+
#endif
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h
index a72cae03b99b..ea63c6c7c66f 100644
--- a/drivers/gpu/drm/armada/armada_drm.h
+++ b/drivers/gpu/drm/armada/armada_drm.h
@@ -59,26 +59,23 @@ void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *,
struct armada_private;
struct armada_variant {
- bool has_spu_adv_reg;
+ bool has_spu_adv_reg;
uint32_t spu_adv_reg;
- int (*init)(struct armada_private *, struct device *);
- int (*crtc_init)(struct armada_crtc *);
- int (*crtc_compute_clock)(struct armada_crtc *,
- const struct drm_display_mode *,
- uint32_t *);
+ int (*init)(struct armada_crtc *, struct device *);
+ int (*compute_clock)(struct armada_crtc *,
+ const struct drm_display_mode *,
+ uint32_t *);
};
/* Variant ops */
extern const struct armada_variant armada510_ops;
struct armada_private {
- const struct armada_variant *variant;
struct work_struct fb_unref_work;
DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8);
struct drm_fb_helper *fbdev;
struct armada_crtc *dcrtc[2];
struct drm_mm linear;
- struct clk *extclk[2];
struct drm_property *csc_yuv_prop;
struct drm_property *csc_rgb_prop;
struct drm_property *colorkey_prop;
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index 8ab3cd1a8cdb..908e5316eac4 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -6,7 +6,9 @@
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
+#include <linux/component.h>
#include <linux/module.h>
+#include <linux/of_graph.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include "armada_crtc.h"
@@ -52,6 +54,11 @@ static const struct armada_drm_slave_config tda19988_config = {
};
#endif
+static bool is_componentized(struct device *dev)
+{
+ return dev->of_node || dev->platform_data;
+}
+
static void armada_drm_unref_work(struct work_struct *work)
{
struct armada_private *priv =
@@ -85,6 +92,7 @@ void armada_drm_queue_unref_work(struct drm_device *dev,
static int armada_drm_load(struct drm_device *dev, unsigned long flags)
{
const struct platform_device_id *id;
+ const struct armada_variant *variant;
struct armada_private *priv;
struct resource *res[ARRAY_SIZE(priv->dcrtc)];
struct resource *mem = NULL;
@@ -107,7 +115,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
return -EINVAL;
}
- if (!res[0] || !mem)
+ if (!mem)
return -ENXIO;
if (!devm_request_mem_region(dev->dev, mem->start,
@@ -128,11 +136,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
if (!id)
return -ENXIO;
- priv->variant = (struct armada_variant *)id->driver_data;
-
- ret = priv->variant->init(priv, dev->dev);
- if (ret)
- return ret;
+ variant = (const struct armada_variant *)id->driver_data;
INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
INIT_KFIFO(priv->fb_unref);
@@ -155,40 +159,51 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
/* Create all LCD controllers */
for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
+ int irq;
+
if (!res[n])
break;
- ret = armada_drm_crtc_create(dev, n, res[n]);
+ irq = platform_get_irq(dev->platformdev, n);
+ if (irq < 0)
+ goto err_kms;
+
+ ret = armada_drm_crtc_create(dev, dev->dev, res[n], irq,
+ variant, NULL);
if (ret)
goto err_kms;
}
+ if (is_componentized(dev->dev)) {
+ ret = component_bind_all(dev->dev, dev);
+ if (ret)
+ goto err_kms;
+ } else {
#ifdef CONFIG_DRM_ARMADA_TDA1998X
- ret = armada_drm_connector_slave_create(dev, &tda19988_config);
- if (ret)
- goto err_kms;
+ ret = armada_drm_connector_slave_create(dev, &tda19988_config);
+ if (ret)
+ goto err_kms;
#endif
+ }
- ret = drm_vblank_init(dev, n);
- if (ret)
- goto err_kms;
-
- ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
+ ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret)
- goto err_kms;
+ goto err_comp;
+ dev->irq_enabled = true;
dev->vblank_disable_allowed = 1;
ret = armada_fbdev_init(dev);
if (ret)
- goto err_irq;
+ goto err_comp;
drm_kms_helper_poll_init(dev);
return 0;
- err_irq:
- drm_irq_uninstall(dev);
+ err_comp:
+ if (is_componentized(dev->dev))
+ component_unbind_all(dev->dev, dev);
err_kms:
drm_mode_config_cleanup(dev);
drm_mm_takedown(&priv->linear);
@@ -203,7 +218,10 @@ static int armada_drm_unload(struct drm_device *dev)
drm_kms_helper_poll_fini(dev);
armada_fbdev_fini(dev);
- drm_irq_uninstall(dev);
+
+ if (is_componentized(dev->dev))
+ component_unbind_all(dev->dev, dev);
+
drm_mode_config_cleanup(dev);
drm_mm_takedown(&priv->linear);
flush_work(&priv->fb_unref_work);
@@ -259,52 +277,6 @@ static void armada_drm_disable_vblank(struct drm_device *dev, int crtc)
armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
}
-static irqreturn_t armada_drm_irq_handler(int irq, void *arg)
-{
- struct drm_device *dev = arg;
- struct armada_private *priv = dev->dev_private;
- struct armada_crtc *dcrtc = priv->dcrtc[0];
- uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
- irqreturn_t handled = IRQ_NONE;
-
- /*
- * This is rediculous - rather than writing bits to clear, we
- * have to set the actual status register value. This is racy.
- */
- writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
-
- /* Mask out those interrupts we haven't enabled */
- v = stat & dcrtc->irq_ena;
-
- if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
- armada_drm_crtc_irq(dcrtc, stat);
- handled = IRQ_HANDLED;
- }
-
- return handled;
-}
-
-static int armada_drm_irq_postinstall(struct drm_device *dev)
-{
- struct armada_private *priv = dev->dev_private;
- struct armada_crtc *dcrtc = priv->dcrtc[0];
-
- spin_lock_irq(&dev->vbl_lock);
- writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
- writel(0, dcrtc->base + LCD_SPU_IRQ_ISR);
- spin_unlock_irq(&dev->vbl_lock);
-
- return 0;
-}
-
-static void armada_drm_irq_uninstall(struct drm_device *dev)
-{
- struct armada_private *priv = dev->dev_private;
- struct armada_crtc *dcrtc = priv->dcrtc[0];
-
- writel(0, dcrtc->base + LCD_SPU_IRQ_ENA);
-}
-
static struct drm_ioctl_desc armada_ioctls[] = {
DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,
DRM_UNLOCKED),
@@ -337,12 +309,10 @@ static struct drm_driver armada_drm_driver = {
.postclose = NULL,
.lastclose = armada_drm_lastclose,
.unload = armada_drm_unload,
+ .set_busid = drm_platform_set_busid,
.get_vblank_counter = drm_vblank_count,
.enable_vblank = armada_drm_enable_vblank,
.disable_vblank = armada_drm_disable_vblank,
- .irq_handler = armada_drm_irq_handler,
- .irq_postinstall = armada_drm_irq_postinstall,
- .irq_uninstall = armada_drm_irq_uninstall,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = armada_drm_debugfs_init,
.debugfs_cleanup = armada_drm_debugfs_cleanup,
@@ -367,14 +337,135 @@ static struct drm_driver armada_drm_driver = {
.fops = &armada_drm_fops,
};
+static int armada_drm_bind(struct device *dev)
+{
+ return drm_platform_init(&armada_drm_driver, to_platform_device(dev));
+}
+
+static void armada_drm_unbind(struct device *dev)
+{
+ drm_put_dev(dev_get_drvdata(dev));
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static int compare_dev_name(struct device *dev, void *data)
+{
+ const char *name = data;
+ return !strcmp(dev_name(dev), name);
+}
+
+static void armada_add_endpoints(struct device *dev,
+ struct component_match **match, struct device_node *port)
+{
+ struct device_node *ep, *remote;
+
+ for_each_child_of_node(port, ep) {
+ remote = of_graph_get_remote_port_parent(ep);
+ if (!remote || !of_device_is_available(remote)) {
+ of_node_put(remote);
+ continue;
+ } else if (!of_device_is_available(remote->parent)) {
+ dev_warn(dev, "parent device of %s is not available\n",
+ remote->full_name);
+ of_node_put(remote);
+ continue;
+ }
+
+ component_match_add(dev, match, compare_of, remote);
+ of_node_put(remote);
+ }
+}
+
+static int armada_drm_find_components(struct device *dev,
+ struct component_match **match)
+{
+ struct device_node *port;
+ int i;
+
+ if (dev->of_node) {
+ struct device_node *np = dev->of_node;
+
+ for (i = 0; ; i++) {
+ port = of_parse_phandle(np, "ports", i);
+ if (!port)
+ break;
+
+ component_match_add(dev, match, compare_of, port);
+ of_node_put(port);
+ }
+
+ if (i == 0) {
+ dev_err(dev, "missing 'ports' property\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; ; i++) {
+ port = of_parse_phandle(np, "ports", i);
+ if (!port)
+ break;
+
+ armada_add_endpoints(dev, match, port);
+ of_node_put(port);
+ }
+ } else if (dev->platform_data) {
+ char **devices = dev->platform_data;
+ struct device *d;
+
+ for (i = 0; devices[i]; i++)
+ component_match_add(dev, match, compare_dev_name,
+ devices[i]);
+
+ if (i == 0) {
+ dev_err(dev, "missing 'ports' property\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; devices[i]; i++) {
+ d = bus_find_device_by_name(&platform_bus_type, NULL,
+ devices[i]);
+ if (d && d->of_node) {
+ for_each_child_of_node(d->of_node, port)
+ armada_add_endpoints(dev, match, port);
+ }
+ put_device(d);
+ }
+ }
+
+ return 0;
+}
+
+static const struct component_master_ops armada_master_ops = {
+ .bind = armada_drm_bind,
+ .unbind = armada_drm_unbind,
+};
+
static int armada_drm_probe(struct platform_device *pdev)
{
- return drm_platform_init(&armada_drm_driver, pdev);
+ if (is_componentized(&pdev->dev)) {
+ struct component_match *match = NULL;
+ int ret;
+
+ ret = armada_drm_find_components(&pdev->dev, &match);
+ if (ret < 0)
+ return ret;
+
+ return component_master_add_with_match(&pdev->dev,
+ &armada_master_ops, match);
+ } else {
+ return drm_platform_init(&armada_drm_driver, pdev);
+ }
}
static int armada_drm_remove(struct platform_device *pdev)
{
- drm_put_dev(platform_get_drvdata(pdev));
+ if (is_componentized(&pdev->dev))
+ component_master_del(&pdev->dev, &armada_master_ops);
+ else
+ drm_put_dev(platform_get_drvdata(pdev));
return 0;
}
@@ -402,14 +493,24 @@ static struct platform_driver armada_drm_platform_driver = {
static int __init armada_drm_init(void)
{
+ int ret;
+
armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls);
- return platform_driver_register(&armada_drm_platform_driver);
+
+ ret = platform_driver_register(&armada_lcd_platform_driver);
+ if (ret)
+ return ret;
+ ret = platform_driver_register(&armada_drm_platform_driver);
+ if (ret)
+ platform_driver_unregister(&armada_lcd_platform_driver);
+ return ret;
}
module_init(armada_drm_init);
static void __exit armada_drm_exit(void)
{
platform_driver_unregister(&armada_drm_platform_driver);
+ platform_driver_unregister(&armada_lcd_platform_driver);
}
module_exit(armada_drm_exit);
diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
index fd166f532ab9..7838e731b0de 100644
--- a/drivers/gpu/drm/armada/armada_fbdev.c
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh,
return ret;
}
-static struct drm_fb_helper_funcs armada_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
.gamma_set = armada_drm_crtc_gamma_set,
.gamma_get = armada_drm_crtc_gamma_get,
.fb_probe = armada_fb_probe,
@@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev)
priv->fbdev = fbh;
- fbh->funcs = &armada_fb_helper_funcs;
+ drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
ret = drm_fb_helper_init(dev, fbh, 1, 1);
if (ret) {
diff --git a/drivers/gpu/drm/armada/armada_gem.h b/drivers/gpu/drm/armada/armada_gem.h
index 00b6cd461a03..b000ea3a829a 100644
--- a/drivers/gpu/drm/armada/armada_gem.h
+++ b/drivers/gpu/drm/armada/armada_gem.h
@@ -8,6 +8,8 @@
#ifndef ARMADA_GEM_H
#define ARMADA_GEM_H
+#include <drm/drm_gem.h>
+
/* GEM */
struct armada_gem_object {
struct drm_gem_object obj;
diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c
index d685a5421485..abbc309fe539 100644
--- a/drivers/gpu/drm/armada/armada_output.c
+++ b/drivers/gpu/drm/armada/armada_output.c
@@ -48,7 +48,7 @@ static void armada_drm_connector_destroy(struct drm_connector *conn)
{
struct armada_connector *dconn = drm_to_armada_conn(conn);
- drm_sysfs_connector_remove(conn);
+ drm_connector_unregister(conn);
drm_connector_cleanup(conn);
kfree(dconn);
}
@@ -141,7 +141,7 @@ int armada_output_create(struct drm_device *dev,
if (ret)
goto err_conn;
- ret = drm_sysfs_connector_add(&dconn->conn);
+ ret = drm_connector_register(&dconn->conn);
if (ret)
goto err_sysfs;
diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c
index 5da4b62285fa..76f07f38b941 100644
--- a/drivers/gpu/drm/ast/ast_dp501.c
+++ b/drivers/gpu/drm/ast/ast_dp501.c
@@ -379,11 +379,39 @@ static bool ast_init_dvo(struct drm_device *dev)
return true;
}
+
+static void ast_init_analog(struct drm_device *dev)
+{
+ struct ast_private *ast = dev->dev_private;
+ u32 data;
+
+ /*
+ * Set DAC source to VGA mode in SCU2C via the P2A
+ * bridge. First configure the P2U to target the SCU
+ * in case it isn't at this stage.
+ */
+ ast_write32(ast, 0xf004, 0x1e6e0000);
+ ast_write32(ast, 0xf000, 0x1);
+
+ /* Then unlock the SCU with the magic password */
+ ast_write32(ast, 0x12000, 0x1688a8a8);
+ ast_write32(ast, 0x12000, 0x1688a8a8);
+ ast_write32(ast, 0x12000, 0x1688a8a8);
+
+ /* Finally, clear bits [17:16] of SCU2c */
+ data = ast_read32(ast, 0x1202c);
+ data &= 0xfffcffff;
+ ast_write32(ast, 0, data);
+
+ /* Disable DVO */
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x00);
+}
+
void ast_init_3rdtx(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
u8 jreg;
- u32 data;
+
if (ast->chip == AST2300 || ast->chip == AST2400) {
jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff);
switch (jreg & 0x0e) {
@@ -399,12 +427,8 @@ void ast_init_3rdtx(struct drm_device *dev)
default:
if (ast->tx_chip_type == AST_TX_SIL164)
ast_init_dvo(dev);
- else {
- ast_write32(ast, 0x12000, 0x1688a8a8);
- data = ast_read32(ast, 0x1202c);
- data &= 0xfffcffff;
- ast_write32(ast, 0, data);
- }
+ else
+ ast_init_analog(dev);
}
}
}
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
index 44074fbcf7ff..9a32d9dfdd26 100644
--- a/drivers/gpu/drm/ast/ast_drv.c
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -51,7 +51,7 @@ static struct drm_driver driver;
.subdevice = PCI_ANY_ID, \
.driver_data = (unsigned long) info }
-static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
+static const struct pci_device_id pciidlist[] = {
AST_VGA_DEVICE(PCI_CHIP_AST2000, NULL),
AST_VGA_DEVICE(PCI_CHIP_AST2100, NULL),
/* AST_VGA_DEVICE(PCI_CHIP_AST1180, NULL), - don't bind to 1180 for now */
@@ -199,6 +199,7 @@ static struct drm_driver driver = {
.load = ast_driver_load,
.unload = ast_driver_unload,
+ .set_busid = drm_pci_set_busid,
.fops = &ast_fops,
.name = DRIVER_NAME,
diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h
index 5d6a87573c33..86205a28e56b 100644
--- a/drivers/gpu/drm/ast/ast_drv.h
+++ b/drivers/gpu/drm/ast/ast_drv.h
@@ -36,6 +36,8 @@
#include <drm/ttm/ttm_memory.h>
#include <drm/ttm/ttm_module.h>
+#include <drm/drm_gem.h>
+
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
@@ -125,8 +127,9 @@ struct ast_gem_object;
#define AST_IO_AR_PORT_WRITE (0x40)
#define AST_IO_MISC_PORT_WRITE (0x42)
+#define AST_IO_VGA_ENABLE_PORT (0x43)
#define AST_IO_SEQ_PORT (0x44)
-#define AST_DAC_INDEX_READ (0x3c7)
+#define AST_IO_DAC_INDEX_READ (0x47)
#define AST_IO_DAC_INDEX_WRITE (0x48)
#define AST_IO_DAC_DATA (0x49)
#define AST_IO_GR_PORT (0x4E)
@@ -134,6 +137,8 @@ struct ast_gem_object;
#define AST_IO_INPUT_STATUS1_READ (0x5A)
#define AST_IO_MISC_PORT_READ (0x4C)
+#define AST_IO_MM_OFFSET (0x380)
+
#define __ast_read(x) \
static inline u##x ast_read##x(struct ast_private *ast, u32 reg) { \
u##x val = 0;\
@@ -316,7 +321,7 @@ struct ast_bo {
struct ttm_placement placement;
struct ttm_bo_kmap_obj kmap;
struct drm_gem_object gem;
- u32 placements[3];
+ struct ttm_place placements[3];
int pin_count;
};
#define gem_to_ast_bo(gobj) container_of((gobj), struct ast_bo, gem)
@@ -362,7 +367,7 @@ static inline int ast_bo_reserve(struct ast_bo *bo, bool no_wait)
{
int ret;
- ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+ ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, NULL);
if (ret) {
if (ret != -ERESTARTSYS && ret != -EBUSY)
DRM_ERROR("reserve failed %p\n", bo);
@@ -381,6 +386,9 @@ int ast_bo_push_sysram(struct ast_bo *bo);
int ast_mmap(struct file *filp, struct vm_area_struct *vma);
/* ast post */
+void ast_enable_vga(struct drm_device *dev);
+void ast_enable_mmio(struct drm_device *dev);
+bool ast_is_vga_enabled(struct drm_device *dev);
void ast_post_gpu(struct drm_device *dev);
u32 ast_mindwm(struct ast_private *ast, u32 r);
void ast_moutdwm(struct ast_private *ast, u32 r, u32 v);
diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
index a28640f47c27..5c60ae524c45 100644
--- a/drivers/gpu/drm/ast/ast_fb.c
+++ b/drivers/gpu/drm/ast/ast_fb.c
@@ -186,7 +186,8 @@ static int astfb_create_object(struct ast_fbdev *afbdev,
static int astfb_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
- struct ast_fbdev *afbdev = (struct ast_fbdev *)helper;
+ struct ast_fbdev *afbdev =
+ container_of(helper, struct ast_fbdev, helper);
struct drm_device *dev = afbdev->helper.dev;
struct drm_mode_fb_cmd2 mode_cmd;
struct drm_framebuffer *fb;
@@ -287,7 +288,7 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
*blue = ast_crtc->lut_b[regno] << 8;
}
-static struct drm_fb_helper_funcs ast_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs ast_fb_helper_funcs = {
.gamma_set = ast_fb_gamma_set,
.gamma_get = ast_fb_gamma_get,
.fb_probe = astfb_create,
@@ -328,8 +329,10 @@ int ast_fbdev_init(struct drm_device *dev)
return -ENOMEM;
ast->fbdev = afbdev;
- afbdev->helper.funcs = &ast_fb_helper_funcs;
spin_lock_init(&afbdev->dirty_lock);
+
+ drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs);
+
ret = drm_fb_helper_init(dev, &afbdev->helper,
1, 1);
if (ret) {
diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
index a2cc6be97983..035dacc93382 100644
--- a/drivers/gpu/drm/ast/ast_main.c
+++ b/drivers/gpu/drm/ast/ast_main.c
@@ -63,10 +63,11 @@ uint8_t ast_get_index_reg_mask(struct ast_private *ast,
}
-static int ast_detect_chip(struct drm_device *dev)
+static int ast_detect_chip(struct drm_device *dev, bool *need_post)
{
struct ast_private *ast = dev->dev_private;
uint32_t data, jreg;
+ ast_open_key(ast);
if (dev->pdev->device == PCI_CHIP_AST1180) {
ast->chip = AST1100;
@@ -104,11 +105,26 @@ static int ast_detect_chip(struct drm_device *dev)
}
ast->vga2_clone = false;
} else {
- ast->chip = 2000;
+ ast->chip = AST2000;
DRM_INFO("AST 2000 detected\n");
}
}
+ /*
+ * If VGA isn't enabled, we need to enable now or subsequent
+ * access to the scratch registers will fail. We also inform
+ * our caller that it needs to POST the chip
+ * (Assumption: VGA not enabled -> need to POST)
+ */
+ if (!ast_is_vga_enabled(dev)) {
+ ast_enable_vga(dev);
+ ast_enable_mmio(dev);
+ DRM_INFO("VGA not enabled on entry, requesting chip POST\n");
+ *need_post = true;
+ } else
+ *need_post = false;
+
+ /* Check if we support wide screen */
switch (ast->chip) {
case AST1180:
ast->support_wide_screen = true;
@@ -124,6 +140,7 @@ static int ast_detect_chip(struct drm_device *dev)
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);
@@ -136,11 +153,29 @@ static int ast_detect_chip(struct drm_device *dev)
break;
}
+ /* Check 3rd Tx option (digital output afaik) */
ast->tx_chip_type = AST_TX_NONE;
- jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xff);
- if (jreg & 0x80)
- ast->tx_chip_type = AST_TX_SIL164;
+
+ /*
+ * VGACRA3 Enhanced Color Mode Register, check if DVO is already
+ * enabled, in that case, assume we have a SIL164 TMDS transmitter
+ *
+ * Don't make that assumption if we the chip wasn't enabled and
+ * is at power-on reset, otherwise we'll incorrectly "detect" a
+ * SIL164 when there is none.
+ */
+ if (!*need_post) {
+ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xff);
+ if (jreg & 0x80)
+ ast->tx_chip_type = AST_TX_SIL164;
+ }
+
if ((ast->chip == AST2300) || (ast->chip == AST2400)) {
+ /*
+ * On AST2300 and 2400, look the configuration set by the SoC in
+ * the SOC scratch register #1 bits 11:8 (interestingly marked
+ * as "reserved" in the spec)
+ */
jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff);
switch (jreg) {
case 0x04:
@@ -161,6 +196,17 @@ static int ast_detect_chip(struct drm_device *dev)
}
}
+ /* Print stuff for diagnostic purposes */
+ switch(ast->tx_chip_type) {
+ case AST_TX_SIL164:
+ DRM_INFO("Using Sil164 TMDS transmitter\n");
+ break;
+ case AST_TX_DP501:
+ DRM_INFO("Using DP501 DisplayPort transmitter\n");
+ break;
+ default:
+ DRM_INFO("Analog VGA only\n");
+ }
return 0;
}
@@ -345,6 +391,7 @@ static u32 ast_get_vram_info(struct drm_device *dev)
int ast_driver_load(struct drm_device *dev, unsigned long flags)
{
struct ast_private *ast;
+ bool need_post;
int ret = 0;
ast = kzalloc(sizeof(struct ast_private), GFP_KERNEL);
@@ -359,13 +406,27 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
ret = -EIO;
goto out_free;
}
- ast->ioregs = pci_iomap(dev->pdev, 2, 0);
+
+ /*
+ * If we don't have IO space at all, use MMIO now and
+ * assume the chip has MMIO enabled by default (rev 0x20
+ * and higher).
+ */
+ if (!(pci_resource_flags(dev->pdev, 2) & IORESOURCE_IO)) {
+ DRM_INFO("platform has no IO space, trying MMIO\n");
+ ast->ioregs = ast->regs + AST_IO_MM_OFFSET;
+ }
+
+ /* "map" IO regs if the above hasn't done so already */
if (!ast->ioregs) {
- ret = -EIO;
- goto out_free;
+ ast->ioregs = pci_iomap(dev->pdev, 2, 0);
+ if (!ast->ioregs) {
+ ret = -EIO;
+ goto out_free;
+ }
}
- ast_detect_chip(dev);
+ ast_detect_chip(dev, &need_post);
if (ast->chip != AST1180) {
ast_get_dram_info(dev);
@@ -373,6 +434,9 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size);
}
+ if (need_post)
+ ast_post_gpu(dev);
+
ret = ast_mm_init(ast);
if (ret)
goto out_free;
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index 114aee941d46..9dc0fd5c1ea4 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -80,6 +80,8 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo
struct ast_private *ast = crtc->dev->dev_private;
u32 refresh_rate_index = 0, mode_id, color_index, refresh_rate;
u32 hborder, vborder;
+ bool check_sync;
+ struct ast_vbios_enhtable *best = NULL;
switch (crtc->primary->fb->bits_per_pixel) {
case 8:
@@ -141,14 +143,34 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo
}
refresh_rate = drm_mode_vrefresh(mode);
- while (vbios_mode->enh_table->refresh_rate < refresh_rate) {
- vbios_mode->enh_table++;
- if ((vbios_mode->enh_table->refresh_rate > refresh_rate) ||
- (vbios_mode->enh_table->refresh_rate == 0xff)) {
- vbios_mode->enh_table--;
- break;
+ check_sync = vbios_mode->enh_table->flags & WideScreenMode;
+ do {
+ struct ast_vbios_enhtable *loop = vbios_mode->enh_table;
+
+ while (loop->refresh_rate != 0xff) {
+ if ((check_sync) &&
+ (((mode->flags & DRM_MODE_FLAG_NVSYNC) &&
+ (loop->flags & PVSync)) ||
+ ((mode->flags & DRM_MODE_FLAG_PVSYNC) &&
+ (loop->flags & NVSync)) ||
+ ((mode->flags & DRM_MODE_FLAG_NHSYNC) &&
+ (loop->flags & PHSync)) ||
+ ((mode->flags & DRM_MODE_FLAG_PHSYNC) &&
+ (loop->flags & NHSync)))) {
+ loop++;
+ continue;
+ }
+ if (loop->refresh_rate <= refresh_rate
+ && (!best || loop->refresh_rate > best->refresh_rate))
+ best = loop;
+ loop++;
}
- }
+ if (best || !check_sync)
+ break;
+ check_sync = 0;
+ } while (1);
+ if (best)
+ vbios_mode->enh_table = best;
hborder = (vbios_mode->enh_table->flags & HBorder) ? 8 : 0;
vborder = (vbios_mode->enh_table->flags & VBorder) ? 8 : 0;
@@ -419,8 +441,10 @@ static void ast_set_sync_reg(struct drm_device *dev, struct drm_display_mode *mo
struct ast_private *ast = dev->dev_private;
u8 jreg;
- jreg = ast_io_read8(ast, AST_IO_MISC_PORT_READ);
- jreg |= (vbios_mode->enh_table->flags & SyncNN);
+ jreg = ast_io_read8(ast, AST_IO_MISC_PORT_READ);
+ jreg &= ~0xC0;
+ if (vbios_mode->enh_table->flags & NVSync) jreg |= 0x80;
+ if (vbios_mode->enh_table->flags & NHSync) jreg |= 0x40;
ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, jreg);
}
@@ -667,17 +691,9 @@ static void ast_encoder_destroy(struct drm_encoder *encoder)
static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connector)
{
int enc_id = connector->encoder_ids[0];
- struct drm_mode_object *obj;
- struct drm_encoder *encoder;
-
/* pick the encoder ids */
- if (enc_id) {
- obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
- if (!obj)
- return NULL;
- encoder = obj_to_encoder(obj);
- return encoder;
- }
+ if (enc_id)
+ return drm_encoder_find(connector->dev, enc_id);
return NULL;
}
@@ -829,7 +845,7 @@ static void ast_connector_destroy(struct drm_connector *connector)
{
struct ast_connector *ast_connector = to_ast_connector(connector);
ast_i2c_destroy(ast_connector->i2c);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -871,7 +887,7 @@ static int ast_connector_init(struct drm_device *dev)
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
@@ -1088,8 +1104,8 @@ static u32 copy_cursor_image(u8 *src, u8 *dst, int width, int height)
srcdata32[1].ul = *((u32 *)(srcxor + 4)) & 0xf0f0f0f0;
data32.b[0] = srcdata32[0].b[1] | (srcdata32[0].b[0] >> 4);
data32.b[1] = srcdata32[0].b[3] | (srcdata32[0].b[2] >> 4);
- data32.b[2] = srcdata32[0].b[1] | (srcdata32[1].b[0] >> 4);
- data32.b[3] = srcdata32[0].b[3] | (srcdata32[1].b[2] >> 4);
+ data32.b[2] = srcdata32[1].b[1] | (srcdata32[1].b[0] >> 4);
+ data32.b[3] = srcdata32[1].b[3] | (srcdata32[1].b[2] >> 4);
writel(data32.ul, dstxor);
csum += data32.ul;
diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c
index 38d437f3a267..810c51d92b99 100644
--- a/drivers/gpu/drm/ast/ast_post.c
+++ b/drivers/gpu/drm/ast/ast_post.c
@@ -33,18 +33,23 @@
static void ast_init_dram_2300(struct drm_device *dev);
-static void
-ast_enable_vga(struct drm_device *dev)
+void ast_enable_vga(struct drm_device *dev)
+{
+ struct ast_private *ast = dev->dev_private;
+
+ ast_io_write8(ast, AST_IO_VGA_ENABLE_PORT, 0x01);
+ ast_io_write8(ast, AST_IO_MISC_PORT_WRITE, 0x01);
+}
+
+void ast_enable_mmio(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
- ast_io_write8(ast, 0x43, 0x01);
- ast_io_write8(ast, 0x42, 0x01);
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa1, 0xff, 0x04);
}
-#if 0 /* will use later */
-static bool
-ast_is_vga_enabled(struct drm_device *dev)
+
+bool ast_is_vga_enabled(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
u8 ch;
@@ -52,7 +57,7 @@ ast_is_vga_enabled(struct drm_device *dev)
if (ast->chip == AST1180) {
/* TODO 1180 */
} else {
- ch = ast_io_read8(ast, 0x43);
+ ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT);
if (ch) {
ast_open_key(ast);
ch = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff);
@@ -61,7 +66,6 @@ ast_is_vga_enabled(struct drm_device *dev)
}
return 0;
}
-#endif
static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff };
static const u8 extreginfo_ast2300a0[] = { 0x0f, 0x04, 0x1c, 0xff };
@@ -371,6 +375,7 @@ void ast_post_gpu(struct drm_device *dev)
pci_write_config_dword(ast->dev->pdev, 0x04, reg);
ast_enable_vga(dev);
+ ast_enable_mmio(dev);
ast_open_key(ast);
ast_set_def_ext_reg(dev);
diff --git a/drivers/gpu/drm/ast/ast_tables.h b/drivers/gpu/drm/ast/ast_tables.h
index 4c761dcea972..3608d5aa7451 100644
--- a/drivers/gpu/drm/ast/ast_tables.h
+++ b/drivers/gpu/drm/ast/ast_tables.h
@@ -35,14 +35,18 @@
#define HalfDCLK 0x00000002
#define DoubleScanMode 0x00000004
#define LineCompareOff 0x00000008
-#define SyncPP 0x00000000
-#define SyncPN 0x00000040
-#define SyncNP 0x00000080
-#define SyncNN 0x000000C0
#define HBorder 0x00000020
#define VBorder 0x00000010
#define WideScreenMode 0x00000100
#define NewModeInfo 0x00000200
+#define NHSync 0x00000400
+#define PHSync 0x00000800
+#define NVSync 0x00001000
+#define PVSync 0x00002000
+#define SyncPP (PVSync | PHSync)
+#define SyncPN (PVSync | NHSync)
+#define SyncNP (NVSync | PHSync)
+#define SyncNN (NVSync | NHSync)
/* DCLK Index */
#define VCLK25_175 0x00
@@ -72,6 +76,7 @@
#define VCLK119 0x17
#define VCLK85_5 0x18
#define VCLK97_75 0x19
+#define VCLK118_25 0x1A
static struct ast_vbios_dclk_info dclk_table[] = {
{0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */
@@ -99,6 +104,8 @@ static struct ast_vbios_dclk_info dclk_table[] = {
{0x25, 0x65, 0x80}, /* 16: VCLK88.75 */
{0x77, 0x58, 0x80}, /* 17: VCLK119 */
{0x32, 0x67, 0x80}, /* 18: VCLK85_5 */
+ {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */
+ {0x3b, 0x2c, 0x81}, /* 1A: VCLK118_25 */
};
static struct ast_vbios_stdtable vbios_stdtable[] = {
@@ -245,8 +252,10 @@ static struct ast_vbios_enhtable res_1360x768[] = {
static struct ast_vbios_enhtable res_1600x900[] = {
{1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* 60Hz CVT RB */
(SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x3A },
- {1760, 1600, 48, 32, 926, 900, 3, 5, VCLK97_75, /* end */
- (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x3A }
+ {2112, 1600, 88,168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x3A },
+ {2112, 1600, 88,168, 934, 900, 3, 5, VCLK118_25, /* 60Hz CVT */
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x3A },
};
static struct ast_vbios_enhtable res_1920x1080[] = {
@@ -260,11 +269,11 @@ static struct ast_vbios_enhtable res_1920x1080[] = {
/* 16:10 */
static struct ast_vbios_enhtable res_1280x800[] = {
{1440, 1280, 48, 32, 823, 800, 3, 6, VCLK71, /* 60Hz RB */
- (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 35 },
+ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x35 },
{1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */
- (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x35 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x35 },
{1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */
- (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x35 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x35 },
};
@@ -272,24 +281,24 @@ static struct ast_vbios_enhtable res_1440x900[] = {
{1600, 1440, 48, 32, 926, 900, 3, 6, VCLK88_75, /* 60Hz RB */
(SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x36 },
{1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */
- (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x36 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x36 },
{1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */
- (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x36 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x36 },
};
static struct ast_vbios_enhtable res_1680x1050[] = {
{1840, 1680, 48, 32, 1080, 1050, 3, 6, VCLK119, /* 60Hz RB */
(SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x37 },
{2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */
- (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x37 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 2, 0x37 },
{2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */
- (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x37 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 2, 0x37 },
};
static struct ast_vbios_enhtable res_1920x1200[] = {
- {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
+ {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB*/
(SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x34 },
- {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
+ {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz RB */
(SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x34 },
};
diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c
index b8246227bab0..08f82eae6939 100644
--- a/drivers/gpu/drm/ast/ast_ttm.c
+++ b/drivers/gpu/drm/ast/ast_ttm.c
@@ -293,18 +293,22 @@ void ast_mm_fini(struct ast_private *ast)
void ast_ttm_placement(struct ast_bo *bo, int domain)
{
u32 c = 0;
- bo->placement.fpfn = 0;
- bo->placement.lpfn = 0;
+ unsigned i;
+
bo->placement.placement = bo->placements;
bo->placement.busy_placement = bo->placements;
if (domain & TTM_PL_FLAG_VRAM)
- bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
+ bo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
if (domain & TTM_PL_FLAG_SYSTEM)
- bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ bo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM;
if (!c)
- bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ bo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM;
bo->placement.num_placement = c;
bo->placement.num_busy_placement = c;
+ for (i = 0; i < c; ++i) {
+ bo->placements[i].fpfn = 0;
+ bo->placements[i].lpfn = 0;
+ }
}
int ast_bo_create(struct drm_device *dev, int size, int align,
@@ -335,7 +339,7 @@ int ast_bo_create(struct drm_device *dev, int size, int align,
ret = ttm_bo_init(&ast->ttm.bdev, &astbo->bo, size,
ttm_bo_type_device, &astbo->placement,
align >> PAGE_SHIFT, false, NULL, acc_size,
- NULL, ast_bo_ttm_destroy);
+ NULL, NULL, ast_bo_ttm_destroy);
if (ret)
return ret;
@@ -360,7 +364,7 @@ int ast_bo_pin(struct ast_bo *bo, u32 pl_flag, u64 *gpu_addr)
ast_ttm_placement(bo, pl_flag);
for (i = 0; i < bo->placement.num_placement; i++)
- bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+ bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
if (ret)
return ret;
@@ -383,7 +387,7 @@ int ast_bo_unpin(struct ast_bo *bo)
return 0;
for (i = 0; i < bo->placement.num_placement ; i++)
- bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
+ bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
if (ret)
return ret;
@@ -407,7 +411,7 @@ int ast_bo_push_sysram(struct ast_bo *bo)
ast_ttm_placement(bo, TTM_PL_FLAG_SYSTEM);
for (i = 0; i < bo->placement.num_placement ; i++)
- bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+ bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
if (ret) {
@@ -423,7 +427,7 @@ int ast_mmap(struct file *filp, struct vm_area_struct *vma)
struct ast_private *ast;
if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
- return drm_mmap(filp, vma);
+ return -EINVAL;
file_priv = filp->private_data;
ast = file_priv->minor->dev->dev_private;
diff --git a/drivers/gpu/drm/ati_pcigart.c b/drivers/gpu/drm/ati_pcigart.c
index c399dea27a3b..6c4d4b6eba80 100644
--- a/drivers/gpu/drm/ati_pcigart.c
+++ b/drivers/gpu/drm/ati_pcigart.c
@@ -34,6 +34,8 @@
#include <linux/export.h>
#include <drm/drmP.h>
+#include <drm/ati_pcigart.h>
+
# define ATI_PCIGART_PAGE_SIZE 4096 /**< PCI GART page size */
static int drm_ati_alloc_pcigart_table(struct drm_device *dev,
diff --git a/drivers/gpu/drm/bochs/bochs.h b/drivers/gpu/drm/bochs/bochs.h
index 7eb52dd44b01..71f2687fc3cc 100644
--- a/drivers/gpu/drm/bochs/bochs.h
+++ b/drivers/gpu/drm/bochs/bochs.h
@@ -7,6 +7,8 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem.h>
+
#include <ttm/ttm_bo_driver.h>
#include <ttm/ttm_page_alloc.h>
@@ -99,7 +101,7 @@ struct bochs_bo {
struct ttm_placement placement;
struct ttm_bo_kmap_obj kmap;
struct drm_gem_object gem;
- u32 placements[3];
+ struct ttm_place placements[3];
int pin_count;
};
diff --git a/drivers/gpu/drm/bochs/bochs_drv.c b/drivers/gpu/drm/bochs/bochs_drv.c
index 9c13df29fd20..98837bde2d25 100644
--- a/drivers/gpu/drm/bochs/bochs_drv.c
+++ b/drivers/gpu/drm/bochs/bochs_drv.c
@@ -82,6 +82,7 @@ static struct drm_driver bochs_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET,
.load = bochs_load,
.unload = bochs_unload,
+ .set_busid = drm_pci_set_busid,
.fops = &bochs_fops,
.name = "bochs-drm",
.desc = "bochs dispi vga interface (qemu stdvga)",
@@ -97,6 +98,7 @@ static struct drm_driver bochs_driver = {
/* ---------------------------------------------------------------------- */
/* pm interface */
+#ifdef CONFIG_PM_SLEEP
static int bochs_pm_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -131,6 +133,7 @@ static int bochs_pm_resume(struct device *dev)
drm_kms_helper_poll_enable(drm_dev);
return 0;
}
+#endif
static const struct dev_pm_ops bochs_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend,
@@ -175,7 +178,7 @@ static void bochs_pci_remove(struct pci_dev *pdev)
drm_put_dev(dev);
}
-static DEFINE_PCI_DEVICE_TABLE(bochs_pci_tbl) = {
+static const struct pci_device_id bochs_pci_tbl[] = {
{
.vendor = 0x1234,
.device = 0x1111,
diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
index 561b84474122..fe95d31cd110 100644
--- a/drivers/gpu/drm/bochs/bochs_fbdev.c
+++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
@@ -72,7 +72,7 @@ static int bochsfb_create(struct drm_fb_helper *helper,
bo = gem_to_bochs_bo(gobj);
- ret = ttm_bo_reserve(&bo->bo, true, false, false, 0);
+ ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL);
if (ret)
return ret;
@@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
*blue = regno;
}
-static struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
.gamma_set = bochs_fb_gamma_set,
.gamma_get = bochs_fb_gamma_get,
.fb_probe = bochsfb_create,
@@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs)
{
int ret;
- bochs->fb.helper.funcs = &bochs_fb_helper_funcs;
+ drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper,
+ &bochs_fb_helper_funcs);
ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
1, 1);
diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c
index dcf2e55f4ae9..6b7efcf363d6 100644
--- a/drivers/gpu/drm/bochs/bochs_kms.c
+++ b/drivers/gpu/drm/bochs/bochs_kms.c
@@ -53,7 +53,7 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
if (old_fb) {
bochs_fb = to_bochs_framebuffer(old_fb);
bo = gem_to_bochs_bo(bochs_fb->obj);
- ret = ttm_bo_reserve(&bo->bo, true, false, false, 0);
+ ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL);
if (ret) {
DRM_ERROR("failed to reserve old_fb bo\n");
} else {
@@ -67,7 +67,7 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
bochs_fb = to_bochs_framebuffer(crtc->primary->fb);
bo = gem_to_bochs_bo(bochs_fb->obj);
- ret = ttm_bo_reserve(&bo->bo, true, false, false, 0);
+ ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL);
if (ret)
return ret;
@@ -216,18 +216,9 @@ static struct drm_encoder *
bochs_connector_best_encoder(struct drm_connector *connector)
{
int enc_id = connector->encoder_ids[0];
- struct drm_mode_object *obj;
- struct drm_encoder *encoder;
-
/* pick the encoder ids */
- if (enc_id) {
- obj = drm_mode_object_find(connector->dev, enc_id,
- DRM_MODE_OBJECT_ENCODER);
- if (!obj)
- return NULL;
- encoder = obj_to_encoder(obj);
- return encoder;
- }
+ if (enc_id)
+ return drm_encoder_find(connector->dev, enc_id);
return NULL;
}
@@ -259,6 +250,7 @@ static void bochs_connector_init(struct drm_device *dev)
DRM_MODE_CONNECTOR_VIRTUAL);
drm_connector_helper_add(connector,
&bochs_connector_connector_helper_funcs);
+ drm_connector_register(connector);
}
diff --git a/drivers/gpu/drm/bochs/bochs_mm.c b/drivers/gpu/drm/bochs/bochs_mm.c
index b9a695d92792..66286ff518d4 100644
--- a/drivers/gpu/drm/bochs/bochs_mm.c
+++ b/drivers/gpu/drm/bochs/bochs_mm.c
@@ -257,20 +257,26 @@ void bochs_mm_fini(struct bochs_device *bochs)
static void bochs_ttm_placement(struct bochs_bo *bo, int domain)
{
+ unsigned i;
u32 c = 0;
- bo->placement.fpfn = 0;
- bo->placement.lpfn = 0;
bo->placement.placement = bo->placements;
bo->placement.busy_placement = bo->placements;
if (domain & TTM_PL_FLAG_VRAM) {
- bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED
+ bo->placements[c++].flags = TTM_PL_FLAG_WC
+ | TTM_PL_FLAG_UNCACHED
| TTM_PL_FLAG_VRAM;
}
if (domain & TTM_PL_FLAG_SYSTEM) {
- bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ bo->placements[c++].flags = TTM_PL_MASK_CACHING
+ | TTM_PL_FLAG_SYSTEM;
}
if (!c) {
- bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ bo->placements[c++].flags = TTM_PL_MASK_CACHING
+ | TTM_PL_FLAG_SYSTEM;
+ }
+ for (i = 0; i < c; ++i) {
+ bo->placements[i].fpfn = 0;
+ bo->placements[i].lpfn = 0;
}
bo->placement.num_placement = c;
bo->placement.num_busy_placement = c;
@@ -294,7 +300,7 @@ int bochs_bo_pin(struct bochs_bo *bo, u32 pl_flag, u64 *gpu_addr)
bochs_ttm_placement(bo, pl_flag);
for (i = 0; i < bo->placement.num_placement; i++)
- bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+ bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
if (ret)
return ret;
@@ -319,7 +325,7 @@ int bochs_bo_unpin(struct bochs_bo *bo)
return 0;
for (i = 0; i < bo->placement.num_placement; i++)
- bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
+ bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
if (ret)
return ret;
@@ -333,7 +339,7 @@ int bochs_mmap(struct file *filp, struct vm_area_struct *vma)
struct bochs_device *bochs;
if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
- return drm_mmap(filp, vma);
+ return -EINVAL;
file_priv = filp->private_data;
bochs = file_priv->minor->dev->dev_private;
@@ -371,7 +377,7 @@ static int bochs_bo_create(struct drm_device *dev, int size, int align,
ret = ttm_bo_init(&bochs->ttm.bdev, &bochsbo->bo, size,
ttm_bo_type_device, &bochsbo->placement,
align >> PAGE_SHIFT, false, NULL, acc_size,
- NULL, bochs_bo_ttm_destroy);
+ NULL, NULL, bochs_bo_ttm_destroy);
if (ret)
return ret;
@@ -387,7 +393,7 @@ int bochs_gem_create(struct drm_device *dev, u32 size, bool iskernel,
*obj = NULL;
- size = ALIGN(size, PAGE_SIZE);
+ size = PAGE_ALIGN(size);
if (size == 0)
return -EINVAL;
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
index 98fd17ae4916..d466696ed5e8 100644
--- a/drivers/gpu/drm/bridge/ptn3460.c
+++ b/drivers/gpu/drm/bridge/ptn3460.c
@@ -328,7 +328,7 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
}
drm_connector_helper_add(&ptn_bridge->connector,
&ptn3460_connector_helper_funcs);
- drm_sysfs_connector_add(&ptn_bridge->connector);
+ drm_connector_register(&ptn_bridge->connector);
drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
return 0;
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c
index 08ce520f61a5..c2a1cba1e984 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.c
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.c
@@ -29,9 +29,11 @@ module_param_named(modeset, cirrus_modeset, int, 0400);
static struct drm_driver driver;
/* only bind to the cirrus chip in qemu */
-static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
+static const struct pci_device_id pciidlist[] = {
{ PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, 0x1af4, 0x1100, 0,
0, 0 },
+ { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, PCI_VENDOR_ID_XEN,
+ 0x0001, 0, 0, 0 },
{0,}
};
@@ -76,6 +78,7 @@ static void cirrus_pci_remove(struct pci_dev *pdev)
drm_put_dev(dev);
}
+#ifdef CONFIG_PM_SLEEP
static int cirrus_pm_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -110,6 +113,7 @@ static int cirrus_pm_resume(struct device *dev)
drm_kms_helper_poll_enable(drm_dev);
return 0;
}
+#endif
static const struct file_operations cirrus_driver_fops = {
.owner = THIS_MODULE,
@@ -126,6 +130,7 @@ static struct drm_driver driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM,
.load = cirrus_driver_load,
.unload = cirrus_driver_unload,
+ .set_busid = drm_pci_set_busid,
.fops = &cirrus_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h
index 117d3eca5e37..d44e69daa239 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.h
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.h
@@ -21,6 +21,8 @@
#include <drm/ttm/ttm_memory.h>
#include <drm/ttm/ttm_module.h>
+#include <drm/drm_gem.h>
+
#define DRIVER_AUTHOR "Matthew Garrett"
#define DRIVER_NAME "cirrus"
@@ -163,7 +165,7 @@ struct cirrus_bo {
struct ttm_placement placement;
struct ttm_bo_kmap_obj kmap;
struct drm_gem_object gem;
- u32 placements[3];
+ struct ttm_place placements[3];
int pin_count;
};
#define gem_to_cirrus_bo(gobj) container_of((gobj), struct cirrus_bo, gem)
@@ -241,7 +243,7 @@ static inline int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait)
{
int ret;
- ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+ ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, NULL);
if (ret) {
if (ret != -ERESTARTSYS && ret != -EBUSY)
DRM_ERROR("reserve failed %p\n", bo);
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
index 32bbba0a787b..d231b1c317af 100644
--- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
@@ -160,7 +160,8 @@ static int cirrusfb_create_object(struct cirrus_fbdev *afbdev,
static int cirrusfb_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
- struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper;
+ struct cirrus_fbdev *gfbdev =
+ container_of(helper, struct cirrus_fbdev, helper);
struct drm_device *dev = gfbdev->helper.dev;
struct cirrus_device *cdev = gfbdev->helper.dev->dev_private;
struct fb_info *info;
@@ -288,7 +289,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,
return 0;
}
-static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
.gamma_set = cirrus_crtc_fb_gamma_set,
.gamma_get = cirrus_crtc_fb_gamma_get,
.fb_probe = cirrusfb_create,
@@ -306,9 +307,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
return -ENOMEM;
cdev->mode_info.gfbdev = gfbdev;
- gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
spin_lock_init(&gfbdev->dirty_lock);
+ drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
+ &cirrus_fb_helper_funcs);
+
ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
if (ret) {
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
index 49332c5fe35b..c7c5a9d91fa0 100644
--- a/drivers/gpu/drm/cirrus/cirrus_mode.c
+++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
@@ -509,19 +509,9 @@ static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector
*connector)
{
int enc_id = connector->encoder_ids[0];
- struct drm_mode_object *obj;
- struct drm_encoder *encoder;
-
/* pick the encoder ids */
- if (enc_id) {
- obj =
- drm_mode_object_find(connector->dev, enc_id,
- DRM_MODE_OBJECT_ENCODER);
- if (!obj)
- return NULL;
- encoder = obj_to_encoder(obj);
- return encoder;
- }
+ if (enc_id)
+ return drm_encoder_find(connector->dev, enc_id);
return NULL;
}
@@ -565,6 +555,7 @@ static struct drm_connector *cirrus_vga_init(struct drm_device *dev)
drm_connector_helper_add(connector, &cirrus_vga_connector_helper_funcs);
+ drm_connector_register(connector);
return connector;
}
diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c
index 92e6b7786097..dfffd528517a 100644
--- a/drivers/gpu/drm/cirrus/cirrus_ttm.c
+++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c
@@ -298,18 +298,21 @@ void cirrus_mm_fini(struct cirrus_device *cirrus)
void cirrus_ttm_placement(struct cirrus_bo *bo, int domain)
{
u32 c = 0;
- bo->placement.fpfn = 0;
- bo->placement.lpfn = 0;
+ unsigned i;
bo->placement.placement = bo->placements;
bo->placement.busy_placement = bo->placements;
if (domain & TTM_PL_FLAG_VRAM)
- bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
+ bo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
if (domain & TTM_PL_FLAG_SYSTEM)
- bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
if (!c)
- bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
bo->placement.num_placement = c;
bo->placement.num_busy_placement = c;
+ for (i = 0; i < c; ++i) {
+ bo->placements[i].fpfn = 0;
+ bo->placements[i].lpfn = 0;
+ }
}
int cirrus_bo_create(struct drm_device *dev, int size, int align,
@@ -340,7 +343,7 @@ int cirrus_bo_create(struct drm_device *dev, int size, int align,
ret = ttm_bo_init(&cirrus->ttm.bdev, &cirrusbo->bo, size,
ttm_bo_type_device, &cirrusbo->placement,
align >> PAGE_SHIFT, false, NULL, acc_size,
- NULL, cirrus_bo_ttm_destroy);
+ NULL, NULL, cirrus_bo_ttm_destroy);
if (ret)
return ret;
@@ -365,7 +368,7 @@ int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr)
cirrus_ttm_placement(bo, pl_flag);
for (i = 0; i < bo->placement.num_placement; i++)
- bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+ bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
if (ret)
return ret;
@@ -392,7 +395,7 @@ int cirrus_bo_push_sysram(struct cirrus_bo *bo)
cirrus_ttm_placement(bo, TTM_PL_FLAG_SYSTEM);
for (i = 0; i < bo->placement.num_placement ; i++)
- bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+ bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
if (ret) {
@@ -408,7 +411,7 @@ int cirrus_mmap(struct file *filp, struct vm_area_struct *vma)
struct cirrus_device *cirrus;
if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
- return drm_mmap(filp, vma);
+ return -EINVAL;
file_priv = filp->private_data;
cirrus = file_priv->minor->dev->dev_private;
diff --git a/drivers/gpu/drm/drm_agpsupport.c b/drivers/gpu/drm/drm_agpsupport.c
index dde205cef384..4b2b4aa5033b 100644
--- a/drivers/gpu/drm/drm_agpsupport.c
+++ b/drivers/gpu/drm/drm_agpsupport.c
@@ -34,6 +34,7 @@
#include <drm/drmP.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include "drm_legacy.h"
#if __OS_HAS_AGP
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
index 3cedae12b3c1..fc8e8aaa34fb 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -34,6 +34,13 @@
*/
#include <drm/drmP.h>
+#include "drm_internal.h"
+
+struct drm_magic_entry {
+ struct list_head head;
+ struct drm_hash_item hash_item;
+ struct drm_file *priv;
+};
/**
* Find the file with the given magic number.
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index 68175b54504b..569064a00693 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -1,18 +1,13 @@
-/**
- * \file drm_bufs.c
- * Generic buffer template
- *
- * \author Rickard E. (Rik) Faith <faith@valinux.com>
- * \author Gareth Hughes <gareth@valinux.com>
- */
-
/*
- * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com
+ * Legacy: Generic DRM Buffer Management
*
* Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Author: Gareth Hughes <gareth@valinux.com>
+ *
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
@@ -39,6 +34,7 @@
#include <linux/export.h>
#include <asm/shmparam.h>
#include <drm/drmP.h>
+#include "drm_legacy.h"
static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
struct drm_local_map *map)
@@ -365,9 +361,9 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
return 0;
}
-int drm_addmap(struct drm_device * dev, resource_size_t offset,
- unsigned int size, enum drm_map_type type,
- enum drm_map_flags flags, struct drm_local_map ** map_ptr)
+int drm_legacy_addmap(struct drm_device * dev, resource_size_t offset,
+ unsigned int size, enum drm_map_type type,
+ enum drm_map_flags flags, struct drm_local_map **map_ptr)
{
struct drm_map_list *list;
int rc;
@@ -377,8 +373,7 @@ int drm_addmap(struct drm_device * dev, resource_size_t offset,
*map_ptr = list->map;
return rc;
}
-
-EXPORT_SYMBOL(drm_addmap);
+EXPORT_SYMBOL(drm_legacy_addmap);
/**
* Ioctl to specify a range of memory that is available for mapping by a
@@ -391,8 +386,8 @@ EXPORT_SYMBOL(drm_addmap);
* \return zero on success or a negative value on error.
*
*/
-int drm_addmap_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_addmap_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_map *map = data;
struct drm_map_list *maplist;
@@ -429,9 +424,9 @@ int drm_addmap_ioctl(struct drm_device *dev, void *data,
* its being used, and free any associate resource (such as MTRR's) if it's not
* being on use.
*
- * \sa drm_addmap
+ * \sa drm_legacy_addmap
*/
-int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map)
+int drm_legacy_rmmap_locked(struct drm_device *dev, struct drm_local_map *map)
{
struct drm_map_list *r_list = NULL, *list_t;
drm_dma_handle_t dmah;
@@ -478,26 +473,26 @@ int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map)
dmah.vaddr = map->handle;
dmah.busaddr = map->offset;
dmah.size = map->size;
- __drm_pci_free(dev, &dmah);
+ __drm_legacy_pci_free(dev, &dmah);
break;
}
kfree(map);
return 0;
}
-EXPORT_SYMBOL(drm_rmmap_locked);
+EXPORT_SYMBOL(drm_legacy_rmmap_locked);
-int drm_rmmap(struct drm_device *dev, struct drm_local_map *map)
+int drm_legacy_rmmap(struct drm_device *dev, struct drm_local_map *map)
{
int ret;
mutex_lock(&dev->struct_mutex);
- ret = drm_rmmap_locked(dev, map);
+ ret = drm_legacy_rmmap_locked(dev, map);
mutex_unlock(&dev->struct_mutex);
return ret;
}
-EXPORT_SYMBOL(drm_rmmap);
+EXPORT_SYMBOL(drm_legacy_rmmap);
/* The rmmap ioctl appears to be unnecessary. All mappings are torn down on
* the last close of the device, and this is necessary for cleanup when things
@@ -514,8 +509,8 @@ EXPORT_SYMBOL(drm_rmmap);
* \param arg pointer to a struct drm_map structure.
* \return zero on success or a negative value on error.
*/
-int drm_rmmap_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_rmmap_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_map *request = data;
struct drm_local_map *map = NULL;
@@ -546,7 +541,7 @@ int drm_rmmap_ioctl(struct drm_device *dev, void *data,
return 0;
}
- ret = drm_rmmap_locked(dev, map);
+ ret = drm_legacy_rmmap_locked(dev, map);
mutex_unlock(&dev->struct_mutex);
@@ -599,7 +594,8 @@ static void drm_cleanup_buf_error(struct drm_device * dev,
* reallocates the buffer list of the same size order to accommodate the new
* buffers.
*/
-int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
+int drm_legacy_addbufs_agp(struct drm_device *dev,
+ struct drm_buf_desc *request)
{
struct drm_device_dma *dma = dev->dma;
struct drm_buf_entry *entry;
@@ -759,10 +755,11 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
atomic_dec(&dev->buf_alloc);
return 0;
}
-EXPORT_SYMBOL(drm_addbufs_agp);
+EXPORT_SYMBOL(drm_legacy_addbufs_agp);
#endif /* __OS_HAS_AGP */
-int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
+int drm_legacy_addbufs_pci(struct drm_device *dev,
+ struct drm_buf_desc *request)
{
struct drm_device_dma *dma = dev->dma;
int count;
@@ -964,9 +961,10 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
return 0;
}
-EXPORT_SYMBOL(drm_addbufs_pci);
+EXPORT_SYMBOL(drm_legacy_addbufs_pci);
-static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request)
+static int drm_legacy_addbufs_sg(struct drm_device *dev,
+ struct drm_buf_desc *request)
{
struct drm_device_dma *dma = dev->dma;
struct drm_buf_entry *entry;
@@ -1135,8 +1133,8 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request
* addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent
* PCI memory respectively.
*/
-int drm_addbufs(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_addbufs(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_buf_desc *request = data;
int ret;
@@ -1149,15 +1147,15 @@ int drm_addbufs(struct drm_device *dev, void *data,
#if __OS_HAS_AGP
if (request->flags & _DRM_AGP_BUFFER)
- ret = drm_addbufs_agp(dev, request);
+ ret = drm_legacy_addbufs_agp(dev, request);
else
#endif
if (request->flags & _DRM_SG_BUFFER)
- ret = drm_addbufs_sg(dev, request);
+ ret = drm_legacy_addbufs_sg(dev, request);
else if (request->flags & _DRM_FB_BUFFER)
ret = -EINVAL;
else
- ret = drm_addbufs_pci(dev, request);
+ ret = drm_legacy_addbufs_pci(dev, request);
return ret;
}
@@ -1179,8 +1177,8 @@ int drm_addbufs(struct drm_device *dev, void *data,
* lock, preventing of allocating more buffers after this call. Information
* about each requested buffer is then copied into user space.
*/
-int drm_infobufs(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_infobufs(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_device_dma *dma = dev->dma;
struct drm_buf_info *request = data;
@@ -1217,7 +1215,6 @@ int drm_infobufs(struct drm_device *dev, void *data,
struct drm_buf_desc __user *to =
&request->list[count];
struct drm_buf_entry *from = &dma->bufs[i];
- struct drm_freelist *list = &dma->bufs[i].freelist;
if (copy_to_user(&to->count,
&from->buf_count,
sizeof(from->buf_count)) ||
@@ -1225,19 +1222,19 @@ int drm_infobufs(struct drm_device *dev, void *data,
&from->buf_size,
sizeof(from->buf_size)) ||
copy_to_user(&to->low_mark,
- &list->low_mark,
- sizeof(list->low_mark)) ||
+ &from->low_mark,
+ sizeof(from->low_mark)) ||
copy_to_user(&to->high_mark,
- &list->high_mark,
- sizeof(list->high_mark)))
+ &from->high_mark,
+ sizeof(from->high_mark)))
return -EFAULT;
DRM_DEBUG("%d %d %d %d %d\n",
i,
dma->bufs[i].buf_count,
dma->bufs[i].buf_size,
- dma->bufs[i].freelist.low_mark,
- dma->bufs[i].freelist.high_mark);
+ dma->bufs[i].low_mark,
+ dma->bufs[i].high_mark);
++count;
}
}
@@ -1261,8 +1258,8 @@ int drm_infobufs(struct drm_device *dev, void *data,
*
* \note This ioctl is deprecated and mostly never used.
*/
-int drm_markbufs(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_markbufs(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_device_dma *dma = dev->dma;
struct drm_buf_desc *request = data;
@@ -1290,8 +1287,8 @@ int drm_markbufs(struct drm_device *dev, void *data,
if (request->high_mark < 0 || request->high_mark > entry->buf_count)
return -EINVAL;
- entry->freelist.low_mark = request->low_mark;
- entry->freelist.high_mark = request->high_mark;
+ entry->low_mark = request->low_mark;
+ entry->high_mark = request->high_mark;
return 0;
}
@@ -1308,8 +1305,8 @@ int drm_markbufs(struct drm_device *dev, void *data,
* Calls free_buffer() for each used buffer.
* This function is primarily used for debugging.
*/
-int drm_freebufs(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_freebufs(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_device_dma *dma = dev->dma;
struct drm_buf_free *request = data;
@@ -1341,7 +1338,7 @@ int drm_freebufs(struct drm_device *dev, void *data,
task_pid_nr(current));
return -EINVAL;
}
- drm_free_buffer(dev, buf);
+ drm_legacy_free_buffer(dev, buf);
}
return 0;
@@ -1361,8 +1358,8 @@ int drm_freebufs(struct drm_device *dev, void *data,
* offset equal to 0, which drm_mmap() interpretes as PCI buffers and calls
* drm_mmap_dma().
*/
-int drm_mapbufs(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_mapbufs(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_device_dma *dma = dev->dma;
int retcode = 0;
@@ -1449,7 +1446,7 @@ int drm_mapbufs(struct drm_device *dev, void *data,
return retcode;
}
-int drm_dma_ioctl(struct drm_device *dev, void *data,
+int drm_legacy_dma_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
if (drm_core_check_feature(dev, DRIVER_MODESET))
@@ -1461,7 +1458,7 @@ int drm_dma_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
}
-struct drm_local_map *drm_getsarea(struct drm_device *dev)
+struct drm_local_map *drm_legacy_getsarea(struct drm_device *dev)
{
struct drm_map_list *entry;
@@ -1473,4 +1470,4 @@ struct drm_local_map *drm_getsarea(struct drm_device *dev)
}
return NULL;
}
-EXPORT_SYMBOL(drm_getsarea);
+EXPORT_SYMBOL(drm_legacy_getsarea);
diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c
index a4b017b6849e..9b23525c0ed0 100644
--- a/drivers/gpu/drm/drm_context.c
+++ b/drivers/gpu/drm/drm_context.c
@@ -1,18 +1,13 @@
-/**
- * \file drm_context.c
- * IOCTLs for generic contexts
- *
- * \author Rickard E. (Rik) Faith <faith@valinux.com>
- * \author Gareth Hughes <gareth@valinux.com>
- */
-
/*
- * Created: Fri Nov 24 18:31:37 2000 by gareth@valinux.com
+ * Legacy: Generic DRM Contexts
*
* Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
* Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ * Author: Gareth Hughes <gareth@valinux.com>
+ *
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
@@ -33,14 +28,14 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
-/*
- * ChangeLog:
- * 2001-11-16 Torsten Duwe <duwe@caldera.de>
- * added context constructor/destructor hooks,
- * needed by SiS driver's memory management.
- */
-
#include <drm/drmP.h>
+#include "drm_legacy.h"
+
+struct drm_ctx_list {
+ struct list_head head;
+ drm_context_t handle;
+ struct drm_file *tag;
+};
/******************************************************************/
/** \name Context bitmap support */
@@ -56,7 +51,7 @@
* in drm_device::ctx_idr, while holding the drm_device::struct_mutex
* lock.
*/
-void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle)
+void drm_legacy_ctxbitmap_free(struct drm_device * dev, int ctx_handle)
{
mutex_lock(&dev->struct_mutex);
idr_remove(&dev->ctx_idr, ctx_handle);
@@ -72,7 +67,7 @@ void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle)
* Allocate a new idr from drm_device::ctx_idr while holding the
* drm_device::struct_mutex lock.
*/
-static int drm_ctxbitmap_next(struct drm_device * dev)
+static int drm_legacy_ctxbitmap_next(struct drm_device * dev)
{
int ret;
@@ -90,7 +85,7 @@ static int drm_ctxbitmap_next(struct drm_device * dev)
*
* Initialise the drm_device::ctx_idr
*/
-int drm_ctxbitmap_init(struct drm_device * dev)
+int drm_legacy_ctxbitmap_init(struct drm_device * dev)
{
idr_init(&dev->ctx_idr);
return 0;
@@ -104,13 +99,43 @@ int drm_ctxbitmap_init(struct drm_device * dev)
* Free all idr members using drm_ctx_sarea_free helper function
* while holding the drm_device::struct_mutex lock.
*/
-void drm_ctxbitmap_cleanup(struct drm_device * dev)
+void drm_legacy_ctxbitmap_cleanup(struct drm_device * dev)
{
mutex_lock(&dev->struct_mutex);
idr_destroy(&dev->ctx_idr);
mutex_unlock(&dev->struct_mutex);
}
+/**
+ * drm_ctxbitmap_flush() - Flush all contexts owned by a file
+ * @dev: DRM device to operate on
+ * @file: Open file to flush contexts for
+ *
+ * This iterates over all contexts on @dev and drops them if they're owned by
+ * @file. Note that after this call returns, new contexts might be added if
+ * the file is still alive.
+ */
+void drm_legacy_ctxbitmap_flush(struct drm_device *dev, struct drm_file *file)
+{
+ struct drm_ctx_list *pos, *tmp;
+
+ mutex_lock(&dev->ctxlist_mutex);
+
+ list_for_each_entry_safe(pos, tmp, &dev->ctxlist, head) {
+ if (pos->tag == file &&
+ pos->handle != DRM_KERNEL_CONTEXT) {
+ if (dev->driver->context_dtor)
+ dev->driver->context_dtor(dev, pos->handle);
+
+ drm_legacy_ctxbitmap_free(dev, pos->handle);
+ list_del(&pos->head);
+ kfree(pos);
+ }
+ }
+
+ mutex_unlock(&dev->ctxlist_mutex);
+}
+
/*@}*/
/******************************************************************/
@@ -129,8 +154,8 @@ void drm_ctxbitmap_cleanup(struct drm_device * dev)
* Gets the map from drm_device::ctx_idr with the handle specified and
* returns its handle.
*/
-int drm_getsareactx(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_getsareactx(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_ctx_priv_map *request = data;
struct drm_local_map *map;
@@ -173,8 +198,8 @@ int drm_getsareactx(struct drm_device *dev, void *data,
* Searches the mapping specified in \p arg and update the entry in
* drm_device::ctx_idr with it.
*/
-int drm_setsareactx(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_setsareactx(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_ctx_priv_map *request = data;
struct drm_local_map *map = NULL;
@@ -273,8 +298,8 @@ static int drm_context_switch_complete(struct drm_device *dev,
* \param arg user argument pointing to a drm_ctx_res structure.
* \return zero on success or a negative number on failure.
*/
-int drm_resctx(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_resctx(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_ctx_res *res = data;
struct drm_ctx ctx;
@@ -304,16 +329,16 @@ int drm_resctx(struct drm_device *dev, void *data,
*
* Get a new handle for the context and copy to userspace.
*/
-int drm_addctx(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_addctx(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_ctx_list *ctx_entry;
struct drm_ctx *ctx = data;
- ctx->handle = drm_ctxbitmap_next(dev);
+ ctx->handle = drm_legacy_ctxbitmap_next(dev);
if (ctx->handle == DRM_KERNEL_CONTEXT) {
/* Skip kernel's context and get a new one. */
- ctx->handle = drm_ctxbitmap_next(dev);
+ ctx->handle = drm_legacy_ctxbitmap_next(dev);
}
DRM_DEBUG("%d\n", ctx->handle);
if (ctx->handle == -1) {
@@ -348,7 +373,8 @@ int drm_addctx(struct drm_device *dev, void *data,
* \param arg user argument pointing to a drm_ctx structure.
* \return zero on success or a negative number on failure.
*/
-int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
+int drm_legacy_getctx(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_ctx *ctx = data;
@@ -369,8 +395,8 @@ int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
*
* Calls context_switch().
*/
-int drm_switchctx(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_switchctx(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_ctx *ctx = data;
@@ -389,8 +415,8 @@ int drm_switchctx(struct drm_device *dev, void *data,
*
* Calls context_switch_complete().
*/
-int drm_newctx(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_newctx(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_ctx *ctx = data;
@@ -411,8 +437,8 @@ int drm_newctx(struct drm_device *dev, void *data,
*
* If not the special kernel context, calls ctxbitmap_free() to free the specified context.
*/
-int drm_rmctx(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_rmctx(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_ctx *ctx = data;
@@ -420,7 +446,7 @@ int drm_rmctx(struct drm_device *dev, void *data,
if (ctx->handle != DRM_KERNEL_CONTEXT) {
if (dev->driver->context_dtor)
dev->driver->context_dtor(dev, ctx->handle);
- drm_ctxbitmap_free(dev, ctx->handle);
+ drm_legacy_ctxbitmap_free(dev, ctx->handle);
}
mutex_lock(&dev->ctxlist_mutex);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index fe94cc10cd35..e79c8d3700d8 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -40,101 +40,11 @@
#include <drm/drm_modeset_lock.h>
#include "drm_crtc_internal.h"
+#include "drm_internal.h"
-/**
- * drm_modeset_lock_all - take all modeset locks
- * @dev: drm device
- *
- * This function takes all modeset locks, suitable where a more fine-grained
- * scheme isn't (yet) implemented. Locks must be dropped with
- * drm_modeset_unlock_all.
- */
-void drm_modeset_lock_all(struct drm_device *dev)
-{
- struct drm_mode_config *config = &dev->mode_config;
- struct drm_modeset_acquire_ctx *ctx;
- int ret;
-
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (WARN_ON(!ctx))
- return;
-
- mutex_lock(&config->mutex);
-
- drm_modeset_acquire_init(ctx, 0);
-
-retry:
- ret = drm_modeset_lock(&config->connection_mutex, ctx);
- if (ret)
- goto fail;
- ret = drm_modeset_lock_all_crtcs(dev, ctx);
- if (ret)
- goto fail;
-
- WARN_ON(config->acquire_ctx);
-
- /* now we hold the locks, so now that it is safe, stash the
- * ctx for drm_modeset_unlock_all():
- */
- config->acquire_ctx = ctx;
-
- drm_warn_on_modeset_not_all_locked(dev);
-
- return;
-
-fail:
- if (ret == -EDEADLK) {
- drm_modeset_backoff(ctx);
- goto retry;
- }
-}
-EXPORT_SYMBOL(drm_modeset_lock_all);
-
-/**
- * drm_modeset_unlock_all - drop all modeset locks
- * @dev: device
- *
- * This function drop all modeset locks taken by drm_modeset_lock_all.
- */
-void drm_modeset_unlock_all(struct drm_device *dev)
-{
- struct drm_mode_config *config = &dev->mode_config;
- struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
-
- if (WARN_ON(!ctx))
- return;
-
- config->acquire_ctx = NULL;
- drm_modeset_drop_locks(ctx);
- drm_modeset_acquire_fini(ctx);
-
- kfree(ctx);
-
- mutex_unlock(&dev->mode_config.mutex);
-}
-EXPORT_SYMBOL(drm_modeset_unlock_all);
-
-/**
- * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
- * @dev: device
- *
- * Useful as a debug assert.
- */
-void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
-{
- struct drm_crtc *crtc;
-
- /* Locking is currently fubar in the panic handler. */
- if (oops_in_progress)
- return;
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
- WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
-
- WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
- WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
-}
-EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
+static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *r,
+ struct drm_file *file_priv);
/* Avoid boilerplate. I'm tired of typing. */
#define DRM_ENUM_NAME_FN(fnname, list) \
@@ -178,6 +88,12 @@ static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] =
{ DRM_MODE_SCALE_ASPECT, "Full aspect" },
};
+static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = {
+ { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" },
+ { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" },
+ { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" },
+};
+
/*
* Non-global properties, but "required" for certain connectors.
*/
@@ -357,6 +273,32 @@ const char *drm_get_format_name(uint32_t format)
}
EXPORT_SYMBOL(drm_get_format_name);
+/*
+ * Internal function to assign a slot in the object idr and optionally
+ * register the object into the idr.
+ */
+static int drm_mode_object_get_reg(struct drm_device *dev,
+ struct drm_mode_object *obj,
+ uint32_t obj_type,
+ bool register_obj)
+{
+ int ret;
+
+ mutex_lock(&dev->mode_config.idr_mutex);
+ ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL);
+ if (ret >= 0) {
+ /*
+ * Set up the object linking under the protection of the idr
+ * lock so that other users can't see inconsistent state.
+ */
+ obj->id = ret;
+ obj->type = obj_type;
+ }
+ mutex_unlock(&dev->mode_config.idr_mutex);
+
+ return ret < 0 ? ret : 0;
+}
+
/**
* drm_mode_object_get - allocate a new modeset identifier
* @dev: DRM device
@@ -375,21 +317,15 @@ EXPORT_SYMBOL(drm_get_format_name);
int drm_mode_object_get(struct drm_device *dev,
struct drm_mode_object *obj, uint32_t obj_type)
{
- int ret;
+ return drm_mode_object_get_reg(dev, obj, obj_type, true);
+}
+static void drm_mode_object_register(struct drm_device *dev,
+ struct drm_mode_object *obj)
+{
mutex_lock(&dev->mode_config.idr_mutex);
- ret = idr_alloc(&dev->mode_config.crtc_idr, obj, 1, 0, GFP_KERNEL);
- if (ret >= 0) {
- /*
- * Set up the object linking under the protection of the idr
- * lock so that other users can't see inconsistent state.
- */
- obj->id = ret;
- obj->type = obj_type;
- }
+ idr_replace(&dev->mode_config.crtc_idr, obj, obj->id);
mutex_unlock(&dev->mode_config.idr_mutex);
-
- return ret < 0 ? ret : 0;
}
/**
@@ -416,8 +352,12 @@ static struct drm_mode_object *_object_find(struct drm_device *dev,
mutex_lock(&dev->mode_config.idr_mutex);
obj = idr_find(&dev->mode_config.crtc_idr, id);
- if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
- (obj->id != id))
+ if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type)
+ obj = NULL;
+ if (obj && obj->id != id)
+ obj = NULL;
+ /* don't leak out unref'd fb's */
+ if (obj && (obj->type == DRM_MODE_OBJECT_FB))
obj = NULL;
mutex_unlock(&dev->mode_config.idr_mutex);
@@ -444,9 +384,6 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
* function.*/
WARN_ON(type == DRM_MODE_OBJECT_FB);
obj = _object_find(dev, id, type);
- /* don't leak out unref'd fb's */
- if (obj && (obj->type == DRM_MODE_OBJECT_FB))
- obj = NULL;
return obj;
}
EXPORT_SYMBOL(drm_mode_object_find);
@@ -484,9 +421,6 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
if (ret)
goto out;
- /* Grab the idr reference. */
- drm_framebuffer_reference(fb);
-
dev->mode_config.num_fb++;
list_add(&fb->head, &dev->mode_config.fb_list);
out:
@@ -496,10 +430,34 @@ out:
}
EXPORT_SYMBOL(drm_framebuffer_init);
+/* dev->mode_config.fb_lock must be held! */
+static void __drm_framebuffer_unregister(struct drm_device *dev,
+ struct drm_framebuffer *fb)
+{
+ mutex_lock(&dev->mode_config.idr_mutex);
+ idr_remove(&dev->mode_config.crtc_idr, fb->base.id);
+ mutex_unlock(&dev->mode_config.idr_mutex);
+
+ fb->base.id = 0;
+}
+
static void drm_framebuffer_free(struct kref *kref)
{
struct drm_framebuffer *fb =
container_of(kref, struct drm_framebuffer, refcount);
+ struct drm_device *dev = fb->dev;
+
+ /*
+ * The lookup idr holds a weak reference, which has not necessarily been
+ * removed at this point. Check for that.
+ */
+ mutex_lock(&dev->mode_config.fb_lock);
+ if (fb->base.id) {
+ /* Mark fb as reaped and drop idr ref. */
+ __drm_framebuffer_unregister(dev, fb);
+ }
+ mutex_unlock(&dev->mode_config.fb_lock);
+
fb->funcs->destroy(fb);
}
@@ -536,8 +494,10 @@ struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
mutex_lock(&dev->mode_config.fb_lock);
fb = __drm_framebuffer_lookup(dev, id);
- if (fb)
- drm_framebuffer_reference(fb);
+ if (fb) {
+ if (!kref_get_unless_zero(&fb->refcount))
+ fb = NULL;
+ }
mutex_unlock(&dev->mode_config.fb_lock);
return fb;
@@ -581,19 +541,6 @@ static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
kref_put(&fb->refcount, drm_framebuffer_free_bug);
}
-/* dev->mode_config.fb_lock must be held! */
-static void __drm_framebuffer_unregister(struct drm_device *dev,
- struct drm_framebuffer *fb)
-{
- mutex_lock(&dev->mode_config.idr_mutex);
- idr_remove(&dev->mode_config.crtc_idr, fb->base.id);
- mutex_unlock(&dev->mode_config.idr_mutex);
-
- fb->base.id = 0;
-
- __drm_framebuffer_unreference(fb);
-}
-
/**
* drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
* @fb: fb to unregister
@@ -723,7 +670,7 @@ DEFINE_WW_CLASS(crtc_ww_class);
*/
int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
struct drm_plane *primary,
- void *cursor,
+ struct drm_plane *cursor,
const struct drm_crtc_funcs *funcs)
{
struct drm_mode_config *config = &dev->mode_config;
@@ -733,11 +680,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
crtc->funcs = funcs;
crtc->invert_dimensions = false;
- drm_modeset_lock_all(dev);
drm_modeset_lock_init(&crtc->mutex);
- /* dropped by _unlock_all(): */
- drm_modeset_lock(&crtc->mutex, config->acquire_ctx);
-
ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
if (ret)
goto out;
@@ -748,11 +691,13 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
config->num_crtc++;
crtc->primary = primary;
+ crtc->cursor = cursor;
if (primary)
primary->possible_crtcs = 1 << drm_crtc_index(crtc);
+ if (cursor)
+ cursor->possible_crtcs = 1 << drm_crtc_index(crtc);
out:
- drm_modeset_unlock_all(dev);
return ret;
}
@@ -819,6 +764,59 @@ static void drm_mode_remove(struct drm_connector *connector,
}
/**
+ * drm_connector_get_cmdline_mode - reads the user's cmdline mode
+ * @connector: connector to quwery
+ * @mode: returned mode
+ *
+ * The kernel supports per-connector configration of its consoles through
+ * use of the video= parameter. This function parses that option and
+ * extracts the user's specified mode (or enable/disable status) for a
+ * particular connector. This is typically only used during the early fbdev
+ * setup.
+ */
+static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
+{
+ struct drm_cmdline_mode *mode = &connector->cmdline_mode;
+ char *option = NULL;
+
+ if (fb_get_options(connector->name, &option))
+ return;
+
+ if (!drm_mode_parse_command_line_for_connector(option,
+ connector,
+ mode))
+ return;
+
+ if (mode->force) {
+ const char *s;
+
+ switch (mode->force) {
+ case DRM_FORCE_OFF:
+ s = "OFF";
+ break;
+ case DRM_FORCE_ON_DIGITAL:
+ s = "ON - dig";
+ break;
+ default:
+ case DRM_FORCE_ON:
+ s = "ON";
+ break;
+ }
+
+ DRM_INFO("forcing %s connector %s\n", connector->name, s);
+ connector->force = mode->force;
+ }
+
+ DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
+ connector->name,
+ mode->xres, mode->yres,
+ mode->refresh_specified ? mode->refresh : 60,
+ mode->rb ? " reduced blanking" : "",
+ mode->margins ? " with margins" : "",
+ mode->interlace ? " interlaced" : "");
+}
+
+/**
* drm_connector_init - Init a preallocated connector
* @dev: DRM device
* @connector: the connector to init
@@ -842,7 +840,7 @@ int drm_connector_init(struct drm_device *dev,
drm_modeset_lock_all(dev);
- ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
+ ret = drm_mode_object_get_reg(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR, false);
if (ret)
goto out_unlock;
@@ -870,6 +868,8 @@ int drm_connector_init(struct drm_device *dev,
connector->edid_blob_ptr = NULL;
connector->status = connector_status_unknown;
+ drm_connector_get_cmdline_mode(connector);
+
list_add_tail(&connector->head, &dev->mode_config.connector_list);
dev->mode_config.num_connector++;
@@ -881,6 +881,8 @@ int drm_connector_init(struct drm_device *dev,
drm_object_attach_property(&connector->base,
dev->mode_config.dpms_property, 0);
+ connector->debugfs_entry = NULL;
+
out_put:
if (ret)
drm_mode_object_put(dev, &connector->base);
@@ -921,6 +923,72 @@ void drm_connector_cleanup(struct drm_connector *connector)
EXPORT_SYMBOL(drm_connector_cleanup);
/**
+ * drm_connector_index - find the index of a registered connector
+ * @connector: connector to find index for
+ *
+ * Given a registered connector, return the index of that connector within a DRM
+ * device's list of connectors.
+ */
+unsigned int drm_connector_index(struct drm_connector *connector)
+{
+ unsigned int index = 0;
+ struct drm_connector *tmp;
+
+ list_for_each_entry(tmp, &connector->dev->mode_config.connector_list, head) {
+ if (tmp == connector)
+ return index;
+
+ index++;
+ }
+
+ BUG();
+}
+EXPORT_SYMBOL(drm_connector_index);
+
+/**
+ * drm_connector_register - register a connector
+ * @connector: the connector to register
+ *
+ * Register userspace interfaces for a connector
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_connector_register(struct drm_connector *connector)
+{
+ int ret;
+
+ drm_mode_object_register(connector->dev, &connector->base);
+
+ ret = drm_sysfs_connector_add(connector);
+ if (ret)
+ return ret;
+
+ ret = drm_debugfs_connector_add(connector);
+ if (ret) {
+ drm_sysfs_connector_remove(connector);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_register);
+
+/**
+ * drm_connector_unregister - unregister a connector
+ * @connector: the connector to unregister
+ *
+ * Unregister userspace interfaces for a connector
+ */
+void drm_connector_unregister(struct drm_connector *connector)
+{
+ drm_sysfs_connector_remove(connector);
+ drm_debugfs_connector_remove(connector);
+}
+EXPORT_SYMBOL(drm_connector_unregister);
+
+
+/**
* drm_connector_unplug_all - unregister connector userspace interfaces
* @dev: drm device
*
@@ -934,7 +1002,7 @@ void drm_connector_unplug_all(struct drm_device *dev)
/* taking the mode config mutex ends up in a clash with sysfs */
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
}
EXPORT_SYMBOL(drm_connector_unplug_all);
@@ -1182,6 +1250,29 @@ void drm_plane_cleanup(struct drm_plane *plane)
EXPORT_SYMBOL(drm_plane_cleanup);
/**
+ * drm_plane_index - find the index of a registered plane
+ * @plane: plane to find index for
+ *
+ * Given a registered plane, return the index of that CRTC within a DRM
+ * device's list of planes.
+ */
+unsigned int drm_plane_index(struct drm_plane *plane)
+{
+ unsigned int index = 0;
+ struct drm_plane *tmp;
+
+ list_for_each_entry(tmp, &plane->dev->mode_config.plane_list, head) {
+ if (tmp == plane)
+ return index;
+
+ index++;
+ }
+
+ BUG();
+}
+EXPORT_SYMBOL(drm_plane_index);
+
+/**
* drm_plane_force_disable - Forcibly disable a plane
* @plane: plane to disable
*
@@ -1192,19 +1283,21 @@ EXPORT_SYMBOL(drm_plane_cleanup);
*/
void drm_plane_force_disable(struct drm_plane *plane)
{
- struct drm_framebuffer *old_fb = plane->fb;
int ret;
- if (!old_fb)
+ if (!plane->fb)
return;
+ plane->old_fb = plane->fb;
ret = plane->funcs->disable_plane(plane);
if (ret) {
DRM_ERROR("failed to disable plane with busy fb\n");
+ plane->old_fb = NULL;
return;
}
/* disconnect the plane from the fb and crtc: */
- __drm_framebuffer_unreference(old_fb);
+ __drm_framebuffer_unreference(plane->old_fb);
+ plane->old_fb = NULL;
plane->fb = NULL;
plane->crtc = NULL;
}
@@ -1214,6 +1307,7 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
{
struct drm_property *edid;
struct drm_property *dpms;
+ struct drm_property *dev_path;
/*
* Standard properties (apply to all connectors)
@@ -1228,6 +1322,12 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
ARRAY_SIZE(drm_dpms_enum_list));
dev->mode_config.dpms_property = dpms;
+ dev_path = drm_property_create(dev,
+ DRM_MODE_PROP_BLOB |
+ DRM_MODE_PROP_IMMUTABLE,
+ "PATH", 0);
+ dev->mode_config.path_property = dev_path;
+
return 0;
}
@@ -1384,6 +1484,33 @@ int drm_mode_create_scaling_mode_property(struct drm_device *dev)
EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
/**
+ * drm_mode_create_aspect_ratio_property - create aspect ratio property
+ * @dev: DRM device
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_create_aspect_ratio_property(struct drm_device *dev)
+{
+ if (dev->mode_config.aspect_ratio_property)
+ return 0;
+
+ dev->mode_config.aspect_ratio_property =
+ drm_property_create_enum(dev, 0, "aspect ratio",
+ drm_aspect_ratio_enum_list,
+ ARRAY_SIZE(drm_aspect_ratio_enum_list));
+
+ if (dev->mode_config.aspect_ratio_property == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
+
+/**
* drm_mode_create_dirty_property - create dirty property
* @dev: DRM device
*
@@ -1470,6 +1597,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
+void drm_reinit_primary_mode_group(struct drm_device *dev)
+{
+ drm_modeset_lock_all(dev);
+ drm_mode_group_destroy(&dev->primary->mode_group);
+ drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
+ drm_modeset_unlock_all(dev);
+}
+EXPORT_SYMBOL(drm_reinit_primary_mode_group);
+
/**
* drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
* @out: drm_mode_modeinfo struct to return to the user
@@ -2118,63 +2254,38 @@ out:
return ret;
}
-/**
- * drm_mode_setplane - configure a plane's configuration
- * @dev: DRM device
- * @data: ioctl data*
- * @file_priv: DRM file info
+/*
+ * setplane_internal - setplane handler for internal callers
*
- * Set plane configuration, including placement, fb, scaling, and other factors.
- * Or pass a NULL fb to disable.
+ * Note that we assume an extra reference has already been taken on fb. If the
+ * update fails, this reference will be dropped before return; if it succeeds,
+ * the previous framebuffer (if any) will be unreferenced instead.
*
- * Returns:
- * Zero on success, errno on failure.
+ * src_{x,y,w,h} are provided in 16.16 fixed point format
*/
-int drm_mode_setplane(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+static int __setplane_internal(struct drm_plane *plane,
+ struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int32_t crtc_x, int32_t crtc_y,
+ uint32_t crtc_w, uint32_t crtc_h,
+ /* src_{x,y,w,h} values are 16.16 fixed point */
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
{
- struct drm_mode_set_plane *plane_req = data;
- struct drm_plane *plane;
- struct drm_crtc *crtc;
- struct drm_framebuffer *fb = NULL, *old_fb = NULL;
int ret = 0;
unsigned int fb_width, fb_height;
int i;
- if (!drm_core_check_feature(dev, DRIVER_MODESET))
- return -EINVAL;
-
- /*
- * First, find the plane, crtc, and fb objects. If not available,
- * we don't bother to call the driver.
- */
- plane = drm_plane_find(dev, plane_req->plane_id);
- if (!plane) {
- DRM_DEBUG_KMS("Unknown plane ID %d\n",
- plane_req->plane_id);
- return -ENOENT;
- }
-
/* No fb means shut it down */
- if (!plane_req->fb_id) {
- drm_modeset_lock_all(dev);
- old_fb = plane->fb;
+ if (!fb) {
+ plane->old_fb = plane->fb;
ret = plane->funcs->disable_plane(plane);
if (!ret) {
plane->crtc = NULL;
plane->fb = NULL;
} else {
- old_fb = NULL;
+ plane->old_fb = NULL;
}
- drm_modeset_unlock_all(dev);
- goto out;
- }
-
- crtc = drm_crtc_find(dev, plane_req->crtc_id);
- if (!crtc) {
- DRM_DEBUG_KMS("Unknown crtc ID %d\n",
- plane_req->crtc_id);
- ret = -ENOENT;
goto out;
}
@@ -2185,14 +2296,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
goto out;
}
- fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
- if (!fb) {
- DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
- plane_req->fb_id);
- ret = -ENOENT;
- goto out;
- }
-
/* Check whether this plane supports the fb pixel format. */
for (i = 0; i < plane->format_count; i++)
if (fb->pixel_format == plane->format_types[i])
@@ -2208,62 +2311,141 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
fb_height = fb->height << 16;
/* Make sure source coordinates are inside the fb. */
- if (plane_req->src_w > fb_width ||
- plane_req->src_x > fb_width - plane_req->src_w ||
- plane_req->src_h > fb_height ||
- plane_req->src_y > fb_height - plane_req->src_h) {
+ if (src_w > fb_width ||
+ src_x > fb_width - src_w ||
+ src_h > fb_height ||
+ src_y > fb_height - src_h) {
DRM_DEBUG_KMS("Invalid source coordinates "
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
- plane_req->src_w >> 16,
- ((plane_req->src_w & 0xffff) * 15625) >> 10,
- plane_req->src_h >> 16,
- ((plane_req->src_h & 0xffff) * 15625) >> 10,
- plane_req->src_x >> 16,
- ((plane_req->src_x & 0xffff) * 15625) >> 10,
- plane_req->src_y >> 16,
- ((plane_req->src_y & 0xffff) * 15625) >> 10);
+ src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
+ src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
+ src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
+ src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);
ret = -ENOSPC;
goto out;
}
- /* Give drivers some help against integer overflows */
- if (plane_req->crtc_w > INT_MAX ||
- plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
- plane_req->crtc_h > INT_MAX ||
- plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
- DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
- plane_req->crtc_w, plane_req->crtc_h,
- plane_req->crtc_x, plane_req->crtc_y);
- ret = -ERANGE;
- goto out;
- }
-
- drm_modeset_lock_all(dev);
- old_fb = plane->fb;
+ plane->old_fb = plane->fb;
ret = plane->funcs->update_plane(plane, crtc, fb,
- plane_req->crtc_x, plane_req->crtc_y,
- plane_req->crtc_w, plane_req->crtc_h,
- plane_req->src_x, plane_req->src_y,
- plane_req->src_w, plane_req->src_h);
+ crtc_x, crtc_y, crtc_w, crtc_h,
+ src_x, src_y, src_w, src_h);
if (!ret) {
plane->crtc = crtc;
plane->fb = fb;
fb = NULL;
} else {
- old_fb = NULL;
+ plane->old_fb = NULL;
}
- drm_modeset_unlock_all(dev);
out:
if (fb)
drm_framebuffer_unreference(fb);
- if (old_fb)
- drm_framebuffer_unreference(old_fb);
+ if (plane->old_fb)
+ drm_framebuffer_unreference(plane->old_fb);
+ plane->old_fb = NULL;
+
+ return ret;
+}
+
+static int setplane_internal(struct drm_plane *plane,
+ struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int32_t crtc_x, int32_t crtc_y,
+ uint32_t crtc_w, uint32_t crtc_h,
+ /* src_{x,y,w,h} values are 16.16 fixed point */
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ int ret;
+
+ drm_modeset_lock_all(plane->dev);
+ ret = __setplane_internal(plane, crtc, fb,
+ crtc_x, crtc_y, crtc_w, crtc_h,
+ src_x, src_y, src_w, src_h);
+ drm_modeset_unlock_all(plane->dev);
return ret;
}
/**
+ * drm_mode_setplane - configure a plane's configuration
+ * @dev: DRM device
+ * @data: ioctl data*
+ * @file_priv: DRM file info
+ *
+ * Set plane configuration, including placement, fb, scaling, and other factors.
+ * Or pass a NULL fb to disable (planes may be disabled without providing a
+ * valid crtc).
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_setplane(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_mode_set_plane *plane_req = data;
+ struct drm_mode_object *obj;
+ struct drm_plane *plane;
+ struct drm_crtc *crtc = NULL;
+ struct drm_framebuffer *fb = NULL;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return -EINVAL;
+
+ /* Give drivers some help against integer overflows */
+ if (plane_req->crtc_w > INT_MAX ||
+ plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
+ plane_req->crtc_h > INT_MAX ||
+ plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
+ DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
+ plane_req->crtc_w, plane_req->crtc_h,
+ plane_req->crtc_x, plane_req->crtc_y);
+ return -ERANGE;
+ }
+
+ /*
+ * First, find the plane, crtc, and fb objects. If not available,
+ * we don't bother to call the driver.
+ */
+ obj = drm_mode_object_find(dev, plane_req->plane_id,
+ DRM_MODE_OBJECT_PLANE);
+ if (!obj) {
+ DRM_DEBUG_KMS("Unknown plane ID %d\n",
+ plane_req->plane_id);
+ return -ENOENT;
+ }
+ plane = obj_to_plane(obj);
+
+ if (plane_req->fb_id) {
+ fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
+ if (!fb) {
+ DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
+ plane_req->fb_id);
+ return -ENOENT;
+ }
+
+ obj = drm_mode_object_find(dev, plane_req->crtc_id,
+ DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ DRM_DEBUG_KMS("Unknown crtc ID %d\n",
+ plane_req->crtc_id);
+ return -ENOENT;
+ }
+ crtc = obj_to_crtc(obj);
+ }
+
+ /*
+ * setplane_internal will take care of deref'ing either the old or new
+ * framebuffer depending on success.
+ */
+ return setplane_internal(plane, crtc, fb,
+ plane_req->crtc_x, plane_req->crtc_y,
+ plane_req->crtc_w, plane_req->crtc_h,
+ plane_req->src_x, plane_req->src_y,
+ plane_req->src_w, plane_req->src_h);
+}
+
+/**
* drm_mode_set_config_internal - helper to call ->set_config
* @set: modeset config to set
*
@@ -2286,7 +2468,7 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
* crtcs. Atomic modeset will have saner semantics ...
*/
list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head)
- tmp->old_fb = tmp->primary->fb;
+ tmp->primary->old_fb = tmp->primary->fb;
fb = set->fb;
@@ -2299,8 +2481,9 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
if (tmp->primary->fb)
drm_framebuffer_reference(tmp->primary->fb);
- if (tmp->old_fb)
- drm_framebuffer_unreference(tmp->old_fb);
+ if (tmp->primary->old_fb)
+ drm_framebuffer_unreference(tmp->primary->old_fb);
+ tmp->primary->old_fb = NULL;
}
return ret;
@@ -2509,6 +2692,101 @@ out:
return ret;
}
+/**
+ * drm_mode_cursor_universal - translate legacy cursor ioctl call into a
+ * universal plane handler call
+ * @crtc: crtc to update cursor for
+ * @req: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Legacy cursor ioctl's work directly with driver buffer handles. To
+ * translate legacy ioctl calls into universal plane handler calls, we need to
+ * wrap the native buffer handle in a drm_framebuffer.
+ *
+ * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB
+ * buffer with a pitch of 4*width; the universal plane interface should be used
+ * directly in cases where the hardware can support other buffer settings and
+ * userspace wants to make use of these capabilities.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
+static int drm_mode_cursor_universal(struct drm_crtc *crtc,
+ struct drm_mode_cursor2 *req,
+ struct drm_file *file_priv)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_framebuffer *fb = NULL;
+ struct drm_mode_fb_cmd2 fbreq = {
+ .width = req->width,
+ .height = req->height,
+ .pixel_format = DRM_FORMAT_ARGB8888,
+ .pitches = { req->width * 4 },
+ .handles = { req->handle },
+ };
+ int32_t crtc_x, crtc_y;
+ uint32_t crtc_w = 0, crtc_h = 0;
+ uint32_t src_w = 0, src_h = 0;
+ int ret = 0;
+
+ BUG_ON(!crtc->cursor);
+ WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL);
+
+ /*
+ * Obtain fb we'll be using (either new or existing) and take an extra
+ * reference to it if fb != null. setplane will take care of dropping
+ * the reference if the plane update fails.
+ */
+ if (req->flags & DRM_MODE_CURSOR_BO) {
+ if (req->handle) {
+ fb = add_framebuffer_internal(dev, &fbreq, file_priv);
+ if (IS_ERR(fb)) {
+ DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
+ return PTR_ERR(fb);
+ }
+
+ drm_framebuffer_reference(fb);
+ } else {
+ fb = NULL;
+ }
+ } else {
+ fb = crtc->cursor->fb;
+ if (fb)
+ drm_framebuffer_reference(fb);
+ }
+
+ if (req->flags & DRM_MODE_CURSOR_MOVE) {
+ crtc_x = req->x;
+ crtc_y = req->y;
+ } else {
+ crtc_x = crtc->cursor_x;
+ crtc_y = crtc->cursor_y;
+ }
+
+ if (fb) {
+ crtc_w = fb->width;
+ crtc_h = fb->height;
+ src_w = fb->width << 16;
+ src_h = fb->height << 16;
+ }
+
+ /*
+ * setplane_internal will take care of deref'ing either the old or new
+ * framebuffer depending on success.
+ */
+ ret = __setplane_internal(crtc->cursor, crtc, fb,
+ crtc_x, crtc_y, crtc_w, crtc_h,
+ 0, 0, src_w, src_h);
+
+ /* Update successful; save new cursor position, if necessary */
+ if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) {
+ crtc->cursor_x = req->x;
+ crtc->cursor_y = req->y;
+ }
+
+ return ret;
+}
+
static int drm_mode_cursor_common(struct drm_device *dev,
struct drm_mode_cursor2 *req,
struct drm_file *file_priv)
@@ -2528,7 +2806,16 @@ static int drm_mode_cursor_common(struct drm_device *dev,
return -ENOENT;
}
- drm_modeset_lock(&crtc->mutex, NULL);
+ /*
+ * If this crtc has a universal cursor plane, call that plane's update
+ * handler rather than using legacy cursor handlers.
+ */
+ drm_modeset_lock_crtc(crtc);
+ if (crtc->cursor) {
+ ret = drm_mode_cursor_universal(crtc, req, file_priv);
+ goto out;
+ }
+
if (req->flags & DRM_MODE_CURSOR_BO) {
if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
ret = -ENXIO;
@@ -2552,7 +2839,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
}
}
out:
- drm_modeset_unlock(&crtc->mutex);
+ drm_modeset_unlock_crtc(crtc);
return ret;
@@ -2827,56 +3114,38 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
return 0;
}
-/**
- * drm_mode_addfb2 - add an FB to the graphics configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Add a new FB to the specified CRTC, given a user request with format. This is
- * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
- * and uses fourcc codes as pixel format specifiers.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, errno on failure.
- */
-int drm_mode_addfb2(struct drm_device *dev,
- void *data, struct drm_file *file_priv)
+static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *r,
+ struct drm_file *file_priv)
{
- struct drm_mode_fb_cmd2 *r = data;
struct drm_mode_config *config = &dev->mode_config;
struct drm_framebuffer *fb;
int ret;
- if (!drm_core_check_feature(dev, DRIVER_MODESET))
- return -EINVAL;
-
if (r->flags & ~DRM_MODE_FB_INTERLACED) {
DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
if ((config->min_width > r->width) || (r->width > config->max_width)) {
DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
r->width, config->min_width, config->max_width);
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
if ((config->min_height > r->height) || (r->height > config->max_height)) {
DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
r->height, config->min_height, config->max_height);
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
ret = framebuffer_check(r);
if (ret)
- return ret;
+ return ERR_PTR(ret);
fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
if (IS_ERR(fb)) {
DRM_DEBUG_KMS("could not create framebuffer\n");
- return PTR_ERR(fb);
+ return fb;
}
mutex_lock(&file_priv->fbs_lock);
@@ -2885,8 +3154,37 @@ int drm_mode_addfb2(struct drm_device *dev,
DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
mutex_unlock(&file_priv->fbs_lock);
+ return fb;
+}
- return ret;
+/**
+ * drm_mode_addfb2 - add an FB to the graphics configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Add a new FB to the specified CRTC, given a user request with format. This is
+ * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
+ * and uses fourcc codes as pixel format specifiers.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_addfb2(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ struct drm_framebuffer *fb;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return -EINVAL;
+
+ fb = add_framebuffer_internal(dev, data, file_priv);
+ if (IS_ERR(fb))
+ return PTR_ERR(fb);
+
+ return 0;
}
/**
@@ -3102,7 +3400,16 @@ void drm_fb_release(struct drm_file *priv)
struct drm_device *dev = priv->minor->dev;
struct drm_framebuffer *fb, *tfb;
- mutex_lock(&priv->fbs_lock);
+ /*
+ * When the file gets released that means no one else can access the fb
+ * list any more, so no need to grab fpriv->fbs_lock. And we need to to
+ * avoid upsetting lockdep since the universal cursor code adds a
+ * framebuffer while holding mutex locks.
+ *
+ * Note that a real deadlock between fpriv->fbs_lock and the modeset
+ * locks is impossible here since no one else but this function can get
+ * at it any more.
+ */
list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
mutex_lock(&dev->mode_config.fb_lock);
@@ -3115,7 +3422,6 @@ void drm_fb_release(struct drm_file *priv)
/* This will also drop the fpriv->fbs reference. */
drm_framebuffer_remove(fb);
}
- mutex_unlock(&priv->fbs_lock);
}
/**
@@ -3176,7 +3482,7 @@ fail:
EXPORT_SYMBOL(drm_property_create);
/**
- * drm_property_create - create a new enumeration property type
+ * drm_property_create_enum - create a new enumeration property type
* @dev: drm device
* @flags: flags specifying the property type
* @name: name of the property
@@ -3222,14 +3528,15 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
EXPORT_SYMBOL(drm_property_create_enum);
/**
- * drm_property_create - create a new bitmask property type
+ * drm_property_create_bitmask - create a new bitmask property type
* @dev: drm device
* @flags: flags specifying the property type
* @name: name of the property
* @props: enumeration lists with property bitflags
- * @num_values: number of pre-defined values
+ * @num_props: size of the @props array
+ * @supported_bits: bitmask of all supported enumeration values
*
- * This creates a new generic drm property which can then be attached to a drm
+ * This creates a new bitmask drm property which can then be attached to a drm
* object with drm_object_attach_property. The returned property object must be
* freed with drm_property_destroy.
*
@@ -3242,19 +3549,28 @@ EXPORT_SYMBOL(drm_property_create_enum);
struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
int flags, const char *name,
const struct drm_prop_enum_list *props,
- int num_values)
+ int num_props,
+ uint64_t supported_bits)
{
struct drm_property *property;
- int i, ret;
+ int i, ret, index = 0;
+ int num_values = hweight64(supported_bits);
flags |= DRM_MODE_PROP_BITMASK;
property = drm_property_create(dev, flags, name, num_values);
if (!property)
return NULL;
+ for (i = 0; i < num_props; i++) {
+ if (!(supported_bits & (1ULL << props[i].type)))
+ continue;
- for (i = 0; i < num_values; i++) {
- ret = drm_property_add_enum(property, i,
+ if (WARN_ON(index >= num_values)) {
+ drm_property_destroy(dev, property);
+ return NULL;
+ }
+
+ ret = drm_property_add_enum(property, index++,
props[i].type,
props[i].name);
if (ret) {
@@ -3284,7 +3600,7 @@ static struct drm_property *property_create_range(struct drm_device *dev,
}
/**
- * drm_property_create - create a new ranged property type
+ * drm_property_create_range - create a new ranged property type
* @dev: drm device
* @flags: flags specifying the property type
* @name: name of the property
@@ -3703,6 +4019,25 @@ done:
return ret;
}
+int drm_mode_connector_set_path_property(struct drm_connector *connector,
+ char *path)
+{
+ struct drm_device *dev = connector->dev;
+ int ret, size;
+ size = strlen(path) + 1;
+
+ connector->path_blob_ptr = drm_property_create_blob(connector->dev,
+ size, path);
+ if (!connector->path_blob_ptr)
+ return -EINVAL;
+
+ ret = drm_object_property_set_value(&connector->base,
+ dev->mode_config.path_property,
+ connector->path_blob_ptr->base.id);
+ return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_set_path_property);
+
/**
* drm_mode_connector_update_edid_property - update the edid property of a connector
* @connector: drm connector
@@ -3720,6 +4055,10 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
struct drm_device *dev = connector->dev;
int ret, size;
+ /* ignore requests to set edid when overridden */
+ if (connector->override_edid)
+ return 0;
+
if (connector->edid_blob_ptr)
drm_property_destroy_blob(dev, connector->edid_blob_ptr);
@@ -3857,12 +4196,25 @@ static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
return ret;
}
-static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj,
- struct drm_property *property,
- uint64_t value)
+/**
+ * drm_mode_plane_set_obj_prop - set the value of a property
+ * @plane: drm plane object to set property value for
+ * @property: property to set
+ * @value: value the property should be set to
+ *
+ * This functions sets a given property on a given plane object. This function
+ * calls the driver's ->set_property callback and changes the software state of
+ * the property if the callback succeeds.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
+ struct drm_property *property,
+ uint64_t value)
{
int ret = -EINVAL;
- struct drm_plane *plane = obj_to_plane(obj);
+ struct drm_mode_object *obj = &plane->base;
if (plane->funcs->set_property)
ret = plane->funcs->set_property(plane, property, value);
@@ -3871,6 +4223,7 @@ static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj,
return ret;
}
+EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
/**
* drm_mode_getproperty_ioctl - get the current value of a object's property
@@ -4009,7 +4362,8 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value);
break;
case DRM_MODE_OBJECT_PLANE:
- ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value);
+ ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj),
+ property, arg->value);
break;
}
@@ -4229,7 +4583,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
{
struct drm_mode_crtc_page_flip *page_flip = data;
struct drm_crtc *crtc;
- struct drm_framebuffer *fb = NULL, *old_fb = NULL;
+ struct drm_framebuffer *fb = NULL;
struct drm_pending_vblank_event *e = NULL;
unsigned long flags;
int ret = -EINVAL;
@@ -4245,7 +4599,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
if (!crtc)
return -ENOENT;
- drm_modeset_lock(&crtc->mutex, NULL);
+ drm_modeset_lock_crtc(crtc);
if (crtc->primary->fb == NULL) {
/* The framebuffer is currently unbound, presumably
* due to a hotplug event, that userspace has not
@@ -4301,7 +4655,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
(void (*) (struct drm_pending_event *)) kfree;
}
- old_fb = crtc->primary->fb;
+ crtc->primary->old_fb = crtc->primary->fb;
ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
if (ret) {
if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
@@ -4311,7 +4665,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
kfree(e);
}
/* Keep the old fb, don't unref it. */
- old_fb = NULL;
+ crtc->primary->old_fb = NULL;
} else {
/*
* Warn if the driver hasn't properly updated the crtc->fb
@@ -4327,9 +4681,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
out:
if (fb)
drm_framebuffer_unreference(fb);
- if (old_fb)
- drm_framebuffer_unreference(old_fb);
- drm_modeset_unlock(&crtc->mutex);
+ if (crtc->primary->old_fb)
+ drm_framebuffer_unreference(crtc->primary->old_fb);
+ crtc->primary->old_fb = NULL;
+ drm_modeset_unlock_crtc(crtc);
return ret;
}
@@ -4345,9 +4700,14 @@ out:
void drm_mode_config_reset(struct drm_device *dev)
{
struct drm_crtc *crtc;
+ struct drm_plane *plane;
struct drm_encoder *encoder;
struct drm_connector *connector;
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head)
+ if (plane->funcs->reset)
+ plane->funcs->reset(plane);
+
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
if (crtc->funcs->reset)
crtc->funcs->reset(crtc);
@@ -4396,8 +4756,9 @@ int drm_mode_create_dumb_ioctl(struct drm_device *dev,
return -EINVAL;
/* overflow checks for 32bit size calculations */
+ /* NOTE: DIV_ROUND_UP() can overflow */
cpp = DIV_ROUND_UP(args->bpp, 8);
- if (cpp > 0xffffffffU / args->width)
+ if (!cpp || cpp > 0xffffffffU / args->width)
return -EINVAL;
stride = cpp * args->width;
if (args->height > 0xffffffffU / stride)
@@ -4680,6 +5041,36 @@ int drm_format_vert_chroma_subsampling(uint32_t format)
EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
/**
+ * drm_rotation_simplify() - Try to simplify the rotation
+ * @rotation: Rotation to be simplified
+ * @supported_rotations: Supported rotations
+ *
+ * Attempt to simplify the rotation to a form that is supported.
+ * Eg. if the hardware supports everything except DRM_REFLECT_X
+ * one could call this function like this:
+ *
+ * drm_rotation_simplify(rotation, BIT(DRM_ROTATE_0) |
+ * BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_180) |
+ * BIT(DRM_ROTATE_270) | BIT(DRM_REFLECT_Y));
+ *
+ * to eliminate the DRM_ROTATE_X flag. Depending on what kind of
+ * transforms the hardware supports, this function may not
+ * be able to produce a supported transform, so the caller should
+ * check the result afterwards.
+ */
+unsigned int drm_rotation_simplify(unsigned int rotation,
+ unsigned int supported_rotations)
+{
+ if (rotation & ~supported_rotations) {
+ rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y);
+ rotation = (rotation & ~0xf) | BIT((ffs(rotation & 0xf) + 1) % 4);
+ }
+
+ return rotation;
+}
+EXPORT_SYMBOL(drm_rotation_simplify);
+
+/**
* drm_mode_config_init - initialize DRM mode_configuration structure
* @dev: DRM device
*
@@ -4797,3 +5188,21 @@ void drm_mode_config_cleanup(struct drm_device *dev)
drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
}
EXPORT_SYMBOL(drm_mode_config_cleanup);
+
+struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev,
+ unsigned int supported_rotations)
+{
+ static const struct drm_prop_enum_list props[] = {
+ { DRM_ROTATE_0, "rotate-0" },
+ { DRM_ROTATE_90, "rotate-90" },
+ { DRM_ROTATE_180, "rotate-180" },
+ { DRM_ROTATE_270, "rotate-270" },
+ { DRM_REFLECT_X, "reflect-x" },
+ { DRM_REFLECT_Y, "reflect-y" },
+ };
+
+ return drm_property_create_bitmask(dev, 0, "rotation",
+ props, ARRAY_SIZE(props),
+ supported_rotations);
+}
+EXPORT_SYMBOL(drm_mode_create_rotation_property);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 78b37f3febd3..6c65a0a28fbd 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -818,6 +818,7 @@ void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
&fb->bits_per_pixel);
fb->pixel_format = mode_cmd->pixel_format;
+ fb->flags = mode_cmd->flags;
}
EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index b4b51d46f339..3bcf8e6a85b3 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -35,6 +35,8 @@
#include <linux/slab.h>
#include <linux/export.h>
#include <drm/drmP.h>
+#include <drm/drm_edid.h>
+#include "drm_internal.h"
#if defined(CONFIG_DEBUG_FS)
@@ -48,9 +50,7 @@ static const struct drm_info_list drm_debugfs_list[] = {
{"clients", drm_clients_info, 0},
{"bufs", drm_bufs_info, 0},
{"gem_names", drm_gem_name_info, DRIVER_GEM},
-#if DRM_DEBUG_CODE
{"vma", drm_vma_info, 0},
-#endif
};
#define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list)
@@ -237,5 +237,186 @@ int drm_debugfs_cleanup(struct drm_minor *minor)
return 0;
}
+static int connector_show(struct seq_file *m, void *data)
+{
+ struct drm_connector *connector = m->private;
+ const char *status;
+
+ switch (connector->force) {
+ case DRM_FORCE_ON:
+ status = "on\n";
+ break;
+
+ case DRM_FORCE_ON_DIGITAL:
+ status = "digital\n";
+ break;
+
+ case DRM_FORCE_OFF:
+ status = "off\n";
+ break;
+
+ case DRM_FORCE_UNSPECIFIED:
+ status = "unspecified\n";
+ break;
+
+ default:
+ return 0;
+ }
+
+ seq_puts(m, status);
+
+ return 0;
+}
+
+static int connector_open(struct inode *inode, struct file *file)
+{
+ struct drm_connector *dev = inode->i_private;
+
+ return single_open(file, connector_show, dev);
+}
+
+static ssize_t connector_write(struct file *file, const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ struct seq_file *m = file->private_data;
+ struct drm_connector *connector = m->private;
+ char buf[12];
+
+ if (len > sizeof(buf) - 1)
+ return -EINVAL;
+
+ if (copy_from_user(buf, ubuf, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ if (!strcmp(buf, "on"))
+ connector->force = DRM_FORCE_ON;
+ else if (!strcmp(buf, "digital"))
+ connector->force = DRM_FORCE_ON_DIGITAL;
+ else if (!strcmp(buf, "off"))
+ connector->force = DRM_FORCE_OFF;
+ else if (!strcmp(buf, "unspecified"))
+ connector->force = DRM_FORCE_UNSPECIFIED;
+ else
+ return -EINVAL;
+
+ return len;
+}
+
+static int edid_show(struct seq_file *m, void *data)
+{
+ struct drm_connector *connector = m->private;
+ struct drm_property_blob *edid = connector->edid_blob_ptr;
+
+ if (connector->override_edid && edid)
+ seq_write(m, edid->data, edid->length);
+
+ return 0;
+}
+
+static int edid_open(struct inode *inode, struct file *file)
+{
+ struct drm_connector *dev = inode->i_private;
+
+ return single_open(file, edid_show, dev);
+}
+
+static ssize_t edid_write(struct file *file, const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ struct seq_file *m = file->private_data;
+ struct drm_connector *connector = m->private;
+ char *buf;
+ struct edid *edid;
+ int ret;
+
+ buf = memdup_user(ubuf, len);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ edid = (struct edid *) buf;
+
+ if (len == 5 && !strncmp(buf, "reset", 5)) {
+ connector->override_edid = false;
+ ret = drm_mode_connector_update_edid_property(connector, NULL);
+ } else if (len < EDID_LENGTH ||
+ EDID_LENGTH * (1 + edid->extensions) > len)
+ ret = -EINVAL;
+ else {
+ connector->override_edid = false;
+ ret = drm_mode_connector_update_edid_property(connector, edid);
+ if (!ret)
+ connector->override_edid = true;
+ }
+
+ kfree(buf);
+
+ return (ret) ? ret : len;
+}
+
+static const struct file_operations drm_edid_fops = {
+ .owner = THIS_MODULE,
+ .open = edid_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = edid_write
+};
+
+
+static const struct file_operations drm_connector_fops = {
+ .owner = THIS_MODULE,
+ .open = connector_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = connector_write
+};
+
+int drm_debugfs_connector_add(struct drm_connector *connector)
+{
+ struct drm_minor *minor = connector->dev->primary;
+ struct dentry *root, *ent;
+
+ if (!minor->debugfs_root)
+ return -1;
+
+ root = debugfs_create_dir(connector->name, minor->debugfs_root);
+ if (!root)
+ return -ENOMEM;
+
+ connector->debugfs_entry = root;
+
+ /* force */
+ ent = debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector,
+ &drm_connector_fops);
+ if (!ent)
+ goto error;
+
+ /* edid */
+ ent = debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root,
+ connector, &drm_edid_fops);
+ if (!ent)
+ goto error;
+
+ return 0;
+
+error:
+ debugfs_remove_recursive(connector->debugfs_entry);
+ connector->debugfs_entry = NULL;
+ return -ENOMEM;
+}
+
+void drm_debugfs_connector_remove(struct drm_connector *connector)
+{
+ if (!connector->debugfs_entry)
+ return;
+
+ debugfs_remove_recursive(connector->debugfs_entry);
+
+ connector->debugfs_entry = NULL;
+}
+
#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/gpu/drm/drm_dma.c b/drivers/gpu/drm/drm_dma.c
index 8a140a953754..ea481800ef56 100644
--- a/drivers/gpu/drm/drm_dma.c
+++ b/drivers/gpu/drm/drm_dma.c
@@ -35,6 +35,7 @@
#include <linux/export.h>
#include <drm/drmP.h>
+#include "drm_legacy.h"
/**
* Initialize the DMA data.
@@ -124,7 +125,7 @@ void drm_legacy_dma_takedown(struct drm_device *dev)
*
* Resets the fields of \p buf.
*/
-void drm_free_buffer(struct drm_device *dev, struct drm_buf * buf)
+void drm_legacy_free_buffer(struct drm_device *dev, struct drm_buf * buf)
{
if (!buf)
return;
@@ -142,8 +143,8 @@ void drm_free_buffer(struct drm_device *dev, struct drm_buf * buf)
*
* Frees each buffer associated with \p file_priv not already on the hardware.
*/
-void drm_core_reclaim_buffers(struct drm_device *dev,
- struct drm_file *file_priv)
+void drm_legacy_reclaim_buffers(struct drm_device *dev,
+ struct drm_file *file_priv)
{
struct drm_device_dma *dma = dev->dma;
int i;
@@ -154,7 +155,7 @@ void drm_core_reclaim_buffers(struct drm_device *dev,
if (dma->buflist[i]->file_priv == file_priv) {
switch (dma->buflist[i]->list) {
case DRM_LIST_NONE:
- drm_free_buffer(dev, dma->buflist[i]);
+ drm_legacy_free_buffer(dev, dma->buflist[i]);
break;
case DRM_LIST_WAIT:
dma->buflist[i]->list = DRM_LIST_RECLAIM;
@@ -166,5 +167,3 @@ void drm_core_reclaim_buffers(struct drm_device *dev,
}
}
}
-
-EXPORT_SYMBOL(drm_core_reclaim_buffers);
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
new file mode 100644
index 000000000000..070f913d2dba
--- /dev/null
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -0,0 +1,2753 @@
+/*
+ * Copyright © 2014 Red Hat
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/i2c.h>
+#include <drm/drm_dp_mst_helper.h>
+#include <drm/drmP.h>
+
+#include <drm/drm_fixed.h>
+
+/**
+ * DOC: dp mst helper
+ *
+ * These functions contain parts of the DisplayPort 1.2a MultiStream Transport
+ * protocol. The helpers contain a topology manager and bandwidth manager.
+ * The helpers encapsulate the sending and received of sideband msgs.
+ */
+static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
+ char *buf);
+static int test_calc_pbn_mode(void);
+
+static void drm_dp_put_port(struct drm_dp_mst_port *port);
+
+static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
+ int id,
+ struct drm_dp_payload *payload);
+
+static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port,
+ int offset, int size, u8 *bytes);
+
+static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_branch *mstb);
+static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_branch *mstb,
+ struct drm_dp_mst_port *port);
+static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
+ u8 *guid);
+
+static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux);
+static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux);
+static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr);
+/* sideband msg handling */
+static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles)
+{
+ u8 bitmask = 0x80;
+ u8 bitshift = 7;
+ u8 array_index = 0;
+ int number_of_bits = num_nibbles * 4;
+ u8 remainder = 0;
+
+ while (number_of_bits != 0) {
+ number_of_bits--;
+ remainder <<= 1;
+ remainder |= (data[array_index] & bitmask) >> bitshift;
+ bitmask >>= 1;
+ bitshift--;
+ if (bitmask == 0) {
+ bitmask = 0x80;
+ bitshift = 7;
+ array_index++;
+ }
+ if ((remainder & 0x10) == 0x10)
+ remainder ^= 0x13;
+ }
+
+ number_of_bits = 4;
+ while (number_of_bits != 0) {
+ number_of_bits--;
+ remainder <<= 1;
+ if ((remainder & 0x10) != 0)
+ remainder ^= 0x13;
+ }
+
+ return remainder;
+}
+
+static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes)
+{
+ u8 bitmask = 0x80;
+ u8 bitshift = 7;
+ u8 array_index = 0;
+ int number_of_bits = number_of_bytes * 8;
+ u16 remainder = 0;
+
+ while (number_of_bits != 0) {
+ number_of_bits--;
+ remainder <<= 1;
+ remainder |= (data[array_index] & bitmask) >> bitshift;
+ bitmask >>= 1;
+ bitshift--;
+ if (bitmask == 0) {
+ bitmask = 0x80;
+ bitshift = 7;
+ array_index++;
+ }
+ if ((remainder & 0x100) == 0x100)
+ remainder ^= 0xd5;
+ }
+
+ number_of_bits = 8;
+ while (number_of_bits != 0) {
+ number_of_bits--;
+ remainder <<= 1;
+ if ((remainder & 0x100) != 0)
+ remainder ^= 0xd5;
+ }
+
+ return remainder & 0xff;
+}
+static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr)
+{
+ u8 size = 3;
+ size += (hdr->lct / 2);
+ return size;
+}
+
+static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
+ u8 *buf, int *len)
+{
+ int idx = 0;
+ int i;
+ u8 crc4;
+ buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf);
+ for (i = 0; i < (hdr->lct / 2); i++)
+ buf[idx++] = hdr->rad[i];
+ buf[idx++] = (hdr->broadcast << 7) | (hdr->path_msg << 6) |
+ (hdr->msg_len & 0x3f);
+ buf[idx++] = (hdr->somt << 7) | (hdr->eomt << 6) | (hdr->seqno << 4);
+
+ crc4 = drm_dp_msg_header_crc4(buf, (idx * 2) - 1);
+ buf[idx - 1] |= (crc4 & 0xf);
+
+ *len = idx;
+}
+
+static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
+ u8 *buf, int buflen, u8 *hdrlen)
+{
+ u8 crc4;
+ u8 len;
+ int i;
+ u8 idx;
+ if (buf[0] == 0)
+ return false;
+ len = 3;
+ len += ((buf[0] & 0xf0) >> 4) / 2;
+ if (len > buflen)
+ return false;
+ crc4 = drm_dp_msg_header_crc4(buf, (len * 2) - 1);
+
+ if ((crc4 & 0xf) != (buf[len - 1] & 0xf)) {
+ DRM_DEBUG_KMS("crc4 mismatch 0x%x 0x%x\n", crc4, buf[len - 1]);
+ return false;
+ }
+
+ hdr->lct = (buf[0] & 0xf0) >> 4;
+ hdr->lcr = (buf[0] & 0xf);
+ idx = 1;
+ for (i = 0; i < (hdr->lct / 2); i++)
+ hdr->rad[i] = buf[idx++];
+ hdr->broadcast = (buf[idx] >> 7) & 0x1;
+ hdr->path_msg = (buf[idx] >> 6) & 0x1;
+ hdr->msg_len = buf[idx] & 0x3f;
+ idx++;
+ hdr->somt = (buf[idx] >> 7) & 0x1;
+ hdr->eomt = (buf[idx] >> 6) & 0x1;
+ hdr->seqno = (buf[idx] >> 4) & 0x1;
+ idx++;
+ *hdrlen = idx;
+ return true;
+}
+
+static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
+ struct drm_dp_sideband_msg_tx *raw)
+{
+ int idx = 0;
+ int i;
+ u8 *buf = raw->msg;
+ buf[idx++] = req->req_type & 0x7f;
+
+ switch (req->req_type) {
+ case DP_ENUM_PATH_RESOURCES:
+ buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
+ idx++;
+ break;
+ case DP_ALLOCATE_PAYLOAD:
+ buf[idx] = (req->u.allocate_payload.port_number & 0xf) << 4 |
+ (req->u.allocate_payload.number_sdp_streams & 0xf);
+ idx++;
+ buf[idx] = (req->u.allocate_payload.vcpi & 0x7f);
+ idx++;
+ buf[idx] = (req->u.allocate_payload.pbn >> 8);
+ idx++;
+ buf[idx] = (req->u.allocate_payload.pbn & 0xff);
+ idx++;
+ for (i = 0; i < req->u.allocate_payload.number_sdp_streams / 2; i++) {
+ buf[idx] = ((req->u.allocate_payload.sdp_stream_sink[i * 2] & 0xf) << 4) |
+ (req->u.allocate_payload.sdp_stream_sink[i * 2 + 1] & 0xf);
+ idx++;
+ }
+ if (req->u.allocate_payload.number_sdp_streams & 1) {
+ i = req->u.allocate_payload.number_sdp_streams - 1;
+ buf[idx] = (req->u.allocate_payload.sdp_stream_sink[i] & 0xf) << 4;
+ idx++;
+ }
+ break;
+ case DP_QUERY_PAYLOAD:
+ buf[idx] = (req->u.query_payload.port_number & 0xf) << 4;
+ idx++;
+ buf[idx] = (req->u.query_payload.vcpi & 0x7f);
+ idx++;
+ break;
+ case DP_REMOTE_DPCD_READ:
+ buf[idx] = (req->u.dpcd_read.port_number & 0xf) << 4;
+ buf[idx] |= ((req->u.dpcd_read.dpcd_address & 0xf0000) >> 16) & 0xf;
+ idx++;
+ buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff00) >> 8;
+ idx++;
+ buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff);
+ idx++;
+ buf[idx] = (req->u.dpcd_read.num_bytes);
+ idx++;
+ break;
+
+ case DP_REMOTE_DPCD_WRITE:
+ buf[idx] = (req->u.dpcd_write.port_number & 0xf) << 4;
+ buf[idx] |= ((req->u.dpcd_write.dpcd_address & 0xf0000) >> 16) & 0xf;
+ idx++;
+ buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff00) >> 8;
+ idx++;
+ buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff);
+ idx++;
+ buf[idx] = (req->u.dpcd_write.num_bytes);
+ idx++;
+ memcpy(&buf[idx], req->u.dpcd_write.bytes, req->u.dpcd_write.num_bytes);
+ idx += req->u.dpcd_write.num_bytes;
+ break;
+ case DP_REMOTE_I2C_READ:
+ buf[idx] = (req->u.i2c_read.port_number & 0xf) << 4;
+ buf[idx] |= (req->u.i2c_read.num_transactions & 0x3);
+ idx++;
+ for (i = 0; i < (req->u.i2c_read.num_transactions & 0x3); i++) {
+ buf[idx] = req->u.i2c_read.transactions[i].i2c_dev_id & 0x7f;
+ idx++;
+ buf[idx] = req->u.i2c_read.transactions[i].num_bytes;
+ idx++;
+ memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes);
+ idx += req->u.i2c_read.transactions[i].num_bytes;
+
+ buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 5;
+ buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf);
+ idx++;
+ }
+ buf[idx] = (req->u.i2c_read.read_i2c_device_id) & 0x7f;
+ idx++;
+ buf[idx] = (req->u.i2c_read.num_bytes_read);
+ idx++;
+ break;
+
+ case DP_REMOTE_I2C_WRITE:
+ buf[idx] = (req->u.i2c_write.port_number & 0xf) << 4;
+ idx++;
+ buf[idx] = (req->u.i2c_write.write_i2c_device_id) & 0x7f;
+ idx++;
+ buf[idx] = (req->u.i2c_write.num_bytes);
+ idx++;
+ memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
+ idx += req->u.i2c_write.num_bytes;
+ break;
+ }
+ raw->cur_len = idx;
+}
+
+static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len)
+{
+ u8 crc4;
+ crc4 = drm_dp_msg_data_crc4(msg, len);
+ msg[len] = crc4;
+}
+
+static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body *rep,
+ struct drm_dp_sideband_msg_tx *raw)
+{
+ int idx = 0;
+ u8 *buf = raw->msg;
+
+ buf[idx++] = (rep->reply_type & 0x1) << 7 | (rep->req_type & 0x7f);
+
+ raw->cur_len = idx;
+}
+
+/* this adds a chunk of msg to the builder to get the final msg */
+static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg,
+ u8 *replybuf, u8 replybuflen, bool hdr)
+{
+ int ret;
+ u8 crc4;
+
+ if (hdr) {
+ u8 hdrlen;
+ struct drm_dp_sideband_msg_hdr recv_hdr;
+ ret = drm_dp_decode_sideband_msg_hdr(&recv_hdr, replybuf, replybuflen, &hdrlen);
+ if (ret == false) {
+ print_hex_dump(KERN_DEBUG, "failed hdr", DUMP_PREFIX_NONE, 16, 1, replybuf, replybuflen, false);
+ return false;
+ }
+
+ /* get length contained in this portion */
+ msg->curchunk_len = recv_hdr.msg_len;
+ msg->curchunk_hdrlen = hdrlen;
+
+ /* we have already gotten an somt - don't bother parsing */
+ if (recv_hdr.somt && msg->have_somt)
+ return false;
+
+ if (recv_hdr.somt) {
+ memcpy(&msg->initial_hdr, &recv_hdr, sizeof(struct drm_dp_sideband_msg_hdr));
+ msg->have_somt = true;
+ }
+ if (recv_hdr.eomt)
+ msg->have_eomt = true;
+
+ /* copy the bytes for the remainder of this header chunk */
+ msg->curchunk_idx = min(msg->curchunk_len, (u8)(replybuflen - hdrlen));
+ memcpy(&msg->chunk[0], replybuf + hdrlen, msg->curchunk_idx);
+ } else {
+ memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen);
+ msg->curchunk_idx += replybuflen;
+ }
+
+ if (msg->curchunk_idx >= msg->curchunk_len) {
+ /* do CRC */
+ crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1);
+ /* copy chunk into bigger msg */
+ memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1);
+ msg->curlen += msg->curchunk_len - 1;
+ }
+ return true;
+}
+
+static bool drm_dp_sideband_parse_link_address(struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+ int idx = 1;
+ int i;
+ memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16);
+ idx += 16;
+ repmsg->u.link_addr.nports = raw->msg[idx] & 0xf;
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+ for (i = 0; i < repmsg->u.link_addr.nports; i++) {
+ if (raw->msg[idx] & 0x80)
+ repmsg->u.link_addr.ports[i].input_port = 1;
+
+ repmsg->u.link_addr.ports[i].peer_device_type = (raw->msg[idx] >> 4) & 0x7;
+ repmsg->u.link_addr.ports[i].port_number = (raw->msg[idx] & 0xf);
+
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+ repmsg->u.link_addr.ports[i].mcs = (raw->msg[idx] >> 7) & 0x1;
+ repmsg->u.link_addr.ports[i].ddps = (raw->msg[idx] >> 6) & 0x1;
+ if (repmsg->u.link_addr.ports[i].input_port == 0)
+ repmsg->u.link_addr.ports[i].legacy_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+ if (repmsg->u.link_addr.ports[i].input_port == 0) {
+ repmsg->u.link_addr.ports[i].dpcd_revision = (raw->msg[idx]);
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+ memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16);
+ idx += 16;
+ if (idx > raw->curlen)
+ goto fail_len;
+ repmsg->u.link_addr.ports[i].num_sdp_streams = (raw->msg[idx] >> 4) & 0xf;
+ repmsg->u.link_addr.ports[i].num_sdp_stream_sinks = (raw->msg[idx] & 0xf);
+ idx++;
+
+ }
+ if (idx > raw->curlen)
+ goto fail_len;
+ }
+
+ return true;
+fail_len:
+ DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
+ return false;
+}
+
+static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+ int idx = 1;
+ repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf;
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+ repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx];
+ if (idx > raw->curlen)
+ goto fail_len;
+
+ memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
+ return true;
+fail_len:
+ DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
+ return false;
+}
+
+static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+ int idx = 1;
+ repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf;
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+ return true;
+fail_len:
+ DRM_DEBUG_KMS("parse length fail %d %d\n", idx, raw->curlen);
+ return false;
+}
+
+static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+ int idx = 1;
+
+ repmsg->u.remote_i2c_read_ack.port_number = (raw->msg[idx] & 0xf);
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+ repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx];
+ idx++;
+ /* TODO check */
+ memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);
+ return true;
+fail_len:
+ DRM_DEBUG_KMS("remote i2c reply parse length fail %d %d\n", idx, raw->curlen);
+ return false;
+}
+
+static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+ int idx = 1;
+ repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+ repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+ idx += 2;
+ if (idx > raw->curlen)
+ goto fail_len;
+ repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+ idx += 2;
+ if (idx > raw->curlen)
+ goto fail_len;
+ return true;
+fail_len:
+ DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen);
+ return false;
+}
+
+static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+ int idx = 1;
+ repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+ repmsg->u.allocate_payload.vcpi = raw->msg[idx];
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+ repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+ idx += 2;
+ if (idx > raw->curlen)
+ goto fail_len;
+ return true;
+fail_len:
+ DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen);
+ return false;
+}
+
+static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+ int idx = 1;
+ repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+ repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
+ idx += 2;
+ if (idx > raw->curlen)
+ goto fail_len;
+ return true;
+fail_len:
+ DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen);
+ return false;
+}
+
+static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_reply_body *msg)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->reply_type = (raw->msg[0] & 0x80) >> 7;
+ msg->req_type = (raw->msg[0] & 0x7f);
+
+ if (msg->reply_type) {
+ memcpy(msg->u.nak.guid, &raw->msg[1], 16);
+ msg->u.nak.reason = raw->msg[17];
+ msg->u.nak.nak_data = raw->msg[18];
+ return false;
+ }
+
+ switch (msg->req_type) {
+ case DP_LINK_ADDRESS:
+ return drm_dp_sideband_parse_link_address(raw, msg);
+ case DP_QUERY_PAYLOAD:
+ return drm_dp_sideband_parse_query_payload_ack(raw, msg);
+ case DP_REMOTE_DPCD_READ:
+ return drm_dp_sideband_parse_remote_dpcd_read(raw, msg);
+ case DP_REMOTE_DPCD_WRITE:
+ return drm_dp_sideband_parse_remote_dpcd_write(raw, msg);
+ case DP_REMOTE_I2C_READ:
+ return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg);
+ case DP_ENUM_PATH_RESOURCES:
+ return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg);
+ case DP_ALLOCATE_PAYLOAD:
+ return drm_dp_sideband_parse_allocate_payload_ack(raw, msg);
+ default:
+ DRM_ERROR("Got unknown reply 0x%02x\n", msg->req_type);
+ return false;
+ }
+}
+
+static bool drm_dp_sideband_parse_connection_status_notify(struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_req_body *msg)
+{
+ int idx = 1;
+
+ msg->u.conn_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+
+ memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16);
+ idx += 16;
+ if (idx > raw->curlen)
+ goto fail_len;
+
+ msg->u.conn_stat.legacy_device_plug_status = (raw->msg[idx] >> 6) & 0x1;
+ msg->u.conn_stat.displayport_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
+ msg->u.conn_stat.message_capability_status = (raw->msg[idx] >> 4) & 0x1;
+ msg->u.conn_stat.input_port = (raw->msg[idx] >> 3) & 0x1;
+ msg->u.conn_stat.peer_device_type = (raw->msg[idx] & 0x7);
+ idx++;
+ return true;
+fail_len:
+ DRM_DEBUG_KMS("connection status reply parse length fail %d %d\n", idx, raw->curlen);
+ return false;
+}
+
+static bool drm_dp_sideband_parse_resource_status_notify(struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_req_body *msg)
+{
+ int idx = 1;
+
+ msg->u.resource_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
+ idx++;
+ if (idx > raw->curlen)
+ goto fail_len;
+
+ memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16);
+ idx += 16;
+ if (idx > raw->curlen)
+ goto fail_len;
+
+ msg->u.resource_stat.available_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
+ idx++;
+ return true;
+fail_len:
+ DRM_DEBUG_KMS("resource status reply parse length fail %d %d\n", idx, raw->curlen);
+ return false;
+}
+
+static bool drm_dp_sideband_parse_req(struct drm_dp_sideband_msg_rx *raw,
+ struct drm_dp_sideband_msg_req_body *msg)
+{
+ memset(msg, 0, sizeof(*msg));
+ msg->req_type = (raw->msg[0] & 0x7f);
+
+ switch (msg->req_type) {
+ case DP_CONNECTION_STATUS_NOTIFY:
+ return drm_dp_sideband_parse_connection_status_notify(raw, msg);
+ case DP_RESOURCE_STATUS_NOTIFY:
+ return drm_dp_sideband_parse_resource_status_notify(raw, msg);
+ default:
+ DRM_ERROR("Got unknown request 0x%02x\n", msg->req_type);
+ return false;
+ }
+}
+
+static int build_dpcd_write(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes, u8 *bytes)
+{
+ struct drm_dp_sideband_msg_req_body req;
+
+ req.req_type = DP_REMOTE_DPCD_WRITE;
+ req.u.dpcd_write.port_number = port_num;
+ req.u.dpcd_write.dpcd_address = offset;
+ req.u.dpcd_write.num_bytes = num_bytes;
+ req.u.dpcd_write.bytes = bytes;
+ drm_dp_encode_sideband_req(&req, msg);
+
+ return 0;
+}
+
+static int build_link_address(struct drm_dp_sideband_msg_tx *msg)
+{
+ struct drm_dp_sideband_msg_req_body req;
+
+ req.req_type = DP_LINK_ADDRESS;
+ drm_dp_encode_sideband_req(&req, msg);
+ return 0;
+}
+
+static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg, int port_num)
+{
+ struct drm_dp_sideband_msg_req_body req;
+
+ req.req_type = DP_ENUM_PATH_RESOURCES;
+ req.u.port_num.port_number = port_num;
+ drm_dp_encode_sideband_req(&req, msg);
+ msg->path_msg = true;
+ return 0;
+}
+
+static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_num,
+ u8 vcpi, uint16_t pbn)
+{
+ struct drm_dp_sideband_msg_req_body req;
+ memset(&req, 0, sizeof(req));
+ req.req_type = DP_ALLOCATE_PAYLOAD;
+ req.u.allocate_payload.port_number = port_num;
+ req.u.allocate_payload.vcpi = vcpi;
+ req.u.allocate_payload.pbn = pbn;
+ drm_dp_encode_sideband_req(&req, msg);
+ msg->path_msg = true;
+ return 0;
+}
+
+static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_vcpi *vcpi)
+{
+ int ret, vcpi_ret;
+
+ mutex_lock(&mgr->payload_lock);
+ ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1);
+ if (ret > mgr->max_payloads) {
+ ret = -EINVAL;
+ DRM_DEBUG_KMS("out of payload ids %d\n", ret);
+ goto out_unlock;
+ }
+
+ vcpi_ret = find_first_zero_bit(&mgr->vcpi_mask, mgr->max_payloads + 1);
+ if (vcpi_ret > mgr->max_payloads) {
+ ret = -EINVAL;
+ DRM_DEBUG_KMS("out of vcpi ids %d\n", ret);
+ goto out_unlock;
+ }
+
+ set_bit(ret, &mgr->payload_mask);
+ set_bit(vcpi_ret, &mgr->vcpi_mask);
+ vcpi->vcpi = vcpi_ret + 1;
+ mgr->proposed_vcpis[ret - 1] = vcpi;
+out_unlock:
+ mutex_unlock(&mgr->payload_lock);
+ return ret;
+}
+
+static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
+ int vcpi)
+{
+ int i;
+ if (vcpi == 0)
+ return;
+
+ mutex_lock(&mgr->payload_lock);
+ DRM_DEBUG_KMS("putting payload %d\n", vcpi);
+ clear_bit(vcpi - 1, &mgr->vcpi_mask);
+
+ for (i = 0; i < mgr->max_payloads; i++) {
+ if (mgr->proposed_vcpis[i])
+ if (mgr->proposed_vcpis[i]->vcpi == vcpi) {
+ mgr->proposed_vcpis[i] = NULL;
+ clear_bit(i + 1, &mgr->payload_mask);
+ }
+ }
+ mutex_unlock(&mgr->payload_lock);
+}
+
+static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_sideband_msg_tx *txmsg)
+{
+ bool ret;
+ mutex_lock(&mgr->qlock);
+ ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX ||
+ txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT);
+ mutex_unlock(&mgr->qlock);
+ return ret;
+}
+
+static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
+ struct drm_dp_sideband_msg_tx *txmsg)
+{
+ struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+ int ret;
+
+ ret = wait_event_timeout(mgr->tx_waitq,
+ check_txmsg_state(mgr, txmsg),
+ (4 * HZ));
+ mutex_lock(&mstb->mgr->qlock);
+ if (ret > 0) {
+ if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
+ ret = -EIO;
+ goto out;
+ }
+ } else {
+ DRM_DEBUG_KMS("timedout msg send %p %d %d\n", txmsg, txmsg->state, txmsg->seqno);
+
+ /* dump some state */
+ ret = -EIO;
+
+ /* remove from q */
+ if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED ||
+ txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND) {
+ list_del(&txmsg->next);
+ }
+
+ if (txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND ||
+ txmsg->state == DRM_DP_SIDEBAND_TX_SENT) {
+ mstb->tx_slots[txmsg->seqno] = NULL;
+ }
+ }
+out:
+ mutex_unlock(&mgr->qlock);
+
+ return ret;
+}
+
+static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad)
+{
+ struct drm_dp_mst_branch *mstb;
+
+ mstb = kzalloc(sizeof(*mstb), GFP_KERNEL);
+ if (!mstb)
+ return NULL;
+
+ mstb->lct = lct;
+ if (lct > 1)
+ memcpy(mstb->rad, rad, lct / 2);
+ INIT_LIST_HEAD(&mstb->ports);
+ kref_init(&mstb->kref);
+ return mstb;
+}
+
+static void drm_dp_destroy_mst_branch_device(struct kref *kref)
+{
+ struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref);
+ struct drm_dp_mst_port *port, *tmp;
+ bool wake_tx = false;
+
+ cancel_work_sync(&mstb->mgr->work);
+
+ /*
+ * destroy all ports - don't need lock
+ * as there are no more references to the mst branch
+ * device at this point.
+ */
+ list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
+ list_del(&port->next);
+ drm_dp_put_port(port);
+ }
+
+ /* drop any tx slots msg */
+ mutex_lock(&mstb->mgr->qlock);
+ if (mstb->tx_slots[0]) {
+ mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+ mstb->tx_slots[0] = NULL;
+ wake_tx = true;
+ }
+ if (mstb->tx_slots[1]) {
+ mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+ mstb->tx_slots[1] = NULL;
+ wake_tx = true;
+ }
+ mutex_unlock(&mstb->mgr->qlock);
+
+ if (wake_tx)
+ wake_up(&mstb->mgr->tx_waitq);
+ kfree(mstb);
+}
+
+static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb)
+{
+ kref_put(&mstb->kref, drm_dp_destroy_mst_branch_device);
+}
+
+
+static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt)
+{
+ switch (old_pdt) {
+ case DP_PEER_DEVICE_DP_LEGACY_CONV:
+ case DP_PEER_DEVICE_SST_SINK:
+ /* remove i2c over sideband */
+ drm_dp_mst_unregister_i2c_bus(&port->aux);
+ break;
+ case DP_PEER_DEVICE_MST_BRANCHING:
+ drm_dp_put_mst_branch_device(port->mstb);
+ port->mstb = NULL;
+ break;
+ }
+}
+
+static void drm_dp_destroy_port(struct kref *kref)
+{
+ struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref);
+ struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+ if (!port->input) {
+ port->vcpi.num_slots = 0;
+ if (port->connector)
+ (*port->mgr->cbs->destroy_connector)(mgr, port->connector);
+ drm_dp_port_teardown_pdt(port, port->pdt);
+
+ if (!port->input && port->vcpi.vcpi > 0)
+ drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
+ }
+ kfree(port);
+
+ (*mgr->cbs->hotplug)(mgr);
+}
+
+static void drm_dp_put_port(struct drm_dp_mst_port *port)
+{
+ kref_put(&port->kref, drm_dp_destroy_port);
+}
+
+static struct drm_dp_mst_branch *drm_dp_mst_get_validated_mstb_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_branch *to_find)
+{
+ struct drm_dp_mst_port *port;
+ struct drm_dp_mst_branch *rmstb;
+ if (to_find == mstb) {
+ kref_get(&mstb->kref);
+ return mstb;
+ }
+ list_for_each_entry(port, &mstb->ports, next) {
+ if (port->mstb) {
+ rmstb = drm_dp_mst_get_validated_mstb_ref_locked(port->mstb, to_find);
+ if (rmstb)
+ return rmstb;
+ }
+ }
+ return NULL;
+}
+
+static struct drm_dp_mst_branch *drm_dp_get_validated_mstb_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb)
+{
+ struct drm_dp_mst_branch *rmstb = NULL;
+ mutex_lock(&mgr->lock);
+ if (mgr->mst_primary)
+ rmstb = drm_dp_mst_get_validated_mstb_ref_locked(mgr->mst_primary, mstb);
+ mutex_unlock(&mgr->lock);
+ return rmstb;
+}
+
+static struct drm_dp_mst_port *drm_dp_mst_get_port_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_port *to_find)
+{
+ struct drm_dp_mst_port *port, *mport;
+
+ list_for_each_entry(port, &mstb->ports, next) {
+ if (port == to_find) {
+ kref_get(&port->kref);
+ return port;
+ }
+ if (port->mstb) {
+ mport = drm_dp_mst_get_port_ref_locked(port->mstb, to_find);
+ if (mport)
+ return mport;
+ }
+ }
+ return NULL;
+}
+
+static struct drm_dp_mst_port *drm_dp_get_validated_port_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+ struct drm_dp_mst_port *rport = NULL;
+ mutex_lock(&mgr->lock);
+ if (mgr->mst_primary)
+ rport = drm_dp_mst_get_port_ref_locked(mgr->mst_primary, port);
+ mutex_unlock(&mgr->lock);
+ return rport;
+}
+
+static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num)
+{
+ struct drm_dp_mst_port *port;
+
+ list_for_each_entry(port, &mstb->ports, next) {
+ if (port->port_num == port_num) {
+ kref_get(&port->kref);
+ return port;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * calculate a new RAD for this MST branch device
+ * if parent has an LCT of 2 then it has 1 nibble of RAD,
+ * if parent has an LCT of 3 then it has 2 nibbles of RAD,
+ */
+static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
+ u8 *rad)
+{
+ int lct = port->parent->lct;
+ int shift = 4;
+ int idx = lct / 2;
+ if (lct > 1) {
+ memcpy(rad, port->parent->rad, idx);
+ shift = (lct % 2) ? 4 : 0;
+ } else
+ rad[0] = 0;
+
+ rad[idx] |= port->port_num << shift;
+ return lct + 1;
+}
+
+/*
+ * return sends link address for new mstb
+ */
+static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port)
+{
+ int ret;
+ u8 rad[6], lct;
+ bool send_link = false;
+ switch (port->pdt) {
+ case DP_PEER_DEVICE_DP_LEGACY_CONV:
+ case DP_PEER_DEVICE_SST_SINK:
+ /* add i2c over sideband */
+ ret = drm_dp_mst_register_i2c_bus(&port->aux);
+ break;
+ case DP_PEER_DEVICE_MST_BRANCHING:
+ lct = drm_dp_calculate_rad(port, rad);
+
+ port->mstb = drm_dp_add_mst_branch_device(lct, rad);
+ port->mstb->mgr = port->mgr;
+ port->mstb->port_parent = port;
+
+ send_link = true;
+ break;
+ }
+ return send_link;
+}
+
+static void drm_dp_check_port_guid(struct drm_dp_mst_branch *mstb,
+ struct drm_dp_mst_port *port)
+{
+ int ret;
+ if (port->dpcd_rev >= 0x12) {
+ port->guid_valid = drm_dp_validate_guid(mstb->mgr, port->guid);
+ if (!port->guid_valid) {
+ ret = drm_dp_send_dpcd_write(mstb->mgr,
+ port,
+ DP_GUID,
+ 16, port->guid);
+ port->guid_valid = true;
+ }
+ }
+}
+
+static void build_mst_prop_path(struct drm_dp_mst_port *port,
+ struct drm_dp_mst_branch *mstb,
+ char *proppath)
+{
+ int i;
+ char temp[8];
+ snprintf(proppath, 255, "mst:%d", mstb->mgr->conn_base_id);
+ for (i = 0; i < (mstb->lct - 1); i++) {
+ int shift = (i % 2) ? 0 : 4;
+ int port_num = mstb->rad[i / 2] >> shift;
+ snprintf(temp, 8, "-%d", port_num);
+ strncat(proppath, temp, 255);
+ }
+ snprintf(temp, 8, "-%d", port->port_num);
+ strncat(proppath, temp, 255);
+}
+
+static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
+ struct device *dev,
+ struct drm_dp_link_addr_reply_port *port_msg)
+{
+ struct drm_dp_mst_port *port;
+ bool ret;
+ bool created = false;
+ int old_pdt = 0;
+ int old_ddps = 0;
+ port = drm_dp_get_port(mstb, port_msg->port_number);
+ if (!port) {
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return;
+ kref_init(&port->kref);
+ port->parent = mstb;
+ port->port_num = port_msg->port_number;
+ port->mgr = mstb->mgr;
+ port->aux.name = "DPMST";
+ port->aux.dev = dev;
+ created = true;
+ } else {
+ old_pdt = port->pdt;
+ old_ddps = port->ddps;
+ }
+
+ port->pdt = port_msg->peer_device_type;
+ port->input = port_msg->input_port;
+ port->mcs = port_msg->mcs;
+ port->ddps = port_msg->ddps;
+ port->ldps = port_msg->legacy_device_plug_status;
+ port->dpcd_rev = port_msg->dpcd_revision;
+ port->num_sdp_streams = port_msg->num_sdp_streams;
+ port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks;
+ memcpy(port->guid, port_msg->peer_guid, 16);
+
+ /* manage mstb port lists with mgr lock - take a reference
+ for this list */
+ if (created) {
+ mutex_lock(&mstb->mgr->lock);
+ kref_get(&port->kref);
+ list_add(&port->next, &mstb->ports);
+ mutex_unlock(&mstb->mgr->lock);
+ }
+
+ if (old_ddps != port->ddps) {
+ if (port->ddps) {
+ drm_dp_check_port_guid(mstb, port);
+ if (!port->input)
+ drm_dp_send_enum_path_resources(mstb->mgr, mstb, port);
+ } else {
+ port->guid_valid = false;
+ port->available_pbn = 0;
+ }
+ }
+
+ if (old_pdt != port->pdt && !port->input) {
+ drm_dp_port_teardown_pdt(port, old_pdt);
+
+ ret = drm_dp_port_setup_pdt(port);
+ if (ret == true) {
+ drm_dp_send_link_address(mstb->mgr, port->mstb);
+ port->mstb->link_address_sent = true;
+ }
+ }
+
+ if (created && !port->input) {
+ char proppath[255];
+ build_mst_prop_path(port, mstb, proppath);
+ port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath);
+ }
+
+ /* put reference to this port */
+ drm_dp_put_port(port);
+}
+
+static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
+ struct drm_dp_connection_status_notify *conn_stat)
+{
+ struct drm_dp_mst_port *port;
+ int old_pdt;
+ int old_ddps;
+ bool dowork = false;
+ port = drm_dp_get_port(mstb, conn_stat->port_number);
+ if (!port)
+ return;
+
+ old_ddps = port->ddps;
+ old_pdt = port->pdt;
+ port->pdt = conn_stat->peer_device_type;
+ port->mcs = conn_stat->message_capability_status;
+ port->ldps = conn_stat->legacy_device_plug_status;
+ port->ddps = conn_stat->displayport_device_plug_status;
+
+ if (old_ddps != port->ddps) {
+ if (port->ddps) {
+ drm_dp_check_port_guid(mstb, port);
+ dowork = true;
+ } else {
+ port->guid_valid = false;
+ port->available_pbn = 0;
+ }
+ }
+ if (old_pdt != port->pdt && !port->input) {
+ drm_dp_port_teardown_pdt(port, old_pdt);
+
+ if (drm_dp_port_setup_pdt(port))
+ dowork = true;
+ }
+
+ drm_dp_put_port(port);
+ if (dowork)
+ queue_work(system_long_wq, &mstb->mgr->work);
+
+}
+
+static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr,
+ u8 lct, u8 *rad)
+{
+ struct drm_dp_mst_branch *mstb;
+ struct drm_dp_mst_port *port;
+ int i;
+ /* find the port by iterating down */
+ mstb = mgr->mst_primary;
+
+ for (i = 0; i < lct - 1; i++) {
+ int shift = (i % 2) ? 0 : 4;
+ int port_num = rad[i / 2] >> shift;
+
+ list_for_each_entry(port, &mstb->ports, next) {
+ if (port->port_num == port_num) {
+ if (!port->mstb) {
+ DRM_ERROR("failed to lookup MSTB with lct %d, rad %02x\n", lct, rad[0]);
+ return NULL;
+ }
+
+ mstb = port->mstb;
+ break;
+ }
+ }
+ }
+ kref_get(&mstb->kref);
+ return mstb;
+}
+
+static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_branch *mstb)
+{
+ struct drm_dp_mst_port *port;
+
+ if (!mstb->link_address_sent) {
+ drm_dp_send_link_address(mgr, mstb);
+ mstb->link_address_sent = true;
+ }
+ list_for_each_entry(port, &mstb->ports, next) {
+ if (port->input)
+ continue;
+
+ if (!port->ddps)
+ continue;
+
+ if (!port->available_pbn)
+ drm_dp_send_enum_path_resources(mgr, mstb, port);
+
+ if (port->mstb)
+ drm_dp_check_and_send_link_address(mgr, port->mstb);
+ }
+}
+
+static void drm_dp_mst_link_probe_work(struct work_struct *work)
+{
+ struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work);
+
+ drm_dp_check_and_send_link_address(mgr, mgr->mst_primary);
+
+}
+
+static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
+ u8 *guid)
+{
+ static u8 zero_guid[16];
+
+ if (!memcmp(guid, zero_guid, 16)) {
+ u64 salt = get_jiffies_64();
+ memcpy(&guid[0], &salt, sizeof(u64));
+ memcpy(&guid[8], &salt, sizeof(u64));
+ return false;
+ }
+ return true;
+}
+
+#if 0
+static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes)
+{
+ struct drm_dp_sideband_msg_req_body req;
+
+ req.req_type = DP_REMOTE_DPCD_READ;
+ req.u.dpcd_read.port_number = port_num;
+ req.u.dpcd_read.dpcd_address = offset;
+ req.u.dpcd_read.num_bytes = num_bytes;
+ drm_dp_encode_sideband_req(&req, msg);
+
+ return 0;
+}
+#endif
+
+static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr,
+ bool up, u8 *msg, int len)
+{
+ int ret;
+ int regbase = up ? DP_SIDEBAND_MSG_UP_REP_BASE : DP_SIDEBAND_MSG_DOWN_REQ_BASE;
+ int tosend, total, offset;
+ int retries = 0;
+
+retry:
+ total = len;
+ offset = 0;
+ do {
+ tosend = min3(mgr->max_dpcd_transaction_bytes, 16, total);
+
+ ret = drm_dp_dpcd_write(mgr->aux, regbase + offset,
+ &msg[offset],
+ tosend);
+ if (ret != tosend) {
+ if (ret == -EIO && retries < 5) {
+ retries++;
+ goto retry;
+ }
+ DRM_DEBUG_KMS("failed to dpcd write %d %d\n", tosend, ret);
+ WARN(1, "fail\n");
+
+ return -EIO;
+ }
+ offset += tosend;
+ total -= tosend;
+ } while (total > 0);
+ return 0;
+}
+
+static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr,
+ struct drm_dp_sideband_msg_tx *txmsg)
+{
+ struct drm_dp_mst_branch *mstb = txmsg->dst;
+
+ /* both msg slots are full */
+ if (txmsg->seqno == -1) {
+ if (mstb->tx_slots[0] && mstb->tx_slots[1]) {
+ DRM_DEBUG_KMS("%s: failed to find slot\n", __func__);
+ return -EAGAIN;
+ }
+ if (mstb->tx_slots[0] == NULL && mstb->tx_slots[1] == NULL) {
+ txmsg->seqno = mstb->last_seqno;
+ mstb->last_seqno ^= 1;
+ } else if (mstb->tx_slots[0] == NULL)
+ txmsg->seqno = 0;
+ else
+ txmsg->seqno = 1;
+ mstb->tx_slots[txmsg->seqno] = txmsg;
+ }
+ hdr->broadcast = 0;
+ hdr->path_msg = txmsg->path_msg;
+ hdr->lct = mstb->lct;
+ hdr->lcr = mstb->lct - 1;
+ if (mstb->lct > 1)
+ memcpy(hdr->rad, mstb->rad, mstb->lct / 2);
+ hdr->seqno = txmsg->seqno;
+ return 0;
+}
+/*
+ * process a single block of the next message in the sideband queue
+ */
+static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_sideband_msg_tx *txmsg,
+ bool up)
+{
+ u8 chunk[48];
+ struct drm_dp_sideband_msg_hdr hdr;
+ int len, space, idx, tosend;
+ int ret;
+
+ memset(&hdr, 0, sizeof(struct drm_dp_sideband_msg_hdr));
+
+ if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED) {
+ txmsg->seqno = -1;
+ txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND;
+ }
+
+ /* make hdr from dst mst - for replies use seqno
+ otherwise assign one */
+ ret = set_hdr_from_dst_qlock(&hdr, txmsg);
+ if (ret < 0)
+ return ret;
+
+ /* amount left to send in this message */
+ len = txmsg->cur_len - txmsg->cur_offset;
+
+ /* 48 - sideband msg size - 1 byte for data CRC, x header bytes */
+ space = 48 - 1 - drm_dp_calc_sb_hdr_size(&hdr);
+
+ tosend = min(len, space);
+ if (len == txmsg->cur_len)
+ hdr.somt = 1;
+ if (space >= len)
+ hdr.eomt = 1;
+
+
+ hdr.msg_len = tosend + 1;
+ drm_dp_encode_sideband_msg_hdr(&hdr, chunk, &idx);
+ memcpy(&chunk[idx], &txmsg->msg[txmsg->cur_offset], tosend);
+ /* add crc at end */
+ drm_dp_crc_sideband_chunk_req(&chunk[idx], tosend);
+ idx += tosend + 1;
+
+ ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx);
+ if (ret) {
+ DRM_DEBUG_KMS("sideband msg failed to send\n");
+ return ret;
+ }
+
+ txmsg->cur_offset += tosend;
+ if (txmsg->cur_offset == txmsg->cur_len) {
+ txmsg->state = DRM_DP_SIDEBAND_TX_SENT;
+ return 1;
+ }
+ return 0;
+}
+
+/* must be called holding qlock */
+static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
+{
+ struct drm_dp_sideband_msg_tx *txmsg;
+ int ret;
+
+ /* construct a chunk from the first msg in the tx_msg queue */
+ if (list_empty(&mgr->tx_msg_downq)) {
+ mgr->tx_down_in_progress = false;
+ return;
+ }
+ mgr->tx_down_in_progress = true;
+
+ txmsg = list_first_entry(&mgr->tx_msg_downq, struct drm_dp_sideband_msg_tx, next);
+ ret = process_single_tx_qlock(mgr, txmsg, false);
+ if (ret == 1) {
+ /* txmsg is sent it should be in the slots now */
+ list_del(&txmsg->next);
+ } else if (ret) {
+ DRM_DEBUG_KMS("failed to send msg in q %d\n", ret);
+ list_del(&txmsg->next);
+ if (txmsg->seqno != -1)
+ txmsg->dst->tx_slots[txmsg->seqno] = NULL;
+ txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+ wake_up(&mgr->tx_waitq);
+ }
+ if (list_empty(&mgr->tx_msg_downq)) {
+ mgr->tx_down_in_progress = false;
+ return;
+ }
+}
+
+/* called holding qlock */
+static void process_single_up_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
+{
+ struct drm_dp_sideband_msg_tx *txmsg;
+ int ret;
+
+ /* construct a chunk from the first msg in the tx_msg queue */
+ if (list_empty(&mgr->tx_msg_upq)) {
+ mgr->tx_up_in_progress = false;
+ return;
+ }
+
+ txmsg = list_first_entry(&mgr->tx_msg_upq, struct drm_dp_sideband_msg_tx, next);
+ ret = process_single_tx_qlock(mgr, txmsg, true);
+ if (ret == 1) {
+ /* up txmsgs aren't put in slots - so free after we send it */
+ list_del(&txmsg->next);
+ kfree(txmsg);
+ } else if (ret)
+ DRM_DEBUG_KMS("failed to send msg in q %d\n", ret);
+ mgr->tx_up_in_progress = true;
+}
+
+static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_sideband_msg_tx *txmsg)
+{
+ mutex_lock(&mgr->qlock);
+ list_add_tail(&txmsg->next, &mgr->tx_msg_downq);
+ if (!mgr->tx_down_in_progress)
+ process_single_down_tx_qlock(mgr);
+ mutex_unlock(&mgr->qlock);
+}
+
+static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_branch *mstb)
+{
+ int len;
+ struct drm_dp_sideband_msg_tx *txmsg;
+ int ret;
+
+ txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+ if (!txmsg)
+ return -ENOMEM;
+
+ txmsg->dst = mstb;
+ len = build_link_address(txmsg);
+
+ drm_dp_queue_down_tx(mgr, txmsg);
+
+ ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+ if (ret > 0) {
+ int i;
+
+ if (txmsg->reply.reply_type == 1)
+ DRM_DEBUG_KMS("link address nak received\n");
+ else {
+ DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports);
+ for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
+ DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n", i,
+ txmsg->reply.u.link_addr.ports[i].input_port,
+ txmsg->reply.u.link_addr.ports[i].peer_device_type,
+ txmsg->reply.u.link_addr.ports[i].port_number,
+ txmsg->reply.u.link_addr.ports[i].dpcd_revision,
+ txmsg->reply.u.link_addr.ports[i].mcs,
+ txmsg->reply.u.link_addr.ports[i].ddps,
+ txmsg->reply.u.link_addr.ports[i].legacy_device_plug_status,
+ txmsg->reply.u.link_addr.ports[i].num_sdp_streams,
+ txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks);
+ }
+ for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
+ drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]);
+ }
+ (*mgr->cbs->hotplug)(mgr);
+ }
+ } else
+ DRM_DEBUG_KMS("link address failed %d\n", ret);
+
+ kfree(txmsg);
+ return 0;
+}
+
+static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_branch *mstb,
+ struct drm_dp_mst_port *port)
+{
+ int len;
+ struct drm_dp_sideband_msg_tx *txmsg;
+ int ret;
+
+ txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+ if (!txmsg)
+ return -ENOMEM;
+
+ txmsg->dst = mstb;
+ len = build_enum_path_resources(txmsg, port->port_num);
+
+ drm_dp_queue_down_tx(mgr, txmsg);
+
+ ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+ if (ret > 0) {
+ if (txmsg->reply.reply_type == 1)
+ DRM_DEBUG_KMS("enum path resources nak received\n");
+ else {
+ if (port->port_num != txmsg->reply.u.path_resources.port_number)
+ DRM_ERROR("got incorrect port in response\n");
+ DRM_DEBUG_KMS("enum path resources %d: %d %d\n", txmsg->reply.u.path_resources.port_number, txmsg->reply.u.path_resources.full_payload_bw_number,
+ txmsg->reply.u.path_resources.avail_payload_bw_number);
+ port->available_pbn = txmsg->reply.u.path_resources.avail_payload_bw_number;
+ }
+ }
+
+ kfree(txmsg);
+ return 0;
+}
+
+static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port,
+ int id,
+ int pbn)
+{
+ struct drm_dp_sideband_msg_tx *txmsg;
+ struct drm_dp_mst_branch *mstb;
+ int len, ret;
+
+ mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
+ if (!mstb)
+ return -EINVAL;
+
+ txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+ if (!txmsg) {
+ ret = -ENOMEM;
+ goto fail_put;
+ }
+
+ txmsg->dst = mstb;
+ len = build_allocate_payload(txmsg, port->port_num,
+ id,
+ pbn);
+
+ drm_dp_queue_down_tx(mgr, txmsg);
+
+ ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+ if (ret > 0) {
+ if (txmsg->reply.reply_type == 1) {
+ ret = -EINVAL;
+ } else
+ ret = 0;
+ }
+ kfree(txmsg);
+fail_put:
+ drm_dp_put_mst_branch_device(mstb);
+ return ret;
+}
+
+static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
+ int id,
+ struct drm_dp_payload *payload)
+{
+ int ret;
+
+ ret = drm_dp_dpcd_write_payload(mgr, id, payload);
+ if (ret < 0) {
+ payload->payload_state = 0;
+ return ret;
+ }
+ payload->payload_state = DP_PAYLOAD_LOCAL;
+ return 0;
+}
+
+static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port,
+ int id,
+ struct drm_dp_payload *payload)
+{
+ int ret;
+ ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn);
+ if (ret < 0)
+ return ret;
+ payload->payload_state = DP_PAYLOAD_REMOTE;
+ return ret;
+}
+
+static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port,
+ int id,
+ struct drm_dp_payload *payload)
+{
+ DRM_DEBUG_KMS("\n");
+ /* its okay for these to fail */
+ if (port) {
+ drm_dp_payload_send_msg(mgr, port, id, 0);
+ }
+
+ drm_dp_dpcd_write_payload(mgr, id, payload);
+ payload->payload_state = DP_PAYLOAD_DELETE_LOCAL;
+ return 0;
+}
+
+static int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
+ int id,
+ struct drm_dp_payload *payload)
+{
+ payload->payload_state = 0;
+ return 0;
+}
+
+/**
+ * drm_dp_update_payload_part1() - Execute payload update part 1
+ * @mgr: manager to use.
+ *
+ * This iterates over all proposed virtual channels, and tries to
+ * allocate space in the link for them. For 0->slots transitions,
+ * this step just writes the VCPI to the MST device. For slots->0
+ * transitions, this writes the updated VCPIs and removes the
+ * remote VC payloads.
+ *
+ * after calling this the driver should generate ACT and payload
+ * packets.
+ */
+int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
+{
+ int i, j;
+ int cur_slots = 1;
+ struct drm_dp_payload req_payload;
+ struct drm_dp_mst_port *port;
+
+ mutex_lock(&mgr->payload_lock);
+ for (i = 0; i < mgr->max_payloads; i++) {
+ /* solve the current payloads - compare to the hw ones
+ - update the hw view */
+ req_payload.start_slot = cur_slots;
+ if (mgr->proposed_vcpis[i]) {
+ port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+ req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots;
+ } else {
+ port = NULL;
+ req_payload.num_slots = 0;
+ }
+
+ if (mgr->payloads[i].start_slot != req_payload.start_slot) {
+ mgr->payloads[i].start_slot = req_payload.start_slot;
+ }
+ /* work out what is required to happen with this payload */
+ if (mgr->payloads[i].num_slots != req_payload.num_slots) {
+
+ /* need to push an update for this payload */
+ if (req_payload.num_slots) {
+ drm_dp_create_payload_step1(mgr, mgr->proposed_vcpis[i]->vcpi, &req_payload);
+ mgr->payloads[i].num_slots = req_payload.num_slots;
+ } else if (mgr->payloads[i].num_slots) {
+ mgr->payloads[i].num_slots = 0;
+ drm_dp_destroy_payload_step1(mgr, port, port->vcpi.vcpi, &mgr->payloads[i]);
+ req_payload.payload_state = mgr->payloads[i].payload_state;
+ mgr->payloads[i].start_slot = 0;
+ }
+ mgr->payloads[i].payload_state = req_payload.payload_state;
+ }
+ cur_slots += req_payload.num_slots;
+ }
+
+ for (i = 0; i < mgr->max_payloads; i++) {
+ if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) {
+ DRM_DEBUG_KMS("removing payload %d\n", i);
+ for (j = i; j < mgr->max_payloads - 1; j++) {
+ memcpy(&mgr->payloads[j], &mgr->payloads[j + 1], sizeof(struct drm_dp_payload));
+ mgr->proposed_vcpis[j] = mgr->proposed_vcpis[j + 1];
+ if (mgr->proposed_vcpis[j] && mgr->proposed_vcpis[j]->num_slots) {
+ set_bit(j + 1, &mgr->payload_mask);
+ } else {
+ clear_bit(j + 1, &mgr->payload_mask);
+ }
+ }
+ memset(&mgr->payloads[mgr->max_payloads - 1], 0, sizeof(struct drm_dp_payload));
+ mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL;
+ clear_bit(mgr->max_payloads, &mgr->payload_mask);
+
+ }
+ }
+ mutex_unlock(&mgr->payload_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_dp_update_payload_part1);
+
+/**
+ * drm_dp_update_payload_part2() - Execute payload update part 2
+ * @mgr: manager to use.
+ *
+ * This iterates over all proposed virtual channels, and tries to
+ * allocate space in the link for them. For 0->slots transitions,
+ * this step writes the remote VC payload commands. For slots->0
+ * this just resets some internal state.
+ */
+int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
+{
+ struct drm_dp_mst_port *port;
+ int i;
+ int ret = 0;
+ mutex_lock(&mgr->payload_lock);
+ for (i = 0; i < mgr->max_payloads; i++) {
+
+ if (!mgr->proposed_vcpis[i])
+ continue;
+
+ port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+
+ DRM_DEBUG_KMS("payload %d %d\n", i, mgr->payloads[i].payload_state);
+ if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) {
+ ret = drm_dp_create_payload_step2(mgr, port, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]);
+ } else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) {
+ ret = drm_dp_destroy_payload_step2(mgr, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]);
+ }
+ if (ret) {
+ mutex_unlock(&mgr->payload_lock);
+ return ret;
+ }
+ }
+ mutex_unlock(&mgr->payload_lock);
+ return 0;
+}
+EXPORT_SYMBOL(drm_dp_update_payload_part2);
+
+#if 0 /* unused as of yet */
+static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port,
+ int offset, int size)
+{
+ int len;
+ struct drm_dp_sideband_msg_tx *txmsg;
+
+ txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+ if (!txmsg)
+ return -ENOMEM;
+
+ len = build_dpcd_read(txmsg, port->port_num, 0, 8);
+ txmsg->dst = port->parent;
+
+ drm_dp_queue_down_tx(mgr, txmsg);
+
+ return 0;
+}
+#endif
+
+static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port,
+ int offset, int size, u8 *bytes)
+{
+ int len;
+ int ret;
+ struct drm_dp_sideband_msg_tx *txmsg;
+ struct drm_dp_mst_branch *mstb;
+
+ mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
+ if (!mstb)
+ return -EINVAL;
+
+ txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+ if (!txmsg) {
+ ret = -ENOMEM;
+ goto fail_put;
+ }
+
+ len = build_dpcd_write(txmsg, port->port_num, offset, size, bytes);
+ txmsg->dst = mstb;
+
+ drm_dp_queue_down_tx(mgr, txmsg);
+
+ ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+ if (ret > 0) {
+ if (txmsg->reply.reply_type == 1) {
+ ret = -EINVAL;
+ } else
+ ret = 0;
+ }
+ kfree(txmsg);
+fail_put:
+ drm_dp_put_mst_branch_device(mstb);
+ return ret;
+}
+
+static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type)
+{
+ struct drm_dp_sideband_msg_reply_body reply;
+
+ reply.reply_type = 1;
+ reply.req_type = req_type;
+ drm_dp_encode_sideband_reply(&reply, msg);
+ return 0;
+}
+
+static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_branch *mstb,
+ int req_type, int seqno, bool broadcast)
+{
+ struct drm_dp_sideband_msg_tx *txmsg;
+
+ txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+ if (!txmsg)
+ return -ENOMEM;
+
+ txmsg->dst = mstb;
+ txmsg->seqno = seqno;
+ drm_dp_encode_up_ack_reply(txmsg, req_type);
+
+ mutex_lock(&mgr->qlock);
+ list_add_tail(&txmsg->next, &mgr->tx_msg_upq);
+ if (!mgr->tx_up_in_progress) {
+ process_single_up_tx_qlock(mgr);
+ }
+ mutex_unlock(&mgr->qlock);
+ return 0;
+}
+
+static int drm_dp_get_vc_payload_bw(int dp_link_bw, int dp_link_count)
+{
+ switch (dp_link_bw) {
+ case DP_LINK_BW_1_62:
+ return 3 * dp_link_count;
+ case DP_LINK_BW_2_7:
+ return 5 * dp_link_count;
+ case DP_LINK_BW_5_4:
+ return 10 * dp_link_count;
+ }
+ BUG();
+}
+
+/**
+ * drm_dp_mst_topology_mgr_set_mst() - Set the MST state for a topology manager
+ * @mgr: manager to set state for
+ * @mst_state: true to enable MST on this connector - false to disable.
+ *
+ * This is called by the driver when it detects an MST capable device plugged
+ * into a DP MST capable port, or when a DP MST capable device is unplugged.
+ */
+int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state)
+{
+ int ret = 0;
+ struct drm_dp_mst_branch *mstb = NULL;
+
+ mutex_lock(&mgr->lock);
+ if (mst_state == mgr->mst_state)
+ goto out_unlock;
+
+ mgr->mst_state = mst_state;
+ /* set the device into MST mode */
+ if (mst_state) {
+ WARN_ON(mgr->mst_primary);
+
+ /* get dpcd info */
+ ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
+ if (ret != DP_RECEIVER_CAP_SIZE) {
+ DRM_DEBUG_KMS("failed to read DPCD\n");
+ goto out_unlock;
+ }
+
+ mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr->dpcd[1], mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK);
+ mgr->total_pbn = 2560;
+ mgr->total_slots = DIV_ROUND_UP(mgr->total_pbn, mgr->pbn_div);
+ mgr->avail_slots = mgr->total_slots;
+
+ /* add initial branch device at LCT 1 */
+ mstb = drm_dp_add_mst_branch_device(1, NULL);
+ if (mstb == NULL) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+ mstb->mgr = mgr;
+
+ /* give this the main reference */
+ mgr->mst_primary = mstb;
+ kref_get(&mgr->mst_primary->kref);
+
+ {
+ struct drm_dp_payload reset_pay;
+ reset_pay.start_slot = 0;
+ reset_pay.num_slots = 0x3f;
+ drm_dp_dpcd_write_payload(mgr, 0, &reset_pay);
+ }
+
+ ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+ DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
+ if (ret < 0) {
+ goto out_unlock;
+ }
+
+
+ /* sort out guid */
+ ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, mgr->guid, 16);
+ if (ret != 16) {
+ DRM_DEBUG_KMS("failed to read DP GUID %d\n", ret);
+ goto out_unlock;
+ }
+
+ mgr->guid_valid = drm_dp_validate_guid(mgr, mgr->guid);
+ if (!mgr->guid_valid) {
+ ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, mgr->guid, 16);
+ mgr->guid_valid = true;
+ }
+
+ queue_work(system_long_wq, &mgr->work);
+
+ ret = 0;
+ } else {
+ /* disable MST on the device */
+ mstb = mgr->mst_primary;
+ mgr->mst_primary = NULL;
+ /* this can fail if the device is gone */
+ drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0);
+ ret = 0;
+ memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload));
+ mgr->payload_mask = 0;
+ set_bit(0, &mgr->payload_mask);
+ mgr->vcpi_mask = 0;
+ }
+
+out_unlock:
+ mutex_unlock(&mgr->lock);
+ if (mstb)
+ drm_dp_put_mst_branch_device(mstb);
+ return ret;
+
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst);
+
+/**
+ * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager
+ * @mgr: manager to suspend
+ *
+ * This function tells the MST device that we can't handle UP messages
+ * anymore. This should stop it from sending any since we are suspended.
+ */
+void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
+{
+ mutex_lock(&mgr->lock);
+ drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+ DP_MST_EN | DP_UPSTREAM_IS_SRC);
+ mutex_unlock(&mgr->lock);
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
+
+/**
+ * drm_dp_mst_topology_mgr_resume() - resume the MST manager
+ * @mgr: manager to resume
+ *
+ * This will fetch DPCD and see if the device is still there,
+ * if it is, it will rewrite the MSTM control bits, and return.
+ *
+ * if the device fails this returns -1, and the driver should do
+ * a full MST reprobe, in case we were undocked.
+ */
+int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr)
+{
+ int ret = 0;
+
+ mutex_lock(&mgr->lock);
+
+ if (mgr->mst_primary) {
+ int sret;
+ sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
+ if (sret != DP_RECEIVER_CAP_SIZE) {
+ DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
+ ret = -1;
+ goto out_unlock;
+ }
+
+ ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+ DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("mst write failed - undocked during suspend?\n");
+ ret = -1;
+ goto out_unlock;
+ }
+ ret = 0;
+ } else
+ ret = -1;
+
+out_unlock:
+ mutex_unlock(&mgr->lock);
+ return ret;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume);
+
+static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up)
+{
+ int len;
+ u8 replyblock[32];
+ int replylen, origlen, curreply;
+ int ret;
+ struct drm_dp_sideband_msg_rx *msg;
+ int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE;
+ msg = up ? &mgr->up_req_recv : &mgr->down_rep_recv;
+
+ len = min(mgr->max_dpcd_transaction_bytes, 16);
+ ret = drm_dp_dpcd_read(mgr->aux, basereg,
+ replyblock, len);
+ if (ret != len) {
+ DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret);
+ return;
+ }
+ ret = drm_dp_sideband_msg_build(msg, replyblock, len, true);
+ if (!ret) {
+ DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]);
+ return;
+ }
+ replylen = msg->curchunk_len + msg->curchunk_hdrlen;
+
+ origlen = replylen;
+ replylen -= len;
+ curreply = len;
+ while (replylen > 0) {
+ len = min3(replylen, mgr->max_dpcd_transaction_bytes, 16);
+ ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply,
+ replyblock, len);
+ if (ret != len) {
+ DRM_DEBUG_KMS("failed to read a chunk\n");
+ }
+ ret = drm_dp_sideband_msg_build(msg, replyblock, len, false);
+ if (ret == false)
+ DRM_DEBUG_KMS("failed to build sideband msg\n");
+ curreply += len;
+ replylen -= len;
+ }
+}
+
+static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
+{
+ int ret = 0;
+
+ drm_dp_get_one_sb_msg(mgr, false);
+
+ if (mgr->down_rep_recv.have_eomt) {
+ struct drm_dp_sideband_msg_tx *txmsg;
+ struct drm_dp_mst_branch *mstb;
+ int slot = -1;
+ mstb = drm_dp_get_mst_branch_device(mgr,
+ mgr->down_rep_recv.initial_hdr.lct,
+ mgr->down_rep_recv.initial_hdr.rad);
+
+ if (!mstb) {
+ DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->down_rep_recv.initial_hdr.lct);
+ memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+ return 0;
+ }
+
+ /* find the message */
+ slot = mgr->down_rep_recv.initial_hdr.seqno;
+ mutex_lock(&mgr->qlock);
+ txmsg = mstb->tx_slots[slot];
+ /* remove from slots */
+ mutex_unlock(&mgr->qlock);
+
+ if (!txmsg) {
+ DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n",
+ mstb,
+ mgr->down_rep_recv.initial_hdr.seqno,
+ mgr->down_rep_recv.initial_hdr.lct,
+ mgr->down_rep_recv.initial_hdr.rad[0],
+ mgr->down_rep_recv.msg[0]);
+ drm_dp_put_mst_branch_device(mstb);
+ memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+ return 0;
+ }
+
+ drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply);
+ if (txmsg->reply.reply_type == 1) {
+ DRM_DEBUG_KMS("Got NAK reply: req 0x%02x, reason 0x%02x, nak data 0x%02x\n", txmsg->reply.req_type, txmsg->reply.u.nak.reason, txmsg->reply.u.nak.nak_data);
+ }
+
+ memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+ drm_dp_put_mst_branch_device(mstb);
+
+ mutex_lock(&mgr->qlock);
+ txmsg->state = DRM_DP_SIDEBAND_TX_RX;
+ mstb->tx_slots[slot] = NULL;
+ mutex_unlock(&mgr->qlock);
+
+ wake_up(&mgr->tx_waitq);
+ }
+ return ret;
+}
+
+static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
+{
+ int ret = 0;
+ drm_dp_get_one_sb_msg(mgr, true);
+
+ if (mgr->up_req_recv.have_eomt) {
+ struct drm_dp_sideband_msg_req_body msg;
+ struct drm_dp_mst_branch *mstb;
+ bool seqno;
+ mstb = drm_dp_get_mst_branch_device(mgr,
+ mgr->up_req_recv.initial_hdr.lct,
+ mgr->up_req_recv.initial_hdr.rad);
+ if (!mstb) {
+ DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
+ memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+ return 0;
+ }
+
+ seqno = mgr->up_req_recv.initial_hdr.seqno;
+ drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
+
+ if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
+ drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false);
+ drm_dp_update_port(mstb, &msg.u.conn_stat);
+ DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
+ (*mgr->cbs->hotplug)(mgr);
+
+ } else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
+ drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false);
+ DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn);
+ }
+
+ drm_dp_put_mst_branch_device(mstb);
+ memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+ }
+ return ret;
+}
+
+/**
+ * drm_dp_mst_hpd_irq() - MST hotplug IRQ notify
+ * @mgr: manager to notify irq for.
+ * @esi: 4 bytes from SINK_COUNT_ESI
+ * @handled: whether the hpd interrupt was consumed or not
+ *
+ * This should be called from the driver when it detects a short IRQ,
+ * along with the value of the DEVICE_SERVICE_IRQ_VECTOR_ESI0. The
+ * topology manager will process the sideband messages received as a result
+ * of this.
+ */
+int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled)
+{
+ int ret = 0;
+ int sc;
+ *handled = false;
+ sc = esi[0] & 0x3f;
+
+ if (sc != mgr->sink_count) {
+ mgr->sink_count = sc;
+ *handled = true;
+ }
+
+ if (esi[1] & DP_DOWN_REP_MSG_RDY) {
+ ret = drm_dp_mst_handle_down_rep(mgr);
+ *handled = true;
+ }
+
+ if (esi[1] & DP_UP_REQ_MSG_RDY) {
+ ret |= drm_dp_mst_handle_up_req(mgr);
+ *handled = true;
+ }
+
+ drm_dp_mst_kick_tx(mgr);
+ return ret;
+}
+EXPORT_SYMBOL(drm_dp_mst_hpd_irq);
+
+/**
+ * drm_dp_mst_detect_port() - get connection status for an MST port
+ * @mgr: manager for this port
+ * @port: unverified pointer to a port
+ *
+ * This returns the current connection state for a port. It validates the
+ * port pointer still exists so the caller doesn't require a reference
+ */
+enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+ enum drm_connector_status status = connector_status_disconnected;
+
+ /* we need to search for the port in the mgr in case its gone */
+ port = drm_dp_get_validated_port_ref(mgr, port);
+ if (!port)
+ return connector_status_disconnected;
+
+ if (!port->ddps)
+ goto out;
+
+ switch (port->pdt) {
+ case DP_PEER_DEVICE_NONE:
+ case DP_PEER_DEVICE_MST_BRANCHING:
+ break;
+
+ case DP_PEER_DEVICE_SST_SINK:
+ status = connector_status_connected;
+ break;
+ case DP_PEER_DEVICE_DP_LEGACY_CONV:
+ if (port->ldps)
+ status = connector_status_connected;
+ break;
+ }
+out:
+ drm_dp_put_port(port);
+ return status;
+}
+EXPORT_SYMBOL(drm_dp_mst_detect_port);
+
+/**
+ * drm_dp_mst_get_edid() - get EDID for an MST port
+ * @connector: toplevel connector to get EDID for
+ * @mgr: manager for this port
+ * @port: unverified pointer to a port.
+ *
+ * This returns an EDID for the port connected to a connector,
+ * It validates the pointer still exists so the caller doesn't require a
+ * reference.
+ */
+struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+ struct edid *edid = NULL;
+
+ /* we need to search for the port in the mgr in case its gone */
+ port = drm_dp_get_validated_port_ref(mgr, port);
+ if (!port)
+ return NULL;
+
+ edid = drm_get_edid(connector, &port->aux.ddc);
+ drm_dp_put_port(port);
+ return edid;
+}
+EXPORT_SYMBOL(drm_dp_mst_get_edid);
+
+/**
+ * drm_dp_find_vcpi_slots() - find slots for this PBN value
+ * @mgr: manager to use
+ * @pbn: payload bandwidth to convert into slots.
+ */
+int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr,
+ int pbn)
+{
+ int num_slots;
+
+ num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+ if (num_slots > mgr->avail_slots)
+ return -ENOSPC;
+ return num_slots;
+}
+EXPORT_SYMBOL(drm_dp_find_vcpi_slots);
+
+static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_vcpi *vcpi, int pbn)
+{
+ int num_slots;
+ int ret;
+
+ num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+ if (num_slots > mgr->avail_slots)
+ return -ENOSPC;
+
+ vcpi->pbn = pbn;
+ vcpi->aligned_pbn = num_slots * mgr->pbn_div;
+ vcpi->num_slots = num_slots;
+
+ ret = drm_dp_mst_assign_payload_id(mgr, vcpi);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+/**
+ * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
+ * @mgr: manager for this port
+ * @port: port to allocate a virtual channel for.
+ * @pbn: payload bandwidth number to request
+ * @slots: returned number of slots for this PBN.
+ */
+bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn, int *slots)
+{
+ int ret;
+
+ port = drm_dp_get_validated_port_ref(mgr, port);
+ if (!port)
+ return false;
+
+ if (port->vcpi.vcpi > 0) {
+ DRM_DEBUG_KMS("payload: vcpi %d already allocated for pbn %d - requested pbn %d\n", port->vcpi.vcpi, port->vcpi.pbn, pbn);
+ if (pbn == port->vcpi.pbn) {
+ *slots = port->vcpi.num_slots;
+ return true;
+ }
+ }
+
+ ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn);
+ if (ret) {
+ DRM_DEBUG_KMS("failed to init vcpi %d %d %d\n", DIV_ROUND_UP(pbn, mgr->pbn_div), mgr->avail_slots, ret);
+ goto out;
+ }
+ DRM_DEBUG_KMS("initing vcpi for %d %d\n", pbn, port->vcpi.num_slots);
+ *slots = port->vcpi.num_slots;
+
+ drm_dp_put_port(port);
+ return true;
+out:
+ return false;
+}
+EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi);
+
+/**
+ * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI
+ * @mgr: manager for this port
+ * @port: unverified pointer to a port.
+ *
+ * This just resets the number of slots for the ports VCPI for later programming.
+ */
+void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+ port = drm_dp_get_validated_port_ref(mgr, port);
+ if (!port)
+ return;
+ port->vcpi.num_slots = 0;
+ drm_dp_put_port(port);
+}
+EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots);
+
+/**
+ * drm_dp_mst_deallocate_vcpi() - deallocate a VCPI
+ * @mgr: manager for this port
+ * @port: unverified port to deallocate vcpi for
+ */
+void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+ port = drm_dp_get_validated_port_ref(mgr, port);
+ if (!port)
+ return;
+
+ drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
+ port->vcpi.num_slots = 0;
+ port->vcpi.pbn = 0;
+ port->vcpi.aligned_pbn = 0;
+ port->vcpi.vcpi = 0;
+ drm_dp_put_port(port);
+}
+EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi);
+
+static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
+ int id, struct drm_dp_payload *payload)
+{
+ u8 payload_alloc[3], status;
+ int ret;
+ int retries = 0;
+
+ drm_dp_dpcd_writeb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS,
+ DP_PAYLOAD_TABLE_UPDATED);
+
+ payload_alloc[0] = id;
+ payload_alloc[1] = payload->start_slot;
+ payload_alloc[2] = payload->num_slots;
+
+ ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3);
+ if (ret != 3) {
+ DRM_DEBUG_KMS("failed to write payload allocation %d\n", ret);
+ goto fail;
+ }
+
+retry:
+ ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("failed to read payload table status %d\n", ret);
+ goto fail;
+ }
+
+ if (!(status & DP_PAYLOAD_TABLE_UPDATED)) {
+ retries++;
+ if (retries < 20) {
+ usleep_range(10000, 20000);
+ goto retry;
+ }
+ DRM_DEBUG_KMS("status not set after read payload table status %d\n", status);
+ ret = -EINVAL;
+ goto fail;
+ }
+ ret = 0;
+fail:
+ return ret;
+}
+
+
+/**
+ * drm_dp_check_act_status() - Check ACT handled status.
+ * @mgr: manager to use
+ *
+ * Check the payload status bits in the DPCD for ACT handled completion.
+ */
+int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr)
+{
+ u8 status;
+ int ret;
+ int count = 0;
+
+ do {
+ ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
+
+ if (ret < 0) {
+ DRM_DEBUG_KMS("failed to read payload table status %d\n", ret);
+ goto fail;
+ }
+
+ if (status & DP_PAYLOAD_ACT_HANDLED)
+ break;
+ count++;
+ udelay(100);
+
+ } while (count < 30);
+
+ if (!(status & DP_PAYLOAD_ACT_HANDLED)) {
+ DRM_DEBUG_KMS("failed to get ACT bit %d after %d retries\n", status, count);
+ ret = -EINVAL;
+ goto fail;
+ }
+ return 0;
+fail:
+ return ret;
+}
+EXPORT_SYMBOL(drm_dp_check_act_status);
+
+/**
+ * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode.
+ * @clock: dot clock for the mode
+ * @bpp: bpp for the mode.
+ *
+ * This uses the formula in the spec to calculate the PBN value for a mode.
+ */
+int drm_dp_calc_pbn_mode(int clock, int bpp)
+{
+ fixed20_12 pix_bw;
+ fixed20_12 fbpp;
+ fixed20_12 result;
+ fixed20_12 margin, tmp;
+ u32 res;
+
+ pix_bw.full = dfixed_const(clock);
+ fbpp.full = dfixed_const(bpp);
+ tmp.full = dfixed_const(8);
+ fbpp.full = dfixed_div(fbpp, tmp);
+
+ result.full = dfixed_mul(pix_bw, fbpp);
+ margin.full = dfixed_const(54);
+ tmp.full = dfixed_const(64);
+ margin.full = dfixed_div(margin, tmp);
+ result.full = dfixed_div(result, margin);
+
+ margin.full = dfixed_const(1006);
+ tmp.full = dfixed_const(1000);
+ margin.full = dfixed_div(margin, tmp);
+ result.full = dfixed_mul(result, margin);
+
+ result.full = dfixed_div(result, tmp);
+ result.full = dfixed_ceil(result);
+ res = dfixed_trunc(result);
+ return res;
+}
+EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
+
+static int test_calc_pbn_mode(void)
+{
+ int ret;
+ ret = drm_dp_calc_pbn_mode(154000, 30);
+ if (ret != 689)
+ return -EINVAL;
+ ret = drm_dp_calc_pbn_mode(234000, 30);
+ if (ret != 1047)
+ return -EINVAL;
+ return 0;
+}
+
+/* we want to kick the TX after we've ack the up/down IRQs. */
+static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr)
+{
+ queue_work(system_long_wq, &mgr->tx_work);
+}
+
+static void drm_dp_mst_dump_mstb(struct seq_file *m,
+ struct drm_dp_mst_branch *mstb)
+{
+ struct drm_dp_mst_port *port;
+ int tabs = mstb->lct;
+ char prefix[10];
+ int i;
+
+ for (i = 0; i < tabs; i++)
+ prefix[i] = '\t';
+ prefix[i] = '\0';
+
+ seq_printf(m, "%smst: %p, %d\n", prefix, mstb, mstb->num_ports);
+ list_for_each_entry(port, &mstb->ports, next) {
+ seq_printf(m, "%sport: %d: ddps: %d ldps: %d, %p, conn: %p\n", prefix, port->port_num, port->ddps, port->ldps, port, port->connector);
+ if (port->mstb)
+ drm_dp_mst_dump_mstb(m, port->mstb);
+ }
+}
+
+static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
+ char *buf)
+{
+ int ret;
+ int i;
+ for (i = 0; i < 4; i++) {
+ ret = drm_dp_dpcd_read(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS + (i * 16), &buf[i * 16], 16);
+ if (ret != 16)
+ break;
+ }
+ if (i == 4)
+ return true;
+ return false;
+}
+
+/**
+ * drm_dp_mst_dump_topology(): dump topology to seq file.
+ * @m: seq_file to dump output to
+ * @mgr: manager to dump current topology for.
+ *
+ * helper to dump MST topology to a seq file for debugfs.
+ */
+void drm_dp_mst_dump_topology(struct seq_file *m,
+ struct drm_dp_mst_topology_mgr *mgr)
+{
+ int i;
+ struct drm_dp_mst_port *port;
+ mutex_lock(&mgr->lock);
+ if (mgr->mst_primary)
+ drm_dp_mst_dump_mstb(m, mgr->mst_primary);
+
+ /* dump VCPIs */
+ mutex_unlock(&mgr->lock);
+
+ mutex_lock(&mgr->payload_lock);
+ seq_printf(m, "vcpi: %lx %lx\n", mgr->payload_mask, mgr->vcpi_mask);
+
+ for (i = 0; i < mgr->max_payloads; i++) {
+ if (mgr->proposed_vcpis[i]) {
+ port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+ seq_printf(m, "vcpi %d: %d %d %d\n", i, port->port_num, port->vcpi.vcpi, port->vcpi.num_slots);
+ } else
+ seq_printf(m, "vcpi %d:unsed\n", i);
+ }
+ for (i = 0; i < mgr->max_payloads; i++) {
+ seq_printf(m, "payload %d: %d, %d, %d\n",
+ i,
+ mgr->payloads[i].payload_state,
+ mgr->payloads[i].start_slot,
+ mgr->payloads[i].num_slots);
+
+
+ }
+ mutex_unlock(&mgr->payload_lock);
+
+ mutex_lock(&mgr->lock);
+ if (mgr->mst_primary) {
+ u8 buf[64];
+ bool bret;
+ int ret;
+ ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE);
+ seq_printf(m, "dpcd: ");
+ for (i = 0; i < DP_RECEIVER_CAP_SIZE; i++)
+ seq_printf(m, "%02x ", buf[i]);
+ seq_printf(m, "\n");
+ ret = drm_dp_dpcd_read(mgr->aux, DP_FAUX_CAP, buf, 2);
+ seq_printf(m, "faux/mst: ");
+ for (i = 0; i < 2; i++)
+ seq_printf(m, "%02x ", buf[i]);
+ seq_printf(m, "\n");
+ ret = drm_dp_dpcd_read(mgr->aux, DP_MSTM_CTRL, buf, 1);
+ seq_printf(m, "mst ctrl: ");
+ for (i = 0; i < 1; i++)
+ seq_printf(m, "%02x ", buf[i]);
+ seq_printf(m, "\n");
+
+ bret = dump_dp_payload_table(mgr, buf);
+ if (bret == true) {
+ seq_printf(m, "payload table: ");
+ for (i = 0; i < 63; i++)
+ seq_printf(m, "%02x ", buf[i]);
+ seq_printf(m, "\n");
+ }
+
+ }
+
+ mutex_unlock(&mgr->lock);
+
+}
+EXPORT_SYMBOL(drm_dp_mst_dump_topology);
+
+static void drm_dp_tx_work(struct work_struct *work)
+{
+ struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work);
+
+ mutex_lock(&mgr->qlock);
+ if (mgr->tx_down_in_progress)
+ process_single_down_tx_qlock(mgr);
+ mutex_unlock(&mgr->qlock);
+}
+
+/**
+ * drm_dp_mst_topology_mgr_init - initialise a topology manager
+ * @mgr: manager struct to initialise
+ * @dev: device providing this structure - for i2c addition.
+ * @aux: DP helper aux channel to talk to this device
+ * @max_dpcd_transaction_bytes: hw specific DPCD transaction limit
+ * @max_payloads: maximum number of payloads this GPU can source
+ * @conn_base_id: the connector object ID the MST device is connected to.
+ *
+ * Return 0 for success, or negative error code on failure
+ */
+int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
+ struct device *dev, struct drm_dp_aux *aux,
+ int max_dpcd_transaction_bytes,
+ int max_payloads, int conn_base_id)
+{
+ mutex_init(&mgr->lock);
+ mutex_init(&mgr->qlock);
+ mutex_init(&mgr->payload_lock);
+ INIT_LIST_HEAD(&mgr->tx_msg_upq);
+ INIT_LIST_HEAD(&mgr->tx_msg_downq);
+ INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
+ INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
+ init_waitqueue_head(&mgr->tx_waitq);
+ mgr->dev = dev;
+ mgr->aux = aux;
+ mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes;
+ mgr->max_payloads = max_payloads;
+ mgr->conn_base_id = conn_base_id;
+ mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL);
+ if (!mgr->payloads)
+ return -ENOMEM;
+ mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL);
+ if (!mgr->proposed_vcpis)
+ return -ENOMEM;
+ set_bit(0, &mgr->payload_mask);
+ test_calc_pbn_mode();
+ return 0;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
+
+/**
+ * drm_dp_mst_topology_mgr_destroy() - destroy topology manager.
+ * @mgr: manager to destroy
+ */
+void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
+{
+ mutex_lock(&mgr->payload_lock);
+ kfree(mgr->payloads);
+ mgr->payloads = NULL;
+ kfree(mgr->proposed_vcpis);
+ mgr->proposed_vcpis = NULL;
+ mutex_unlock(&mgr->payload_lock);
+ mgr->dev = NULL;
+ mgr->aux = NULL;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
+
+/* I2C device */
+static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
+ int num)
+{
+ struct drm_dp_aux *aux = adapter->algo_data;
+ struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux);
+ struct drm_dp_mst_branch *mstb;
+ struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+ unsigned int i;
+ bool reading = false;
+ struct drm_dp_sideband_msg_req_body msg;
+ struct drm_dp_sideband_msg_tx *txmsg = NULL;
+ int ret;
+
+ mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
+ if (!mstb)
+ return -EREMOTEIO;
+
+ /* construct i2c msg */
+ /* see if last msg is a read */
+ if (msgs[num - 1].flags & I2C_M_RD)
+ reading = true;
+
+ if (!reading) {
+ DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ msg.req_type = DP_REMOTE_I2C_READ;
+ msg.u.i2c_read.num_transactions = num - 1;
+ msg.u.i2c_read.port_number = port->port_num;
+ for (i = 0; i < num - 1; i++) {
+ msg.u.i2c_read.transactions[i].i2c_dev_id = msgs[i].addr;
+ msg.u.i2c_read.transactions[i].num_bytes = msgs[i].len;
+ msg.u.i2c_read.transactions[i].bytes = msgs[i].buf;
+ }
+ msg.u.i2c_read.read_i2c_device_id = msgs[num - 1].addr;
+ msg.u.i2c_read.num_bytes_read = msgs[num - 1].len;
+
+ txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+ if (!txmsg) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ txmsg->dst = mstb;
+ drm_dp_encode_sideband_req(&msg, txmsg);
+
+ drm_dp_queue_down_tx(mgr, txmsg);
+
+ ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+ if (ret > 0) {
+
+ if (txmsg->reply.reply_type == 1) { /* got a NAK back */
+ ret = -EREMOTEIO;
+ goto out;
+ }
+ if (txmsg->reply.u.remote_i2c_read_ack.num_bytes != msgs[num - 1].len) {
+ ret = -EIO;
+ goto out;
+ }
+ memcpy(msgs[num - 1].buf, txmsg->reply.u.remote_i2c_read_ack.bytes, msgs[num - 1].len);
+ ret = num;
+ }
+out:
+ kfree(txmsg);
+ drm_dp_put_mst_branch_device(mstb);
+ return ret;
+}
+
+static u32 drm_dp_mst_i2c_functionality(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+ I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+ I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+ I2C_FUNC_10BIT_ADDR;
+}
+
+static const struct i2c_algorithm drm_dp_mst_i2c_algo = {
+ .functionality = drm_dp_mst_i2c_functionality,
+ .master_xfer = drm_dp_mst_i2c_xfer,
+};
+
+/**
+ * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
+ * @aux: DisplayPort AUX channel
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux)
+{
+ aux->ddc.algo = &drm_dp_mst_i2c_algo;
+ aux->ddc.algo_data = aux;
+ aux->ddc.retries = 3;
+
+ aux->ddc.class = I2C_CLASS_DDC;
+ aux->ddc.owner = THIS_MODULE;
+ aux->ddc.dev.parent = aux->dev;
+ aux->ddc.dev.of_node = aux->dev->of_node;
+
+ strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev),
+ sizeof(aux->ddc.name));
+
+ return i2c_add_adapter(&aux->ddc);
+}
+
+/**
+ * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
+ * @aux: DisplayPort AUX channel
+ */
+static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux)
+{
+ i2c_del_adapter(&aux->ddc);
+}
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 8218078b6133..bc3da32d4585 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -1,31 +1,11 @@
-/**
- * \file drm_drv.c
- * Generic driver template
- *
- * \author Rickard E. (Rik) Faith <faith@valinux.com>
- * \author Gareth Hughes <gareth@valinux.com>
- *
- * To use this template, you must at least define the following (samples
- * given for the MGA driver):
- *
- * \code
- * #define DRIVER_AUTHOR "VA Linux Systems, Inc."
- *
- * #define DRIVER_NAME "mga"
- * #define DRIVER_DESC "Matrox G200/G400"
- * #define DRIVER_DATE "20001127"
- *
- * #define drm_x mga_##x
- * \endcode
- */
-
/*
- * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com
+ * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
*
- * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
- * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
+ * Author Rickard E. (Rik) Faith <faith@valinux.com>
+ *
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
@@ -40,432 +20,886 @@
* 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
- * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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.
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
*/
#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mount.h>
#include <linux/slab.h>
-#include <linux/export.h>
#include <drm/drmP.h>
#include <drm/drm_core.h>
+#include "drm_legacy.h"
+#include "drm_internal.h"
+unsigned int drm_debug = 0; /* 1 to enable debug output */
+EXPORT_SYMBOL(drm_debug);
-static int drm_version(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
-
-#define DRM_IOCTL_DEF(ioctl, _func, _flags) \
- [DRM_IOCTL_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .cmd_drv = 0, .name = #ioctl}
-
-/** Ioctl table */
-static const struct drm_ioctl_desc drm_ioctls[] = {
- DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED|DRM_RENDER_ALLOW),
- DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
- DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0),
- DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED|DRM_RENDER_ALLOW),
- DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0),
- DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER),
-
- DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER),
-
- DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_rmmap_ioctl, DRM_AUTH),
-
- DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_getsareactx, DRM_AUTH),
-
- DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY),
-
- DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_addctx, DRM_AUTH|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_GET_CTX, drm_getctx, DRM_AUTH),
- DRM_IOCTL_DEF(DRM_IOCTL_SWITCH_CTX, drm_switchctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_NEW_CTX, drm_newctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_RES_CTX, drm_resctx, DRM_AUTH),
-
- DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-
- DRM_IOCTL_DEF(DRM_IOCTL_LOCK, drm_lock, DRM_AUTH),
- DRM_IOCTL_DEF(DRM_IOCTL_UNLOCK, drm_unlock, DRM_AUTH),
-
- DRM_IOCTL_DEF(DRM_IOCTL_FINISH, drm_noop, DRM_AUTH),
-
- DRM_IOCTL_DEF(DRM_IOCTL_ADD_BUFS, drm_addbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_MARK_BUFS, drm_markbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_INFO_BUFS, drm_infobufs, DRM_AUTH),
- DRM_IOCTL_DEF(DRM_IOCTL_MAP_BUFS, drm_mapbufs, DRM_AUTH),
- DRM_IOCTL_DEF(DRM_IOCTL_FREE_BUFS, drm_freebufs, DRM_AUTH),
- DRM_IOCTL_DEF(DRM_IOCTL_DMA, drm_dma_ioctl, DRM_AUTH),
-
- DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-
-#if __OS_HAS_AGP
- DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_AGP_RELEASE, drm_agp_release_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_AGP_ENABLE, drm_agp_enable_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_AGP_INFO, drm_agp_info_ioctl, DRM_AUTH),
- DRM_IOCTL_DEF(DRM_IOCTL_AGP_ALLOC, drm_agp_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_AGP_FREE, drm_agp_free_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_AGP_BIND, drm_agp_bind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_AGP_UNBIND, drm_agp_unbind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-#endif
-
- DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_sg_alloc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-
- DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED),
-
- DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0),
-
- DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
-
- DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
- DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED),
-
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-
- DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
- DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
-
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
-};
+MODULE_AUTHOR(CORE_AUTHOR);
+MODULE_DESCRIPTION(CORE_DESC);
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM_DESC(debug, "Enable debug output");
+MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
+MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
+MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
-#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
+module_param_named(debug, drm_debug, int, 0600);
-/** File operations structure */
-static const struct file_operations drm_stub_fops = {
- .owner = THIS_MODULE,
- .open = drm_stub_open,
- .llseek = noop_llseek,
-};
+static DEFINE_SPINLOCK(drm_minor_lock);
+static struct idr drm_minors_idr;
-static int __init drm_core_init(void)
+struct class *drm_class;
+static struct dentry *drm_debugfs_root;
+
+void drm_err(const char *func, const char *format, ...)
{
- int ret = -ENOMEM;
+ struct va_format vaf;
+ va_list args;
- drm_global_init();
- drm_connector_ida_init();
- idr_init(&drm_minors_idr);
+ va_start(args, format);
- if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops))
- goto err_p1;
+ vaf.fmt = format;
+ vaf.va = &args;
- drm_class = drm_sysfs_create(THIS_MODULE, "drm");
- if (IS_ERR(drm_class)) {
- printk(KERN_ERR "DRM: Error creating drm class.\n");
- ret = PTR_ERR(drm_class);
- goto err_p2;
+ printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* %pV", func, &vaf);
+
+ va_end(args);
+}
+EXPORT_SYMBOL(drm_err);
+
+void drm_ut_debug_printk(const char *function_name, const char *format, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, format);
+ vaf.fmt = format;
+ vaf.va = &args;
+
+ printk(KERN_DEBUG "[" DRM_NAME ":%s] %pV", function_name, &vaf);
+
+ va_end(args);
+}
+EXPORT_SYMBOL(drm_ut_debug_printk);
+
+#define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */
+
+struct drm_master *drm_master_create(struct drm_minor *minor)
+{
+ struct drm_master *master;
+
+ master = kzalloc(sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return NULL;
+
+ kref_init(&master->refcount);
+ spin_lock_init(&master->lock.spinlock);
+ init_waitqueue_head(&master->lock.lock_queue);
+ if (drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER)) {
+ kfree(master);
+ return NULL;
}
+ INIT_LIST_HEAD(&master->magicfree);
+ master->minor = minor;
- drm_debugfs_root = debugfs_create_dir("dri", NULL);
- if (!drm_debugfs_root) {
- DRM_ERROR("Cannot create /sys/kernel/debug/dri\n");
- ret = -1;
- goto err_p3;
+ return master;
+}
+
+struct drm_master *drm_master_get(struct drm_master *master)
+{
+ kref_get(&master->refcount);
+ return master;
+}
+EXPORT_SYMBOL(drm_master_get);
+
+static void drm_master_destroy(struct kref *kref)
+{
+ struct drm_master *master = container_of(kref, struct drm_master, refcount);
+ struct drm_device *dev = master->minor->dev;
+ struct drm_map_list *r_list, *list_temp;
+
+ mutex_lock(&dev->struct_mutex);
+ if (dev->driver->master_destroy)
+ dev->driver->master_destroy(dev, master);
+
+ list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) {
+ if (r_list->master == master) {
+ drm_legacy_rmmap_locked(dev, r_list->map);
+ r_list = NULL;
+ }
}
- DRM_INFO("Initialized %s %d.%d.%d %s\n",
- CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE);
- return 0;
-err_p3:
- drm_sysfs_destroy();
-err_p2:
- unregister_chrdev(DRM_MAJOR, "drm");
+ if (master->unique) {
+ kfree(master->unique);
+ master->unique = NULL;
+ master->unique_len = 0;
+ }
- idr_destroy(&drm_minors_idr);
-err_p1:
- return ret;
+ drm_ht_remove(&master->magiclist);
+
+ mutex_unlock(&dev->struct_mutex);
+ kfree(master);
}
-static void __exit drm_core_exit(void)
+void drm_master_put(struct drm_master **master)
{
- debugfs_remove(drm_debugfs_root);
- drm_sysfs_destroy();
+ kref_put(&(*master)->refcount, drm_master_destroy);
+ *master = NULL;
+}
+EXPORT_SYMBOL(drm_master_put);
- unregister_chrdev(DRM_MAJOR, "drm");
+int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ int ret = 0;
- drm_connector_ida_destroy();
- idr_destroy(&drm_minors_idr);
+ mutex_lock(&dev->master_mutex);
+ if (file_priv->is_master)
+ goto out_unlock;
+
+ if (file_priv->minor->master) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (!file_priv->master) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ file_priv->minor->master = drm_master_get(file_priv->master);
+ file_priv->is_master = 1;
+ if (dev->driver->master_set) {
+ ret = dev->driver->master_set(dev, file_priv, false);
+ if (unlikely(ret != 0)) {
+ file_priv->is_master = 0;
+ drm_master_put(&file_priv->minor->master);
+ }
+ }
+
+out_unlock:
+ mutex_unlock(&dev->master_mutex);
+ return ret;
}
-module_init(drm_core_init);
-module_exit(drm_core_exit);
+int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ int ret = -EINVAL;
-/**
- * Copy and IOCTL return string to user space
+ mutex_lock(&dev->master_mutex);
+ if (!file_priv->is_master)
+ goto out_unlock;
+
+ if (!file_priv->minor->master)
+ goto out_unlock;
+
+ ret = 0;
+ if (dev->driver->master_drop)
+ dev->driver->master_drop(dev, file_priv, false);
+ drm_master_put(&file_priv->minor->master);
+ file_priv->is_master = 0;
+
+out_unlock:
+ mutex_unlock(&dev->master_mutex);
+ return ret;
+}
+
+/*
+ * DRM Minors
+ * A DRM device can provide several char-dev interfaces on the DRM-Major. Each
+ * of them is represented by a drm_minor object. Depending on the capabilities
+ * of the device-driver, different interfaces are registered.
+ *
+ * Minors can be accessed via dev->$minor_name. This pointer is either
+ * NULL or a valid drm_minor pointer and stays valid as long as the device is
+ * valid. This means, DRM minors have the same life-time as the underlying
+ * device. However, this doesn't mean that the minor is active. Minors are
+ * registered and unregistered dynamically according to device-state.
*/
-static int drm_copy_field(char *buf, size_t *buf_len, const char *value)
+
+static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
+ unsigned int type)
+{
+ switch (type) {
+ case DRM_MINOR_LEGACY:
+ return &dev->primary;
+ case DRM_MINOR_RENDER:
+ return &dev->render;
+ case DRM_MINOR_CONTROL:
+ return &dev->control;
+ default:
+ return NULL;
+ }
+}
+
+static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
+{
+ struct drm_minor *minor;
+ unsigned long flags;
+ int r;
+
+ minor = kzalloc(sizeof(*minor), GFP_KERNEL);
+ if (!minor)
+ return -ENOMEM;
+
+ minor->type = type;
+ minor->dev = dev;
+
+ idr_preload(GFP_KERNEL);
+ spin_lock_irqsave(&drm_minor_lock, flags);
+ r = idr_alloc(&drm_minors_idr,
+ NULL,
+ 64 * type,
+ 64 * (type + 1),
+ GFP_NOWAIT);
+ spin_unlock_irqrestore(&drm_minor_lock, flags);
+ idr_preload_end();
+
+ if (r < 0)
+ goto err_free;
+
+ minor->index = r;
+
+ minor->kdev = drm_sysfs_minor_alloc(minor);
+ if (IS_ERR(minor->kdev)) {
+ r = PTR_ERR(minor->kdev);
+ goto err_index;
+ }
+
+ *drm_minor_get_slot(dev, type) = minor;
+ return 0;
+
+err_index:
+ spin_lock_irqsave(&drm_minor_lock, flags);
+ idr_remove(&drm_minors_idr, minor->index);
+ spin_unlock_irqrestore(&drm_minor_lock, flags);
+err_free:
+ kfree(minor);
+ return r;
+}
+
+static void drm_minor_free(struct drm_device *dev, unsigned int type)
+{
+ struct drm_minor **slot, *minor;
+ unsigned long flags;
+
+ slot = drm_minor_get_slot(dev, type);
+ minor = *slot;
+ if (!minor)
+ return;
+
+ drm_mode_group_destroy(&minor->mode_group);
+ put_device(minor->kdev);
+
+ spin_lock_irqsave(&drm_minor_lock, flags);
+ idr_remove(&drm_minors_idr, minor->index);
+ spin_unlock_irqrestore(&drm_minor_lock, flags);
+
+ kfree(minor);
+ *slot = NULL;
+}
+
+static int drm_minor_register(struct drm_device *dev, unsigned int type)
{
- int len;
+ struct drm_minor *minor;
+ unsigned long flags;
+ int ret;
- /* don't overflow userbuf */
- len = strlen(value);
- if (len > *buf_len)
- len = *buf_len;
+ DRM_DEBUG("\n");
- /* let userspace know exact length of driver value (which could be
- * larger than the userspace-supplied buffer) */
- *buf_len = strlen(value);
+ minor = *drm_minor_get_slot(dev, type);
+ if (!minor)
+ return 0;
- /* finally, try filling in the userbuf */
- if (len && buf)
- if (copy_to_user(buf, value, len))
- return -EFAULT;
+ ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root);
+ if (ret) {
+ DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
+ return ret;
+ }
+
+ ret = device_add(minor->kdev);
+ if (ret)
+ goto err_debugfs;
+
+ /* replace NULL with @minor so lookups will succeed from now on */
+ spin_lock_irqsave(&drm_minor_lock, flags);
+ idr_replace(&drm_minors_idr, minor, minor->index);
+ spin_unlock_irqrestore(&drm_minor_lock, flags);
+
+ DRM_DEBUG("new minor registered %d\n", minor->index);
return 0;
+
+err_debugfs:
+ drm_debugfs_cleanup(minor);
+ return ret;
+}
+
+static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
+{
+ struct drm_minor *minor;
+ unsigned long flags;
+
+ minor = *drm_minor_get_slot(dev, type);
+ if (!minor || !device_is_registered(minor->kdev))
+ return;
+
+ /* replace @minor with NULL so lookups will fail from now on */
+ spin_lock_irqsave(&drm_minor_lock, flags);
+ idr_replace(&drm_minors_idr, NULL, minor->index);
+ spin_unlock_irqrestore(&drm_minor_lock, flags);
+
+ device_del(minor->kdev);
+ dev_set_drvdata(minor->kdev, NULL); /* safety belt */
+ drm_debugfs_cleanup(minor);
}
/**
- * Get version information
+ * drm_minor_acquire - Acquire a DRM minor
+ * @minor_id: Minor ID of the DRM-minor
+ *
+ * Looks up the given minor-ID and returns the respective DRM-minor object. The
+ * refence-count of the underlying device is increased so you must release this
+ * object with drm_minor_release().
*
- * \param inode device inode.
- * \param filp file pointer.
- * \param cmd command.
- * \param arg user argument, pointing to a drm_version structure.
- * \return zero on success or negative number on failure.
+ * As long as you hold this minor, it is guaranteed that the object and the
+ * minor->dev pointer will stay valid! However, the device may get unplugged and
+ * unregistered while you hold the minor.
*
- * Fills in the version information in \p arg.
+ * Returns:
+ * Pointer to minor-object with increased device-refcount, or PTR_ERR on
+ * failure.
*/
-static int drm_version(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+struct drm_minor *drm_minor_acquire(unsigned int minor_id)
{
- struct drm_version *version = data;
- int err;
+ struct drm_minor *minor;
+ unsigned long flags;
+
+ spin_lock_irqsave(&drm_minor_lock, flags);
+ minor = idr_find(&drm_minors_idr, minor_id);
+ if (minor)
+ drm_dev_ref(minor->dev);
+ spin_unlock_irqrestore(&drm_minor_lock, flags);
+
+ if (!minor) {
+ return ERR_PTR(-ENODEV);
+ } else if (drm_device_is_unplugged(minor->dev)) {
+ drm_dev_unref(minor->dev);
+ return ERR_PTR(-ENODEV);
+ }
- version->version_major = dev->driver->major;
- version->version_minor = dev->driver->minor;
- version->version_patchlevel = dev->driver->patchlevel;
- err = drm_copy_field(version->name, &version->name_len,
- dev->driver->name);
- if (!err)
- err = drm_copy_field(version->date, &version->date_len,
- dev->driver->date);
- if (!err)
- err = drm_copy_field(version->desc, &version->desc_len,
- dev->driver->desc);
+ return minor;
+}
- return err;
+/**
+ * drm_minor_release - Release DRM minor
+ * @minor: Pointer to DRM minor object
+ *
+ * Release a minor that was previously acquired via drm_minor_acquire().
+ */
+void drm_minor_release(struct drm_minor *minor)
+{
+ drm_dev_unref(minor->dev);
}
/**
- * drm_ioctl_permit - Check ioctl permissions against caller
+ * drm_put_dev - Unregister and release a DRM device
+ * @dev: DRM device
+ *
+ * Called at module unload time or when a PCI device is unplugged.
*
- * @flags: ioctl permission flags.
- * @file_priv: Pointer to struct drm_file identifying the caller.
+ * Use of this function is discouraged. It will eventually go away completely.
+ * Please use drm_dev_unregister() and drm_dev_unref() explicitly instead.
*
- * Checks whether the caller is allowed to run an ioctl with the
- * indicated permissions. If so, returns zero. Otherwise returns an
- * error code suitable for ioctl return.
+ * Cleans up all DRM device, calling drm_lastclose().
*/
-static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
+void drm_put_dev(struct drm_device *dev)
{
- /* ROOT_ONLY is only for CAP_SYS_ADMIN */
- if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)))
- return -EACCES;
-
- /* AUTH is only for authenticated or render client */
- if (unlikely((flags & DRM_AUTH) && !drm_is_render_client(file_priv) &&
- !file_priv->authenticated))
- return -EACCES;
-
- /* MASTER is only for master or control clients */
- if (unlikely((flags & DRM_MASTER) && !file_priv->is_master &&
- !drm_is_control_client(file_priv)))
- return -EACCES;
-
- /* Control clients must be explicitly allowed */
- if (unlikely(!(flags & DRM_CONTROL_ALLOW) &&
- drm_is_control_client(file_priv)))
- return -EACCES;
-
- /* Render clients must be explicitly allowed */
- if (unlikely(!(flags & DRM_RENDER_ALLOW) &&
- drm_is_render_client(file_priv)))
- return -EACCES;
+ DRM_DEBUG("\n");
- return 0;
+ if (!dev) {
+ DRM_ERROR("cleanup called no dev\n");
+ return;
+ }
+
+ drm_dev_unregister(dev);
+ drm_dev_unref(dev);
+}
+EXPORT_SYMBOL(drm_put_dev);
+
+void drm_unplug_dev(struct drm_device *dev)
+{
+ /* for a USB device */
+ drm_minor_unregister(dev, DRM_MINOR_LEGACY);
+ drm_minor_unregister(dev, DRM_MINOR_RENDER);
+ drm_minor_unregister(dev, DRM_MINOR_CONTROL);
+
+ mutex_lock(&drm_global_mutex);
+
+ drm_device_set_unplugged(dev);
+
+ if (dev->open_count == 0) {
+ drm_put_dev(dev);
+ }
+ mutex_unlock(&drm_global_mutex);
+}
+EXPORT_SYMBOL(drm_unplug_dev);
+
+/*
+ * DRM internal mount
+ * We want to be able to allocate our own "struct address_space" to control
+ * memory-mappings in VRAM (or stolen RAM, ...). However, core MM does not allow
+ * stand-alone address_space objects, so we need an underlying inode. As there
+ * is no way to allocate an independent inode easily, we need a fake internal
+ * VFS mount-point.
+ *
+ * The drm_fs_inode_new() function allocates a new inode, drm_fs_inode_free()
+ * frees it again. You are allowed to use iget() and iput() to get references to
+ * the inode. But each drm_fs_inode_new() call must be paired with exactly one
+ * drm_fs_inode_free() call (which does not have to be the last iput()).
+ * We use drm_fs_inode_*() to manage our internal VFS mount-point and share it
+ * between multiple inode-users. You could, technically, call
+ * iget() + drm_fs_inode_free() directly after alloc and sometime later do an
+ * iput(), but this way you'd end up with a new vfsmount for each inode.
+ */
+
+static int drm_fs_cnt;
+static struct vfsmount *drm_fs_mnt;
+
+static const struct dentry_operations drm_fs_dops = {
+ .d_dname = simple_dname,
+};
+
+static const struct super_operations drm_fs_sops = {
+ .statfs = simple_statfs,
+};
+
+static struct dentry *drm_fs_mount(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data)
+{
+ return mount_pseudo(fs_type,
+ "drm:",
+ &drm_fs_sops,
+ &drm_fs_dops,
+ 0x010203ff);
+}
+
+static struct file_system_type drm_fs_type = {
+ .name = "drm",
+ .owner = THIS_MODULE,
+ .mount = drm_fs_mount,
+ .kill_sb = kill_anon_super,
+};
+
+static struct inode *drm_fs_inode_new(void)
+{
+ struct inode *inode;
+ int r;
+
+ r = simple_pin_fs(&drm_fs_type, &drm_fs_mnt, &drm_fs_cnt);
+ if (r < 0) {
+ DRM_ERROR("Cannot mount pseudo fs: %d\n", r);
+ return ERR_PTR(r);
+ }
+
+ inode = alloc_anon_inode(drm_fs_mnt->mnt_sb);
+ if (IS_ERR(inode))
+ simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
+
+ return inode;
+}
+
+static void drm_fs_inode_free(struct inode *inode)
+{
+ if (inode) {
+ iput(inode);
+ simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
+ }
}
/**
- * Called whenever a process performs an ioctl on /dev/drm.
+ * drm_dev_alloc - Allocate new DRM device
+ * @driver: DRM driver to allocate device for
+ * @parent: Parent device object
+ *
+ * Allocate and initialize a new DRM device. No device registration is done.
+ * Call drm_dev_register() to advertice the device to user space and register it
+ * with other core subsystems.
*
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param arg user argument.
- * \return zero on success or negative number on failure.
+ * The initial ref-count of the object is 1. Use drm_dev_ref() and
+ * drm_dev_unref() to take and drop further ref-counts.
*
- * Looks up the ioctl function in the ::ioctls table, checking for root
- * previleges if so required, and dispatches to the respective function.
+ * RETURNS:
+ * Pointer to new DRM device, or NULL if out of memory.
*/
-long drm_ioctl(struct file *filp,
- unsigned int cmd, unsigned long arg)
+struct drm_device *drm_dev_alloc(struct drm_driver *driver,
+ struct device *parent)
{
- struct drm_file *file_priv = filp->private_data;
struct drm_device *dev;
- const struct drm_ioctl_desc *ioctl = NULL;
- drm_ioctl_t *func;
- unsigned int nr = DRM_IOCTL_NR(cmd);
- int retcode = -EINVAL;
- char stack_kdata[128];
- char *kdata = NULL;
- unsigned int usize, asize;
-
- dev = file_priv->minor->dev;
-
- if (drm_device_is_unplugged(dev))
- return -ENODEV;
-
- if ((nr >= DRM_CORE_IOCTL_COUNT) &&
- ((nr < DRM_COMMAND_BASE) || (nr >= DRM_COMMAND_END)))
- goto err_i1;
- if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) &&
- (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) {
- u32 drv_size;
- ioctl = &dev->driver->ioctls[nr - DRM_COMMAND_BASE];
- drv_size = _IOC_SIZE(ioctl->cmd_drv);
- usize = asize = _IOC_SIZE(cmd);
- if (drv_size > asize)
- asize = drv_size;
- cmd = ioctl->cmd_drv;
+ int ret;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ kref_init(&dev->ref);
+ dev->dev = parent;
+ dev->driver = driver;
+
+ INIT_LIST_HEAD(&dev->filelist);
+ INIT_LIST_HEAD(&dev->ctxlist);
+ INIT_LIST_HEAD(&dev->vmalist);
+ INIT_LIST_HEAD(&dev->maplist);
+ INIT_LIST_HEAD(&dev->vblank_event_list);
+
+ spin_lock_init(&dev->buf_lock);
+ spin_lock_init(&dev->event_lock);
+ mutex_init(&dev->struct_mutex);
+ mutex_init(&dev->ctxlist_mutex);
+ mutex_init(&dev->master_mutex);
+
+ dev->anon_inode = drm_fs_inode_new();
+ if (IS_ERR(dev->anon_inode)) {
+ ret = PTR_ERR(dev->anon_inode);
+ DRM_ERROR("Cannot allocate anonymous inode: %d\n", ret);
+ goto err_free;
}
- else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) {
- u32 drv_size;
-
- ioctl = &drm_ioctls[nr];
- drv_size = _IOC_SIZE(ioctl->cmd);
- usize = asize = _IOC_SIZE(cmd);
- if (drv_size > asize)
- asize = drv_size;
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ ret = drm_minor_alloc(dev, DRM_MINOR_CONTROL);
+ if (ret)
+ goto err_minors;
+ }
- cmd = ioctl->cmd;
- } else
- goto err_i1;
+ if (drm_core_check_feature(dev, DRIVER_RENDER)) {
+ ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
+ if (ret)
+ goto err_minors;
+ }
- DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n",
- task_pid_nr(current),
- (long)old_encode_dev(file_priv->minor->kdev->devt),
- file_priv->authenticated, ioctl->name);
+ ret = drm_minor_alloc(dev, DRM_MINOR_LEGACY);
+ if (ret)
+ goto err_minors;
- /* Do not trust userspace, use our own definition */
- func = ioctl->func;
+ if (drm_ht_create(&dev->map_hash, 12))
+ goto err_minors;
- if (unlikely(!func)) {
- DRM_DEBUG("no function\n");
- retcode = -EINVAL;
- goto err_i1;
+ ret = drm_legacy_ctxbitmap_init(dev);
+ if (ret) {
+ DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+ goto err_ht;
}
- retcode = drm_ioctl_permit(ioctl->flags, file_priv);
- if (unlikely(retcode))
- goto err_i1;
-
- if (cmd & (IOC_IN | IOC_OUT)) {
- if (asize <= sizeof(stack_kdata)) {
- kdata = stack_kdata;
- } else {
- kdata = kmalloc(asize, GFP_KERNEL);
- if (!kdata) {
- retcode = -ENOMEM;
- goto err_i1;
- }
+ if (drm_core_check_feature(dev, DRIVER_GEM)) {
+ ret = drm_gem_init(dev);
+ if (ret) {
+ DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n");
+ goto err_ctxbitmap;
}
- if (asize > usize)
- memset(kdata + usize, 0, asize - usize);
}
- if (cmd & IOC_IN) {
- if (copy_from_user(kdata, (void __user *)arg,
- usize) != 0) {
- retcode = -EFAULT;
- goto err_i1;
- }
- } else if (cmd & IOC_OUT) {
- memset(kdata, 0, usize);
- }
+ return dev;
+
+err_ctxbitmap:
+ drm_legacy_ctxbitmap_cleanup(dev);
+err_ht:
+ drm_ht_remove(&dev->map_hash);
+err_minors:
+ drm_minor_free(dev, DRM_MINOR_LEGACY);
+ drm_minor_free(dev, DRM_MINOR_RENDER);
+ drm_minor_free(dev, DRM_MINOR_CONTROL);
+ drm_fs_inode_free(dev->anon_inode);
+err_free:
+ mutex_destroy(&dev->master_mutex);
+ kfree(dev);
+ return NULL;
+}
+EXPORT_SYMBOL(drm_dev_alloc);
+
+static void drm_dev_release(struct kref *ref)
+{
+ struct drm_device *dev = container_of(ref, struct drm_device, ref);
+
+ if (drm_core_check_feature(dev, DRIVER_GEM))
+ drm_gem_destroy(dev);
+
+ drm_legacy_ctxbitmap_cleanup(dev);
+ drm_ht_remove(&dev->map_hash);
+ drm_fs_inode_free(dev->anon_inode);
+
+ drm_minor_free(dev, DRM_MINOR_LEGACY);
+ drm_minor_free(dev, DRM_MINOR_RENDER);
+ drm_minor_free(dev, DRM_MINOR_CONTROL);
+
+ mutex_destroy(&dev->master_mutex);
+ kfree(dev->unique);
+ kfree(dev);
+}
+
+/**
+ * drm_dev_ref - Take reference of a DRM device
+ * @dev: device to take reference of or NULL
+ *
+ * This increases the ref-count of @dev by one. You *must* already own a
+ * reference when calling this. Use drm_dev_unref() to drop this reference
+ * again.
+ *
+ * This function never fails. However, this function does not provide *any*
+ * guarantee whether the device is alive or running. It only provides a
+ * reference to the object and the memory associated with it.
+ */
+void drm_dev_ref(struct drm_device *dev)
+{
+ if (dev)
+ kref_get(&dev->ref);
+}
+EXPORT_SYMBOL(drm_dev_ref);
+
+/**
+ * drm_dev_unref - Drop reference of a DRM device
+ * @dev: device to drop reference of or NULL
+ *
+ * This decreases the ref-count of @dev by one. The device is destroyed if the
+ * ref-count drops to zero.
+ */
+void drm_dev_unref(struct drm_device *dev)
+{
+ if (dev)
+ kref_put(&dev->ref, drm_dev_release);
+}
+EXPORT_SYMBOL(drm_dev_unref);
+
+/**
+ * drm_dev_register - Register DRM device
+ * @dev: Device to register
+ * @flags: Flags passed to the driver's .load() function
+ *
+ * Register the DRM device @dev with the system, advertise device to user-space
+ * and start normal device operation. @dev must be allocated via drm_dev_alloc()
+ * previously.
+ *
+ * Never call this twice on any device!
+ *
+ * RETURNS:
+ * 0 on success, negative error code on failure.
+ */
+int drm_dev_register(struct drm_device *dev, unsigned long flags)
+{
+ int ret;
+
+ mutex_lock(&drm_global_mutex);
+
+ ret = drm_minor_register(dev, DRM_MINOR_CONTROL);
+ if (ret)
+ goto err_minors;
- if (ioctl->flags & DRM_UNLOCKED)
- retcode = func(dev, kdata, file_priv);
- else {
- mutex_lock(&drm_global_mutex);
- retcode = func(dev, kdata, file_priv);
- mutex_unlock(&drm_global_mutex);
+ ret = drm_minor_register(dev, DRM_MINOR_RENDER);
+ if (ret)
+ goto err_minors;
+
+ ret = drm_minor_register(dev, DRM_MINOR_LEGACY);
+ if (ret)
+ goto err_minors;
+
+ if (dev->driver->load) {
+ ret = dev->driver->load(dev, flags);
+ if (ret)
+ goto err_minors;
}
- if (cmd & IOC_OUT) {
- if (copy_to_user((void __user *)arg, kdata,
- usize) != 0)
- retcode = -EFAULT;
+ /* setup grouping for legacy outputs */
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ ret = drm_mode_group_init_legacy_group(dev,
+ &dev->primary->mode_group);
+ if (ret)
+ goto err_unload;
}
- err_i1:
- if (!ioctl)
- DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
- task_pid_nr(current),
- (long)old_encode_dev(file_priv->minor->kdev->devt),
- file_priv->authenticated, cmd, nr);
-
- if (kdata != stack_kdata)
- kfree(kdata);
- if (retcode)
- DRM_DEBUG("ret = %d\n", retcode);
- return retcode;
+ ret = 0;
+ goto out_unlock;
+
+err_unload:
+ if (dev->driver->unload)
+ dev->driver->unload(dev);
+err_minors:
+ drm_minor_unregister(dev, DRM_MINOR_LEGACY);
+ drm_minor_unregister(dev, DRM_MINOR_RENDER);
+ drm_minor_unregister(dev, DRM_MINOR_CONTROL);
+out_unlock:
+ mutex_unlock(&drm_global_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(drm_dev_register);
+
+/**
+ * drm_dev_unregister - Unregister DRM device
+ * @dev: Device to unregister
+ *
+ * Unregister the DRM device from the system. This does the reverse of
+ * drm_dev_register() but does not deallocate the device. The caller must call
+ * drm_dev_unref() to drop their final reference.
+ */
+void drm_dev_unregister(struct drm_device *dev)
+{
+ struct drm_map_list *r_list, *list_temp;
+
+ drm_lastclose(dev);
+
+ if (dev->driver->unload)
+ dev->driver->unload(dev);
+
+ if (dev->agp)
+ drm_pci_agp_destroy(dev);
+
+ drm_vblank_cleanup(dev);
+
+ list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
+ drm_legacy_rmmap(dev, r_list->map);
+
+ drm_minor_unregister(dev, DRM_MINOR_LEGACY);
+ drm_minor_unregister(dev, DRM_MINOR_RENDER);
+ drm_minor_unregister(dev, DRM_MINOR_CONTROL);
}
-EXPORT_SYMBOL(drm_ioctl);
+EXPORT_SYMBOL(drm_dev_unregister);
/**
- * drm_ioctl_flags - Check for core ioctl and return ioctl permission flags
+ * drm_dev_set_unique - Set the unique name of a DRM device
+ * @dev: device of which to set the unique name
+ * @fmt: format string for unique name
+ *
+ * Sets the unique name of a DRM device using the specified format string and
+ * a variable list of arguments. Drivers can use this at driver probe time if
+ * the unique name of the devices they drive is static.
*
- * @nr: Ioctl number.
- * @flags: Where to return the ioctl permission flags
+ * Return: 0 on success or a negative error code on failure.
*/
-bool drm_ioctl_flags(unsigned int nr, unsigned int *flags)
+int drm_dev_set_unique(struct drm_device *dev, const char *fmt, ...)
{
- if ((nr >= DRM_COMMAND_END && nr < DRM_CORE_IOCTL_COUNT) ||
- (nr < DRM_COMMAND_BASE)) {
- *flags = drm_ioctls[nr].flags;
- return true;
+ va_list ap;
+
+ kfree(dev->unique);
+
+ va_start(ap, fmt);
+ dev->unique = kvasprintf(GFP_KERNEL, fmt, ap);
+ va_end(ap);
+
+ return dev->unique ? 0 : -ENOMEM;
+}
+EXPORT_SYMBOL(drm_dev_set_unique);
+
+/*
+ * DRM Core
+ * The DRM core module initializes all global DRM objects and makes them
+ * available to drivers. Once setup, drivers can probe their respective
+ * devices.
+ * Currently, core management includes:
+ * - The "DRM-Global" key/value database
+ * - Global ID management for connectors
+ * - DRM major number allocation
+ * - DRM minor management
+ * - DRM sysfs class
+ * - DRM debugfs root
+ *
+ * Furthermore, the DRM core provides dynamic char-dev lookups. For each
+ * interface registered on a DRM device, you can request minor numbers from DRM
+ * core. DRM core takes care of major-number management and char-dev
+ * registration. A stub ->open() callback forwards any open() requests to the
+ * registered minor.
+ */
+
+static int drm_stub_open(struct inode *inode, struct file *filp)
+{
+ const struct file_operations *new_fops;
+ struct drm_minor *minor;
+ int err;
+
+ DRM_DEBUG("\n");
+
+ mutex_lock(&drm_global_mutex);
+ minor = drm_minor_acquire(iminor(inode));
+ if (IS_ERR(minor)) {
+ err = PTR_ERR(minor);
+ goto out_unlock;
+ }
+
+ new_fops = fops_get(minor->dev->driver->fops);
+ if (!new_fops) {
+ err = -ENODEV;
+ goto out_release;
}
- return false;
+ replace_fops(filp, new_fops);
+ if (filp->f_op->open)
+ err = filp->f_op->open(inode, filp);
+ else
+ err = 0;
+
+out_release:
+ drm_minor_release(minor);
+out_unlock:
+ mutex_unlock(&drm_global_mutex);
+ return err;
}
-EXPORT_SYMBOL(drm_ioctl_flags);
+
+static const struct file_operations drm_stub_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_stub_open,
+ .llseek = noop_llseek,
+};
+
+static int __init drm_core_init(void)
+{
+ int ret = -ENOMEM;
+
+ drm_global_init();
+ drm_connector_ida_init();
+ idr_init(&drm_minors_idr);
+
+ if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops))
+ goto err_p1;
+
+ drm_class = drm_sysfs_create(THIS_MODULE, "drm");
+ if (IS_ERR(drm_class)) {
+ printk(KERN_ERR "DRM: Error creating drm class.\n");
+ ret = PTR_ERR(drm_class);
+ goto err_p2;
+ }
+
+ drm_debugfs_root = debugfs_create_dir("dri", NULL);
+ if (!drm_debugfs_root) {
+ DRM_ERROR("Cannot create /sys/kernel/debug/dri\n");
+ ret = -1;
+ goto err_p3;
+ }
+
+ DRM_INFO("Initialized %s %d.%d.%d %s\n",
+ CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE);
+ return 0;
+err_p3:
+ drm_sysfs_destroy();
+err_p2:
+ unregister_chrdev(DRM_MAJOR, "drm");
+
+ idr_destroy(&drm_minors_idr);
+err_p1:
+ return ret;
+}
+
+static void __exit drm_core_exit(void)
+{
+ debugfs_remove(drm_debugfs_root);
+ drm_sysfs_destroy();
+
+ unregister_chrdev(DRM_MAJOR, "drm");
+
+ drm_connector_ida_destroy();
+ idr_destroy(&drm_minors_idr);
+}
+
+module_init(drm_core_init);
+module_exit(drm_core_exit);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index dfa9769b26b5..3bf999134bcc 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -632,27 +632,27 @@ static const struct drm_display_mode edid_cea_modes[] = {
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
DRM_MODE_FLAG_INTERLACE),
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
- /* 6 - 1440x480i@60Hz */
- { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
- 1602, 1716, 0, 480, 488, 494, 525, 0,
+ /* 6 - 720(1440)x480i@60Hz */
+ { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
+ 801, 858, 0, 480, 488, 494, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
- /* 7 - 1440x480i@60Hz */
- { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
- 1602, 1716, 0, 480, 488, 494, 525, 0,
+ /* 7 - 720(1440)x480i@60Hz */
+ { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
+ 801, 858, 0, 480, 488, 494, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
- /* 8 - 1440x240@60Hz */
- { DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
- 1602, 1716, 0, 240, 244, 247, 262, 0,
+ /* 8 - 720(1440)x240@60Hz */
+ { DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
+ 801, 858, 0, 240, 244, 247, 262, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_DBLCLK),
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
- /* 9 - 1440x240@60Hz */
- { DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
- 1602, 1716, 0, 240, 244, 247, 262, 0,
+ /* 9 - 720(1440)x240@60Hz */
+ { DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, 720, 739,
+ 801, 858, 0, 240, 244, 247, 262, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_DBLCLK),
.vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
@@ -714,27 +714,27 @@ static const struct drm_display_mode edid_cea_modes[] = {
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
DRM_MODE_FLAG_INTERLACE),
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
- /* 21 - 1440x576i@50Hz */
- { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
- 1590, 1728, 0, 576, 580, 586, 625, 0,
+ /* 21 - 720(1440)x576i@50Hz */
+ { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
+ 795, 864, 0, 576, 580, 586, 625, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
- /* 22 - 1440x576i@50Hz */
- { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
- 1590, 1728, 0, 576, 580, 586, 625, 0,
+ /* 22 - 720(1440)x576i@50Hz */
+ { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
+ 795, 864, 0, 576, 580, 586, 625, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
- /* 23 - 1440x288@50Hz */
- { DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
- 1590, 1728, 0, 288, 290, 293, 312, 0,
+ /* 23 - 720(1440)x288@50Hz */
+ { DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
+ 795, 864, 0, 288, 290, 293, 312, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_DBLCLK),
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
- /* 24 - 1440x288@50Hz */
- { DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
- 1590, 1728, 0, 288, 290, 293, 312, 0,
+ /* 24 - 720(1440)x288@50Hz */
+ { DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, 720, 732,
+ 795, 864, 0, 288, 290, 293, 312, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_DBLCLK),
.vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
@@ -837,17 +837,17 @@ static const struct drm_display_mode edid_cea_modes[] = {
796, 864, 0, 576, 581, 586, 625, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
- /* 44 - 1440x576i@100Hz */
- { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
- 1590, 1728, 0, 576, 580, 586, 625, 0,
+ /* 44 - 720(1440)x576i@100Hz */
+ { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
+ 795, 864, 0, 576, 580, 586, 625, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
- DRM_MODE_FLAG_DBLCLK),
+ DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
- /* 45 - 1440x576i@100Hz */
- { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
- 1590, 1728, 0, 576, 580, 586, 625, 0,
+ /* 45 - 720(1440)x576i@100Hz */
+ { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
+ 795, 864, 0, 576, 580, 586, 625, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
- DRM_MODE_FLAG_DBLCLK),
+ DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
/* 46 - 1920x1080i@120Hz */
{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
@@ -870,15 +870,15 @@ static const struct drm_display_mode edid_cea_modes[] = {
798, 858, 0, 480, 489, 495, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
.vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
- /* 50 - 1440x480i@120Hz */
- { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
- 1602, 1716, 0, 480, 488, 494, 525, 0,
+ /* 50 - 720(1440)x480i@120Hz */
+ { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739,
+ 801, 858, 0, 480, 488, 494, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
- /* 51 - 1440x480i@120Hz */
- { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
- 1602, 1716, 0, 480, 488, 494, 525, 0,
+ /* 51 - 720(1440)x480i@120Hz */
+ { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 27000, 720, 739,
+ 801, 858, 0, 480, 488, 494, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
@@ -892,15 +892,15 @@ static const struct drm_display_mode edid_cea_modes[] = {
796, 864, 0, 576, 581, 586, 625, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
.vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
- /* 54 - 1440x576i@200Hz */
- { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
- 1590, 1728, 0, 576, 580, 586, 625, 0,
+ /* 54 - 720(1440)x576i@200Hz */
+ { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
+ 795, 864, 0, 576, 580, 586, 625, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
- /* 55 - 1440x576i@200Hz */
- { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
- 1590, 1728, 0, 576, 580, 586, 625, 0,
+ /* 55 - 720(1440)x576i@200Hz */
+ { DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
+ 795, 864, 0, 576, 580, 586, 625, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
@@ -914,15 +914,15 @@ static const struct drm_display_mode edid_cea_modes[] = {
798, 858, 0, 480, 489, 495, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
.vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
- /* 58 - 1440x480i@240 */
- { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
- 1602, 1716, 0, 480, 488, 494, 525, 0,
+ /* 58 - 720(1440)x480i@240 */
+ { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739,
+ 801, 858, 0, 480, 488, 494, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
- /* 59 - 1440x480i@240 */
- { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
- 1602, 1716, 0, 480, 488, 494, 525, 0,
+ /* 59 - 720(1440)x480i@240 */
+ { DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 54000, 720, 739,
+ 801, 858, 0, 480, 488, 494, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK),
.vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
@@ -2103,7 +2103,8 @@ static int
add_inferred_modes(struct drm_connector *connector, struct edid *edid)
{
struct detailed_mode_closure closure = {
- connector, edid, 0, 0, 0
+ .connector = connector,
+ .edid = edid,
};
if (version_greater(edid, 1, 0))
@@ -2169,7 +2170,8 @@ add_established_modes(struct drm_connector *connector, struct edid *edid)
((edid->established_timings.mfg_rsvd & 0x80) << 9);
int i, modes = 0;
struct detailed_mode_closure closure = {
- connector, edid, 0, 0, 0
+ .connector = connector,
+ .edid = edid,
};
for (i = 0; i <= EDID_EST_TIMINGS; i++) {
@@ -2227,7 +2229,8 @@ add_standard_modes(struct drm_connector *connector, struct edid *edid)
{
int i, modes = 0;
struct detailed_mode_closure closure = {
- connector, edid, 0, 0, 0
+ .connector = connector,
+ .edid = edid,
};
for (i = 0; i < EDID_STD_TIMINGS; i++) {
@@ -2313,7 +2316,8 @@ static int
add_cvt_modes(struct drm_connector *connector, struct edid *edid)
{
struct detailed_mode_closure closure = {
- connector, edid, 0, 0, 0
+ .connector = connector,
+ .edid = edid,
};
if (version_greater(edid, 1, 2))
@@ -2357,11 +2361,10 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
u32 quirks)
{
struct detailed_mode_closure closure = {
- connector,
- edid,
- 1,
- quirks,
- 0
+ .connector = connector,
+ .edid = edid,
+ .preferred = 1,
+ .quirks = quirks,
};
if (closure.preferred && !version_greater(edid, 1, 3))
@@ -3305,6 +3308,7 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
struct drm_device *dev = encoder->dev;
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+ WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder == encoder && connector->eld[0])
@@ -3432,10 +3436,10 @@ EXPORT_SYMBOL(drm_rgb_quant_range_selectable);
/**
* drm_assign_hdmi_deep_color_info - detect whether monitor supports
* hdmi deep color modes and update drm_display_info if so.
- *
* @edid: monitor EDID information
* @info: Updated with maximum supported deep color bpc and color format
* if deep color supported.
+ * @connector: DRM connector, used only for debug output
*
* Parse the CEA extension according to CEA-861-B.
* Return true if HDMI deep color supported, false if not or unknown.
@@ -3775,8 +3779,14 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
- /* Populate picture aspect ratio from CEA mode list */
- if (frame->video_code > 0)
+ /*
+ * Populate picture aspect ratio from either
+ * user input (if specified) or from the CEA mode list.
+ */
+ if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 ||
+ mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9)
+ frame->picture_aspect = mode->picture_aspect_ratio;
+ else if (frame->video_code > 0)
frame->picture_aspect = drm_get_cea_aspect_ratio(
frame->video_code);
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index f27c883be391..cc0ae047ed3b 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -327,7 +327,7 @@ err_drm_gem_cma_free_object:
return ret;
}
-static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
+static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
.fb_probe = drm_fbdev_cma_create,
};
@@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
return ERR_PTR(-ENOMEM);
}
- fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
helper = &fbdev_cma->fb_helper;
+ drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
+
ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
if (ret < 0) {
dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index d5d8cea1a679..0c0c39bac23d 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list);
* helper functions used by many drivers to implement the kernel mode setting
* interfaces.
*
- * Initialization is done as a three-step process with drm_fb_helper_init(),
- * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
- * Drivers with fancier requirements than the default behaviour can override the
- * second step with their own code. Teardown is done with drm_fb_helper_fini().
+ * Initialization is done as a four-step process with drm_fb_helper_prepare(),
+ * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
+ * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
+ * default behaviour can override the third step with their own code.
+ * Teardown is done with drm_fb_helper_fini().
*
* At runtime drivers should restore the fbdev console by calling
* drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
@@ -63,6 +64,19 @@ static LIST_HEAD(kernel_fb_helper_list);
*
* All other functions exported by the fb helper library can be used to
* implement the fbdev driver interface by the driver.
+ *
+ * It is possible, though perhaps somewhat tricky, to implement race-free
+ * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
+ * helper must be called first to initialize the minimum required to make
+ * hotplug detection work. Drivers also need to make sure to properly set up
+ * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init()
+ * it is safe to enable interrupts and start processing hotplug events. At the
+ * same time, drivers should initialize all modeset objects such as CRTCs,
+ * encoders and connectors. To finish up the fbdev helper initialization, the
+ * drm_fb_helper_init() function is called. To probe for all attached displays
+ * and set up an initial configuration using the detected hardware, drivers
+ * should call drm_fb_helper_single_add_all_connectors() followed by
+ * drm_fb_helper_initial_config().
*/
/**
@@ -105,59 +119,57 @@ fail:
}
EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
-static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
+int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector)
{
- struct drm_fb_helper_connector *fb_helper_conn;
- int i;
+ struct drm_fb_helper_connector **temp;
+ struct drm_fb_helper_connector *fb_helper_connector;
- for (i = 0; i < fb_helper->connector_count; i++) {
- struct drm_cmdline_mode *mode;
- struct drm_connector *connector;
- char *option = NULL;
+ WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
+ if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
+ temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL);
+ if (!temp)
+ return -ENOMEM;
- fb_helper_conn = fb_helper->connector_info[i];
- connector = fb_helper_conn->connector;
- mode = &fb_helper_conn->cmdline_mode;
+ fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1;
+ fb_helper->connector_info = temp;
+ }
- /* do something on return - turn off connector maybe */
- if (fb_get_options(connector->name, &option))
- continue;
- if (drm_mode_parse_command_line_for_connector(option,
- connector,
- mode)) {
- if (mode->force) {
- const char *s;
- switch (mode->force) {
- case DRM_FORCE_OFF:
- s = "OFF";
- break;
- case DRM_FORCE_ON_DIGITAL:
- s = "ON - dig";
- break;
- default:
- case DRM_FORCE_ON:
- s = "ON";
- break;
- }
-
- DRM_INFO("forcing %s connector %s\n",
- connector->name, s);
- connector->force = mode->force;
- }
+ fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
+ if (!fb_helper_connector)
+ return -ENOMEM;
- DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
- connector->name,
- mode->xres, mode->yres,
- mode->refresh_specified ? mode->refresh : 60,
- mode->rb ? " reduced blanking" : "",
- mode->margins ? " with margins" : "",
- mode->interlace ? " interlaced" : "");
- }
+ fb_helper_connector->connector = connector;
+ fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
+ return 0;
+}
+EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
+
+int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
+ struct drm_connector *connector)
+{
+ struct drm_fb_helper_connector *fb_helper_connector;
+ int i, j;
+
+ WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
+
+ for (i = 0; i < fb_helper->connector_count; i++) {
+ if (fb_helper->connector_info[i]->connector == connector)
+ break;
+ }
+
+ if (i == fb_helper->connector_count)
+ return -EINVAL;
+ fb_helper_connector = fb_helper->connector_info[i];
+ for (j = i + 1; j < fb_helper->connector_count; j++) {
+ fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
}
+ fb_helper->connector_count--;
+ kfree(fb_helper_connector);
return 0;
}
+EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
{
@@ -199,9 +211,6 @@ int drm_fb_helper_debug_enter(struct fb_info *info)
struct drm_crtc_helper_funcs *funcs;
int i;
- if (list_empty(&kernel_fb_helper_list))
- return false;
-
list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
for (i = 0; i < helper->crtc_count; i++) {
struct drm_mode_set *mode_set =
@@ -282,10 +291,17 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
drm_warn_on_modeset_not_all_locked(dev);
- list_for_each_entry(plane, &dev->mode_config.plane_list, head)
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
if (plane->type != DRM_PLANE_TYPE_PRIMARY)
drm_plane_force_disable(plane);
+ if (dev->mode_config.rotation_property) {
+ drm_mode_plane_set_obj_prop(plane,
+ dev->mode_config.rotation_property,
+ BIT(DRM_ROTATE_0));
+ }
+ }
+
for (i = 0; i < fb_helper->crtc_count; i++) {
struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
struct drm_crtc *crtc = mode_set->crtc;
@@ -356,11 +372,11 @@ static bool drm_fb_helper_force_kernel_mode(void)
if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
continue;
- /* NOTE: we use lockless flag below to avoid grabbing other
- * modeset locks. So just trylock the underlying mutex
- * directly:
+ /*
+ * NOTE: Use trylock mode to avoid deadlocks and sleeping in
+ * panic context.
*/
- if (!mutex_trylock(&dev->mode_config.mutex)) {
+ if (__drm_modeset_lock_all(dev, true) != 0) {
error = true;
continue;
}
@@ -369,7 +385,7 @@ static bool drm_fb_helper_force_kernel_mode(void)
if (ret)
error = true;
- mutex_unlock(&dev->mode_config.mutex);
+ drm_modeset_unlock_all(dev);
}
return error;
}
@@ -531,6 +547,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
}
/**
+ * drm_fb_helper_prepare - setup a drm_fb_helper structure
+ * @dev: DRM device
+ * @helper: driver-allocated fbdev helper structure to set up
+ * @funcs: pointer to structure of functions associate with this helper
+ *
+ * Sets up the bare minimum to make the framebuffer helper usable. This is
+ * useful to implement race-free initialization of the polling helpers.
+ */
+void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
+ const struct drm_fb_helper_funcs *funcs)
+{
+ INIT_LIST_HEAD(&helper->kernel_fb_list);
+ helper->funcs = funcs;
+ helper->dev = dev;
+}
+EXPORT_SYMBOL(drm_fb_helper_prepare);
+
+/**
* drm_fb_helper_init - initialize a drm_fb_helper structure
* @dev: drm device
* @fb_helper: driver-allocated fbdev helper structure to initialize
@@ -542,8 +576,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
* nor register the fbdev. This is only done in drm_fb_helper_initial_config()
* to allow driver writes more control over the exact init sequence.
*
- * Drivers must set fb_helper->funcs before calling
- * drm_fb_helper_initial_config().
+ * Drivers must call drm_fb_helper_prepare() before calling this function.
*
* RETURNS:
* Zero if everything went ok, nonzero otherwise.
@@ -558,10 +591,6 @@ int drm_fb_helper_init(struct drm_device *dev,
if (!max_conn_count)
return -EINVAL;
- fb_helper->dev = dev;
-
- INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
-
fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
if (!fb_helper->crtc_info)
return -ENOMEM;
@@ -572,6 +601,7 @@ int drm_fb_helper_init(struct drm_device *dev,
kfree(fb_helper->crtc_info);
return -ENOMEM;
}
+ fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
fb_helper->connector_count = 0;
for (i = 0; i < crtc_count; i++) {
@@ -936,7 +966,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
struct drm_cmdline_mode *cmdline_mode;
- cmdline_mode = &fb_helper_conn->cmdline_mode;
+ cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
if (cmdline_mode->bpp_specified) {
switch (cmdline_mode->bpp) {
@@ -1056,7 +1086,6 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
info->fix.ypanstep = 1; /* doing it in hw */
info->fix.ywrapstep = 0;
info->fix.accel = FB_ACCEL_NONE;
- info->fix.type_aux = 0;
info->fix.line_length = pitch;
return;
@@ -1184,9 +1213,7 @@ EXPORT_SYMBOL(drm_has_preferred_mode);
static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
{
- struct drm_cmdline_mode *cmdline_mode;
- cmdline_mode = &fb_connector->cmdline_mode;
- return cmdline_mode->specified;
+ return fb_connector->connector->cmdline_mode.specified;
}
struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
@@ -1196,7 +1223,7 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f
struct drm_display_mode *mode = NULL;
bool prefer_non_interlace;
- cmdline_mode = &fb_helper_conn->cmdline_mode;
+ cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
if (cmdline_mode->specified == false)
return mode;
@@ -1581,8 +1608,6 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
struct drm_device *dev = fb_helper->dev;
int count = 0;
- drm_fb_helper_parse_command_line(fb_helper);
-
mutex_lock(&dev->mode_config.mutex);
count = drm_fb_helper_probe_connector_modes(fb_helper,
dev->mode_config.max_width,
@@ -1613,8 +1638,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
* either the output polling work or a work item launched from the driver's
* hotplug interrupt).
*
- * Note that the driver must ensure that this is only called _after_ the fb has
- * been fully set up, i.e. after the call to drm_fb_helper_initial_config.
+ * Note that drivers may call this even before calling
+ * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
+ * for a race-free fbcon setup and will make sure that the fbdev emulation will
+ * not miss any hotplug events.
*
* RETURNS:
* 0 on success and a non-zero error code otherwise.
@@ -1624,11 +1651,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
struct drm_device *dev = fb_helper->dev;
u32 max_width, max_height;
- if (!fb_helper->fb)
- return 0;
-
mutex_lock(&fb_helper->dev->mode_config.mutex);
- if (!drm_fb_helper_is_bound(fb_helper)) {
+ if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
fb_helper->delayed_hotplug = true;
mutex_unlock(&fb_helper->dev->mode_config.mutex);
return 0;
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 021fe5d11df5..ed7bc68f7e87 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -38,10 +38,11 @@
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include "drm_legacy.h"
+#include "drm_internal.h"
/* from BKL pushdown */
DEFINE_MUTEX(drm_global_mutex);
-EXPORT_SYMBOL(drm_global_mutex);
static int drm_open_helper(struct file *filp, struct drm_minor *minor);
@@ -112,55 +113,12 @@ err_undo:
EXPORT_SYMBOL(drm_open);
/**
- * File \c open operation.
- *
- * \param inode device inode.
- * \param filp file pointer.
- *
- * Puts the dev->fops corresponding to the device minor number into
- * \p filp, call the \c open method, and restore the file operations.
- */
-int drm_stub_open(struct inode *inode, struct file *filp)
-{
- struct drm_device *dev;
- struct drm_minor *minor;
- int err = -ENODEV;
- const struct file_operations *new_fops;
-
- DRM_DEBUG("\n");
-
- mutex_lock(&drm_global_mutex);
- minor = drm_minor_acquire(iminor(inode));
- if (IS_ERR(minor))
- goto out_unlock;
-
- dev = minor->dev;
- new_fops = fops_get(dev->driver->fops);
- if (!new_fops)
- goto out_release;
-
- replace_fops(filp, new_fops);
- if (filp->f_op->open)
- err = filp->f_op->open(inode, filp);
-
-out_release:
- drm_minor_release(minor);
-out_unlock:
- mutex_unlock(&drm_global_mutex);
- return err;
-}
-
-/**
* Check whether DRI will run on this CPU.
*
* \return non-zero if the DRI will run on this CPU, or zero otherwise.
*/
static int drm_cpu_valid(void)
{
-#if defined(__i386__)
- if (boot_cpu_data.x86 == 3)
- return 0; /* No cmpxchg on a 386 */
-#endif
#if defined(__sparc__) && !defined(__sparc_v9__)
return 0; /* No cmpxchg before v9 sparc. */
#endif
@@ -203,8 +161,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
priv->minor = minor;
/* for compatibility root is always authenticated */
- priv->always_authenticated = capable(CAP_SYS_ADMIN);
- priv->authenticated = priv->always_authenticated;
+ priv->authenticated = capable(CAP_SYS_ADMIN);
priv->lock_count = 0;
INIT_LIST_HEAD(&priv->lhead);
@@ -214,7 +171,7 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
init_waitqueue_head(&priv->event_wait);
priv->event_space = 4096; /* set aside 4k for event buffer */
- if (dev->driver->driver_features & DRIVER_GEM)
+ if (drm_core_check_feature(dev, DRIVER_GEM))
drm_gem_open(dev, priv);
if (drm_core_check_feature(dev, DRIVER_PRIME))
@@ -299,7 +256,7 @@ out_close:
out_prime_destroy:
if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_prime_destroy_file_private(&priv->prime);
- if (dev->driver->driver_features & DRIVER_GEM)
+ if (drm_core_check_feature(dev, DRIVER_GEM))
drm_gem_release(dev, priv);
put_pid(priv->pid);
kfree(priv);
@@ -311,11 +268,11 @@ static void drm_master_release(struct drm_device *dev, struct file *filp)
{
struct drm_file *file_priv = filp->private_data;
- if (drm_i_have_hw_lock(dev, file_priv)) {
+ if (drm_legacy_i_have_hw_lock(dev, file_priv)) {
DRM_DEBUG("File %p released, freeing lock for context %d\n",
filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
- drm_lock_free(&file_priv->master->lock,
- _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
+ drm_legacy_lock_free(&file_priv->master->lock,
+ _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
}
}
@@ -373,8 +330,6 @@ static void drm_legacy_dev_reinit(struct drm_device *dev)
*/
int drm_lastclose(struct drm_device * dev)
{
- struct drm_vma_entry *vma, *vma_temp;
-
DRM_DEBUG("\n");
if (dev->driver->lastclose)
@@ -389,13 +344,7 @@ int drm_lastclose(struct drm_device * dev)
drm_agp_clear(dev);
drm_legacy_sg_cleanup(dev);
-
- /* Clear vma list (only built for debugging) */
- list_for_each_entry_safe(vma, vma_temp, &dev->vmalist, head) {
- list_del(&vma->head);
- kfree(vma);
- }
-
+ drm_legacy_vma_flush(dev);
drm_legacy_dma_takedown(dev);
mutex_unlock(&dev->struct_mutex);
@@ -429,6 +378,10 @@ int drm_release(struct inode *inode, struct file *filp)
DRM_DEBUG("open_count = %d\n", dev->open_count);
+ mutex_lock(&dev->struct_mutex);
+ list_del(&file_priv->lhead);
+ mutex_unlock(&dev->struct_mutex);
+
if (dev->driver->preclose)
dev->driver->preclose(dev, file_priv);
@@ -451,54 +404,28 @@ int drm_release(struct inode *inode, struct file *filp)
drm_master_release(dev, filp);
if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
- drm_core_reclaim_buffers(dev, file_priv);
+ drm_legacy_reclaim_buffers(dev, file_priv);
drm_events_release(file_priv);
- if (dev->driver->driver_features & DRIVER_MODESET)
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_fb_release(file_priv);
- if (dev->driver->driver_features & DRIVER_GEM)
+ if (drm_core_check_feature(dev, DRIVER_GEM))
drm_gem_release(dev, file_priv);
- mutex_lock(&dev->ctxlist_mutex);
- if (!list_empty(&dev->ctxlist)) {
- struct drm_ctx_list *pos, *n;
-
- list_for_each_entry_safe(pos, n, &dev->ctxlist, head) {
- if (pos->tag == file_priv &&
- pos->handle != DRM_KERNEL_CONTEXT) {
- if (dev->driver->context_dtor)
- dev->driver->context_dtor(dev,
- pos->handle);
-
- drm_ctxbitmap_free(dev, pos->handle);
-
- list_del(&pos->head);
- kfree(pos);
- }
- }
- }
- mutex_unlock(&dev->ctxlist_mutex);
+ drm_legacy_ctxbitmap_flush(dev, file_priv);
mutex_lock(&dev->master_mutex);
if (file_priv->is_master) {
struct drm_master *master = file_priv->master;
- struct drm_file *temp;
-
- mutex_lock(&dev->struct_mutex);
- list_for_each_entry(temp, &dev->filelist, lhead) {
- if ((temp->master == file_priv->master) &&
- (temp != file_priv))
- temp->authenticated = temp->always_authenticated;
- }
/**
* Since the master is disappearing, so is the
* possibility to lock.
*/
-
+ mutex_lock(&dev->struct_mutex);
if (master->lock.hw_lock) {
if (dev->sigdata.lock == master->lock.hw_lock)
dev->sigdata.lock = NULL;
@@ -522,10 +449,6 @@ int drm_release(struct inode *inode, struct file *filp)
file_priv->is_master = 0;
mutex_unlock(&dev->master_mutex);
- mutex_lock(&dev->struct_mutex);
- list_del(&file_priv->lhead);
- mutex_unlock(&dev->struct_mutex);
-
if (dev->driver->postclose)
dev->driver->postclose(dev, file_priv);
@@ -533,6 +456,8 @@ int drm_release(struct inode *inode, struct file *filp)
if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_prime_destroy_file_private(&file_priv->prime);
+ WARN_ON(!list_empty(&file_priv->event_list));
+
put_pid(file_priv->pid);
kfree(file_priv);
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index f7d71190aad5..f6ca51259fa3 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -38,6 +38,8 @@
#include <linux/dma-buf.h>
#include <drm/drmP.h>
#include <drm/drm_vma_manager.h>
+#include <drm/drm_gem.h>
+#include "drm_internal.h"
/** @file drm_gem.c
*
@@ -146,7 +148,7 @@ int drm_gem_object_init(struct drm_device *dev,
EXPORT_SYMBOL(drm_gem_object_init);
/**
- * drm_gem_object_init - initialize an allocated private GEM object
+ * drm_gem_private_object_init - initialize an allocated private GEM object
* @dev: drm_device the object should be initialized for
* @obj: drm_gem_object to initialize
* @size: object size
@@ -441,18 +443,31 @@ EXPORT_SYMBOL(drm_gem_create_mmap_offset);
* drm_gem_get_pages - helper to allocate backing pages for a GEM object
* from shmem
* @obj: obj in question
- * @gfpmask: gfp mask of requested pages
+ *
+ * This reads the page-array of the shmem-backing storage of the given gem
+ * object. An array of pages is returned. If a page is not allocated or
+ * swapped-out, this will allocate/swap-in the required pages. Note that the
+ * whole object is covered by the page-array and pinned in memory.
+ *
+ * Use drm_gem_put_pages() to release the array and unpin all pages.
+ *
+ * This uses the GFP-mask set on the shmem-mapping (see mapping_set_gfp_mask()).
+ * If you require other GFP-masks, you have to do those allocations yourself.
+ *
+ * Note that you are not allowed to change gfp-zones during runtime. That is,
+ * shmem_read_mapping_page_gfp() must be called with the same gfp_zone(gfp) as
+ * set during initialization. If you have special zone constraints, set them
+ * after drm_gem_init_object() via mapping_set_gfp_mask(). shmem-core takes care
+ * to keep pages in the required zone during swap-in.
*/
-struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask)
+struct page **drm_gem_get_pages(struct drm_gem_object *obj)
{
- struct inode *inode;
struct address_space *mapping;
struct page *p, **pages;
int i, npages;
/* This is the shared memory object that backs the GEM resource */
- inode = file_inode(obj->filp);
- mapping = inode->i_mapping;
+ mapping = file_inode(obj->filp)->i_mapping;
/* We already BUG_ON() for non-page-aligned sizes in
* drm_gem_object_init(), so we should never hit this unless
@@ -466,10 +481,8 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask)
if (pages == NULL)
return ERR_PTR(-ENOMEM);
- gfpmask |= mapping_gfp_mask(mapping);
-
for (i = 0; i < npages; i++) {
- p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
+ p = shmem_read_mapping_page(mapping, i);
if (IS_ERR(p))
goto fail;
pages[i] = p;
@@ -479,7 +492,7 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask)
* __GFP_DMA32 to be set in mapping_gfp_mask(inode->i_mapping)
* so shmem can relocate pages during swapin if required.
*/
- BUG_ON((gfpmask & __GFP_DMA32) &&
+ BUG_ON((mapping_gfp_mask(mapping) & __GFP_DMA32) &&
(page_to_pfn(p) >= 0x00100000UL));
}
@@ -568,7 +581,7 @@ drm_gem_close_ioctl(struct drm_device *dev, void *data,
struct drm_gem_close *args = data;
int ret;
- if (!(dev->driver->driver_features & DRIVER_GEM))
+ if (!drm_core_check_feature(dev, DRIVER_GEM))
return -ENODEV;
ret = drm_gem_handle_delete(file_priv, args->handle);
@@ -595,7 +608,7 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data,
struct drm_gem_object *obj;
int ret;
- if (!(dev->driver->driver_features & DRIVER_GEM))
+ if (!drm_core_check_feature(dev, DRIVER_GEM))
return -ENODEV;
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
@@ -648,7 +661,7 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data,
int ret;
u32 handle;
- if (!(dev->driver->driver_features & DRIVER_GEM))
+ if (!drm_core_check_feature(dev, DRIVER_GEM))
return -ENODEV;
mutex_lock(&dev->object_name_lock);
@@ -876,7 +889,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
vma_pages(vma));
if (!node) {
mutex_unlock(&dev->struct_mutex);
- return drm_mmap(filp, vma);
+ return -EINVAL;
} else if (!drm_vma_node_is_allowed(node, filp)) {
mutex_unlock(&dev->struct_mutex);
return -EACCES;
diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c
index 05c97c5350a1..0316310e2cc4 100644
--- a/drivers/gpu/drm/drm_gem_cma_helper.c
+++ b/drivers/gpu/drm/drm_gem_cma_helper.c
@@ -316,7 +316,8 @@ out:
EXPORT_SYMBOL_GPL(drm_gem_cma_prime_get_sg_table);
struct drm_gem_object *
-drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size,
+drm_gem_cma_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
struct sg_table *sgt)
{
struct drm_gem_cma_object *cma_obj;
@@ -325,14 +326,14 @@ drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size,
return ERR_PTR(-EINVAL);
/* Create a CMA GEM buffer. */
- cma_obj = __drm_gem_cma_create(dev, size);
+ cma_obj = __drm_gem_cma_create(dev, attach->dmabuf->size);
if (IS_ERR(cma_obj))
- return ERR_PTR(PTR_ERR(cma_obj));
+ return ERR_CAST(cma_obj);
cma_obj->paddr = sg_dma_address(sgt->sgl);
cma_obj->sgt = sgt;
- DRM_DEBUG_PRIME("dma_addr = %pad, size = %zu\n", &cma_obj->paddr, size);
+ DRM_DEBUG_PRIME("dma_addr = %pad, size = %zu\n", &cma_obj->paddr, attach->dmabuf->size);
return &cma_obj->base;
}
diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c
index 7e4bae760e27..c3b80fd65d62 100644
--- a/drivers/gpu/drm/drm_hashtab.c
+++ b/drivers/gpu/drm/drm_hashtab.c
@@ -125,7 +125,7 @@ int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item)
parent = &entry->head;
}
if (parent) {
- hlist_add_after_rcu(parent, &item->head);
+ hlist_add_behind_rcu(&item->head, parent);
} else {
hlist_add_head_rcu(&item->head, h_list);
}
diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c
index 86feedd5e6f6..51efebd434f3 100644
--- a/drivers/gpu/drm/drm_info.c
+++ b/drivers/gpu/drm/drm_info.c
@@ -35,6 +35,9 @@
#include <linux/seq_file.h>
#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+
+#include "drm_legacy.h"
/**
* Called when "/proc/dri/.../name" is read.
@@ -132,7 +135,7 @@ int drm_bufs_info(struct seq_file *m, void *data)
i,
dma->bufs[i].buf_size,
dma->bufs[i].buf_count,
- atomic_read(&dma->bufs[i].freelist.count),
+ 0,
dma->bufs[i].seg_count,
seg_pages,
seg_pages * PAGE_SIZE / 1024);
@@ -183,15 +186,32 @@ int drm_clients_info(struct seq_file *m, void *data)
struct drm_device *dev = node->minor->dev;
struct drm_file *priv;
+ seq_printf(m,
+ "%20s %5s %3s master a %5s %10s\n",
+ "command",
+ "pid",
+ "dev",
+ "uid",
+ "magic");
+
+ /* dev->filelist is sorted youngest first, but we want to present
+ * oldest first (i.e. kernel, servers, clients), so walk backwardss.
+ */
mutex_lock(&dev->struct_mutex);
- seq_printf(m, "a dev pid uid magic\n\n");
- list_for_each_entry(priv, &dev->filelist, lhead) {
- seq_printf(m, "%c %3d %5d %5d %10u\n",
- priv->authenticated ? 'y' : 'n',
- priv->minor->index,
+ list_for_each_entry_reverse(priv, &dev->filelist, lhead) {
+ struct task_struct *task;
+
+ rcu_read_lock(); /* locks pid_task()->comm */
+ task = pid_task(priv->pid, PIDTYPE_PID);
+ seq_printf(m, "%20s %5d %3d %c %c %5d %10u\n",
+ task ? task->comm : "<unknown>",
pid_vnr(priv->pid),
+ priv->minor->index,
+ priv->is_master ? 'y' : 'n',
+ priv->authenticated ? 'y' : 'n',
from_kuid_munged(seq_user_ns(m), priv->uid),
priv->magic);
+ rcu_read_unlock();
}
mutex_unlock(&dev->struct_mutex);
return 0;
@@ -223,62 +243,3 @@ int drm_gem_name_info(struct seq_file *m, void *data)
return 0;
}
-
-#if DRM_DEBUG_CODE
-
-int drm_vma_info(struct seq_file *m, void *data)
-{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_vma_entry *pt;
- struct vm_area_struct *vma;
- unsigned long vma_count = 0;
-#if defined(__i386__)
- unsigned int pgprot;
-#endif
-
- mutex_lock(&dev->struct_mutex);
- list_for_each_entry(pt, &dev->vmalist, head)
- vma_count++;
-
- seq_printf(m, "vma use count: %lu, high_memory = %pK, 0x%pK\n",
- vma_count, high_memory,
- (void *)(unsigned long)virt_to_phys(high_memory));
-
- list_for_each_entry(pt, &dev->vmalist, head) {
- vma = pt->vma;
- if (!vma)
- continue;
- seq_printf(m,
- "\n%5d 0x%pK-0x%pK %c%c%c%c%c%c 0x%08lx000",
- pt->pid,
- (void *)vma->vm_start, (void *)vma->vm_end,
- vma->vm_flags & VM_READ ? 'r' : '-',
- vma->vm_flags & VM_WRITE ? 'w' : '-',
- vma->vm_flags & VM_EXEC ? 'x' : '-',
- vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
- vma->vm_flags & VM_LOCKED ? 'l' : '-',
- vma->vm_flags & VM_IO ? 'i' : '-',
- vma->vm_pgoff);
-
-#if defined(__i386__)
- pgprot = pgprot_val(vma->vm_page_prot);
- seq_printf(m, " %c%c%c%c%c%c%c%c%c",
- pgprot & _PAGE_PRESENT ? 'p' : '-',
- pgprot & _PAGE_RW ? 'w' : 'r',
- pgprot & _PAGE_USER ? 'u' : 's',
- pgprot & _PAGE_PWT ? 't' : 'b',
- pgprot & _PAGE_PCD ? 'u' : 'c',
- pgprot & _PAGE_ACCESSED ? 'a' : '-',
- pgprot & _PAGE_DIRTY ? 'd' : '-',
- pgprot & _PAGE_PSE ? 'm' : 'k',
- pgprot & _PAGE_GLOBAL ? 'g' : 'l');
-#endif
- seq_printf(m, "\n");
- }
- mutex_unlock(&dev->struct_mutex);
- return 0;
-}
-
-#endif
-
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
new file mode 100644
index 000000000000..7cc0a3516871
--- /dev/null
+++ b/drivers/gpu/drm/drm_internal.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ * Daniel Vetter <daniel.vetter@ffwll.ch>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ */
+
+/* drm_irq.c */
+extern unsigned int drm_timestamp_monotonic;
+
+/* drm_fops.c */
+extern struct mutex drm_global_mutex;
+int drm_lastclose(struct drm_device *dev);
+
+/* drm_pci.c */
+int drm_pci_set_unique(struct drm_device *dev,
+ struct drm_master *master,
+ struct drm_unique *u);
+int drm_irq_by_busid(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/* drm_vm.c */
+int drm_vma_info(struct seq_file *m, void *data);
+void drm_vm_open_locked(struct drm_device *dev, struct vm_area_struct *vma);
+void drm_vm_close_locked(struct drm_device *dev, struct vm_area_struct *vma);
+
+/* drm_prime.c */
+int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv);
+void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv);
+void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpriv,
+ struct dma_buf *dma_buf);
+
+/* drm_info.c */
+int drm_name_info(struct seq_file *m, void *data);
+int drm_vm_info(struct seq_file *m, void *data);
+int drm_bufs_info(struct seq_file *m, void *data);
+int drm_vblank_info(struct seq_file *m, void *data);
+int drm_clients_info(struct seq_file *m, void* data);
+int drm_gem_name_info(struct seq_file *m, void *data);
+
+/* drm_irq.c */
+int drm_control(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_modeset_ctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+/* drm_auth.c */
+int drm_getmagic(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_authmagic(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_remove_magic(struct drm_master *master, drm_magic_t magic);
+
+/* drm_sysfs.c */
+extern struct class *drm_class;
+
+struct class *drm_sysfs_create(struct module *owner, char *name);
+void drm_sysfs_destroy(void);
+struct device *drm_sysfs_minor_alloc(struct drm_minor *minor);
+int drm_sysfs_connector_add(struct drm_connector *connector);
+void drm_sysfs_connector_remove(struct drm_connector *connector);
+
+/* drm_gem.c */
+int drm_gem_init(struct drm_device *dev);
+void drm_gem_destroy(struct drm_device *dev);
+int drm_gem_handle_create_tail(struct drm_file *file_priv,
+ struct drm_gem_object *obj,
+ u32 *handlep);
+int drm_gem_close_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_gem_flink_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_gem_open_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+void drm_gem_open(struct drm_device *dev, struct drm_file *file_private);
+void drm_gem_release(struct drm_device *dev, struct drm_file *file_private);
+
+/* drm_drv.c */
+int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+struct drm_master *drm_master_create(struct drm_minor *minor);
+
+/* drm_debugfs.c */
+#if defined(CONFIG_DEBUG_FS)
+int drm_debugfs_init(struct drm_minor *minor, int minor_id,
+ struct dentry *root);
+int drm_debugfs_cleanup(struct drm_minor *minor);
+int drm_debugfs_connector_add(struct drm_connector *connector);
+void drm_debugfs_connector_remove(struct drm_connector *connector);
+#else
+static inline int drm_debugfs_init(struct drm_minor *minor, int minor_id,
+ struct dentry *root)
+{
+ return 0;
+}
+
+static inline int drm_debugfs_cleanup(struct drm_minor *minor)
+{
+ return 0;
+}
+
+static inline int drm_debugfs_connector_add(struct drm_connector *connector)
+{
+ return 0;
+}
+static inline void drm_debugfs_connector_remove(struct drm_connector *connector)
+{
+}
+#endif
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 69c61f392e66..00587a1e3c83 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -1,11 +1,3 @@
-/**
- * \file drm_ioctl.c
- * IOCTL processing for DRM
- *
- * \author Rickard E. (Rik) Faith <faith@valinux.com>
- * \author Gareth Hughes <gareth@valinux.com>
- */
-
/*
* Created: Fri Jan 8 09:01:26 1999 by faith@valinux.com
*
@@ -13,6 +5,9 @@
* Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
* All Rights Reserved.
*
+ * Author Rickard E. (Rik) Faith <faith@valinux.com>
+ * Author Gareth Hughes <gareth@valinux.com>
+ *
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
@@ -35,6 +30,8 @@
#include <drm/drmP.h>
#include <drm/drm_core.h>
+#include "drm_legacy.h"
+#include "drm_internal.h"
#include <linux/pci.h>
#include <linux/export.h>
@@ -42,6 +39,9 @@
#include <asm/mtrr.h>
#endif
+static int drm_version(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
/**
* Get the bus id.
*
@@ -53,7 +53,7 @@
*
* Copies the bus id from drm_device::unique into user space.
*/
-int drm_getunique(struct drm_device *dev, void *data,
+static int drm_getunique(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_unique *u = data;
@@ -75,7 +75,6 @@ drm_unset_busid(struct drm_device *dev,
kfree(master->unique);
master->unique = NULL;
master->unique_len = 0;
- master->unique_size = 0;
}
/**
@@ -93,7 +92,7 @@ drm_unset_busid(struct drm_device *dev,
* version 1.1 or greater. Also note that KMS is all version 1.1 and later and
* UMS was only ever supported on pci devices.
*/
-int drm_setunique(struct drm_device *dev, void *data,
+static int drm_setunique(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_unique *u = data;
@@ -131,15 +130,15 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
if (master->unique != NULL)
drm_unset_busid(dev, master);
- if (dev->driver->bus && dev->driver->bus->set_busid) {
- ret = dev->driver->bus->set_busid(dev, master);
+ if (dev->driver->set_busid) {
+ ret = dev->driver->set_busid(dev, master);
if (ret) {
drm_unset_busid(dev, master);
return ret;
}
} else {
if (WARN(dev->unique == NULL,
- "No drm_bus.set_busid() implementation provided by "
+ "No drm_driver.set_busid() implementation provided by "
"%ps. Use drm_dev_set_unique() to set the unique "
"name explicitly.", dev->driver))
return -EINVAL;
@@ -165,7 +164,7 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
* Searches for the mapping with the specified offset and copies its information
* into userspace
*/
-int drm_getmap(struct drm_device *dev, void *data,
+static int drm_getmap(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_map *map = data;
@@ -226,7 +225,7 @@ int drm_getmap(struct drm_device *dev, void *data,
* Searches for the client with the specified index and copies its information
* into userspace
*/
-int drm_getclient(struct drm_device *dev, void *data,
+static int drm_getclient(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_client *client = data;
@@ -266,7 +265,7 @@ int drm_getclient(struct drm_device *dev, void *data,
*
* \return zero on success or a negative number on failure.
*/
-int drm_getstats(struct drm_device *dev, void *data,
+static int drm_getstats(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_stats *stats = data;
@@ -280,7 +279,7 @@ int drm_getstats(struct drm_device *dev, void *data,
/**
* Get device/driver capabilities
*/
-int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
+static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct drm_get_cap *req = data;
@@ -330,7 +329,7 @@ int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
/**
* Set device/driver capabilities
*/
-int
+static int
drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct drm_set_client_cap *req = data;
@@ -342,8 +341,6 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
file_priv->stereo_allowed = req->value;
break;
case DRM_CLIENT_CAP_UNIVERSAL_PLANES:
- if (!drm_universal_planes)
- return -EINVAL;
if (req->value > 1)
return -EINVAL;
file_priv->universal_planes = req->value;
@@ -366,7 +363,7 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
*
* Sets the requested interface version
*/
-int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv)
+static int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct drm_set_version *sv = data;
int if_version, retcode = 0;
@@ -417,3 +414,358 @@ int drm_noop(struct drm_device *dev, void *data,
return 0;
}
EXPORT_SYMBOL(drm_noop);
+
+/**
+ * Copy and IOCTL return string to user space
+ */
+static int drm_copy_field(char __user *buf, size_t *buf_len, const char *value)
+{
+ int len;
+
+ /* don't overflow userbuf */
+ len = strlen(value);
+ if (len > *buf_len)
+ len = *buf_len;
+
+ /* let userspace know exact length of driver value (which could be
+ * larger than the userspace-supplied buffer) */
+ *buf_len = strlen(value);
+
+ /* finally, try filling in the userbuf */
+ if (len && buf)
+ if (copy_to_user(buf, value, len))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Get version information
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_version structure.
+ * \return zero on success or negative number on failure.
+ *
+ * Fills in the version information in \p arg.
+ */
+static int drm_version(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_version *version = data;
+ int err;
+
+ version->version_major = dev->driver->major;
+ version->version_minor = dev->driver->minor;
+ version->version_patchlevel = dev->driver->patchlevel;
+ err = drm_copy_field(version->name, &version->name_len,
+ dev->driver->name);
+ if (!err)
+ err = drm_copy_field(version->date, &version->date_len,
+ dev->driver->date);
+ if (!err)
+ err = drm_copy_field(version->desc, &version->desc_len,
+ dev->driver->desc);
+
+ return err;
+}
+
+/**
+ * drm_ioctl_permit - Check ioctl permissions against caller
+ *
+ * @flags: ioctl permission flags.
+ * @file_priv: Pointer to struct drm_file identifying the caller.
+ *
+ * Checks whether the caller is allowed to run an ioctl with the
+ * indicated permissions. If so, returns zero. Otherwise returns an
+ * error code suitable for ioctl return.
+ */
+static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
+{
+ /* ROOT_ONLY is only for CAP_SYS_ADMIN */
+ if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)))
+ return -EACCES;
+
+ /* AUTH is only for authenticated or render client */
+ if (unlikely((flags & DRM_AUTH) && !drm_is_render_client(file_priv) &&
+ !file_priv->authenticated))
+ return -EACCES;
+
+ /* MASTER is only for master or control clients */
+ if (unlikely((flags & DRM_MASTER) && !file_priv->is_master &&
+ !drm_is_control_client(file_priv)))
+ return -EACCES;
+
+ /* Control clients must be explicitly allowed */
+ if (unlikely(!(flags & DRM_CONTROL_ALLOW) &&
+ drm_is_control_client(file_priv)))
+ return -EACCES;
+
+ /* Render clients must be explicitly allowed */
+ if (unlikely(!(flags & DRM_RENDER_ALLOW) &&
+ drm_is_render_client(file_priv)))
+ return -EACCES;
+
+ return 0;
+}
+
+#define DRM_IOCTL_DEF(ioctl, _func, _flags) \
+ [DRM_IOCTL_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .cmd_drv = 0, .name = #ioctl}
+
+/** Ioctl table */
+static const struct drm_ioctl_desc drm_ioctls[] = {
+ DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
+ DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0),
+ DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0),
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_AUTH|DRM_MASTER),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_ADD_MAP, drm_legacy_addmap_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_RM_MAP, drm_legacy_rmmap_ioctl, DRM_AUTH),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_legacy_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_legacy_getsareactx, DRM_AUTH),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_legacy_addctx, DRM_AUTH|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_legacy_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_GET_CTX, drm_legacy_getctx, DRM_AUTH),
+ DRM_IOCTL_DEF(DRM_IOCTL_SWITCH_CTX, drm_legacy_switchctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_NEW_CTX, drm_legacy_newctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_RES_CTX, drm_legacy_resctx, DRM_AUTH),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_LOCK, drm_legacy_lock, DRM_AUTH),
+ DRM_IOCTL_DEF(DRM_IOCTL_UNLOCK, drm_legacy_unlock, DRM_AUTH),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_FINISH, drm_noop, DRM_AUTH),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_ADD_BUFS, drm_legacy_addbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_MARK_BUFS, drm_legacy_markbufs, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_INFO_BUFS, drm_legacy_infobufs, DRM_AUTH),
+ DRM_IOCTL_DEF(DRM_IOCTL_MAP_BUFS, drm_legacy_mapbufs, DRM_AUTH),
+ DRM_IOCTL_DEF(DRM_IOCTL_FREE_BUFS, drm_legacy_freebufs, DRM_AUTH),
+ DRM_IOCTL_DEF(DRM_IOCTL_DMA, drm_legacy_dma_ioctl, DRM_AUTH),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+
+#if __OS_HAS_AGP
+ DRM_IOCTL_DEF(DRM_IOCTL_AGP_ACQUIRE, drm_agp_acquire_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_AGP_RELEASE, drm_agp_release_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_AGP_ENABLE, drm_agp_enable_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_AGP_INFO, drm_agp_info_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF(DRM_IOCTL_AGP_ALLOC, drm_agp_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_AGP_FREE, drm_agp_free_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_AGP_BIND, drm_agp_bind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_AGP_UNBIND, drm_agp_unbind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+#endif
+
+ DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_legacy_sg_alloc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_legacy_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_noop, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+};
+
+#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
+
+/**
+ * Called whenever a process performs an ioctl on /dev/drm.
+ *
+ * \param inode device inode.
+ * \param file_priv DRM file private.
+ * \param cmd command.
+ * \param arg user argument.
+ * \return zero on success or negative number on failure.
+ *
+ * Looks up the ioctl function in the ::ioctls table, checking for root
+ * previleges if so required, and dispatches to the respective function.
+ */
+long drm_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct drm_file *file_priv = filp->private_data;
+ struct drm_device *dev;
+ const struct drm_ioctl_desc *ioctl = NULL;
+ drm_ioctl_t *func;
+ unsigned int nr = DRM_IOCTL_NR(cmd);
+ int retcode = -EINVAL;
+ char stack_kdata[128];
+ char *kdata = NULL;
+ unsigned int usize, asize;
+
+ dev = file_priv->minor->dev;
+
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
+ if ((nr >= DRM_CORE_IOCTL_COUNT) &&
+ ((nr < DRM_COMMAND_BASE) || (nr >= DRM_COMMAND_END)))
+ goto err_i1;
+ if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) &&
+ (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) {
+ u32 drv_size;
+ ioctl = &dev->driver->ioctls[nr - DRM_COMMAND_BASE];
+ drv_size = _IOC_SIZE(ioctl->cmd_drv);
+ usize = asize = _IOC_SIZE(cmd);
+ if (drv_size > asize)
+ asize = drv_size;
+ cmd = ioctl->cmd_drv;
+ }
+ else if ((nr >= DRM_COMMAND_END) || (nr < DRM_COMMAND_BASE)) {
+ u32 drv_size;
+
+ ioctl = &drm_ioctls[nr];
+
+ drv_size = _IOC_SIZE(ioctl->cmd);
+ usize = asize = _IOC_SIZE(cmd);
+ if (drv_size > asize)
+ asize = drv_size;
+
+ cmd = ioctl->cmd;
+ } else
+ goto err_i1;
+
+ DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n",
+ task_pid_nr(current),
+ (long)old_encode_dev(file_priv->minor->kdev->devt),
+ file_priv->authenticated, ioctl->name);
+
+ /* Do not trust userspace, use our own definition */
+ func = ioctl->func;
+
+ if (unlikely(!func)) {
+ DRM_DEBUG("no function\n");
+ retcode = -EINVAL;
+ goto err_i1;
+ }
+
+ retcode = drm_ioctl_permit(ioctl->flags, file_priv);
+ if (unlikely(retcode))
+ goto err_i1;
+
+ if (cmd & (IOC_IN | IOC_OUT)) {
+ if (asize <= sizeof(stack_kdata)) {
+ kdata = stack_kdata;
+ } else {
+ kdata = kmalloc(asize, GFP_KERNEL);
+ if (!kdata) {
+ retcode = -ENOMEM;
+ goto err_i1;
+ }
+ }
+ if (asize > usize)
+ memset(kdata + usize, 0, asize - usize);
+ }
+
+ if (cmd & IOC_IN) {
+ if (copy_from_user(kdata, (void __user *)arg,
+ usize) != 0) {
+ retcode = -EFAULT;
+ goto err_i1;
+ }
+ } else if (cmd & IOC_OUT) {
+ memset(kdata, 0, usize);
+ }
+
+ if (ioctl->flags & DRM_UNLOCKED)
+ retcode = func(dev, kdata, file_priv);
+ else {
+ mutex_lock(&drm_global_mutex);
+ retcode = func(dev, kdata, file_priv);
+ mutex_unlock(&drm_global_mutex);
+ }
+
+ if (cmd & IOC_OUT) {
+ if (copy_to_user((void __user *)arg, kdata,
+ usize) != 0)
+ retcode = -EFAULT;
+ }
+
+ err_i1:
+ if (!ioctl)
+ DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
+ task_pid_nr(current),
+ (long)old_encode_dev(file_priv->minor->kdev->devt),
+ file_priv->authenticated, cmd, nr);
+
+ if (kdata != stack_kdata)
+ kfree(kdata);
+ if (retcode)
+ DRM_DEBUG("ret = %d\n", retcode);
+ return retcode;
+}
+EXPORT_SYMBOL(drm_ioctl);
+
+/**
+ * drm_ioctl_flags - Check for core ioctl and return ioctl permission flags
+ *
+ * @nr: Ioctl number.
+ * @flags: Where to return the ioctl permission flags
+ */
+bool drm_ioctl_flags(unsigned int nr, unsigned int *flags)
+{
+ if ((nr >= DRM_COMMAND_END && nr < DRM_CORE_IOCTL_COUNT) ||
+ (nr < DRM_COMMAND_BASE)) {
+ *flags = drm_ioctls[nr].flags;
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL(drm_ioctl_flags);
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 08ba1209228e..5ef03c216a27 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -34,6 +34,7 @@
#include <drm/drmP.h>
#include "drm_trace.h"
+#include "drm_internal.h"
#include <linux/interrupt.h> /* For task queue support */
#include <linux/slab.h>
@@ -55,12 +56,91 @@
*/
#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
+static bool
+drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
+ struct timeval *tvblank, unsigned flags);
+
+static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */
+
/*
- * Clear vblank timestamp buffer for a crtc.
+ * Default to use monotonic timestamps for wait-for-vblank and page-flip
+ * complete events.
+ */
+unsigned int drm_timestamp_monotonic = 1;
+
+static int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */
+
+module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
+module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
+module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
+
+/**
+ * drm_update_vblank_count - update the master vblank counter
+ * @dev: DRM device
+ * @crtc: counter to update
+ *
+ * Call back into the driver to update the appropriate vblank counter
+ * (specified by @crtc). Deal with wraparound, if it occurred, and
+ * update the last read value so we can deal with wraparound on the next
+ * call if necessary.
+ *
+ * Only necessary when going from off->on, to account for frames we
+ * didn't get an interrupt for.
+ *
+ * Note: caller must hold dev->vbl_lock since this reads & writes
+ * device vblank fields.
*/
-static void clear_vblank_timestamps(struct drm_device *dev, int crtc)
+static void drm_update_vblank_count(struct drm_device *dev, int crtc)
{
- memset(dev->vblank[crtc].time, 0, sizeof(dev->vblank[crtc].time));
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
+ u32 cur_vblank, diff, tslot;
+ bool rc;
+ struct timeval t_vblank;
+
+ /*
+ * Interrupts were disabled prior to this call, so deal with counter
+ * wrap if needed.
+ * NOTE! It's possible we lost a full dev->max_vblank_count events
+ * here if the register is small or we had vblank interrupts off for
+ * a long time.
+ *
+ * We repeat the hardware vblank counter & timestamp query until
+ * we get consistent results. This to prevent races between gpu
+ * updating its hardware counter while we are retrieving the
+ * corresponding vblank timestamp.
+ */
+ do {
+ cur_vblank = dev->driver->get_vblank_counter(dev, crtc);
+ rc = drm_get_last_vbltimestamp(dev, crtc, &t_vblank, 0);
+ } while (cur_vblank != dev->driver->get_vblank_counter(dev, crtc));
+
+ /* Deal with counter wrap */
+ diff = cur_vblank - vblank->last;
+ if (cur_vblank < vblank->last) {
+ diff += dev->max_vblank_count;
+
+ DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n",
+ crtc, vblank->last, cur_vblank, diff);
+ }
+
+ DRM_DEBUG("updating vblank count on crtc %d, missed %d\n",
+ crtc, diff);
+
+ if (diff == 0)
+ return;
+
+ /* Reinitialize corresponding vblank timestamp if high-precision query
+ * available. Skip this step if query unsupported or failed. Will
+ * reinitialize delayed at next vblank interrupt in that case.
+ */
+ if (rc) {
+ tslot = atomic_read(&vblank->count) + diff;
+ vblanktimestamp(dev, crtc, tslot) = t_vblank;
+ }
+
+ smp_mb__before_atomic();
+ atomic_add(diff, &vblank->count);
+ smp_mb__after_atomic();
}
/*
@@ -71,10 +151,11 @@ static void clear_vblank_timestamps(struct drm_device *dev, int crtc)
*/
static void vblank_disable_and_save(struct drm_device *dev, int crtc)
{
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
unsigned long irqflags;
u32 vblcount;
s64 diff_ns;
- int vblrc;
+ bool vblrc;
struct timeval tvblank;
int count = DRM_TIMESTAMP_MAXRETRIES;
@@ -84,8 +165,28 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
*/
spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
+ /*
+ * If the vblank interrupt was already disbled update the count
+ * and timestamp to maintain the appearance that the counter
+ * has been ticking all along until this time. This makes the
+ * count account for the entire time between drm_vblank_on() and
+ * drm_vblank_off().
+ *
+ * But only do this if precise vblank timestamps are available.
+ * Otherwise we might read a totally bogus timestamp since drivers
+ * lacking precise timestamp support rely upon sampling the system clock
+ * at vblank interrupt time. Which obviously won't work out well if the
+ * vblank interrupt is disabled.
+ */
+ if (!vblank->enabled &&
+ drm_get_last_vbltimestamp(dev, crtc, &tvblank, 0)) {
+ drm_update_vblank_count(dev, crtc);
+ spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
+ return;
+ }
+
dev->driver->disable_vblank(dev, crtc);
- dev->vblank[crtc].enabled = false;
+ vblank->enabled = false;
/* No further vblank irq's will be processed after
* this point. Get current hardware vblank count and
@@ -100,9 +201,9 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
* delayed gpu counter increment.
*/
do {
- dev->vblank[crtc].last = dev->driver->get_vblank_counter(dev, crtc);
+ vblank->last = dev->driver->get_vblank_counter(dev, crtc);
vblrc = drm_get_last_vbltimestamp(dev, crtc, &tvblank, 0);
- } while (dev->vblank[crtc].last != dev->driver->get_vblank_counter(dev, crtc) && (--count) && vblrc);
+ } while (vblank->last != dev->driver->get_vblank_counter(dev, crtc) && (--count) && vblrc);
if (!count)
vblrc = 0;
@@ -110,7 +211,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
/* Compute time difference to stored timestamp of last vblank
* as updated by last invocation of drm_handle_vblank() in vblank irq.
*/
- vblcount = atomic_read(&dev->vblank[crtc].count);
+ vblcount = atomic_read(&vblank->count);
diff_ns = timeval_to_ns(&tvblank) -
timeval_to_ns(&vblanktimestamp(dev, crtc, vblcount));
@@ -126,14 +227,18 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
* available. In that case we can't account for this and just
* hope for the best.
*/
- if ((vblrc > 0) && (abs64(diff_ns) > 1000000)) {
- atomic_inc(&dev->vblank[crtc].count);
+ if (vblrc && (abs64(diff_ns) > 1000000)) {
+ /* Store new timestamp in ringbuffer. */
+ vblanktimestamp(dev, crtc, vblcount + 1) = tvblank;
+
+ /* Increment cooked vblank count. This also atomically commits
+ * the timestamp computed above.
+ */
+ smp_mb__before_atomic();
+ atomic_inc(&vblank->count);
smp_mb__after_atomic();
}
- /* Invalidate all timestamps while vblank irq's are off. */
- clear_vblank_timestamps(dev, crtc);
-
spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
}
@@ -164,14 +269,20 @@ static void vblank_disable_fn(unsigned long arg)
void drm_vblank_cleanup(struct drm_device *dev)
{
int crtc;
+ unsigned long irqflags;
/* Bail if the driver didn't call drm_vblank_init() */
if (dev->num_crtcs == 0)
return;
for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
- del_timer_sync(&dev->vblank[crtc].disable_timer);
- vblank_disable_fn((unsigned long)&dev->vblank[crtc]);
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
+
+ del_timer_sync(&vblank->disable_timer);
+
+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
+ vblank_disable_and_save(dev, crtc);
+ spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
}
kfree(dev->vblank);
@@ -204,11 +315,13 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
goto err;
for (i = 0; i < num_crtcs; i++) {
- dev->vblank[i].dev = dev;
- dev->vblank[i].crtc = i;
- init_waitqueue_head(&dev->vblank[i].queue);
- setup_timer(&dev->vblank[i].disable_timer, vblank_disable_fn,
- (unsigned long)&dev->vblank[i]);
+ struct drm_vblank_crtc *vblank = &dev->vblank[i];
+
+ vblank->dev = dev;
+ vblank->crtc = i;
+ init_waitqueue_head(&vblank->queue);
+ setup_timer(&vblank->disable_timer, vblank_disable_fn,
+ (unsigned long)vblank);
}
DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
@@ -224,7 +337,7 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
return 0;
err:
- drm_vblank_cleanup(dev);
+ dev->num_crtcs = 0;
return ret;
}
EXPORT_SYMBOL(drm_vblank_init);
@@ -360,9 +473,11 @@ int drm_irq_uninstall(struct drm_device *dev)
if (dev->num_crtcs) {
spin_lock_irqsave(&dev->vbl_lock, irqflags);
for (i = 0; i < dev->num_crtcs; i++) {
- wake_up(&dev->vblank[i].queue);
- dev->vblank[i].enabled = false;
- dev->vblank[i].last =
+ struct drm_vblank_crtc *vblank = &dev->vblank[i];
+
+ wake_up(&vblank->queue);
+ vblank->enabled = false;
+ vblank->last =
dev->driver->get_vblank_counter(dev, i);
}
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
@@ -617,7 +732,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
* within vblank area, counting down the number of lines until
* start of scanout.
*/
- invbl = vbl_status & DRM_SCANOUTPOS_INVBL;
+ invbl = vbl_status & DRM_SCANOUTPOS_IN_VBLANK;
/* Convert scanout position into elapsed time at raw_time query
* since start of scanout at first display scanline. delta_ns
@@ -647,7 +762,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
vbl_status = DRM_VBLANKTIME_SCANOUTPOS_METHOD;
if (invbl)
- vbl_status |= DRM_VBLANKTIME_INVBL;
+ vbl_status |= DRM_VBLANKTIME_IN_VBLANK;
return vbl_status;
}
@@ -679,10 +794,11 @@ static struct timeval get_drm_timestamp(void)
* call, i.e., it isn't very precisely locked to the true vblank.
*
* Returns:
- * Non-zero if timestamp is considered to be very precise, zero otherwise.
+ * True if timestamp is considered to be very precise, false otherwise.
*/
-u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
- struct timeval *tvblank, unsigned flags)
+static bool
+drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
+ struct timeval *tvblank, unsigned flags)
{
int ret;
@@ -694,7 +810,7 @@ u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
ret = dev->driver->get_vblank_timestamp(dev, crtc, &max_error,
tvblank, flags);
if (ret > 0)
- return (u32) ret;
+ return true;
}
/* GPU high precision timestamp query unsupported or failed.
@@ -702,9 +818,8 @@ u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
*/
*tvblank = get_drm_timestamp();
- return 0;
+ return false;
}
-EXPORT_SYMBOL(drm_get_last_vbltimestamp);
/**
* drm_vblank_count - retrieve "cooked" vblank counter value
@@ -720,7 +835,11 @@ EXPORT_SYMBOL(drm_get_last_vbltimestamp);
*/
u32 drm_vblank_count(struct drm_device *dev, int crtc)
{
- return atomic_read(&dev->vblank[crtc].count);
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
+
+ if (WARN_ON(crtc >= dev->num_crtcs))
+ return 0;
+ return atomic_read(&vblank->count);
}
EXPORT_SYMBOL(drm_vblank_count);
@@ -740,18 +859,22 @@ EXPORT_SYMBOL(drm_vblank_count);
u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc,
struct timeval *vblanktime)
{
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
u32 cur_vblank;
+ if (WARN_ON(crtc >= dev->num_crtcs))
+ return 0;
+
/* Read timestamp from slot of _vblank_time ringbuffer
* that corresponds to current vblank count. Retry if
* count has incremented during readout. This works like
* a seqlock.
*/
do {
- cur_vblank = atomic_read(&dev->vblank[crtc].count);
+ cur_vblank = atomic_read(&vblank->count);
*vblanktime = vblanktimestamp(dev, crtc, cur_vblank);
smp_rmb();
- } while (cur_vblank != atomic_read(&dev->vblank[crtc].count));
+ } while (cur_vblank != atomic_read(&vblank->count));
return cur_vblank;
}
@@ -800,83 +923,20 @@ void drm_send_vblank_event(struct drm_device *dev, int crtc,
EXPORT_SYMBOL(drm_send_vblank_event);
/**
- * drm_update_vblank_count - update the master vblank counter
- * @dev: DRM device
- * @crtc: counter to update
- *
- * Call back into the driver to update the appropriate vblank counter
- * (specified by @crtc). Deal with wraparound, if it occurred, and
- * update the last read value so we can deal with wraparound on the next
- * call if necessary.
- *
- * Only necessary when going from off->on, to account for frames we
- * didn't get an interrupt for.
- *
- * Note: caller must hold dev->vbl_lock since this reads & writes
- * device vblank fields.
- */
-static void drm_update_vblank_count(struct drm_device *dev, int crtc)
-{
- u32 cur_vblank, diff, tslot, rc;
- struct timeval t_vblank;
-
- /*
- * Interrupts were disabled prior to this call, so deal with counter
- * wrap if needed.
- * NOTE! It's possible we lost a full dev->max_vblank_count events
- * here if the register is small or we had vblank interrupts off for
- * a long time.
- *
- * We repeat the hardware vblank counter & timestamp query until
- * we get consistent results. This to prevent races between gpu
- * updating its hardware counter while we are retrieving the
- * corresponding vblank timestamp.
- */
- do {
- cur_vblank = dev->driver->get_vblank_counter(dev, crtc);
- rc = drm_get_last_vbltimestamp(dev, crtc, &t_vblank, 0);
- } while (cur_vblank != dev->driver->get_vblank_counter(dev, crtc));
-
- /* Deal with counter wrap */
- diff = cur_vblank - dev->vblank[crtc].last;
- if (cur_vblank < dev->vblank[crtc].last) {
- diff += dev->max_vblank_count;
-
- DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n",
- crtc, dev->vblank[crtc].last, cur_vblank, diff);
- }
-
- DRM_DEBUG("enabling vblank interrupts on crtc %d, missed %d\n",
- crtc, diff);
-
- /* Reinitialize corresponding vblank timestamp if high-precision query
- * available. Skip this step if query unsupported or failed. Will
- * reinitialize delayed at next vblank interrupt in that case.
- */
- if (rc) {
- tslot = atomic_read(&dev->vblank[crtc].count) + diff;
- vblanktimestamp(dev, crtc, tslot) = t_vblank;
- }
-
- smp_mb__before_atomic();
- atomic_add(diff, &dev->vblank[crtc].count);
- smp_mb__after_atomic();
-}
-
-/**
* drm_vblank_enable - enable the vblank interrupt on a CRTC
* @dev: DRM device
* @crtc: CRTC in question
*/
static int drm_vblank_enable(struct drm_device *dev, int crtc)
{
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
int ret = 0;
assert_spin_locked(&dev->vbl_lock);
spin_lock(&dev->vblank_time_lock);
- if (!dev->vblank[crtc].enabled) {
+ if (!vblank->enabled) {
/*
* Enable vblank irqs under vblank_time_lock protection.
* All vblank count & timestamp updates are held off
@@ -887,9 +947,9 @@ static int drm_vblank_enable(struct drm_device *dev, int crtc)
ret = dev->driver->enable_vblank(dev, crtc);
DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret);
if (ret)
- atomic_dec(&dev->vblank[crtc].refcount);
+ atomic_dec(&vblank->refcount);
else {
- dev->vblank[crtc].enabled = true;
+ vblank->enabled = true;
drm_update_vblank_count(dev, crtc);
}
}
@@ -914,16 +974,20 @@ static int drm_vblank_enable(struct drm_device *dev, int crtc)
*/
int drm_vblank_get(struct drm_device *dev, int crtc)
{
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
unsigned long irqflags;
int ret = 0;
+ if (WARN_ON(crtc >= dev->num_crtcs))
+ return -EINVAL;
+
spin_lock_irqsave(&dev->vbl_lock, irqflags);
/* Going from 0->1 means we have to enable interrupts again */
- if (atomic_add_return(1, &dev->vblank[crtc].refcount) == 1) {
+ if (atomic_add_return(1, &vblank->refcount) == 1) {
ret = drm_vblank_enable(dev, crtc);
} else {
- if (!dev->vblank[crtc].enabled) {
- atomic_dec(&dev->vblank[crtc].refcount);
+ if (!vblank->enabled) {
+ atomic_dec(&vblank->refcount);
ret = -EINVAL;
}
}
@@ -963,13 +1027,23 @@ EXPORT_SYMBOL(drm_crtc_vblank_get);
*/
void drm_vblank_put(struct drm_device *dev, int crtc)
{
- BUG_ON(atomic_read(&dev->vblank[crtc].refcount) == 0);
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
+
+ BUG_ON(atomic_read(&vblank->refcount) == 0);
+
+ if (WARN_ON(crtc >= dev->num_crtcs))
+ return;
/* Last user schedules interrupt disable */
- if (atomic_dec_and_test(&dev->vblank[crtc].refcount) &&
- (drm_vblank_offdelay > 0))
- mod_timer(&dev->vblank[crtc].disable_timer,
- jiffies + ((drm_vblank_offdelay * HZ)/1000));
+ if (atomic_dec_and_test(&vblank->refcount)) {
+ if (drm_vblank_offdelay == 0)
+ return;
+ else if (dev->vblank_disable_immediate || drm_vblank_offdelay < 0)
+ vblank_disable_fn((unsigned long)vblank);
+ else
+ mod_timer(&vblank->disable_timer,
+ jiffies + ((drm_vblank_offdelay * HZ)/1000));
+ }
}
EXPORT_SYMBOL(drm_vblank_put);
@@ -989,6 +1063,50 @@ void drm_crtc_vblank_put(struct drm_crtc *crtc)
EXPORT_SYMBOL(drm_crtc_vblank_put);
/**
+ * drm_wait_one_vblank - wait for one vblank
+ * @dev: DRM device
+ * @crtc: crtc index
+ *
+ * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
+ * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
+ * due to lack of driver support or because the crtc is off.
+ */
+void drm_wait_one_vblank(struct drm_device *dev, int crtc)
+{
+ int ret;
+ u32 last;
+
+ ret = drm_vblank_get(dev, crtc);
+ if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", crtc, ret))
+ return;
+
+ last = drm_vblank_count(dev, crtc);
+
+ ret = wait_event_timeout(dev->vblank[crtc].queue,
+ last != drm_vblank_count(dev, crtc),
+ msecs_to_jiffies(100));
+
+ WARN(ret == 0, "vblank wait timed out on crtc %i\n", crtc);
+
+ drm_vblank_put(dev, crtc);
+}
+EXPORT_SYMBOL(drm_wait_one_vblank);
+
+/**
+ * drm_crtc_wait_one_vblank - wait for one vblank
+ * @crtc: DRM crtc
+ *
+ * This waits for one vblank to pass on @crtc, using the irq driver interfaces.
+ * It is a failure to call this when the vblank irq for @crtc is disabled, e.g.
+ * due to lack of driver support or because the crtc is off.
+ */
+void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
+{
+ drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
+
+/**
* drm_vblank_off - disable vblank events on a CRTC
* @dev: DRM device
* @crtc: CRTC in question
@@ -1004,19 +1122,34 @@ EXPORT_SYMBOL(drm_crtc_vblank_put);
*/
void drm_vblank_off(struct drm_device *dev, int crtc)
{
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
struct drm_pending_vblank_event *e, *t;
struct timeval now;
unsigned long irqflags;
unsigned int seq;
- spin_lock_irqsave(&dev->vbl_lock, irqflags);
+ if (WARN_ON(crtc >= dev->num_crtcs))
+ return;
+
+ spin_lock_irqsave(&dev->event_lock, irqflags);
+
+ spin_lock(&dev->vbl_lock);
vblank_disable_and_save(dev, crtc);
- wake_up(&dev->vblank[crtc].queue);
+ wake_up(&vblank->queue);
+
+ /*
+ * Prevent subsequent drm_vblank_get() from re-enabling
+ * the vblank interrupt by bumping the refcount.
+ */
+ if (!vblank->inmodeset) {
+ atomic_inc(&vblank->refcount);
+ vblank->inmodeset = 1;
+ }
+ spin_unlock(&dev->vbl_lock);
/* Send any queued vblank events, lest the natives grow disquiet */
seq = drm_vblank_count_and_time(dev, crtc, &now);
- spin_lock(&dev->event_lock);
list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
if (e->pipe != crtc)
continue;
@@ -1027,9 +1160,7 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
drm_vblank_put(dev, e->pipe);
send_vblank_event(dev, e, seq, &now);
}
- spin_unlock(&dev->event_lock);
-
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+ spin_unlock_irqrestore(&dev->event_lock, irqflags);
}
EXPORT_SYMBOL(drm_vblank_off);
@@ -1066,11 +1197,35 @@ EXPORT_SYMBOL(drm_crtc_vblank_off);
*/
void drm_vblank_on(struct drm_device *dev, int crtc)
{
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
unsigned long irqflags;
+ if (WARN_ON(crtc >= dev->num_crtcs))
+ return;
+
spin_lock_irqsave(&dev->vbl_lock, irqflags);
- /* re-enable interrupts if there's are users left */
- if (atomic_read(&dev->vblank[crtc].refcount) != 0)
+ /* Drop our private "prevent drm_vblank_get" refcount */
+ if (vblank->inmodeset) {
+ atomic_dec(&vblank->refcount);
+ vblank->inmodeset = 0;
+ }
+
+ /*
+ * sample the current counter to avoid random jumps
+ * when drm_vblank_enable() applies the diff
+ *
+ * -1 to make sure user will never see the same
+ * vblank counter value before and after a modeset
+ */
+ vblank->last =
+ (dev->driver->get_vblank_counter(dev, crtc) - 1) &
+ dev->max_vblank_count;
+ /*
+ * re-enable interrupts if there are users left, or the
+ * user wishes vblank interrupts to be enabled all the time.
+ */
+ if (atomic_read(&vblank->refcount) != 0 ||
+ (!dev->vblank_disable_immediate && drm_vblank_offdelay == 0))
WARN_ON(drm_vblank_enable(dev, crtc));
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
}
@@ -1118,9 +1273,15 @@ EXPORT_SYMBOL(drm_crtc_vblank_on);
*/
void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
{
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
+
/* vblank is not initialized (IRQ not installed ?), or has been freed */
if (!dev->num_crtcs)
return;
+
+ if (WARN_ON(crtc >= dev->num_crtcs))
+ return;
+
/*
* To avoid all the problems that might happen if interrupts
* were enabled/disabled around or between these calls, we just
@@ -1128,10 +1289,10 @@ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
* to avoid corrupting the count if multiple, mismatch calls occur),
* so that interrupts remain enabled in the interim.
*/
- if (!dev->vblank[crtc].inmodeset) {
- dev->vblank[crtc].inmodeset = 0x1;
+ if (!vblank->inmodeset) {
+ vblank->inmodeset = 0x1;
if (drm_vblank_get(dev, crtc) == 0)
- dev->vblank[crtc].inmodeset |= 0x2;
+ vblank->inmodeset |= 0x2;
}
}
EXPORT_SYMBOL(drm_vblank_pre_modeset);
@@ -1146,21 +1307,22 @@ EXPORT_SYMBOL(drm_vblank_pre_modeset);
*/
void drm_vblank_post_modeset(struct drm_device *dev, int crtc)
{
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
unsigned long irqflags;
/* vblank is not initialized (IRQ not installed ?), or has been freed */
if (!dev->num_crtcs)
return;
- if (dev->vblank[crtc].inmodeset) {
+ if (vblank->inmodeset) {
spin_lock_irqsave(&dev->vbl_lock, irqflags);
dev->vblank_disable_allowed = true;
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
- if (dev->vblank[crtc].inmodeset & 0x2)
+ if (vblank->inmodeset & 0x2)
drm_vblank_put(dev, crtc);
- dev->vblank[crtc].inmodeset = 0;
+ vblank->inmodeset = 0;
}
}
EXPORT_SYMBOL(drm_vblank_post_modeset);
@@ -1212,6 +1374,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
union drm_wait_vblank *vblwait,
struct drm_file *file_priv)
{
+ struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
struct drm_pending_vblank_event *e;
struct timeval now;
unsigned long flags;
@@ -1235,6 +1398,18 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
spin_lock_irqsave(&dev->event_lock, flags);
+ /*
+ * drm_vblank_off() might have been called after we called
+ * drm_vblank_get(). drm_vblank_off() holds event_lock
+ * around the vblank disable, so no need for further locking.
+ * The reference from drm_vblank_get() protects against
+ * vblank disable from another source.
+ */
+ if (!vblank->enabled) {
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+
if (file_priv->event_space < sizeof e->event) {
ret = -EBUSY;
goto err_unlock;
@@ -1295,6 +1470,7 @@ err_put:
int drm_wait_vblank(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
+ struct drm_vblank_crtc *vblank;
union drm_wait_vblank *vblwait = data;
int ret;
unsigned int flags, seq, crtc, high_crtc;
@@ -1324,6 +1500,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
if (crtc >= dev->num_crtcs)
return -EINVAL;
+ vblank = &dev->vblank[crtc];
+
ret = drm_vblank_get(dev, crtc);
if (ret) {
DRM_DEBUG("failed to acquire vblank counter, %d\n", ret);
@@ -1356,11 +1534,11 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
DRM_DEBUG("waiting on vblank count %d, crtc %d\n",
vblwait->request.sequence, crtc);
- dev->vblank[crtc].last_wait = vblwait->request.sequence;
- DRM_WAIT_ON(ret, dev->vblank[crtc].queue, 3 * HZ,
+ vblank->last_wait = vblwait->request.sequence;
+ DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
(((drm_vblank_count(dev, crtc) -
vblwait->request.sequence) <= (1 << 23)) ||
- !dev->vblank[crtc].enabled ||
+ !vblank->enabled ||
!dev->irq_enabled));
if (ret != -EINTR) {
@@ -1385,12 +1563,11 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
{
struct drm_pending_vblank_event *e, *t;
struct timeval now;
- unsigned long flags;
unsigned int seq;
- seq = drm_vblank_count_and_time(dev, crtc, &now);
+ assert_spin_locked(&dev->event_lock);
- spin_lock_irqsave(&dev->event_lock, flags);
+ seq = drm_vblank_count_and_time(dev, crtc, &now);
list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
if (e->pipe != crtc)
@@ -1406,8 +1583,6 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
send_vblank_event(dev, e, seq, &now);
}
- spin_unlock_irqrestore(&dev->event_lock, flags);
-
trace_drm_vblank_event(crtc, seq);
}
@@ -1421,6 +1596,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
*/
bool drm_handle_vblank(struct drm_device *dev, int crtc)
{
+ struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
u32 vblcount;
s64 diff_ns;
struct timeval tvblank;
@@ -1429,15 +1605,21 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
if (!dev->num_crtcs)
return false;
+ if (WARN_ON(crtc >= dev->num_crtcs))
+ return false;
+
+ spin_lock_irqsave(&dev->event_lock, irqflags);
+
/* Need timestamp lock to prevent concurrent execution with
* vblank enable/disable, as this would cause inconsistent
* or corrupted timestamps and vblank counts.
*/
- spin_lock_irqsave(&dev->vblank_time_lock, irqflags);
+ spin_lock(&dev->vblank_time_lock);
/* Vblank irq handling disabled. Nothing to do. */
- if (!dev->vblank[crtc].enabled) {
- spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
+ if (!vblank->enabled) {
+ spin_unlock(&dev->vblank_time_lock);
+ spin_unlock_irqrestore(&dev->event_lock, irqflags);
return false;
}
@@ -1446,7 +1628,7 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
*/
/* Get current timestamp and count. */
- vblcount = atomic_read(&dev->vblank[crtc].count);
+ vblcount = atomic_read(&vblank->count);
drm_get_last_vbltimestamp(dev, crtc, &tvblank, DRM_CALLED_FROM_VBLIRQ);
/* Compute time difference to timestamp of last vblank */
@@ -1470,17 +1652,20 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
* the timestamp computed above.
*/
smp_mb__before_atomic();
- atomic_inc(&dev->vblank[crtc].count);
+ atomic_inc(&vblank->count);
smp_mb__after_atomic();
} else {
DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n",
crtc, (int) diff_ns);
}
- wake_up(&dev->vblank[crtc].queue);
+ spin_unlock(&dev->vblank_time_lock);
+
+ wake_up(&vblank->queue);
drm_handle_vblank_events(dev, crtc);
- spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
+ spin_unlock_irqrestore(&dev->event_lock, irqflags);
+
return true;
}
EXPORT_SYMBOL(drm_handle_vblank);
diff --git a/drivers/gpu/drm/drm_legacy.h b/drivers/gpu/drm/drm_legacy.h
new file mode 100644
index 000000000000..c1dc61473db5
--- /dev/null
+++ b/drivers/gpu/drm/drm_legacy.h
@@ -0,0 +1,113 @@
+#ifndef __DRM_LEGACY_H__
+#define __DRM_LEGACY_H__
+
+/*
+ * Copyright (c) 2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ */
+
+/*
+ * This file contains legacy interfaces that modern drm drivers
+ * should no longer be using. They cannot be removed as legacy
+ * drivers use them, and removing them are API breaks.
+ */
+#include <linux/list.h>
+#include <drm/drm_legacy.h>
+
+struct agp_memory;
+struct drm_device;
+struct drm_file;
+
+/*
+ * Generic DRM Contexts
+ */
+
+#define DRM_KERNEL_CONTEXT 0
+#define DRM_RESERVED_CONTEXTS 1
+
+int drm_legacy_ctxbitmap_init(struct drm_device *dev);
+void drm_legacy_ctxbitmap_cleanup(struct drm_device *dev);
+void drm_legacy_ctxbitmap_free(struct drm_device *dev, int ctx_handle);
+void drm_legacy_ctxbitmap_flush(struct drm_device *dev, struct drm_file *file);
+
+int drm_legacy_resctx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_addctx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_getctx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_switchctx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_newctx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_rmctx(struct drm_device *d, void *v, struct drm_file *f);
+
+int drm_legacy_setsareactx(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_getsareactx(struct drm_device *d, void *v, struct drm_file *f);
+
+/*
+ * Generic Buffer Management
+ */
+
+#define DRM_MAP_HASH_OFFSET 0x10000000
+
+int drm_legacy_addmap_ioctl(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_rmmap_ioctl(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_addbufs(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_infobufs(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_markbufs(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_freebufs(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_mapbufs(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_dma_ioctl(struct drm_device *d, void *v, struct drm_file *f);
+
+void drm_legacy_vma_flush(struct drm_device *d);
+
+/*
+ * AGP Support
+ */
+
+struct drm_agp_mem {
+ unsigned long handle;
+ struct agp_memory *memory;
+ unsigned long bound;
+ int pages;
+ struct list_head head;
+};
+
+/*
+ * Generic Userspace Locking-API
+ */
+
+int drm_legacy_i_have_hw_lock(struct drm_device *d, struct drm_file *f);
+int drm_legacy_lock(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_unlock(struct drm_device *d, void *v, struct drm_file *f);
+int drm_legacy_lock_free(struct drm_lock_data *lock, unsigned int ctx);
+
+/* DMA support */
+int drm_legacy_dma_setup(struct drm_device *dev);
+void drm_legacy_dma_takedown(struct drm_device *dev);
+void drm_legacy_free_buffer(struct drm_device *dev,
+ struct drm_buf * buf);
+void drm_legacy_reclaim_buffers(struct drm_device *dev,
+ struct drm_file *filp);
+
+/* Scatter Gather Support */
+void drm_legacy_sg_cleanup(struct drm_device *dev);
+int drm_legacy_sg_alloc(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+int drm_legacy_sg_free(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+#endif /* __DRM_LEGACY_H__ */
diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c
index f6452682141b..f861361a635e 100644
--- a/drivers/gpu/drm/drm_lock.c
+++ b/drivers/gpu/drm/drm_lock.c
@@ -35,6 +35,8 @@
#include <linux/export.h>
#include <drm/drmP.h>
+#include "drm_legacy.h"
+#include "drm_internal.h"
static int drm_notifier(void *priv);
@@ -51,7 +53,8 @@ static int drm_lock_take(struct drm_lock_data *lock_data, unsigned int context);
*
* Add the current task to the lock wait queue, and attempt to take to lock.
*/
-int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
+int drm_legacy_lock(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
DECLARE_WAITQUEUE(entry, current);
struct drm_lock *lock = data;
@@ -119,7 +122,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
sigaddset(&dev->sigmask, SIGTTOU);
dev->sigdata.context = lock->context;
dev->sigdata.lock = master->lock.hw_lock;
- block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+ block_all_signals(drm_notifier, dev, &dev->sigmask);
}
if (dev->driver->dma_quiescent && (lock->flags & _DRM_LOCK_QUIESCENT))
@@ -145,7 +148,7 @@ int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
*
* Transfer and free the lock.
*/
-int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
+int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
struct drm_lock *lock = data;
struct drm_master *master = file_priv->master;
@@ -156,7 +159,7 @@ int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
return -EINVAL;
}
- if (drm_lock_free(&master->lock, lock->context)) {
+ if (drm_legacy_lock_free(&master->lock, lock->context)) {
/* FIXME: Should really bail out here. */
}
@@ -249,7 +252,7 @@ static int drm_lock_transfer(struct drm_lock_data *lock_data,
* Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task
* waiting on the lock queue.
*/
-int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context)
+int drm_legacy_lock_free(struct drm_lock_data *lock_data, unsigned int context)
{
unsigned int old, new, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
@@ -285,26 +288,27 @@ int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context)
* If the lock is not held, then let the signal proceed as usual. If the lock
* is held, then set the contended flag and keep the signal blocked.
*
- * \param priv pointer to a drm_sigdata structure.
+ * \param priv pointer to a drm_device structure.
* \return one if the signal should be delivered normally, or zero if the
* signal should be blocked.
*/
static int drm_notifier(void *priv)
{
- struct drm_sigdata *s = (struct drm_sigdata *) priv;
+ struct drm_device *dev = priv;
+ struct drm_hw_lock *lock = dev->sigdata.lock;
unsigned int old, new, prev;
/* Allow signal delivery if lock isn't held */
- if (!s->lock || !_DRM_LOCK_IS_HELD(s->lock->lock)
- || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context)
+ if (!lock || !_DRM_LOCK_IS_HELD(lock->lock)
+ || _DRM_LOCKING_CONTEXT(lock->lock) != dev->sigdata.context)
return 1;
/* Otherwise, set flag to force call to
drmUnlock */
do {
- old = s->lock->lock;
+ old = lock->lock;
new = old | _DRM_LOCK_CONT;
- prev = cmpxchg(&s->lock->lock, old, new);
+ prev = cmpxchg(&lock->lock, old, new);
} while (prev != old);
return 0;
}
@@ -322,7 +326,7 @@ static int drm_notifier(void *priv)
* having to worry about starvation.
*/
-void drm_idlelock_take(struct drm_lock_data *lock_data)
+void drm_legacy_idlelock_take(struct drm_lock_data *lock_data)
{
int ret;
@@ -339,9 +343,9 @@ void drm_idlelock_take(struct drm_lock_data *lock_data)
}
spin_unlock_bh(&lock_data->spinlock);
}
-EXPORT_SYMBOL(drm_idlelock_take);
+EXPORT_SYMBOL(drm_legacy_idlelock_take);
-void drm_idlelock_release(struct drm_lock_data *lock_data)
+void drm_legacy_idlelock_release(struct drm_lock_data *lock_data)
{
unsigned int old, prev;
volatile unsigned int *lock = &lock_data->hw_lock->lock;
@@ -359,9 +363,10 @@ void drm_idlelock_release(struct drm_lock_data *lock_data)
}
spin_unlock_bh(&lock_data->spinlock);
}
-EXPORT_SYMBOL(drm_idlelock_release);
+EXPORT_SYMBOL(drm_legacy_idlelock_release);
-int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv)
+int drm_legacy_i_have_hw_lock(struct drm_device *dev,
+ struct drm_file *file_priv)
{
struct drm_master *master = file_priv->master;
return (file_priv->lock_count && master->lock.hw_lock &&
diff --git a/drivers/gpu/drm/drm_memory.c b/drivers/gpu/drm/drm_memory.c
index 00c67c0f2381..a521ef6ff807 100644
--- a/drivers/gpu/drm/drm_memory.c
+++ b/drivers/gpu/drm/drm_memory.c
@@ -36,8 +36,20 @@
#include <linux/highmem.h>
#include <linux/export.h>
#include <drm/drmP.h>
+#include "drm_legacy.h"
#if __OS_HAS_AGP
+
+#ifdef HAVE_PAGE_AGP
+# include <asm/agp.h>
+#else
+# ifdef __powerpc__
+# define PAGE_AGP __pgprot(_PAGE_KERNEL | _PAGE_NO_CACHE)
+# else
+# define PAGE_AGP PAGE_KERNEL
+# endif
+#endif
+
static void *agp_remap(unsigned long offset, unsigned long size,
struct drm_device * dev)
{
@@ -108,25 +120,25 @@ static inline void *agp_remap(unsigned long offset, unsigned long size,
#endif /* agp */
-void drm_core_ioremap(struct drm_local_map *map, struct drm_device *dev)
+void drm_legacy_ioremap(struct drm_local_map *map, struct drm_device *dev)
{
if (dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP)
map->handle = agp_remap(map->offset, map->size, dev);
else
map->handle = ioremap(map->offset, map->size);
}
-EXPORT_SYMBOL(drm_core_ioremap);
+EXPORT_SYMBOL(drm_legacy_ioremap);
-void drm_core_ioremap_wc(struct drm_local_map *map, struct drm_device *dev)
+void drm_legacy_ioremap_wc(struct drm_local_map *map, struct drm_device *dev)
{
if (dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP)
map->handle = agp_remap(map->offset, map->size, dev);
else
map->handle = ioremap_wc(map->offset, map->size);
}
-EXPORT_SYMBOL(drm_core_ioremap_wc);
+EXPORT_SYMBOL(drm_legacy_ioremap_wc);
-void drm_core_ioremapfree(struct drm_local_map *map, struct drm_device *dev)
+void drm_legacy_ioremapfree(struct drm_local_map *map, struct drm_device *dev)
{
if (!map->handle || !map->size)
return;
@@ -136,4 +148,4 @@ void drm_core_ioremapfree(struct drm_local_map *map, struct drm_device *dev)
else
iounmap(map->handle);
}
-EXPORT_SYMBOL(drm_core_ioremapfree);
+EXPORT_SYMBOL(drm_legacy_ioremapfree);
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index e633df2f68d8..eb6dfe52cab2 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -201,16 +201,15 @@ EXPORT_SYMBOL(mipi_dsi_detach);
/**
* mipi_dsi_dcs_write - send DCS write command
* @dsi: DSI device
- * @channel: virtual channel
* @data: pointer to the command followed by parameters
* @len: length of @data
*/
-int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel,
- const void *data, size_t len)
+ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, const void *data,
+ size_t len)
{
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
struct mipi_dsi_msg msg = {
- .channel = channel,
+ .channel = dsi->channel,
.tx_buf = data,
.tx_len = len
};
@@ -232,6 +231,9 @@ int mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, unsigned int channel,
break;
}
+ if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
+ msg.flags = MIPI_DSI_MSG_USE_LPM;
+
return ops->transfer(dsi->host, &msg);
}
EXPORT_SYMBOL(mipi_dsi_dcs_write);
@@ -239,19 +241,18 @@ EXPORT_SYMBOL(mipi_dsi_dcs_write);
/**
* mipi_dsi_dcs_read - send DCS read request command
* @dsi: DSI device
- * @channel: virtual channel
* @cmd: DCS read command
* @data: pointer to read buffer
* @len: length of @data
*
* Function returns number of read bytes or error code.
*/
-ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel,
- u8 cmd, void *data, size_t len)
+ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
+ size_t len)
{
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
struct mipi_dsi_msg msg = {
- .channel = channel,
+ .channel = dsi->channel,
.type = MIPI_DSI_DCS_READ,
.tx_buf = &cmd,
.tx_len = 1,
@@ -262,6 +263,9 @@ ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, unsigned int channel,
if (!ops || !ops->transfer)
return -ENOSYS;
+ if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
+ msg.flags = MIPI_DSI_MSG_USE_LPM;
+
return ops->transfer(dsi->host, &msg);
}
EXPORT_SYMBOL(mipi_dsi_dcs_read);
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index bedf1894e17e..d1b7d2006529 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1259,6 +1259,7 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev,
if (!mode)
return NULL;
+ mode->type |= DRM_MODE_TYPE_USERDEF;
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
return mode;
}
diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
index 0dc57d5ecd10..474e4d12a2d8 100644
--- a/drivers/gpu/drm/drm_modeset_lock.c
+++ b/drivers/gpu/drm/drm_modeset_lock.c
@@ -35,7 +35,7 @@
* of extra utility/tracking out of our acquire-ctx. This is provided
* by drm_modeset_lock / drm_modeset_acquire_ctx.
*
- * For basic principles of ww_mutex, see: Documentation/ww-mutex-design.txt
+ * For basic principles of ww_mutex, see: Documentation/locking/ww-mutex-design.txt
*
* The basic usage pattern is to:
*
@@ -57,6 +57,212 @@
/**
+ * __drm_modeset_lock_all - internal helper to grab all modeset locks
+ * @dev: DRM device
+ * @trylock: trylock mode for atomic contexts
+ *
+ * This is a special version of drm_modeset_lock_all() which can also be used in
+ * atomic contexts. Then @trylock must be set to true.
+ *
+ * Returns:
+ * 0 on success or negative error code on failure.
+ */
+int __drm_modeset_lock_all(struct drm_device *dev,
+ bool trylock)
+{
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_modeset_acquire_ctx *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx),
+ trylock ? GFP_ATOMIC : GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (trylock) {
+ if (!mutex_trylock(&config->mutex))
+ return -EBUSY;
+ } else {
+ mutex_lock(&config->mutex);
+ }
+
+ drm_modeset_acquire_init(ctx, 0);
+ ctx->trylock_only = trylock;
+
+retry:
+ ret = drm_modeset_lock(&config->connection_mutex, ctx);
+ if (ret)
+ goto fail;
+ ret = drm_modeset_lock_all_crtcs(dev, ctx);
+ if (ret)
+ goto fail;
+
+ WARN_ON(config->acquire_ctx);
+
+ /* now we hold the locks, so now that it is safe, stash the
+ * ctx for drm_modeset_unlock_all():
+ */
+ config->acquire_ctx = ctx;
+
+ drm_warn_on_modeset_not_all_locked(dev);
+
+ return 0;
+
+fail:
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(ctx);
+ goto retry;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(__drm_modeset_lock_all);
+
+/**
+ * drm_modeset_lock_all - take all modeset locks
+ * @dev: drm device
+ *
+ * This function takes all modeset locks, suitable where a more fine-grained
+ * scheme isn't (yet) implemented. Locks must be dropped with
+ * drm_modeset_unlock_all.
+ */
+void drm_modeset_lock_all(struct drm_device *dev)
+{
+ WARN_ON(__drm_modeset_lock_all(dev, false) != 0);
+}
+EXPORT_SYMBOL(drm_modeset_lock_all);
+
+/**
+ * drm_modeset_unlock_all - drop all modeset locks
+ * @dev: device
+ *
+ * This function drop all modeset locks taken by drm_modeset_lock_all.
+ */
+void drm_modeset_unlock_all(struct drm_device *dev)
+{
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
+
+ if (WARN_ON(!ctx))
+ return;
+
+ config->acquire_ctx = NULL;
+ drm_modeset_drop_locks(ctx);
+ drm_modeset_acquire_fini(ctx);
+
+ kfree(ctx);
+
+ mutex_unlock(&dev->mode_config.mutex);
+}
+EXPORT_SYMBOL(drm_modeset_unlock_all);
+
+/**
+ * drm_modeset_lock_crtc - lock crtc with hidden acquire ctx
+ * @crtc: drm crtc
+ *
+ * This function locks the given crtc using a hidden acquire context. This is
+ * necessary so that drivers internally using the atomic interfaces can grab
+ * further locks with the lock acquire context.
+ */
+void drm_modeset_lock_crtc(struct drm_crtc *crtc)
+{
+ struct drm_modeset_acquire_ctx *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (WARN_ON(!ctx))
+ return;
+
+ drm_modeset_acquire_init(ctx, 0);
+
+retry:
+ ret = drm_modeset_lock(&crtc->mutex, ctx);
+ if (ret)
+ goto fail;
+
+ WARN_ON(crtc->acquire_ctx);
+
+ /* now we hold the locks, so now that it is safe, stash the
+ * ctx for drm_modeset_unlock_crtc():
+ */
+ crtc->acquire_ctx = ctx;
+
+ return;
+
+fail:
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(ctx);
+ goto retry;
+ }
+}
+EXPORT_SYMBOL(drm_modeset_lock_crtc);
+
+/**
+ * drm_modeset_legacy_acquire_ctx - find acquire ctx for legacy ioctls
+ * @crtc: drm crtc
+ *
+ * Legacy ioctl operations like cursor updates or page flips only have per-crtc
+ * locking, and store the acquire ctx in the corresponding crtc. All other
+ * legacy operations take all locks and use a global acquire context. This
+ * function grabs the right one.
+ */
+struct drm_modeset_acquire_ctx *
+drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc)
+{
+ if (crtc->acquire_ctx)
+ return crtc->acquire_ctx;
+
+ WARN_ON(!crtc->dev->mode_config.acquire_ctx);
+
+ return crtc->dev->mode_config.acquire_ctx;
+}
+EXPORT_SYMBOL(drm_modeset_legacy_acquire_ctx);
+
+/**
+ * drm_modeset_unlock_crtc - drop crtc lock
+ * @crtc: drm crtc
+ *
+ * This drops the crtc lock acquire with drm_modeset_lock_crtc() and all other
+ * locks acquired through the hidden context.
+ */
+void drm_modeset_unlock_crtc(struct drm_crtc *crtc)
+{
+ struct drm_modeset_acquire_ctx *ctx = crtc->acquire_ctx;
+
+ if (WARN_ON(!ctx))
+ return;
+
+ crtc->acquire_ctx = NULL;
+ drm_modeset_drop_locks(ctx);
+ drm_modeset_acquire_fini(ctx);
+
+ kfree(ctx);
+}
+EXPORT_SYMBOL(drm_modeset_unlock_crtc);
+
+/**
+ * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked
+ * @dev: device
+ *
+ * Useful as a debug assert.
+ */
+void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+
+ /* Locking is currently fubar in the panic handler. */
+ if (oops_in_progress)
+ return;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
+
+ WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+ WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+}
+EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
+
+/**
* drm_modeset_acquire_init - initialize acquire context
* @ctx: the acquire context
* @flags: for future
@@ -108,7 +314,12 @@ static inline int modeset_lock(struct drm_modeset_lock *lock,
WARN_ON(ctx->contended);
- if (interruptible && slow) {
+ if (ctx->trylock_only) {
+ if (!ww_mutex_trylock(&lock->mutex))
+ return -EBUSY;
+ else
+ return 0;
+ } else if (interruptible && slow) {
ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
} else if (interruptible) {
ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
new file mode 100644
index 000000000000..16150a00c237
--- /dev/null
+++ b/drivers/gpu/drm/drm_of.c
@@ -0,0 +1,67 @@
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/of_graph.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_of.h>
+
+/**
+ * drm_crtc_port_mask - find the mask of a registered CRTC by port OF node
+ * @dev: DRM device
+ * @port: port OF node
+ *
+ * Given a port OF node, return the possible mask of the corresponding
+ * CRTC within a device's list of CRTCs. Returns zero if not found.
+ */
+static uint32_t drm_crtc_port_mask(struct drm_device *dev,
+ struct device_node *port)
+{
+ unsigned int index = 0;
+ struct drm_crtc *tmp;
+
+ list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) {
+ if (tmp->port == port)
+ return 1 << index;
+
+ index++;
+ }
+
+ return 0;
+}
+
+/**
+ * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port
+ * @dev: DRM device
+ * @port: encoder port to scan for endpoints
+ *
+ * Scan all endpoints attached to a port, locate their attached CRTCs,
+ * and generate the DRM mask of CRTCs which may be attached to this
+ * encoder.
+ *
+ * See Documentation/devicetree/bindings/graph.txt for the bindings.
+ */
+uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
+ struct device_node *port)
+{
+ struct device_node *remote_port, *ep = NULL;
+ uint32_t possible_crtcs = 0;
+
+ do {
+ ep = of_graph_get_next_endpoint(port, ep);
+ if (!ep)
+ break;
+
+ remote_port = of_graph_get_remote_port(ep);
+ if (!remote_port) {
+ of_node_put(ep);
+ return 0;
+ }
+
+ possible_crtcs |= drm_crtc_port_mask(dev, remote_port);
+
+ of_node_put(remote_port);
+ } while (1);
+
+ return possible_crtcs;
+}
+EXPORT_SYMBOL(drm_of_find_possible_crtcs);
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
index 020cfd934854..fd29f03645b8 100644
--- a/drivers/gpu/drm/drm_pci.c
+++ b/drivers/gpu/drm/drm_pci.c
@@ -27,6 +27,7 @@
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <drm/drmP.h>
+#include "drm_legacy.h"
/**
* drm_pci_alloc - Allocate a PCI consistent memory block, for DMA.
@@ -81,7 +82,7 @@ EXPORT_SYMBOL(drm_pci_alloc);
*
* This function is for internal use in the Linux-specific DRM core code.
*/
-void __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
+void __drm_legacy_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
{
unsigned long addr;
size_t sz;
@@ -105,7 +106,7 @@ void __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
*/
void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
{
- __drm_pci_free(dev, dmah);
+ __drm_legacy_pci_free(dev, dmah);
kfree(dmah);
}
@@ -127,34 +128,20 @@ static int drm_get_pci_domain(struct drm_device *dev)
return pci_domain_nr(dev->pdev->bus);
}
-static int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
+int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
{
- int len, ret;
- master->unique_len = 40;
- master->unique_size = master->unique_len;
- master->unique = kmalloc(master->unique_size, GFP_KERNEL);
- if (master->unique == NULL)
+ master->unique = kasprintf(GFP_KERNEL, "pci:%04x:%02x:%02x.%d",
+ drm_get_pci_domain(dev),
+ dev->pdev->bus->number,
+ PCI_SLOT(dev->pdev->devfn),
+ PCI_FUNC(dev->pdev->devfn));
+ if (!master->unique)
return -ENOMEM;
-
- len = snprintf(master->unique, master->unique_len,
- "pci:%04x:%02x:%02x.%d",
- drm_get_pci_domain(dev),
- dev->pdev->bus->number,
- PCI_SLOT(dev->pdev->devfn),
- PCI_FUNC(dev->pdev->devfn));
-
- if (len >= master->unique_len) {
- DRM_ERROR("buffer overflow");
- ret = -EINVAL;
- goto err;
- } else
- master->unique_len = len;
-
+ master->unique_len = strlen(master->unique);
return 0;
-err:
- return ret;
}
+EXPORT_SYMBOL(drm_pci_set_busid);
int drm_pci_set_unique(struct drm_device *dev,
struct drm_master *master,
@@ -163,8 +150,7 @@ int drm_pci_set_unique(struct drm_device *dev,
int domain, bus, slot, func, ret;
master->unique_len = u->unique_len;
- master->unique_size = u->unique_len + 1;
- master->unique = kmalloc(master->unique_size, GFP_KERNEL);
+ master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL);
if (!master->unique) {
ret = -ENOMEM;
goto err;
@@ -269,10 +255,6 @@ void drm_pci_agp_destroy(struct drm_device *dev)
}
}
-static struct drm_bus drm_pci_bus = {
- .set_busid = drm_pci_set_busid,
-};
-
/**
* drm_get_pci_dev - Register a PCI device with the DRM subsystem
* @pdev: PCI device
@@ -353,8 +335,6 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
DRM_DEBUG("\n");
- driver->bus = &drm_pci_bus;
-
if (driver->driver_features & DRIVER_MODESET)
return pci_register_driver(pdriver);
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index 6d133149cc74..827ec1a3040b 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -335,9 +335,10 @@ struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev,
}
/* possible_crtc's will be filled in later by crtc_init */
- ret = drm_plane_init(dev, primary, 0, &drm_primary_helper_funcs,
- formats, num_formats,
- DRM_PLANE_TYPE_PRIMARY);
+ ret = drm_universal_plane_init(dev, primary, 0,
+ &drm_primary_helper_funcs,
+ formats, num_formats,
+ DRM_PLANE_TYPE_PRIMARY);
if (ret) {
kfree(primary);
primary = NULL;
diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c
index d5b76f148c12..5314c9d5fef4 100644
--- a/drivers/gpu/drm/drm_platform.c
+++ b/drivers/gpu/drm/drm_platform.c
@@ -68,42 +68,23 @@ err_free:
return ret;
}
-static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master)
+int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master)
{
- int len, ret, id;
-
- master->unique_len = 13 + strlen(dev->platformdev->name);
- master->unique_size = master->unique_len;
- master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL);
-
- if (master->unique == NULL)
- return -ENOMEM;
+ int id;
id = dev->platformdev->id;
-
- /* if only a single instance of the platform device, id will be
- * set to -1.. use 0 instead to avoid a funny looking bus-id:
- */
- if (id == -1)
+ if (id < 0)
id = 0;
- len = snprintf(master->unique, master->unique_len,
- "platform:%s:%02d", dev->platformdev->name, id);
-
- if (len > master->unique_len) {
- DRM_ERROR("Unique buffer overflowed\n");
- ret = -EINVAL;
- goto err;
- }
+ master->unique = kasprintf(GFP_KERNEL, "platform:%s:%02d",
+ dev->platformdev->name, id);
+ if (!master->unique)
+ return -ENOMEM;
+ master->unique_len = strlen(master->unique);
return 0;
-err:
- return ret;
}
-
-static struct drm_bus drm_platform_bus = {
- .set_busid = drm_platform_set_busid,
-};
+EXPORT_SYMBOL(drm_platform_set_busid);
/**
* drm_platform_init - Register a platform device with the DRM subsystem
@@ -120,7 +101,6 @@ int drm_platform_init(struct drm_driver *driver, struct platform_device *platfor
{
DRM_DEBUG("\n");
- driver->bus = &drm_platform_bus;
return drm_get_platform_dev(platform_device, driver);
}
EXPORT_SYMBOL(drm_platform_init);
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index 99d578bad17e..78ca30808422 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -29,6 +29,9 @@
#include <linux/export.h>
#include <linux/dma-buf.h>
#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+
+#include "drm_internal.h"
/*
* DMA-BUF/GEM Object references and lifetime overview:
@@ -522,7 +525,7 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
goto fail_detach;
}
- obj = dev->driver->gem_prime_import_sg_table(dev, dma_buf->size, sgt);
+ obj = dev->driver->gem_prime_import_sg_table(dev, attach, sgt);
if (IS_ERR(obj)) {
ret = PTR_ERR(obj);
goto fail_unmap;
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index d22676b89cbb..6857e9ad6339 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -82,6 +82,22 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
return;
}
+static int drm_helper_probe_add_cmdline_mode(struct drm_connector *connector)
+{
+ struct drm_display_mode *mode;
+
+ if (!connector->cmdline_mode.specified)
+ return 0;
+
+ mode = drm_mode_create_from_cmdline_mode(connector->dev,
+ &connector->cmdline_mode);
+ if (mode == NULL)
+ return 0;
+
+ drm_mode_probed_add(connector, mode);
+ return 1;
+}
+
static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connector *connector,
uint32_t maxX, uint32_t maxY, bool merge_type_bits)
{
@@ -130,10 +146,18 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
count = drm_load_edid_firmware(connector);
if (count == 0)
#endif
- count = (*connector_funcs->get_modes)(connector);
+ {
+ if (connector->override_edid) {
+ struct edid *edid = (struct edid *) connector->edid_blob_ptr->data;
+
+ count = drm_add_edid_modes(connector, edid);
+ } else
+ count = (*connector_funcs->get_modes)(connector);
+ }
if (count == 0 && connector->status == connector_status_connected)
count = drm_add_modes_noedid(connector, 1024, 768);
+ count += drm_helper_probe_add_cmdline_mode(connector);
if (count == 0)
goto prune;
diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c
index 7047ca025787..631f5afd451c 100644
--- a/drivers/gpu/drm/drm_rect.c
+++ b/drivers/gpu/drm/drm_rect.c
@@ -293,3 +293,143 @@ void drm_rect_debug_print(const struct drm_rect *r, bool fixed_point)
DRM_DEBUG_KMS("%dx%d%+d%+d\n", w, h, r->x1, r->y1);
}
EXPORT_SYMBOL(drm_rect_debug_print);
+
+/**
+ * drm_rect_rotate - Rotate the rectangle
+ * @r: rectangle to be rotated
+ * @width: Width of the coordinate space
+ * @height: Height of the coordinate space
+ * @rotation: Transformation to be applied
+ *
+ * Apply @rotation to the coordinates of rectangle @r.
+ *
+ * @width and @height combined with @rotation define
+ * the location of the new origin.
+ *
+ * @width correcsponds to the horizontal and @height
+ * to the vertical axis of the untransformed coordinate
+ * space.
+ */
+void drm_rect_rotate(struct drm_rect *r,
+ int width, int height,
+ unsigned int rotation)
+{
+ struct drm_rect tmp;
+
+ if (rotation & (BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y))) {
+ tmp = *r;
+
+ if (rotation & BIT(DRM_REFLECT_X)) {
+ r->x1 = width - tmp.x2;
+ r->x2 = width - tmp.x1;
+ }
+
+ if (rotation & BIT(DRM_REFLECT_Y)) {
+ r->y1 = height - tmp.y2;
+ r->y2 = height - tmp.y1;
+ }
+ }
+
+ switch (rotation & 0xf) {
+ case BIT(DRM_ROTATE_0):
+ break;
+ case BIT(DRM_ROTATE_90):
+ tmp = *r;
+ r->x1 = tmp.y1;
+ r->x2 = tmp.y2;
+ r->y1 = width - tmp.x2;
+ r->y2 = width - tmp.x1;
+ break;
+ case BIT(DRM_ROTATE_180):
+ tmp = *r;
+ r->x1 = width - tmp.x2;
+ r->x2 = width - tmp.x1;
+ r->y1 = height - tmp.y2;
+ r->y2 = height - tmp.y1;
+ break;
+ case BIT(DRM_ROTATE_270):
+ tmp = *r;
+ r->x1 = height - tmp.y2;
+ r->x2 = height - tmp.y1;
+ r->y1 = tmp.x1;
+ r->y2 = tmp.x2;
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(drm_rect_rotate);
+
+/**
+ * drm_rect_rotate_inv - Inverse rotate the rectangle
+ * @r: rectangle to be rotated
+ * @width: Width of the coordinate space
+ * @height: Height of the coordinate space
+ * @rotation: Transformation whose inverse is to be applied
+ *
+ * Apply the inverse of @rotation to the coordinates
+ * of rectangle @r.
+ *
+ * @width and @height combined with @rotation define
+ * the location of the new origin.
+ *
+ * @width correcsponds to the horizontal and @height
+ * to the vertical axis of the original untransformed
+ * coordinate space, so that you never have to flip
+ * them when doing a rotatation and its inverse.
+ * That is, if you do:
+ *
+ * drm_rotate(&r, width, height, rotation);
+ * drm_rotate_inv(&r, width, height, rotation);
+ *
+ * you will always get back the original rectangle.
+ */
+void drm_rect_rotate_inv(struct drm_rect *r,
+ int width, int height,
+ unsigned int rotation)
+{
+ struct drm_rect tmp;
+
+ switch (rotation & 0xf) {
+ case BIT(DRM_ROTATE_0):
+ break;
+ case BIT(DRM_ROTATE_90):
+ tmp = *r;
+ r->x1 = width - tmp.y2;
+ r->x2 = width - tmp.y1;
+ r->y1 = tmp.x1;
+ r->y2 = tmp.x2;
+ break;
+ case BIT(DRM_ROTATE_180):
+ tmp = *r;
+ r->x1 = width - tmp.x2;
+ r->x2 = width - tmp.x1;
+ r->y1 = height - tmp.y2;
+ r->y2 = height - tmp.y1;
+ break;
+ case BIT(DRM_ROTATE_270):
+ tmp = *r;
+ r->x1 = tmp.y1;
+ r->x2 = tmp.y2;
+ r->y1 = height - tmp.x2;
+ r->y2 = height - tmp.x1;
+ break;
+ default:
+ break;
+ }
+
+ if (rotation & (BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y))) {
+ tmp = *r;
+
+ if (rotation & BIT(DRM_REFLECT_X)) {
+ r->x1 = width - tmp.x2;
+ r->x2 = width - tmp.x1;
+ }
+
+ if (rotation & BIT(DRM_REFLECT_Y)) {
+ r->y1 = height - tmp.y2;
+ r->y2 = height - tmp.y1;
+ }
+ }
+}
+EXPORT_SYMBOL(drm_rect_rotate_inv);
diff --git a/drivers/gpu/drm/drm_scatter.c b/drivers/gpu/drm/drm_scatter.c
index 1c78406f6e71..4f0f3b36d537 100644
--- a/drivers/gpu/drm/drm_scatter.c
+++ b/drivers/gpu/drm/drm_scatter.c
@@ -34,6 +34,7 @@
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <drm/drmP.h>
+#include "drm_legacy.h"
#define DEBUG_SCATTER 0
@@ -78,8 +79,8 @@ void drm_legacy_sg_cleanup(struct drm_device *dev)
# define ScatterHandle(x) (unsigned int)(x)
#endif
-int drm_sg_alloc(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_sg_alloc(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_scatter_gather *request = data;
struct drm_sg_mem *entry;
@@ -194,8 +195,8 @@ int drm_sg_alloc(struct drm_device *dev, void *data,
return -ENOMEM;
}
-int drm_sg_free(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_legacy_sg_free(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_scatter_gather *request = data;
struct drm_sg_mem *entry;
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
deleted file mode 100644
index 14d16464000a..000000000000
--- a/drivers/gpu/drm/drm_stub.c
+++ /dev/null
@@ -1,805 +0,0 @@
-/*
- * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
- *
- * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
- * All Rights Reserved.
- *
- * Author Rickard E. (Rik) Faith <faith@valinux.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * 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
- * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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/fs.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/mount.h>
-#include <linux/slab.h>
-#include <drm/drmP.h>
-#include <drm/drm_core.h>
-
-unsigned int drm_debug = 0; /* 1 to enable debug output */
-EXPORT_SYMBOL(drm_debug);
-
-unsigned int drm_rnodes = 0; /* 1 to enable experimental render nodes API */
-EXPORT_SYMBOL(drm_rnodes);
-
-/* 1 to allow user space to request universal planes (experimental) */
-unsigned int drm_universal_planes = 0;
-EXPORT_SYMBOL(drm_universal_planes);
-
-unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */
-EXPORT_SYMBOL(drm_vblank_offdelay);
-
-unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */
-EXPORT_SYMBOL(drm_timestamp_precision);
-
-/*
- * Default to use monotonic timestamps for wait-for-vblank and page-flip
- * complete events.
- */
-unsigned int drm_timestamp_monotonic = 1;
-
-MODULE_AUTHOR(CORE_AUTHOR);
-MODULE_DESCRIPTION(CORE_DESC);
-MODULE_LICENSE("GPL and additional rights");
-MODULE_PARM_DESC(debug, "Enable debug output");
-MODULE_PARM_DESC(rnodes, "Enable experimental render nodes API");
-MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]");
-MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
-MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
-
-module_param_named(debug, drm_debug, int, 0600);
-module_param_named(rnodes, drm_rnodes, int, 0600);
-module_param_named(universal_planes, drm_universal_planes, int, 0600);
-module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
-module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
-module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
-
-static DEFINE_SPINLOCK(drm_minor_lock);
-struct idr drm_minors_idr;
-
-struct class *drm_class;
-struct dentry *drm_debugfs_root;
-
-int drm_err(const char *func, const char *format, ...)
-{
- struct va_format vaf;
- va_list args;
- int r;
-
- va_start(args, format);
-
- vaf.fmt = format;
- vaf.va = &args;
-
- r = printk(KERN_ERR "[" DRM_NAME ":%s] *ERROR* %pV", func, &vaf);
-
- va_end(args);
-
- return r;
-}
-EXPORT_SYMBOL(drm_err);
-
-void drm_ut_debug_printk(const char *function_name, const char *format, ...)
-{
- struct va_format vaf;
- va_list args;
-
- va_start(args, format);
- vaf.fmt = format;
- vaf.va = &args;
-
- printk(KERN_DEBUG "[" DRM_NAME ":%s] %pV", function_name, &vaf);
-
- va_end(args);
-}
-EXPORT_SYMBOL(drm_ut_debug_printk);
-
-struct drm_master *drm_master_create(struct drm_minor *minor)
-{
- struct drm_master *master;
-
- master = kzalloc(sizeof(*master), GFP_KERNEL);
- if (!master)
- return NULL;
-
- kref_init(&master->refcount);
- spin_lock_init(&master->lock.spinlock);
- init_waitqueue_head(&master->lock.lock_queue);
- if (drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER)) {
- kfree(master);
- return NULL;
- }
- INIT_LIST_HEAD(&master->magicfree);
- master->minor = minor;
-
- return master;
-}
-
-struct drm_master *drm_master_get(struct drm_master *master)
-{
- kref_get(&master->refcount);
- return master;
-}
-EXPORT_SYMBOL(drm_master_get);
-
-static void drm_master_destroy(struct kref *kref)
-{
- struct drm_master *master = container_of(kref, struct drm_master, refcount);
- struct drm_magic_entry *pt, *next;
- struct drm_device *dev = master->minor->dev;
- struct drm_map_list *r_list, *list_temp;
-
- mutex_lock(&dev->struct_mutex);
- if (dev->driver->master_destroy)
- dev->driver->master_destroy(dev, master);
-
- list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) {
- if (r_list->master == master) {
- drm_rmmap_locked(dev, r_list->map);
- r_list = NULL;
- }
- }
-
- if (master->unique) {
- kfree(master->unique);
- master->unique = NULL;
- master->unique_len = 0;
- }
-
- list_for_each_entry_safe(pt, next, &master->magicfree, head) {
- list_del(&pt->head);
- drm_ht_remove_item(&master->magiclist, &pt->hash_item);
- kfree(pt);
- }
-
- drm_ht_remove(&master->magiclist);
-
- mutex_unlock(&dev->struct_mutex);
- kfree(master);
-}
-
-void drm_master_put(struct drm_master **master)
-{
- kref_put(&(*master)->refcount, drm_master_destroy);
- *master = NULL;
-}
-EXPORT_SYMBOL(drm_master_put);
-
-int drm_setmaster_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- int ret = 0;
-
- mutex_lock(&dev->master_mutex);
- if (file_priv->is_master)
- goto out_unlock;
-
- if (file_priv->minor->master) {
- ret = -EINVAL;
- goto out_unlock;
- }
-
- if (!file_priv->master) {
- ret = -EINVAL;
- goto out_unlock;
- }
-
- file_priv->minor->master = drm_master_get(file_priv->master);
- file_priv->is_master = 1;
- if (dev->driver->master_set) {
- ret = dev->driver->master_set(dev, file_priv, false);
- if (unlikely(ret != 0)) {
- file_priv->is_master = 0;
- drm_master_put(&file_priv->minor->master);
- }
- }
-
-out_unlock:
- mutex_unlock(&dev->master_mutex);
- return ret;
-}
-
-int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- int ret = -EINVAL;
-
- mutex_lock(&dev->master_mutex);
- if (!file_priv->is_master)
- goto out_unlock;
-
- if (!file_priv->minor->master)
- goto out_unlock;
-
- ret = 0;
- if (dev->driver->master_drop)
- dev->driver->master_drop(dev, file_priv, false);
- drm_master_put(&file_priv->minor->master);
- file_priv->is_master = 0;
-
-out_unlock:
- mutex_unlock(&dev->master_mutex);
- return ret;
-}
-
-/*
- * DRM Minors
- * A DRM device can provide several char-dev interfaces on the DRM-Major. Each
- * of them is represented by a drm_minor object. Depending on the capabilities
- * of the device-driver, different interfaces are registered.
- *
- * Minors can be accessed via dev->$minor_name. This pointer is either
- * NULL or a valid drm_minor pointer and stays valid as long as the device is
- * valid. This means, DRM minors have the same life-time as the underlying
- * device. However, this doesn't mean that the minor is active. Minors are
- * registered and unregistered dynamically according to device-state.
- */
-
-static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
- unsigned int type)
-{
- switch (type) {
- case DRM_MINOR_LEGACY:
- return &dev->primary;
- case DRM_MINOR_RENDER:
- return &dev->render;
- case DRM_MINOR_CONTROL:
- return &dev->control;
- default:
- return NULL;
- }
-}
-
-static int drm_minor_alloc(struct drm_device *dev, unsigned int type)
-{
- struct drm_minor *minor;
-
- minor = kzalloc(sizeof(*minor), GFP_KERNEL);
- if (!minor)
- return -ENOMEM;
-
- minor->type = type;
- minor->dev = dev;
-
- *drm_minor_get_slot(dev, type) = minor;
- return 0;
-}
-
-static void drm_minor_free(struct drm_device *dev, unsigned int type)
-{
- struct drm_minor **slot;
-
- slot = drm_minor_get_slot(dev, type);
- if (*slot) {
- drm_mode_group_destroy(&(*slot)->mode_group);
- kfree(*slot);
- *slot = NULL;
- }
-}
-
-static int drm_minor_register(struct drm_device *dev, unsigned int type)
-{
- struct drm_minor *new_minor;
- unsigned long flags;
- int ret;
- int minor_id;
-
- DRM_DEBUG("\n");
-
- new_minor = *drm_minor_get_slot(dev, type);
- if (!new_minor)
- return 0;
-
- idr_preload(GFP_KERNEL);
- spin_lock_irqsave(&drm_minor_lock, flags);
- minor_id = idr_alloc(&drm_minors_idr,
- NULL,
- 64 * type,
- 64 * (type + 1),
- GFP_NOWAIT);
- spin_unlock_irqrestore(&drm_minor_lock, flags);
- idr_preload_end();
-
- if (minor_id < 0)
- return minor_id;
-
- new_minor->index = minor_id;
-
- ret = drm_debugfs_init(new_minor, minor_id, drm_debugfs_root);
- if (ret) {
- DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
- goto err_id;
- }
-
- ret = drm_sysfs_device_add(new_minor);
- if (ret) {
- DRM_ERROR("DRM: Error sysfs_device_add.\n");
- goto err_debugfs;
- }
-
- /* replace NULL with @minor so lookups will succeed from now on */
- spin_lock_irqsave(&drm_minor_lock, flags);
- idr_replace(&drm_minors_idr, new_minor, new_minor->index);
- spin_unlock_irqrestore(&drm_minor_lock, flags);
-
- DRM_DEBUG("new minor assigned %d\n", minor_id);
- return 0;
-
-err_debugfs:
- drm_debugfs_cleanup(new_minor);
-err_id:
- spin_lock_irqsave(&drm_minor_lock, flags);
- idr_remove(&drm_minors_idr, minor_id);
- spin_unlock_irqrestore(&drm_minor_lock, flags);
- new_minor->index = 0;
- return ret;
-}
-
-static void drm_minor_unregister(struct drm_device *dev, unsigned int type)
-{
- struct drm_minor *minor;
- unsigned long flags;
-
- minor = *drm_minor_get_slot(dev, type);
- if (!minor || !minor->kdev)
- return;
-
- spin_lock_irqsave(&drm_minor_lock, flags);
- idr_remove(&drm_minors_idr, minor->index);
- spin_unlock_irqrestore(&drm_minor_lock, flags);
- minor->index = 0;
-
- drm_debugfs_cleanup(minor);
- drm_sysfs_device_remove(minor);
-}
-
-/**
- * drm_minor_acquire - Acquire a DRM minor
- * @minor_id: Minor ID of the DRM-minor
- *
- * Looks up the given minor-ID and returns the respective DRM-minor object. The
- * refence-count of the underlying device is increased so you must release this
- * object with drm_minor_release().
- *
- * As long as you hold this minor, it is guaranteed that the object and the
- * minor->dev pointer will stay valid! However, the device may get unplugged and
- * unregistered while you hold the minor.
- *
- * Returns:
- * Pointer to minor-object with increased device-refcount, or PTR_ERR on
- * failure.
- */
-struct drm_minor *drm_minor_acquire(unsigned int minor_id)
-{
- struct drm_minor *minor;
- unsigned long flags;
-
- spin_lock_irqsave(&drm_minor_lock, flags);
- minor = idr_find(&drm_minors_idr, minor_id);
- if (minor)
- drm_dev_ref(minor->dev);
- spin_unlock_irqrestore(&drm_minor_lock, flags);
-
- if (!minor) {
- return ERR_PTR(-ENODEV);
- } else if (drm_device_is_unplugged(minor->dev)) {
- drm_dev_unref(minor->dev);
- return ERR_PTR(-ENODEV);
- }
-
- return minor;
-}
-
-/**
- * drm_minor_release - Release DRM minor
- * @minor: Pointer to DRM minor object
- *
- * Release a minor that was previously acquired via drm_minor_acquire().
- */
-void drm_minor_release(struct drm_minor *minor)
-{
- drm_dev_unref(minor->dev);
-}
-
-/**
- * drm_put_dev - Unregister and release a DRM device
- * @dev: DRM device
- *
- * Called at module unload time or when a PCI device is unplugged.
- *
- * Use of this function is discouraged. It will eventually go away completely.
- * Please use drm_dev_unregister() and drm_dev_unref() explicitly instead.
- *
- * Cleans up all DRM device, calling drm_lastclose().
- */
-void drm_put_dev(struct drm_device *dev)
-{
- DRM_DEBUG("\n");
-
- if (!dev) {
- DRM_ERROR("cleanup called no dev\n");
- return;
- }
-
- drm_dev_unregister(dev);
- drm_dev_unref(dev);
-}
-EXPORT_SYMBOL(drm_put_dev);
-
-void drm_unplug_dev(struct drm_device *dev)
-{
- /* for a USB device */
- drm_minor_unregister(dev, DRM_MINOR_LEGACY);
- drm_minor_unregister(dev, DRM_MINOR_RENDER);
- drm_minor_unregister(dev, DRM_MINOR_CONTROL);
-
- mutex_lock(&drm_global_mutex);
-
- drm_device_set_unplugged(dev);
-
- if (dev->open_count == 0) {
- drm_put_dev(dev);
- }
- mutex_unlock(&drm_global_mutex);
-}
-EXPORT_SYMBOL(drm_unplug_dev);
-
-/*
- * DRM internal mount
- * We want to be able to allocate our own "struct address_space" to control
- * memory-mappings in VRAM (or stolen RAM, ...). However, core MM does not allow
- * stand-alone address_space objects, so we need an underlying inode. As there
- * is no way to allocate an independent inode easily, we need a fake internal
- * VFS mount-point.
- *
- * The drm_fs_inode_new() function allocates a new inode, drm_fs_inode_free()
- * frees it again. You are allowed to use iget() and iput() to get references to
- * the inode. But each drm_fs_inode_new() call must be paired with exactly one
- * drm_fs_inode_free() call (which does not have to be the last iput()).
- * We use drm_fs_inode_*() to manage our internal VFS mount-point and share it
- * between multiple inode-users. You could, technically, call
- * iget() + drm_fs_inode_free() directly after alloc and sometime later do an
- * iput(), but this way you'd end up with a new vfsmount for each inode.
- */
-
-static int drm_fs_cnt;
-static struct vfsmount *drm_fs_mnt;
-
-static const struct dentry_operations drm_fs_dops = {
- .d_dname = simple_dname,
-};
-
-static const struct super_operations drm_fs_sops = {
- .statfs = simple_statfs,
-};
-
-static struct dentry *drm_fs_mount(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *data)
-{
- return mount_pseudo(fs_type,
- "drm:",
- &drm_fs_sops,
- &drm_fs_dops,
- 0x010203ff);
-}
-
-static struct file_system_type drm_fs_type = {
- .name = "drm",
- .owner = THIS_MODULE,
- .mount = drm_fs_mount,
- .kill_sb = kill_anon_super,
-};
-
-static struct inode *drm_fs_inode_new(void)
-{
- struct inode *inode;
- int r;
-
- r = simple_pin_fs(&drm_fs_type, &drm_fs_mnt, &drm_fs_cnt);
- if (r < 0) {
- DRM_ERROR("Cannot mount pseudo fs: %d\n", r);
- return ERR_PTR(r);
- }
-
- inode = alloc_anon_inode(drm_fs_mnt->mnt_sb);
- if (IS_ERR(inode))
- simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
-
- return inode;
-}
-
-static void drm_fs_inode_free(struct inode *inode)
-{
- if (inode) {
- iput(inode);
- simple_release_fs(&drm_fs_mnt, &drm_fs_cnt);
- }
-}
-
-/**
- * drm_dev_alloc - Allocate new DRM device
- * @driver: DRM driver to allocate device for
- * @parent: Parent device object
- *
- * Allocate and initialize a new DRM device. No device registration is done.
- * Call drm_dev_register() to advertice the device to user space and register it
- * with other core subsystems.
- *
- * The initial ref-count of the object is 1. Use drm_dev_ref() and
- * drm_dev_unref() to take and drop further ref-counts.
- *
- * RETURNS:
- * Pointer to new DRM device, or NULL if out of memory.
- */
-struct drm_device *drm_dev_alloc(struct drm_driver *driver,
- struct device *parent)
-{
- struct drm_device *dev;
- int ret;
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return NULL;
-
- kref_init(&dev->ref);
- dev->dev = parent;
- dev->driver = driver;
-
- INIT_LIST_HEAD(&dev->filelist);
- INIT_LIST_HEAD(&dev->ctxlist);
- INIT_LIST_HEAD(&dev->vmalist);
- INIT_LIST_HEAD(&dev->maplist);
- INIT_LIST_HEAD(&dev->vblank_event_list);
-
- spin_lock_init(&dev->buf_lock);
- spin_lock_init(&dev->event_lock);
- mutex_init(&dev->struct_mutex);
- mutex_init(&dev->ctxlist_mutex);
- mutex_init(&dev->master_mutex);
-
- dev->anon_inode = drm_fs_inode_new();
- if (IS_ERR(dev->anon_inode)) {
- ret = PTR_ERR(dev->anon_inode);
- DRM_ERROR("Cannot allocate anonymous inode: %d\n", ret);
- goto err_free;
- }
-
- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
- ret = drm_minor_alloc(dev, DRM_MINOR_CONTROL);
- if (ret)
- goto err_minors;
- }
-
- if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
- ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
- if (ret)
- goto err_minors;
- }
-
- ret = drm_minor_alloc(dev, DRM_MINOR_LEGACY);
- if (ret)
- goto err_minors;
-
- if (drm_ht_create(&dev->map_hash, 12))
- goto err_minors;
-
- ret = drm_ctxbitmap_init(dev);
- if (ret) {
- DRM_ERROR("Cannot allocate memory for context bitmap.\n");
- goto err_ht;
- }
-
- if (driver->driver_features & DRIVER_GEM) {
- ret = drm_gem_init(dev);
- if (ret) {
- DRM_ERROR("Cannot initialize graphics execution manager (GEM)\n");
- goto err_ctxbitmap;
- }
- }
-
- return dev;
-
-err_ctxbitmap:
- drm_ctxbitmap_cleanup(dev);
-err_ht:
- drm_ht_remove(&dev->map_hash);
-err_minors:
- drm_minor_free(dev, DRM_MINOR_LEGACY);
- drm_minor_free(dev, DRM_MINOR_RENDER);
- drm_minor_free(dev, DRM_MINOR_CONTROL);
- drm_fs_inode_free(dev->anon_inode);
-err_free:
- mutex_destroy(&dev->master_mutex);
- kfree(dev);
- return NULL;
-}
-EXPORT_SYMBOL(drm_dev_alloc);
-
-static void drm_dev_release(struct kref *ref)
-{
- struct drm_device *dev = container_of(ref, struct drm_device, ref);
-
- if (dev->driver->driver_features & DRIVER_GEM)
- drm_gem_destroy(dev);
-
- drm_ctxbitmap_cleanup(dev);
- drm_ht_remove(&dev->map_hash);
- drm_fs_inode_free(dev->anon_inode);
-
- drm_minor_free(dev, DRM_MINOR_LEGACY);
- drm_minor_free(dev, DRM_MINOR_RENDER);
- drm_minor_free(dev, DRM_MINOR_CONTROL);
-
- mutex_destroy(&dev->master_mutex);
- kfree(dev->unique);
- kfree(dev);
-}
-
-/**
- * drm_dev_ref - Take reference of a DRM device
- * @dev: device to take reference of or NULL
- *
- * This increases the ref-count of @dev by one. You *must* already own a
- * reference when calling this. Use drm_dev_unref() to drop this reference
- * again.
- *
- * This function never fails. However, this function does not provide *any*
- * guarantee whether the device is alive or running. It only provides a
- * reference to the object and the memory associated with it.
- */
-void drm_dev_ref(struct drm_device *dev)
-{
- if (dev)
- kref_get(&dev->ref);
-}
-EXPORT_SYMBOL(drm_dev_ref);
-
-/**
- * drm_dev_unref - Drop reference of a DRM device
- * @dev: device to drop reference of or NULL
- *
- * This decreases the ref-count of @dev by one. The device is destroyed if the
- * ref-count drops to zero.
- */
-void drm_dev_unref(struct drm_device *dev)
-{
- if (dev)
- kref_put(&dev->ref, drm_dev_release);
-}
-EXPORT_SYMBOL(drm_dev_unref);
-
-/**
- * drm_dev_register - Register DRM device
- * @dev: Device to register
- * @flags: Flags passed to the driver's .load() function
- *
- * Register the DRM device @dev with the system, advertise device to user-space
- * and start normal device operation. @dev must be allocated via drm_dev_alloc()
- * previously.
- *
- * Never call this twice on any device!
- *
- * RETURNS:
- * 0 on success, negative error code on failure.
- */
-int drm_dev_register(struct drm_device *dev, unsigned long flags)
-{
- int ret;
-
- mutex_lock(&drm_global_mutex);
-
- ret = drm_minor_register(dev, DRM_MINOR_CONTROL);
- if (ret)
- goto err_minors;
-
- ret = drm_minor_register(dev, DRM_MINOR_RENDER);
- if (ret)
- goto err_minors;
-
- ret = drm_minor_register(dev, DRM_MINOR_LEGACY);
- if (ret)
- goto err_minors;
-
- if (dev->driver->load) {
- ret = dev->driver->load(dev, flags);
- if (ret)
- goto err_minors;
- }
-
- /* setup grouping for legacy outputs */
- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
- ret = drm_mode_group_init_legacy_group(dev,
- &dev->primary->mode_group);
- if (ret)
- goto err_unload;
- }
-
- ret = 0;
- goto out_unlock;
-
-err_unload:
- if (dev->driver->unload)
- dev->driver->unload(dev);
-err_minors:
- drm_minor_unregister(dev, DRM_MINOR_LEGACY);
- drm_minor_unregister(dev, DRM_MINOR_RENDER);
- drm_minor_unregister(dev, DRM_MINOR_CONTROL);
-out_unlock:
- mutex_unlock(&drm_global_mutex);
- return ret;
-}
-EXPORT_SYMBOL(drm_dev_register);
-
-/**
- * drm_dev_unregister - Unregister DRM device
- * @dev: Device to unregister
- *
- * Unregister the DRM device from the system. This does the reverse of
- * drm_dev_register() but does not deallocate the device. The caller must call
- * drm_dev_unref() to drop their final reference.
- */
-void drm_dev_unregister(struct drm_device *dev)
-{
- struct drm_map_list *r_list, *list_temp;
-
- drm_lastclose(dev);
-
- if (dev->driver->unload)
- dev->driver->unload(dev);
-
- if (dev->agp)
- drm_pci_agp_destroy(dev);
-
- drm_vblank_cleanup(dev);
-
- list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
- drm_rmmap(dev, r_list->map);
-
- drm_minor_unregister(dev, DRM_MINOR_LEGACY);
- drm_minor_unregister(dev, DRM_MINOR_RENDER);
- drm_minor_unregister(dev, DRM_MINOR_CONTROL);
-}
-EXPORT_SYMBOL(drm_dev_unregister);
-
-/**
- * drm_dev_set_unique - Set the unique name of a DRM device
- * @dev: device of which to set the unique name
- * @fmt: format string for unique name
- *
- * Sets the unique name of a DRM device using the specified format string and
- * a variable list of arguments. Drivers can use this at driver probe time if
- * the unique name of the devices they drive is static.
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int drm_dev_set_unique(struct drm_device *dev, const char *fmt, ...)
-{
- va_list ap;
-
- kfree(dev->unique);
-
- va_start(ap, fmt);
- dev->unique = kvasprintf(GFP_KERNEL, fmt, ap);
- va_end(ap);
-
- return dev->unique ? 0 : -ENOMEM;
-}
-EXPORT_SYMBOL(drm_dev_set_unique);
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 369b26278e76..cc3d6d6d67e0 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -21,6 +21,7 @@
#include <drm/drm_sysfs.h>
#include <drm/drm_core.h>
#include <drm/drmP.h>
+#include "drm_internal.h"
#define to_drm_minor(d) dev_get_drvdata(d)
#define to_drm_connector(d) dev_get_drvdata(d)
@@ -438,7 +439,6 @@ err_out_files:
out:
return ret;
}
-EXPORT_SYMBOL(drm_sysfs_connector_add);
/**
* drm_sysfs_connector_remove - remove an connector device from sysfs
@@ -468,7 +468,6 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
device_unregister(connector->kdev);
connector->kdev = NULL;
}
-EXPORT_SYMBOL(drm_sysfs_connector_remove);
/**
* drm_sysfs_hotplug_event - generate a DRM uevent
@@ -495,71 +494,55 @@ static void drm_sysfs_release(struct device *dev)
}
/**
- * drm_sysfs_device_add - adds a class device to sysfs for a character driver
- * @dev: DRM device to be added
- * @head: DRM head in question
+ * drm_sysfs_minor_alloc() - Allocate sysfs device for given minor
+ * @minor: minor to allocate sysfs device for
*
- * Add a DRM device to the DRM's device model class. We use @dev's PCI device
- * as the parent for the Linux device, and make sure it has a file containing
- * the driver we're using (for userspace compatibility).
+ * This allocates a new sysfs device for @minor and returns it. The device is
+ * not registered nor linked. The caller has to use device_add() and
+ * device_del() to register and unregister it.
+ *
+ * Note that dev_get_drvdata() on the new device will return the minor.
+ * However, the device does not hold a ref-count to the minor nor to the
+ * underlying drm_device. This is unproblematic as long as you access the
+ * private data only in sysfs callbacks. device_del() disables those
+ * synchronously, so they cannot be called after you cleanup a minor.
*/
-int drm_sysfs_device_add(struct drm_minor *minor)
+struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
{
- char *minor_str;
+ const char *minor_str;
+ struct device *kdev;
int r;
if (minor->type == DRM_MINOR_CONTROL)
minor_str = "controlD%d";
- else if (minor->type == DRM_MINOR_RENDER)
- minor_str = "renderD%d";
- else
- minor_str = "card%d";
-
- minor->kdev = kzalloc(sizeof(*minor->kdev), GFP_KERNEL);
- if (!minor->kdev) {
- r = -ENOMEM;
- goto error;
- }
-
- device_initialize(minor->kdev);
- minor->kdev->devt = MKDEV(DRM_MAJOR, minor->index);
- minor->kdev->class = drm_class;
- minor->kdev->type = &drm_sysfs_device_minor;
- minor->kdev->parent = minor->dev->dev;
- minor->kdev->release = drm_sysfs_release;
- dev_set_drvdata(minor->kdev, minor);
-
- r = dev_set_name(minor->kdev, minor_str, minor->index);
+ else if (minor->type == DRM_MINOR_RENDER)
+ minor_str = "renderD%d";
+ else
+ minor_str = "card%d";
+
+ kdev = kzalloc(sizeof(*kdev), GFP_KERNEL);
+ if (!kdev)
+ return ERR_PTR(-ENOMEM);
+
+ device_initialize(kdev);
+ kdev->devt = MKDEV(DRM_MAJOR, minor->index);
+ kdev->class = drm_class;
+ kdev->type = &drm_sysfs_device_minor;
+ kdev->parent = minor->dev->dev;
+ kdev->release = drm_sysfs_release;
+ dev_set_drvdata(kdev, minor);
+
+ r = dev_set_name(kdev, minor_str, minor->index);
if (r < 0)
- goto error;
-
- r = device_add(minor->kdev);
- if (r < 0)
- goto error;
-
- return 0;
+ goto err_free;
-error:
- DRM_ERROR("device create failed %d\n", r);
- put_device(minor->kdev);
- return r;
-}
+ return kdev;
-/**
- * drm_sysfs_device_remove - remove DRM device
- * @dev: DRM device to remove
- *
- * This call unregisters and cleans up a class device that was created with a
- * call to drm_sysfs_device_add()
- */
-void drm_sysfs_device_remove(struct drm_minor *minor)
-{
- if (minor->kdev)
- device_unregister(minor->kdev);
- minor->kdev = NULL;
+err_free:
+ put_device(kdev);
+ return ERR_PTR(r);
}
-
/**
* drm_class_device_register - Register a struct device in the drm class.
*
diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c
deleted file mode 100644
index f2fe94aab901..000000000000
--- a/drivers/gpu/drm/drm_usb.c
+++ /dev/null
@@ -1,88 +0,0 @@
-#include <drm/drmP.h>
-#include <drm/drm_usb.h>
-#include <linux/usb.h>
-#include <linux/module.h>
-
-int drm_get_usb_dev(struct usb_interface *interface,
- const struct usb_device_id *id,
- struct drm_driver *driver)
-{
- struct drm_device *dev;
- int ret;
-
- DRM_DEBUG("\n");
-
- dev = drm_dev_alloc(driver, &interface->dev);
- if (!dev)
- return -ENOMEM;
-
- dev->usbdev = interface_to_usbdev(interface);
- usb_set_intfdata(interface, dev);
-
- ret = drm_dev_register(dev, 0);
- if (ret)
- goto err_free;
-
- DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
- driver->name, driver->major, driver->minor, driver->patchlevel,
- driver->date, dev->primary->index);
-
- return 0;
-
-err_free:
- drm_dev_unref(dev);
- return ret;
-
-}
-EXPORT_SYMBOL(drm_get_usb_dev);
-
-static int drm_usb_set_busid(struct drm_device *dev,
- struct drm_master *master)
-{
- return 0;
-}
-
-static struct drm_bus drm_usb_bus = {
- .set_busid = drm_usb_set_busid,
-};
-
-/**
- * drm_usb_init - Register matching USB devices with the DRM subsystem
- * @driver: DRM device driver
- * @udriver: USB device driver
- *
- * Registers one or more devices matched by a USB driver with the DRM
- * subsystem.
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int drm_usb_init(struct drm_driver *driver, struct usb_driver *udriver)
-{
- int res;
- DRM_DEBUG("\n");
-
- driver->bus = &drm_usb_bus;
-
- res = usb_register(udriver);
- return res;
-}
-EXPORT_SYMBOL(drm_usb_init);
-
-/**
- * drm_usb_exit - Unregister matching USB devices from the DRM subsystem
- * @driver: DRM device driver
- * @udriver: USB device driver
- *
- * Unregisters one or more devices matched by a USB driver from the DRM
- * subsystem.
- */
-void drm_usb_exit(struct drm_driver *driver,
- struct usb_driver *udriver)
-{
- usb_deregister(udriver);
-}
-EXPORT_SYMBOL(drm_usb_exit);
-
-MODULE_AUTHOR("David Airlie");
-MODULE_DESCRIPTION("USB DRM support");
-MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c
index 24e045c4f531..4a2c328959e5 100644
--- a/drivers/gpu/drm/drm_vm.c
+++ b/drivers/gpu/drm/drm_vm.c
@@ -35,10 +35,19 @@
#include <drm/drmP.h>
#include <linux/export.h>
+#include <linux/seq_file.h>
#if defined(__ia64__)
#include <linux/efi.h>
#include <linux/slab.h>
#endif
+#include <asm/pgtable.h>
+#include "drm_legacy.h"
+
+struct drm_vma_entry {
+ struct list_head head;
+ struct vm_area_struct *vma;
+ pid_t pid;
+};
static void drm_vm_open(struct vm_area_struct *vma);
static void drm_vm_close(struct vm_area_struct *vma);
@@ -48,15 +57,11 @@ static pgprot_t drm_io_prot(struct drm_local_map *map,
{
pgprot_t tmp = vm_get_page_prot(vma->vm_flags);
-#if defined(__i386__) || defined(__x86_64__)
+#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__)
if (map->type == _DRM_REGISTERS && !(map->flags & _DRM_WRITE_COMBINING))
tmp = pgprot_noncached(tmp);
else
tmp = pgprot_writecombine(tmp);
-#elif defined(__powerpc__)
- pgprot_val(tmp) |= _PAGE_NO_CACHE;
- if (map->type == _DRM_REGISTERS)
- pgprot_val(tmp) |= _PAGE_GUARDED;
#elif defined(__ia64__)
if (efi_range_is_wc(vma->vm_start, vma->vm_end -
vma->vm_start))
@@ -263,7 +268,7 @@ static void drm_vm_shm_close(struct vm_area_struct *vma)
dmah.vaddr = map->handle;
dmah.busaddr = map->offset;
dmah.size = map->size;
- __drm_pci_free(dev, &dmah);
+ __drm_legacy_pci_free(dev, &dmah);
break;
}
kfree(map);
@@ -412,7 +417,6 @@ void drm_vm_open_locked(struct drm_device *dev,
list_add(&vma_entry->head, &dev->vmalist);
}
}
-EXPORT_SYMBOL_GPL(drm_vm_open_locked);
static void drm_vm_open(struct vm_area_struct *vma)
{
@@ -532,7 +536,7 @@ static resource_size_t drm_core_get_reg_ofs(struct drm_device *dev)
* according to the mapping type and remaps the pages. Finally sets the file
* pointer and calls vm_open().
*/
-int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
+static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->minor->dev;
@@ -646,7 +650,7 @@ int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
return 0;
}
-int drm_mmap(struct file *filp, struct vm_area_struct *vma)
+int drm_legacy_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->minor->dev;
@@ -661,4 +665,69 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
return ret;
}
-EXPORT_SYMBOL(drm_mmap);
+EXPORT_SYMBOL(drm_legacy_mmap);
+
+void drm_legacy_vma_flush(struct drm_device *dev)
+{
+ struct drm_vma_entry *vma, *vma_temp;
+
+ /* Clear vma list (only needed for legacy drivers) */
+ list_for_each_entry_safe(vma, vma_temp, &dev->vmalist, head) {
+ list_del(&vma->head);
+ kfree(vma);
+ }
+}
+
+int drm_vma_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_vma_entry *pt;
+ struct vm_area_struct *vma;
+ unsigned long vma_count = 0;
+#if defined(__i386__)
+ unsigned int pgprot;
+#endif
+
+ mutex_lock(&dev->struct_mutex);
+ list_for_each_entry(pt, &dev->vmalist, head)
+ vma_count++;
+
+ seq_printf(m, "vma use count: %lu, high_memory = %pK, 0x%pK\n",
+ vma_count, high_memory,
+ (void *)(unsigned long)virt_to_phys(high_memory));
+
+ list_for_each_entry(pt, &dev->vmalist, head) {
+ vma = pt->vma;
+ if (!vma)
+ continue;
+ seq_printf(m,
+ "\n%5d 0x%pK-0x%pK %c%c%c%c%c%c 0x%08lx000",
+ pt->pid,
+ (void *)vma->vm_start, (void *)vma->vm_end,
+ vma->vm_flags & VM_READ ? 'r' : '-',
+ vma->vm_flags & VM_WRITE ? 'w' : '-',
+ vma->vm_flags & VM_EXEC ? 'x' : '-',
+ vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
+ vma->vm_flags & VM_LOCKED ? 'l' : '-',
+ vma->vm_flags & VM_IO ? 'i' : '-',
+ vma->vm_pgoff);
+
+#if defined(__i386__)
+ pgprot = pgprot_val(vma->vm_page_prot);
+ seq_printf(m, " %c%c%c%c%c%c%c%c%c",
+ pgprot & _PAGE_PRESENT ? 'p' : '-',
+ pgprot & _PAGE_RW ? 'w' : 'r',
+ pgprot & _PAGE_USER ? 'u' : 's',
+ pgprot & _PAGE_PWT ? 't' : 'b',
+ pgprot & _PAGE_PCD ? 'u' : 'c',
+ pgprot & _PAGE_ACCESSED ? 'a' : '-',
+ pgprot & _PAGE_DIRTY ? 'd' : '-',
+ pgprot & _PAGE_PSE ? 'm' : 'k',
+ pgprot & _PAGE_GLOBAL ? 'g' : 'l');
+#endif
+ seq_printf(m, "\n");
+ }
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+}
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 178d2a9672a8..7f9f6f9e9b7e 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -28,6 +28,7 @@ config DRM_EXYNOS_FIMD
bool "Exynos DRM FIMD"
depends on DRM_EXYNOS && !FB_S3C
select FB_MODE_HELPERS
+ select MFD_SYSCON
help
Choose this option if you want to use Exynos FIMD for DRM.
@@ -52,6 +53,7 @@ config DRM_EXYNOS_DP
bool "EXYNOS DRM DP driver support"
depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
default DRM_EXYNOS
+ select DRM_PANEL
help
This enables support for DP device.
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index a8ffc8c1477b..6adb1e5cfb08 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -16,7 +16,6 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/interrupt.h>
-#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
@@ -28,6 +27,7 @@
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
#include <drm/bridge/ptn3460.h>
#include "exynos_drm_drv.h"
@@ -41,7 +41,7 @@ struct bridge_init {
struct device_node *node;
};
-static int exynos_dp_init_dp(struct exynos_dp_device *dp)
+static void exynos_dp_init_dp(struct exynos_dp_device *dp)
{
exynos_dp_reset(dp);
@@ -58,8 +58,6 @@ static int exynos_dp_init_dp(struct exynos_dp_device *dp)
exynos_dp_init_hpd(dp);
exynos_dp_init_aux(dp);
-
- return 0;
}
static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
@@ -331,8 +329,8 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
return retval;
for (lane = 0; lane < lane_count; lane++)
- buf[lane] = DP_TRAIN_PRE_EMPHASIS_0 |
- DP_TRAIN_VOLTAGE_SWING_400;
+ buf[lane] = DP_TRAIN_PRE_EMPH_LEVEL_0 |
+ DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
lane_count, buf);
@@ -875,10 +873,24 @@ static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
static void exynos_dp_hotplug(struct work_struct *work)
{
struct exynos_dp_device *dp;
- int ret;
dp = container_of(work, struct exynos_dp_device, hotplug_work);
+ if (dp->drm_dev)
+ drm_helper_hpd_irq_event(dp->drm_dev);
+}
+
+static void exynos_dp_commit(struct exynos_drm_display *display)
+{
+ struct exynos_dp_device *dp = display->ctx;
+ int ret;
+
+ /* Keep the panel disabled while we configure video */
+ if (dp->panel) {
+ if (drm_panel_disable(dp->panel))
+ DRM_ERROR("failed to disable the panel\n");
+ }
+
ret = exynos_dp_detect_hpd(dp);
if (ret) {
/* Cable has been disconnected, we're done */
@@ -909,6 +921,12 @@ static void exynos_dp_hotplug(struct work_struct *work)
ret = exynos_dp_config_video(dp);
if (ret)
dev_err(dp->dev, "unable to config video\n");
+
+ /* Safe to enable the panel now */
+ if (dp->panel) {
+ if (drm_panel_enable(dp->panel))
+ DRM_ERROR("failed to enable the panel\n");
+ }
}
static enum drm_connector_status exynos_dp_detect(
@@ -919,6 +937,8 @@ static enum drm_connector_status exynos_dp_detect(
static void exynos_dp_connector_destroy(struct drm_connector *connector)
{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
}
static struct drm_connector_funcs exynos_dp_connector_funcs = {
@@ -933,15 +953,18 @@ static int exynos_dp_get_modes(struct drm_connector *connector)
struct exynos_dp_device *dp = ctx_from_connector(connector);
struct drm_display_mode *mode;
+ if (dp->panel)
+ return drm_panel_get_modes(dp->panel);
+
mode = drm_mode_create(connector->dev);
if (!mode) {
DRM_ERROR("failed to create a new display mode.\n");
return 0;
}
- drm_display_mode_from_videomode(&dp->panel.vm, mode);
- mode->width_mm = dp->panel.width_mm;
- mode->height_mm = dp->panel.height_mm;
+ drm_display_mode_from_videomode(&dp->priv.vm, mode);
+ mode->width_mm = dp->priv.width_mm;
+ mode->height_mm = dp->priv.height_mm;
connector->display_info.width_mm = mode->width_mm;
connector->display_info.height_mm = mode->height_mm;
@@ -1018,10 +1041,13 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
}
drm_connector_helper_add(connector, &exynos_dp_connector_helper_funcs);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
drm_mode_connector_attach_encoder(connector, encoder);
- return 0;
+ if (dp->panel)
+ ret = drm_panel_attach(dp->panel, &dp->connector);
+
+ return ret;
}
static void exynos_dp_phy_init(struct exynos_dp_device *dp)
@@ -1050,26 +1076,50 @@ static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
}
}
-static void exynos_dp_poweron(struct exynos_dp_device *dp)
+static void exynos_dp_poweron(struct exynos_drm_display *display)
{
+ struct exynos_dp_device *dp = display->ctx;
+
if (dp->dpms_mode == DRM_MODE_DPMS_ON)
return;
+ if (dp->panel) {
+ if (drm_panel_prepare(dp->panel)) {
+ DRM_ERROR("failed to setup the panel\n");
+ return;
+ }
+ }
+
clk_prepare_enable(dp->clock);
exynos_dp_phy_init(dp);
exynos_dp_init_dp(dp);
enable_irq(dp->irq);
+ exynos_dp_commit(display);
}
-static void exynos_dp_poweroff(struct exynos_dp_device *dp)
+static void exynos_dp_poweroff(struct exynos_drm_display *display)
{
+ struct exynos_dp_device *dp = display->ctx;
+
if (dp->dpms_mode != DRM_MODE_DPMS_ON)
return;
+ if (dp->panel) {
+ if (drm_panel_disable(dp->panel)) {
+ DRM_ERROR("failed to disable the panel\n");
+ return;
+ }
+ }
+
disable_irq(dp->irq);
flush_work(&dp->hotplug_work);
exynos_dp_phy_exit(dp);
clk_disable_unprepare(dp->clock);
+
+ if (dp->panel) {
+ if (drm_panel_unprepare(dp->panel))
+ DRM_ERROR("failed to turnoff the panel\n");
+ }
}
static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
@@ -1078,12 +1128,12 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
switch (mode) {
case DRM_MODE_DPMS_ON:
- exynos_dp_poweron(dp);
+ exynos_dp_poweron(display);
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
- exynos_dp_poweroff(dp);
+ exynos_dp_poweroff(display);
break;
default:
break;
@@ -1094,6 +1144,7 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
static struct exynos_drm_display_ops exynos_dp_display_ops = {
.create_connector = exynos_dp_create_connector,
.dpms = exynos_dp_dpms,
+ .commit = exynos_dp_commit,
};
static struct exynos_drm_display exynos_dp_display = {
@@ -1201,7 +1252,7 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
{
int ret;
- ret = of_get_videomode(dp->dev->of_node, &dp->panel.vm,
+ ret = of_get_videomode(dp->dev->of_node, &dp->priv.vm,
OF_USE_NATIVE_MODE);
if (ret) {
DRM_ERROR("failed: of_get_videomode() : %d\n", ret);
@@ -1215,16 +1266,10 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm_dev = data;
struct resource *res;
- struct exynos_dp_device *dp;
+ struct exynos_dp_device *dp = exynos_dp_display.ctx;
unsigned int irq_flags;
-
int ret = 0;
- dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
- GFP_KERNEL);
- if (!dp)
- return -ENOMEM;
-
dp->dev = &pdev->dev;
dp->dpms_mode = DRM_MODE_DPMS_OFF;
@@ -1236,9 +1281,11 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
if (ret)
return ret;
- ret = exynos_dp_dt_parse_panel(dp);
- if (ret)
- return ret;
+ if (!dp->panel) {
+ ret = exynos_dp_dt_parse_panel(dp);
+ if (ret)
+ return ret;
+ }
dp->clock = devm_clk_get(&pdev->dev, "dp");
if (IS_ERR(dp->clock)) {
@@ -1298,7 +1345,6 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
disable_irq(dp->irq);
dp->drm_dev = drm_dev;
- exynos_dp_display.ctx = dp;
platform_set_drvdata(pdev, &exynos_dp_display);
@@ -1309,13 +1355,8 @@ static void exynos_dp_unbind(struct device *dev, struct device *master,
void *data)
{
struct exynos_drm_display *display = dev_get_drvdata(dev);
- struct exynos_dp_device *dp = display->ctx;
- struct drm_encoder *encoder = dp->encoder;
exynos_dp_dpms(display, DRM_MODE_DPMS_OFF);
-
- encoder->funcs->destroy(encoder);
- drm_connector_cleanup(&dp->connector);
}
static const struct component_ops exynos_dp_ops = {
@@ -1325,6 +1366,9 @@ static const struct component_ops exynos_dp_ops = {
static int exynos_dp_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ struct device_node *panel_node;
+ struct exynos_dp_device *dp;
int ret;
ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
@@ -1332,6 +1376,21 @@ static int exynos_dp_probe(struct platform_device *pdev)
if (ret)
return ret;
+ dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
+ GFP_KERNEL);
+ if (!dp)
+ return -ENOMEM;
+
+ panel_node = of_parse_phandle(dev->of_node, "panel", 0);
+ if (panel_node) {
+ dp->panel = of_drm_find_panel(panel_node);
+ of_node_put(panel_node);
+ if (!dp->panel)
+ return -EPROBE_DEFER;
+ }
+
+ exynos_dp_display.ctx = dp;
+
ret = component_add(&pdev->dev, &exynos_dp_ops);
if (ret)
exynos_drm_component_del(&pdev->dev,
@@ -1376,6 +1435,7 @@ static const struct of_device_id exynos_dp_match[] = {
{ .compatible = "samsung,exynos5-dp" },
{},
};
+MODULE_DEVICE_TABLE(of, exynos_dp_match);
struct platform_driver dp_driver = {
.probe = exynos_dp_probe,
@@ -1390,4 +1450,4 @@ struct platform_driver dp_driver = {
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DP Driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h
index 02cc4f9ab903..a1aee6931bd7 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.h
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.h
@@ -149,6 +149,7 @@ struct exynos_dp_device {
struct drm_device *drm_dev;
struct drm_connector connector;
struct drm_encoder *encoder;
+ struct drm_panel *panel;
struct clk *clock;
unsigned int irq;
void __iomem *reg_base;
@@ -162,7 +163,7 @@ struct exynos_dp_device {
int dpms_mode;
int hpd_gpio;
- struct exynos_drm_panel_info panel;
+ struct exynos_drm_panel_info priv;
};
/* exynos_dp_reg.c */
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c
index 9a16dbe121d1..ba9b3d5ed672 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_connector.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c
@@ -117,20 +117,7 @@ static struct drm_encoder *exynos_drm_best_encoder(
struct drm_device *dev = connector->dev;
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
- struct drm_mode_object *obj;
- struct drm_encoder *encoder;
-
- obj = drm_mode_object_find(dev, exynos_connector->encoder_id,
- DRM_MODE_OBJECT_ENCODER);
- if (!obj) {
- DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
- exynos_connector->encoder_id);
- return NULL;
- }
-
- encoder = obj_to_encoder(obj);
-
- return encoder;
+ return drm_encoder_find(dev, exynos_connector->encoder_id);
}
static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
@@ -185,7 +172,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(exynos_connector);
}
@@ -230,7 +217,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
drm_connector_init(dev, connector, &exynos_connector_funcs, type);
drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
- err = drm_sysfs_connector_add(connector);
+ err = drm_connector_register(connector);
if (err)
goto err_connector;
@@ -250,7 +237,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
return connector;
err_sysfs:
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
err_connector:
drm_connector_cleanup(connector);
kfree(exynos_connector);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 95c9435d0266..45026e693225 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -32,7 +32,6 @@ enum exynos_crtc_mode {
* Exynos specific crtc structure.
*
* @drm_crtc: crtc object.
- * @drm_plane: pointer of private plane object for this crtc
* @manager: the manager associated with this crtc
* @pipe: a crtc index created at load() with a new crtc object creation
* and the crtc object would be set to private->crtc array
@@ -46,7 +45,6 @@ enum exynos_crtc_mode {
*/
struct exynos_drm_crtc {
struct drm_crtc drm_crtc;
- struct drm_plane *plane;
struct exynos_drm_manager *manager;
unsigned int pipe;
unsigned int dpms;
@@ -69,15 +67,20 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
if (mode > DRM_MODE_DPMS_ON) {
/* wait for the completion of page flip. */
- wait_event(exynos_crtc->pending_flip_queue,
- atomic_read(&exynos_crtc->pending_flip) == 0);
- drm_vblank_off(crtc->dev, exynos_crtc->pipe);
+ if (!wait_event_timeout(exynos_crtc->pending_flip_queue,
+ !atomic_read(&exynos_crtc->pending_flip),
+ HZ/20))
+ atomic_set(&exynos_crtc->pending_flip, 0);
+ drm_crtc_vblank_off(crtc);
}
if (manager->ops->dpms)
manager->ops->dpms(manager, mode);
exynos_crtc->dpms = mode;
+
+ if (mode == DRM_MODE_DPMS_ON)
+ drm_crtc_vblank_on(crtc);
}
static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
@@ -92,12 +95,12 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
- exynos_plane_commit(exynos_crtc->plane);
+ exynos_plane_commit(crtc->primary);
if (manager->ops->commit)
manager->ops->commit(manager);
- exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON);
+ exynos_plane_dpms(crtc->primary, DRM_MODE_DPMS_ON);
}
static bool
@@ -121,10 +124,9 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_manager *manager = exynos_crtc->manager;
- struct drm_plane *plane = exynos_crtc->plane;
+ struct drm_framebuffer *fb = crtc->primary->fb;
unsigned int crtc_w;
unsigned int crtc_h;
- int ret;
/*
* copy the mode data adjusted by mode_fixup() into crtc->mode
@@ -132,29 +134,21 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
*/
memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
- crtc_w = crtc->primary->fb->width - x;
- crtc_h = crtc->primary->fb->height - y;
+ crtc_w = fb->width - x;
+ crtc_h = fb->height - y;
if (manager->ops->mode_set)
manager->ops->mode_set(manager, &crtc->mode);
- ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h,
- x, y, crtc_w, crtc_h);
- if (ret)
- return ret;
-
- plane->crtc = crtc;
- plane->fb = crtc->primary->fb;
- drm_framebuffer_reference(plane->fb);
-
- return 0;
+ return exynos_plane_mode_set(crtc->primary, crtc, fb, 0, 0,
+ crtc_w, crtc_h, x, y, crtc_w, crtc_h);
}
static int exynos_drm_crtc_mode_set_commit(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
- struct drm_plane *plane = exynos_crtc->plane;
+ struct drm_framebuffer *fb = crtc->primary->fb;
unsigned int crtc_w;
unsigned int crtc_h;
int ret;
@@ -165,11 +159,11 @@ static int exynos_drm_crtc_mode_set_commit(struct drm_crtc *crtc, int x, int y,
return -EPERM;
}
- crtc_w = crtc->primary->fb->width - x;
- crtc_h = crtc->primary->fb->height - y;
+ crtc_w = fb->width - x;
+ crtc_h = fb->height - y;
- ret = exynos_plane_mode_set(plane, crtc, crtc->primary->fb, 0, 0, crtc_w, crtc_h,
- x, y, crtc_w, crtc_h);
+ ret = exynos_plane_mode_set(crtc->primary, crtc, fb, 0, 0,
+ crtc_w, crtc_h, x, y, crtc_w, crtc_h);
if (ret)
return ret;
@@ -259,6 +253,7 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
spin_lock_irq(&dev->event_lock);
drm_vblank_put(dev, exynos_crtc->pipe);
list_del(&event->base.link);
+ atomic_set(&exynos_crtc->pending_flip, 0);
spin_unlock_irq(&dev->event_lock);
goto out;
@@ -301,8 +296,7 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
exynos_drm_crtc_commit(crtc);
break;
case CRTC_MODE_BLANK:
- exynos_plane_dpms(exynos_crtc->plane,
- DRM_MODE_DPMS_OFF);
+ exynos_plane_dpms(crtc->primary, DRM_MODE_DPMS_OFF);
break;
default:
break;
@@ -348,8 +342,10 @@ static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc)
int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
{
struct exynos_drm_crtc *exynos_crtc;
+ struct drm_plane *plane;
struct exynos_drm_private *private = manager->drm_dev->dev_private;
struct drm_crtc *crtc;
+ int ret;
exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
if (!exynos_crtc)
@@ -361,11 +357,11 @@ int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
exynos_crtc->manager = manager;
exynos_crtc->pipe = manager->pipe;
- exynos_crtc->plane = exynos_plane_init(manager->drm_dev,
- 1 << manager->pipe, true);
- if (!exynos_crtc->plane) {
- kfree(exynos_crtc);
- return -ENOMEM;
+ plane = exynos_plane_init(manager->drm_dev, 1 << manager->pipe,
+ DRM_PLANE_TYPE_PRIMARY);
+ if (IS_ERR(plane)) {
+ ret = PTR_ERR(plane);
+ goto err_plane;
}
manager->crtc = &exynos_crtc->drm_crtc;
@@ -373,12 +369,22 @@ int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
private->crtc[manager->pipe] = crtc;
- drm_crtc_init(manager->drm_dev, crtc, &exynos_crtc_funcs);
+ ret = drm_crtc_init_with_planes(manager->drm_dev, crtc, plane, NULL,
+ &exynos_crtc_funcs);
+ if (ret < 0)
+ goto err_crtc;
+
drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
exynos_drm_crtc_attach_mode_property(crtc);
return 0;
+
+err_crtc:
+ plane->funcs->destroy(plane);
+err_plane:
+ kfree(exynos_crtc);
+ return ret;
}
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
@@ -508,3 +514,11 @@ int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
return -EPERM;
}
+
+void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
+{
+ struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager;
+
+ if (manager->ops->te_handler)
+ manager->ops->te_handler(manager);
+}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
index 9f74b10a8a01..690dcddab725 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
@@ -36,4 +36,11 @@ void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
unsigned int out_type);
+/*
+ * This function calls the crtc device(manager)'s te_handler() callback
+ * to trigger to transfer video image at the tearing effect synchronization
+ * signal.
+ */
+void exynos_drm_crtc_te_handler(struct drm_crtc *crtc);
+
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
index 9e530f205ad2..3dc678ed9949 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -48,7 +48,7 @@ exynos_dpi_detect(struct drm_connector *connector, bool force)
static void exynos_dpi_connector_destroy(struct drm_connector *connector)
{
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
@@ -117,7 +117,7 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
}
drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
@@ -125,14 +125,18 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
static void exynos_dpi_poweron(struct exynos_dpi *ctx)
{
- if (ctx->panel)
+ if (ctx->panel) {
+ drm_panel_prepare(ctx->panel);
drm_panel_enable(ctx->panel);
+ }
}
static void exynos_dpi_poweroff(struct exynos_dpi *ctx)
{
- if (ctx->panel)
+ if (ctx->panel) {
drm_panel_disable(ctx->panel);
+ drm_panel_unprepare(ctx->panel);
+ }
}
static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode)
@@ -334,12 +338,12 @@ err_del_component:
int exynos_dpi_remove(struct device *dev)
{
- struct drm_encoder *encoder = exynos_dpi_display.encoder;
struct exynos_dpi *ctx = exynos_dpi_display.ctx;
exynos_dpi_dpms(&exynos_dpi_display, DRM_MODE_DPMS_OFF);
- encoder->funcs->destroy(encoder);
- drm_connector_cleanup(&ctx->connector);
+
+ if (ctx->panel)
+ drm_panel_detach(ctx->panel);
exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index ab7d182063c3..e5c4c6c8c967 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -15,7 +15,6 @@
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
-#include <linux/anon_inodes.h>
#include <linux/component.h>
#include <drm/exynos_drm.h>
@@ -39,8 +38,6 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
-#define VBLANK_OFF_DELAY 50000
-
static struct platform_device *exynos_drm_pdev;
static DEFINE_MUTEX(drm_component_lock);
@@ -88,44 +85,63 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
struct drm_plane *plane;
unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
- plane = exynos_plane_init(dev, possible_crtcs, false);
- if (!plane)
- goto err_mode_config_cleanup;
- }
-
- /* init kms poll for handling hpd */
- drm_kms_helper_poll_init(dev);
+ plane = exynos_plane_init(dev, possible_crtcs,
+ DRM_PLANE_TYPE_OVERLAY);
+ if (!IS_ERR(plane))
+ continue;
- ret = drm_vblank_init(dev, MAX_CRTC);
- if (ret)
+ ret = PTR_ERR(plane);
goto err_mode_config_cleanup;
+ }
/* setup possible_clones. */
exynos_drm_encoder_setup(dev);
- drm_vblank_offdelay = VBLANK_OFF_DELAY;
-
platform_set_drvdata(dev->platformdev, dev);
/* Try to bind all sub drivers. */
ret = component_bind_all(dev->dev, dev);
if (ret)
- goto err_cleanup_vblank;
+ goto err_mode_config_cleanup;
+
+ ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+ if (ret)
+ goto err_unbind_all;
/* Probe non kms sub drivers and virtual display driver. */
ret = exynos_drm_device_subdrv_probe(dev);
if (ret)
- goto err_unbind_all;
+ goto err_cleanup_vblank;
+
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = true, we can use the vblank feature.
+ *
+ * P.S. note that we wouldn't use drm irq handler but
+ * just specific driver own one instead because
+ * drm framework supports only one irq handler.
+ */
+ dev->irq_enabled = true;
+
+ /*
+ * with vblank_disable_allowed = true, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(after drm_vblank_put function is called)
+ */
+ dev->vblank_disable_allowed = true;
+
+ /* init kms poll for handling hpd */
+ drm_kms_helper_poll_init(dev);
/* force connectors detection */
drm_helper_hpd_irq_event(dev);
return 0;
-err_unbind_all:
- component_unbind_all(dev->dev, dev);
err_cleanup_vblank:
drm_vblank_cleanup(dev);
+err_unbind_all:
+ component_unbind_all(dev->dev, dev);
err_mode_config_cleanup:
drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev);
@@ -140,23 +156,19 @@ static int exynos_drm_unload(struct drm_device *dev)
exynos_drm_device_subdrv_remove(dev);
exynos_drm_fbdev_fini(dev);
- drm_vblank_cleanup(dev);
drm_kms_helper_poll_fini(dev);
- drm_mode_config_cleanup(dev);
+ drm_vblank_cleanup(dev);
+ component_unbind_all(dev->dev, dev);
+ drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev);
- kfree(dev->dev_private);
- component_unbind_all(dev->dev, dev);
+ kfree(dev->dev_private);
dev->dev_private = NULL;
return 0;
}
-static const struct file_operations exynos_drm_gem_fops = {
- .mmap = exynos_drm_gem_mmap_buffer,
-};
-
static int exynos_drm_suspend(struct drm_device *dev, pm_message_t state)
{
struct drm_connector *connector;
@@ -182,8 +194,12 @@ static int exynos_drm_resume(struct drm_device *dev)
drm_modeset_lock_all(dev);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (connector->funcs->dpms)
- connector->funcs->dpms(connector, connector->dpms);
+ if (connector->funcs->dpms) {
+ int dpms = connector->dpms;
+
+ connector->dpms = DRM_MODE_DPMS_OFF;
+ connector->funcs->dpms(connector, dpms);
+ }
}
drm_modeset_unlock_all(dev);
@@ -195,7 +211,6 @@ static int exynos_drm_resume(struct drm_device *dev)
static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
{
struct drm_exynos_file_private *file_priv;
- struct file *anon_filp;
int ret;
file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
@@ -208,21 +223,8 @@ static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
if (ret)
goto err_file_priv_free;
- anon_filp = anon_inode_getfile("exynos_gem", &exynos_drm_gem_fops,
- NULL, 0);
- if (IS_ERR(anon_filp)) {
- ret = PTR_ERR(anon_filp);
- goto err_subdrv_close;
- }
-
- anon_filp->f_mode = FMODE_READ | FMODE_WRITE;
- file_priv->anon_filp = anon_filp;
-
return ret;
-err_subdrv_close:
- exynos_drm_subdrv_close(dev, file);
-
err_file_priv_free:
kfree(file_priv);
file->driver_priv = NULL;
@@ -238,7 +240,6 @@ static void exynos_drm_preclose(struct drm_device *dev,
static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
{
struct exynos_drm_private *private = dev->dev_private;
- struct drm_exynos_file_private *file_priv;
struct drm_pending_vblank_event *v, *vt;
struct drm_pending_event *e, *et;
unsigned long flags;
@@ -264,10 +265,6 @@ static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
}
spin_unlock_irqrestore(&dev->event_lock, flags);
- file_priv = file->driver_priv;
- if (file_priv->anon_filp)
- fput(file_priv->anon_filp);
-
kfree(file->driver_priv);
file->driver_priv = NULL;
}
@@ -286,11 +283,6 @@ static const struct vm_operations_struct exynos_drm_gem_vm_ops = {
static const struct drm_ioctl_desc exynos_ioctls[] = {
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl,
DRM_UNLOCKED | DRM_AUTH),
- DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP_OFFSET,
- exynos_drm_gem_map_offset_ioctl, DRM_UNLOCKED |
- DRM_AUTH),
- DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP,
- exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_GET,
exynos_drm_gem_get_ioctl, DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION,
@@ -334,6 +326,7 @@ static struct drm_driver exynos_drm_driver = {
.preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose,
.postclose = exynos_drm_postclose,
+ .set_busid = drm_platform_set_busid,
.get_vblank_counter = drm_vblank_count,
.enable_vblank = exynos_drm_crtc_enable_vblank,
.disable_vblank = exynos_drm_crtc_disable_vblank,
@@ -362,7 +355,7 @@ static int exynos_drm_sys_suspend(struct device *dev)
struct drm_device *drm_dev = dev_get_drvdata(dev);
pm_message_t message;
- if (pm_runtime_suspended(dev))
+ if (pm_runtime_suspended(dev) || !drm_dev)
return 0;
message.event = PM_EVENT_SUSPEND;
@@ -373,7 +366,7 @@ static int exynos_drm_sys_resume(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
- if (pm_runtime_suspended(dev))
+ if (pm_runtime_suspended(dev) || !drm_dev)
return 0;
return exynos_drm_resume(drm_dev);
@@ -489,21 +482,26 @@ void exynos_drm_component_del(struct device *dev,
mutex_unlock(&drm_component_lock);
}
-static int compare_of(struct device *dev, void *data)
+static int compare_dev(struct device *dev, void *data)
{
return dev == (struct device *)data;
}
-static int exynos_drm_add_components(struct device *dev, struct master *m)
+static struct component_match *exynos_drm_match_add(struct device *dev)
{
+ struct component_match *match = NULL;
struct component_dev *cdev;
unsigned int attach_cnt = 0;
mutex_lock(&drm_component_lock);
- list_for_each_entry(cdev, &drm_component_list, list) {
- int ret;
+ /* Do not retry to probe if there is no any kms driver regitered. */
+ if (list_empty(&drm_component_list)) {
+ mutex_unlock(&drm_component_lock);
+ return ERR_PTR(-ENODEV);
+ }
+ list_for_each_entry(cdev, &drm_component_list, list) {
/*
* Add components to master only in case that crtc and
* encoder/connector device objects exist.
@@ -518,16 +516,10 @@ static int exynos_drm_add_components(struct device *dev, struct master *m)
/*
* fimd and dpi modules have same device object so add
* only crtc device object in this case.
- *
- * TODO. if dpi module follows driver-model driver then
- * below codes can be removed.
*/
if (cdev->crtc_dev == cdev->conn_dev) {
- ret = component_master_add_child(m, compare_of,
- cdev->crtc_dev);
- if (ret < 0)
- return ret;
-
+ component_match_add(dev, &match, compare_dev,
+ cdev->crtc_dev);
goto out_lock;
}
@@ -537,11 +529,8 @@ static int exynos_drm_add_components(struct device *dev, struct master *m)
* connector/encoder need pipe number of crtc when they
* are created.
*/
- ret = component_master_add_child(m, compare_of, cdev->crtc_dev);
- ret |= component_master_add_child(m, compare_of,
- cdev->conn_dev);
- if (ret < 0)
- return ret;
+ component_match_add(dev, &match, compare_dev, cdev->crtc_dev);
+ component_match_add(dev, &match, compare_dev, cdev->conn_dev);
out_lock:
mutex_lock(&drm_component_lock);
@@ -549,7 +538,7 @@ out_lock:
mutex_unlock(&drm_component_lock);
- return attach_cnt ? 0 : -ENODEV;
+ return attach_cnt ? match : ERR_PTR(-EPROBE_DEFER);
}
static int exynos_drm_bind(struct device *dev)
@@ -563,13 +552,13 @@ static void exynos_drm_unbind(struct device *dev)
}
static const struct component_master_ops exynos_drm_ops = {
- .add_components = exynos_drm_add_components,
.bind = exynos_drm_bind,
.unbind = exynos_drm_unbind,
};
static int exynos_drm_platform_probe(struct platform_device *pdev)
{
+ struct component_match *match;
int ret;
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
@@ -602,10 +591,21 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
goto err_unregister_mixer_drv;
#endif
+ match = exynos_drm_match_add(&pdev->dev);
+ if (IS_ERR(match)) {
+ ret = PTR_ERR(match);
+ goto err_unregister_hdmi_drv;
+ }
+
+ ret = component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
+ match);
+ if (ret < 0)
+ goto err_unregister_hdmi_drv;
+
#ifdef CONFIG_DRM_EXYNOS_G2D
ret = platform_driver_register(&g2d_driver);
if (ret < 0)
- goto err_unregister_hdmi_drv;
+ goto err_del_component_master;
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMC
@@ -636,11 +636,7 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
goto err_unregister_ipp_drv;
#endif
- ret = component_master_add(&pdev->dev, &exynos_drm_ops);
- if (ret < 0)
- DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n");
-
- return 0;
+ return ret;
#ifdef CONFIG_DRM_EXYNOS_IPP
err_unregister_ipp_drv:
@@ -665,9 +661,11 @@ err_unregister_g2d_drv:
#ifdef CONFIG_DRM_EXYNOS_G2D
platform_driver_unregister(&g2d_driver);
-err_unregister_hdmi_drv:
+err_del_component_master:
#endif
+ component_master_del(&pdev->dev, &exynos_drm_ops);
+err_unregister_hdmi_drv:
#ifdef CONFIG_DRM_EXYNOS_HDMI
platform_driver_unregister(&hdmi_driver);
err_unregister_mixer_drv:
@@ -748,6 +746,18 @@ static int exynos_drm_init(void)
{
int ret;
+ /*
+ * Register device object only in case of Exynos SoC.
+ *
+ * Below codes resolves temporarily infinite loop issue incurred
+ * by Exynos drm driver when using multi-platform kernel.
+ * So these codes will be replaced with more generic way later.
+ */
+ if (!of_machine_is_compatible("samsung,exynos3") &&
+ !of_machine_is_compatible("samsung,exynos4") &&
+ !of_machine_is_compatible("samsung,exynos5"))
+ return -ENODEV;
+
exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
NULL, 0);
if (IS_ERR(exynos_drm_pdev))
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index 06cde4506278..d22e640f59a0 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -40,8 +40,6 @@ struct drm_device;
struct exynos_drm_overlay;
struct drm_connector;
-extern unsigned int drm_vblank_offdelay;
-
/* This enumerates device type. */
enum exynos_drm_device_type {
EXYNOS_DEVICE_TYPE_NONE,
@@ -188,6 +186,8 @@ struct exynos_drm_display {
* @win_commit: apply hardware specific overlay data to registers.
* @win_enable: enable hardware specific overlay.
* @win_disable: disable hardware specific overlay.
+ * @te_handler: trigger to transfer video image at the tearing effect
+ * synchronization signal if there is a page flip request.
*/
struct exynos_drm_manager;
struct exynos_drm_manager_ops {
@@ -206,6 +206,7 @@ struct exynos_drm_manager_ops {
void (*win_commit)(struct exynos_drm_manager *mgr, int zpos);
void (*win_enable)(struct exynos_drm_manager *mgr, int zpos);
void (*win_disable)(struct exynos_drm_manager *mgr, int zpos);
+ void (*te_handler)(struct exynos_drm_manager *mgr);
};
/*
@@ -236,15 +237,9 @@ struct exynos_drm_g2d_private {
struct list_head userptr_list;
};
-struct exynos_drm_ipp_private {
- struct device *dev;
- struct list_head event_list;
-};
-
struct drm_exynos_file_private {
struct exynos_drm_g2d_private *g2d_priv;
- struct exynos_drm_ipp_private *ipp_priv;
- struct file *anon_filp;
+ struct device *ipp_dev;
};
/*
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 6302aa64f6c1..acf7e9e39dcd 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -16,7 +16,10 @@
#include <drm/drm_panel.h>
#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
#include <linux/irq.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/component.h>
@@ -24,6 +27,7 @@
#include <video/mipi_display.h>
#include <video/videomode.h>
+#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h"
/* returns true iff both arguments logically differs */
@@ -54,9 +58,12 @@
/* FIFO memory AC characteristic register */
#define DSIM_PLLCTRL_REG 0x4c /* PLL control register */
-#define DSIM_PLLTMR_REG 0x50 /* PLL timer register */
#define DSIM_PHYACCHR_REG 0x54 /* D-PHY AC characteristic register */
#define DSIM_PHYACCHR1_REG 0x58 /* D-PHY AC characteristic register1 */
+#define DSIM_PHYCTRL_REG 0x5c
+#define DSIM_PHYTIMING_REG 0x64
+#define DSIM_PHYTIMING1_REG 0x68
+#define DSIM_PHYTIMING2_REG 0x6c
/* DSIM_STATUS */
#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0)
@@ -107,6 +114,8 @@
#define DSIM_SYNC_INFORM (1 << 27)
#define DSIM_EOT_DISABLE (1 << 28)
#define DSIM_MFLUSH_VS (1 << 29)
+/* This flag is valid only for exynos3250/3472/4415/5260/5430 */
+#define DSIM_CLKLANE_STOP (1 << 30)
/* DSIM_ESCMODE */
#define DSIM_TX_TRIGGER_RST (1 << 4)
@@ -200,6 +209,24 @@
#define DSIM_PLL_M(x) ((x) << 4)
#define DSIM_PLL_S(x) ((x) << 1)
+/* DSIM_PHYCTRL */
+#define DSIM_PHYCTRL_ULPS_EXIT(x) (((x) & 0x1ff) << 0)
+
+/* DSIM_PHYTIMING */
+#define DSIM_PHYTIMING_LPX(x) ((x) << 8)
+#define DSIM_PHYTIMING_HS_EXIT(x) ((x) << 0)
+
+/* DSIM_PHYTIMING1 */
+#define DSIM_PHYTIMING1_CLK_PREPARE(x) ((x) << 24)
+#define DSIM_PHYTIMING1_CLK_ZERO(x) ((x) << 16)
+#define DSIM_PHYTIMING1_CLK_POST(x) ((x) << 8)
+#define DSIM_PHYTIMING1_CLK_TRAIL(x) ((x) << 0)
+
+/* DSIM_PHYTIMING2 */
+#define DSIM_PHYTIMING2_HS_PREPARE(x) ((x) << 16)
+#define DSIM_PHYTIMING2_HS_ZERO(x) ((x) << 8)
+#define DSIM_PHYTIMING2_HS_TRAIL(x) ((x) << 0)
+
#define DSI_MAX_BUS_WIDTH 4
#define DSI_NUM_VIRTUAL_CHANNELS 4
#define DSI_TX_FIFO_SIZE 2048
@@ -233,6 +260,13 @@ struct exynos_dsi_transfer {
#define DSIM_STATE_INITIALIZED BIT(1)
#define DSIM_STATE_CMD_LPM BIT(2)
+struct exynos_dsi_driver_data {
+ unsigned int plltmr_reg;
+
+ unsigned int has_freqband:1;
+ unsigned int has_clklane_stop:1;
+};
+
struct exynos_dsi {
struct mipi_dsi_host dsi_host;
struct drm_connector connector;
@@ -247,6 +281,7 @@ struct exynos_dsi {
struct clk *bus_clk;
struct regulator_bulk_data supplies[2];
int irq;
+ int te_gpio;
u32 pll_clk_rate;
u32 burst_clk_rate;
@@ -262,11 +297,48 @@ struct exynos_dsi {
spinlock_t transfer_lock; /* protects transfer_list */
struct list_head transfer_list;
+
+ struct exynos_dsi_driver_data *driver_data;
};
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
#define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
+static struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
+ .plltmr_reg = 0x50,
+ .has_freqband = 1,
+ .has_clklane_stop = 1,
+};
+
+static struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
+ .plltmr_reg = 0x50,
+ .has_freqband = 1,
+ .has_clklane_stop = 1,
+};
+
+static struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
+ .plltmr_reg = 0x58,
+};
+
+static struct of_device_id exynos_dsi_of_match[] = {
+ { .compatible = "samsung,exynos3250-mipi-dsi",
+ .data = &exynos3_dsi_driver_data },
+ { .compatible = "samsung,exynos4210-mipi-dsi",
+ .data = &exynos4_dsi_driver_data },
+ { .compatible = "samsung,exynos5410-mipi-dsi",
+ .data = &exynos5_dsi_driver_data },
+ { }
+};
+
+static inline struct exynos_dsi_driver_data *exynos_dsi_get_driver_data(
+ struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(exynos_dsi_of_match, &pdev->dev);
+
+ return (struct exynos_dsi_driver_data *)of_id->data;
+}
+
static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
{
if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
@@ -340,14 +412,9 @@ static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
unsigned long freq)
{
- static const unsigned long freq_bands[] = {
- 100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
- 270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
- 510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
- 770 * MHZ, 870 * MHZ, 950 * MHZ,
- };
+ struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
unsigned long fin, fout;
- int timeout, band;
+ int timeout;
u8 p, s;
u16 m;
u32 reg;
@@ -366,27 +433,39 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
if (!fout) {
dev_err(dsi->dev,
"failed to find PLL PMS for requested frequency\n");
- return -EFAULT;
+ return 0;
}
+ dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
- for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
- if (fout < freq_bands[band])
- break;
+ writel(500, dsi->reg_base + driver_data->plltmr_reg);
- dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d), band %d\n", fout,
- p, m, s, band);
+ reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
- writel(500, dsi->reg_base + DSIM_PLLTMR_REG);
+ if (driver_data->has_freqband) {
+ static const unsigned long freq_bands[] = {
+ 100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
+ 270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
+ 510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
+ 770 * MHZ, 870 * MHZ, 950 * MHZ,
+ };
+ int band;
+
+ for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
+ if (fout < freq_bands[band])
+ break;
+
+ dev_dbg(dsi->dev, "band %d\n", band);
+
+ reg |= DSIM_FREQ_BAND(band);
+ }
- reg = DSIM_FREQ_BAND(band) | DSIM_PLL_EN
- | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
timeout = 1000;
do {
if (timeout-- == 0) {
dev_err(dsi->dev, "PLL failed to stabilize\n");
- return -EFAULT;
+ return 0;
}
reg = readl(dsi->reg_base + DSIM_STATUS_REG);
} while ((reg & DSIM_PLL_STABLE) == 0);
@@ -433,6 +512,59 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
return 0;
}
+static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
+{
+ struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
+ u32 reg;
+
+ if (driver_data->has_freqband)
+ return;
+
+ /* B D-PHY: D-PHY Master & Slave Analog Block control */
+ reg = DSIM_PHYCTRL_ULPS_EXIT(0x0af);
+ writel(reg, dsi->reg_base + DSIM_PHYCTRL_REG);
+
+ /*
+ * T LPX: Transmitted length of any Low-Power state period
+ * T HS-EXIT: Time that the transmitter drives LP-11 following a HS
+ * burst
+ */
+ reg = DSIM_PHYTIMING_LPX(0x06) | DSIM_PHYTIMING_HS_EXIT(0x0b);
+ writel(reg, dsi->reg_base + DSIM_PHYTIMING_REG);
+
+ /*
+ * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
+ * Line state immediately before the HS-0 Line state starting the
+ * HS transmission
+ * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
+ * transmitting the Clock.
+ * T CLK_POST: Time that the transmitter continues to send HS clock
+ * after the last associated Data Lane has transitioned to LP Mode
+ * Interval is defined as the period from the end of T HS-TRAIL to
+ * the beginning of T CLK-TRAIL
+ * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
+ * the last payload clock bit of a HS transmission burst
+ */
+ reg = DSIM_PHYTIMING1_CLK_PREPARE(0x07) |
+ DSIM_PHYTIMING1_CLK_ZERO(0x27) |
+ DSIM_PHYTIMING1_CLK_POST(0x0d) |
+ DSIM_PHYTIMING1_CLK_TRAIL(0x08);
+ writel(reg, dsi->reg_base + DSIM_PHYTIMING1_REG);
+
+ /*
+ * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
+ * Line state immediately before the HS-0 Line state starting the
+ * HS transmission
+ * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
+ * transmitting the Sync sequence.
+ * T HS-TRAIL: Time that the transmitter drives the flipped differential
+ * state after last payload data bit of a HS transmission burst
+ */
+ reg = DSIM_PHYTIMING2_HS_PREPARE(0x09) | DSIM_PHYTIMING2_HS_ZERO(0x0d) |
+ DSIM_PHYTIMING2_HS_TRAIL(0x0b);
+ writel(reg, dsi->reg_base + DSIM_PHYTIMING2_REG);
+}
+
static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
{
u32 reg;
@@ -449,6 +581,7 @@ static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
static int exynos_dsi_init_link(struct exynos_dsi *dsi)
{
+ struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
int timeout;
u32 reg;
u32 lanes_mask;
@@ -468,13 +601,20 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
/* DSI configuration */
reg = 0;
+ /*
+ * The first bit of mode_flags specifies display configuration.
+ * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video
+ * mode, otherwise it will support command mode.
+ */
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
reg |= DSIM_VIDEO_MODE;
+ /*
+ * The user manual describes that following bits are ignored in
+ * command mode.
+ */
if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
reg |= DSIM_MFLUSH_VS;
- if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
- reg |= DSIM_EOT_DISABLE;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
reg |= DSIM_SYNC_INFORM;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
@@ -491,6 +631,9 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
reg |= DSIM_HSA_MODE;
}
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
+ reg |= DSIM_EOT_DISABLE;
+
switch (dsi->format) {
case MIPI_DSI_FMT_RGB888:
reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
@@ -520,6 +663,20 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
reg |= DSIM_LANE_EN(lanes_mask);
writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+ /*
+ * Use non-continuous clock mode if the periparal wants and
+ * host controller supports
+ *
+ * In non-continous clock mode, host controller will turn off
+ * the HS clock between high-speed transmissions to reduce
+ * power consumption.
+ */
+ if (driver_data->has_clklane_stop &&
+ dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
+ reg |= DSIM_CLKLANE_STOP;
+ writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+ }
+
/* Check clock and data lane state are stop state */
timeout = 100;
do {
@@ -944,17 +1101,90 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
+{
+ struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id;
+ struct drm_encoder *encoder = dsi->encoder;
+
+ if (dsi->state & DSIM_STATE_ENABLED)
+ exynos_drm_crtc_te_handler(encoder->crtc);
+
+ return IRQ_HANDLED;
+}
+
+static void exynos_dsi_enable_irq(struct exynos_dsi *dsi)
+{
+ enable_irq(dsi->irq);
+
+ if (gpio_is_valid(dsi->te_gpio))
+ enable_irq(gpio_to_irq(dsi->te_gpio));
+}
+
+static void exynos_dsi_disable_irq(struct exynos_dsi *dsi)
+{
+ if (gpio_is_valid(dsi->te_gpio))
+ disable_irq(gpio_to_irq(dsi->te_gpio));
+
+ disable_irq(dsi->irq);
+}
+
static int exynos_dsi_init(struct exynos_dsi *dsi)
{
- exynos_dsi_enable_clock(dsi);
exynos_dsi_reset(dsi);
- enable_irq(dsi->irq);
+ exynos_dsi_enable_irq(dsi);
+ exynos_dsi_enable_clock(dsi);
exynos_dsi_wait_for_reset(dsi);
+ exynos_dsi_set_phy_ctrl(dsi);
exynos_dsi_init_link(dsi);
return 0;
}
+static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi)
+{
+ int ret;
+
+ dsi->te_gpio = of_get_named_gpio(dsi->panel_node, "te-gpios", 0);
+ if (!gpio_is_valid(dsi->te_gpio)) {
+ dev_err(dsi->dev, "no te-gpios specified\n");
+ ret = dsi->te_gpio;
+ goto out;
+ }
+
+ ret = gpio_request_one(dsi->te_gpio, GPIOF_IN, "te_gpio");
+ if (ret) {
+ dev_err(dsi->dev, "gpio request failed with %d\n", ret);
+ goto out;
+ }
+
+ /*
+ * This TE GPIO IRQ should not be set to IRQ_NOAUTOEN, because panel
+ * calls drm_panel_init() first then calls mipi_dsi_attach() in probe().
+ * It means that te_gpio is invalid when exynos_dsi_enable_irq() is
+ * called by drm_panel_init() before panel is attached.
+ */
+ ret = request_threaded_irq(gpio_to_irq(dsi->te_gpio),
+ exynos_dsi_te_irq_handler, NULL,
+ IRQF_TRIGGER_RISING, "TE", dsi);
+ if (ret) {
+ dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
+ gpio_free(dsi->te_gpio);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
+{
+ if (gpio_is_valid(dsi->te_gpio)) {
+ free_irq(gpio_to_irq(dsi->te_gpio), dsi);
+ gpio_free(dsi->te_gpio);
+ dsi->te_gpio = -ENOENT;
+ }
+}
+
static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
@@ -968,6 +1198,19 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
if (dsi->connector.dev)
drm_helper_hpd_irq_event(dsi->connector.dev);
+ /*
+ * This is a temporary solution and should be made by more generic way.
+ *
+ * If attached panel device is for command mode one, dsi should register
+ * TE interrupt handler.
+ */
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) {
+ int ret = exynos_dsi_register_te_irq(dsi);
+
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -976,6 +1219,8 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
{
struct exynos_dsi *dsi = host_to_dsi(host);
+ exynos_dsi_unregister_te_irq(dsi);
+
dsi->panel_node = NULL;
if (dsi->connector.dev)
@@ -1089,7 +1334,7 @@ static void exynos_dsi_poweroff(struct exynos_dsi *dsi)
exynos_dsi_disable_clock(dsi);
- disable_irq(dsi->irq);
+ exynos_dsi_disable_irq(dsi);
}
dsi->state &= ~DSIM_STATE_CMD_LPM;
@@ -1115,7 +1360,7 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
if (ret < 0)
return ret;
- ret = drm_panel_enable(dsi->panel);
+ ret = drm_panel_prepare(dsi->panel);
if (ret < 0) {
exynos_dsi_poweroff(dsi);
return ret;
@@ -1124,6 +1369,14 @@ static int exynos_dsi_enable(struct exynos_dsi *dsi)
exynos_dsi_set_display_mode(dsi);
exynos_dsi_set_display_enable(dsi, true);
+ ret = drm_panel_enable(dsi->panel);
+ if (ret < 0) {
+ exynos_dsi_set_display_enable(dsi, false);
+ drm_panel_unprepare(dsi->panel);
+ exynos_dsi_poweroff(dsi);
+ return ret;
+ }
+
dsi->state |= DSIM_STATE_ENABLED;
return 0;
@@ -1134,8 +1387,9 @@ static void exynos_dsi_disable(struct exynos_dsi *dsi)
if (!(dsi->state & DSIM_STATE_ENABLED))
return;
- exynos_dsi_set_display_enable(dsi, false);
drm_panel_disable(dsi->panel);
+ exynos_dsi_set_display_enable(dsi, false);
+ drm_panel_unprepare(dsi->panel);
exynos_dsi_poweroff(dsi);
dsi->state &= ~DSIM_STATE_ENABLED;
@@ -1187,6 +1441,9 @@ exynos_dsi_detect(struct drm_connector *connector, bool force)
static void exynos_dsi_connector_destroy(struct drm_connector *connector)
{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+ connector->dev = NULL;
}
static struct drm_connector_funcs exynos_dsi_connector_funcs = {
@@ -1246,7 +1503,7 @@ static int exynos_dsi_create_connector(struct exynos_drm_display *display,
}
drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
@@ -1278,6 +1535,7 @@ static struct exynos_drm_display exynos_dsi_display = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.ops = &exynos_dsi_display_ops,
};
+MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
/* of_* functions will be removed after merge of of_graph patches */
static struct device_node *
@@ -1402,14 +1660,10 @@ static void exynos_dsi_unbind(struct device *dev, struct device *master,
void *data)
{
struct exynos_dsi *dsi = exynos_dsi_display.ctx;
- struct drm_encoder *encoder = dsi->encoder;
exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF);
mipi_dsi_host_unregister(&dsi->dsi_host);
-
- encoder->funcs->destroy(encoder);
- drm_connector_cleanup(&dsi->connector);
}
static const struct component_ops exynos_dsi_component_ops = {
@@ -1435,6 +1689,9 @@ static int exynos_dsi_probe(struct platform_device *pdev)
goto err_del_component;
}
+ /* To be checked as invalid one */
+ dsi->te_gpio = -ENOENT;
+
init_completion(&dsi->completed);
spin_lock_init(&dsi->transfer_lock);
INIT_LIST_HEAD(&dsi->transfer_list);
@@ -1443,6 +1700,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dsi->dsi_host.dev = &pdev->dev;
dsi->dev = &pdev->dev;
+ dsi->driver_data = exynos_dsi_get_driver_data(pdev);
ret = exynos_dsi_parse_dt(dsi);
if (ret)
@@ -1525,11 +1783,6 @@ static int exynos_dsi_remove(struct platform_device *pdev)
return 0;
}
-static struct of_device_id exynos_dsi_of_match[] = {
- { .compatible = "samsung,exynos4210-mipi-dsi" },
- { }
-};
-
struct platform_driver dsi_driver = {
.probe = exynos_dsi_probe,
.remove = exynos_dsi_remove,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index 65a22cad7b36..d346d1e6eda0 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -165,6 +165,7 @@ exynos_drm_framebuffer_init(struct drm_device *dev,
ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs);
if (ret) {
+ kfree(exynos_fb);
DRM_ERROR("failed to initialize framebuffer\n");
return ERR_PTR(ret);
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index d771b467cf0c..e12ea90c6237 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -123,6 +123,7 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
fbi->screen_base = buffer->kvaddr + offset;
fbi->screen_size = size;
+ fbi->fix.smem_len = size;
return 0;
}
@@ -225,7 +226,7 @@ out:
return ret;
}
-static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
.fb_probe = exynos_drm_fbdev_create,
};
@@ -266,7 +267,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
return -ENOMEM;
private->fb_helper = helper = &fbdev->drm_fb_helper;
- helper->funcs = &exynos_drm_fb_helper_funcs;
+
+ drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
num_crtc = dev->mode_config.num_crtc;
@@ -352,9 +354,6 @@ void exynos_drm_fbdev_fini(struct drm_device *dev)
fbdev = to_exynos_fbdev(private->fb_helper);
- if (fbdev->exynos_gem_obj)
- exynos_drm_gem_destroy(fbdev->exynos_gem_obj);
-
exynos_drm_fbdev_destroy(dev, private->fb_helper);
kfree(fbdev);
private->fb_helper = NULL;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c
index 831dde9034c6..68d38eb6774d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c
@@ -336,9 +336,6 @@ static bool fimc_check_ovf(struct fimc_context *ctx)
fimc_set_bits(ctx, EXYNOS_CIWDOFST,
EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
EXYNOS_CIWDOFST_CLROVFICR);
- fimc_clear_bits(ctx, EXYNOS_CIWDOFST,
- EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
- EXYNOS_CIWDOFST_CLROVFICR);
dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n",
ctx->id, status);
@@ -718,24 +715,24 @@ static int fimc_src_set_addr(struct device *dev,
case IPP_BUF_ENQUEUE:
config = &property->config[EXYNOS_DRM_OPS_SRC];
fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y],
- EXYNOS_CIIYSA(buf_id));
+ EXYNOS_CIIYSA0);
if (config->fmt == DRM_FORMAT_YVU420) {
fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
- EXYNOS_CIICBSA(buf_id));
+ EXYNOS_CIICBSA0);
fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
- EXYNOS_CIICRSA(buf_id));
+ EXYNOS_CIICRSA0);
} else {
fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
- EXYNOS_CIICBSA(buf_id));
+ EXYNOS_CIICBSA0);
fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
- EXYNOS_CIICRSA(buf_id));
+ EXYNOS_CIICRSA0);
}
break;
case IPP_BUF_DEQUEUE:
- fimc_write(ctx, 0x0, EXYNOS_CIIYSA(buf_id));
- fimc_write(ctx, 0x0, EXYNOS_CIICBSA(buf_id));
- fimc_write(ctx, 0x0, EXYNOS_CIICRSA(buf_id));
+ fimc_write(ctx, 0x0, EXYNOS_CIIYSA0);
+ fimc_write(ctx, 0x0, EXYNOS_CIICBSA0);
+ fimc_write(ctx, 0x0, EXYNOS_CIICRSA0);
break;
default:
/* bypass */
@@ -1122,67 +1119,34 @@ static int fimc_dst_set_size(struct device *dev, int swap,
return 0;
}
-static int fimc_dst_get_buf_count(struct fimc_context *ctx)
-{
- u32 cfg, buf_num;
-
- cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ);
-
- buf_num = hweight32(cfg);
-
- DRM_DEBUG_KMS("buf_num[%d]\n", buf_num);
-
- return buf_num;
-}
-
-static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
+static void fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
enum drm_exynos_ipp_buf_type buf_type)
{
- struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
- bool enable;
- u32 cfg;
- u32 mask = 0x00000001 << buf_id;
- int ret = 0;
unsigned long flags;
+ u32 buf_num;
+ u32 cfg;
DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type);
spin_lock_irqsave(&ctx->lock, flags);
- /* mask register set */
cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ);
- switch (buf_type) {
- case IPP_BUF_ENQUEUE:
- enable = true;
- break;
- case IPP_BUF_DEQUEUE:
- enable = false;
- break;
- default:
- dev_err(ippdrv->dev, "invalid buf ctrl parameter.\n");
- ret = -EINVAL;
- goto err_unlock;
- }
+ if (buf_type == IPP_BUF_ENQUEUE)
+ cfg |= (1 << buf_id);
+ else
+ cfg &= ~(1 << buf_id);
- /* sequence id */
- cfg &= ~mask;
- cfg |= (enable << buf_id);
fimc_write(ctx, cfg, EXYNOS_CIFCNTSEQ);
- /* interrupt enable */
- if (buf_type == IPP_BUF_ENQUEUE &&
- fimc_dst_get_buf_count(ctx) >= FIMC_BUF_START)
- fimc_mask_irq(ctx, true);
+ buf_num = hweight32(cfg);
- /* interrupt disable */
- if (buf_type == IPP_BUF_DEQUEUE &&
- fimc_dst_get_buf_count(ctx) <= FIMC_BUF_STOP)
+ if (buf_type == IPP_BUF_ENQUEUE && buf_num >= FIMC_BUF_START)
+ fimc_mask_irq(ctx, true);
+ else if (buf_type == IPP_BUF_DEQUEUE && buf_num <= FIMC_BUF_STOP)
fimc_mask_irq(ctx, false);
-err_unlock:
spin_unlock_irqrestore(&ctx->lock, flags);
- return ret;
}
static int fimc_dst_set_addr(struct device *dev,
@@ -1240,7 +1204,9 @@ static int fimc_dst_set_addr(struct device *dev,
break;
}
- return fimc_dst_set_buf_seq(ctx, buf_id, buf_type);
+ fimc_dst_set_buf_seq(ctx, buf_id, buf_type);
+
+ return 0;
}
static struct exynos_drm_ipp_ops fimc_dst_ops = {
@@ -1291,14 +1257,11 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id)
DRM_DEBUG_KMS("buf_id[%d]\n", buf_id);
- if (fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE) < 0) {
- DRM_ERROR("failed to dequeue.\n");
- return IRQ_HANDLED;
- }
+ fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE);
event_work->ippdrv = ippdrv;
event_work->buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
- queue_work(ippdrv->event_workq, (struct work_struct *)event_work);
+ queue_work(ippdrv->event_workq, &event_work->work);
return IRQ_HANDLED;
}
@@ -1590,11 +1553,8 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
fimc_clear_bits(ctx, EXYNOS_CIOCTRL, EXYNOS_CIOCTRL_WEAVE_MASK);
- if (cmd == IPP_CMD_M2M) {
- fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
-
+ if (cmd == IPP_CMD_M2M)
fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
- }
return 0;
}
@@ -1887,6 +1847,7 @@ static const struct of_device_id fimc_of_match[] = {
{ .compatible = "samsung,exynos4212-fimc" },
{ },
};
+MODULE_DEVICE_TABLE(of, fimc_of_match);
struct platform_driver fimc_driver = {
.probe = fimc_probe,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 33161ad38201..085b066a9993 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -20,6 +20,8 @@
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/component.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
@@ -61,6 +63,24 @@
/* color key value register for hardware window 1 ~ 4. */
#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8))
+/* I80 / RGB trigger control register */
+#define TRIGCON 0x1A4
+#define TRGMODE_I80_RGB_ENABLE_I80 (1 << 0)
+#define SWTRGCMD_I80_RGB_ENABLE (1 << 1)
+
+/* display mode change control register except exynos4 */
+#define VIDOUT_CON 0x000
+#define VIDOUT_CON_F_I80_LDI0 (0x2 << 8)
+
+/* I80 interface control for main LDI register */
+#define I80IFCONFAx(x) (0x1B0 + (x) * 4)
+#define I80IFCONFBx(x) (0x1B8 + (x) * 4)
+#define LCD_CS_SETUP(x) ((x) << 16)
+#define LCD_WR_SETUP(x) ((x) << 12)
+#define LCD_WR_ACTIVE(x) ((x) << 8)
+#define LCD_WR_HOLD(x) ((x) << 4)
+#define I80IFEN_ENABLE (1 << 0)
+
/* FIMD has totally five hardware windows. */
#define WINDOWS_NR 5
@@ -68,10 +88,14 @@
struct fimd_driver_data {
unsigned int timing_base;
+ unsigned int lcdblk_offset;
+ unsigned int lcdblk_vt_shift;
+ unsigned int lcdblk_bypass_shift;
unsigned int has_shadowcon:1;
unsigned int has_clksel:1;
unsigned int has_limited_fmt:1;
+ unsigned int has_vidoutcon:1;
};
static struct fimd_driver_data s3c64xx_fimd_driver_data = {
@@ -80,14 +104,29 @@ static struct fimd_driver_data s3c64xx_fimd_driver_data = {
.has_limited_fmt = 1,
};
+static struct fimd_driver_data exynos3_fimd_driver_data = {
+ .timing_base = 0x20000,
+ .lcdblk_offset = 0x210,
+ .lcdblk_bypass_shift = 1,
+ .has_shadowcon = 1,
+ .has_vidoutcon = 1,
+};
+
static struct fimd_driver_data exynos4_fimd_driver_data = {
.timing_base = 0x0,
+ .lcdblk_offset = 0x210,
+ .lcdblk_vt_shift = 10,
+ .lcdblk_bypass_shift = 1,
.has_shadowcon = 1,
};
static struct fimd_driver_data exynos5_fimd_driver_data = {
.timing_base = 0x20000,
+ .lcdblk_offset = 0x214,
+ .lcdblk_vt_shift = 24,
+ .lcdblk_bypass_shift = 15,
.has_shadowcon = 1,
+ .has_vidoutcon = 1,
};
struct fimd_win_data {
@@ -112,15 +151,22 @@ struct fimd_context {
struct clk *bus_clk;
struct clk *lcd_clk;
void __iomem *regs;
+ struct regmap *sysreg;
struct drm_display_mode mode;
struct fimd_win_data win_data[WINDOWS_NR];
unsigned int default_win;
unsigned long irq_flags;
+ u32 vidcon0;
u32 vidcon1;
+ u32 vidout_con;
+ u32 i80ifcon;
+ bool i80_if;
bool suspended;
int pipe;
wait_queue_head_t wait_vsync_queue;
atomic_t wait_vsync_event;
+ atomic_t win_updated;
+ atomic_t triggering;
struct exynos_drm_panel_info panel;
struct fimd_driver_data *driver_data;
@@ -130,12 +176,15 @@ struct fimd_context {
static const struct of_device_id fimd_driver_dt_match[] = {
{ .compatible = "samsung,s3c6400-fimd",
.data = &s3c64xx_fimd_driver_data },
+ { .compatible = "samsung,exynos3250-fimd",
+ .data = &exynos3_fimd_driver_data },
{ .compatible = "samsung,exynos4210-fimd",
.data = &exynos4_fimd_driver_data },
{ .compatible = "samsung,exynos5250-fimd",
.data = &exynos5_fimd_driver_data },
{},
};
+MODULE_DEVICE_TABLE(of, fimd_driver_dt_match);
static inline struct fimd_driver_data *drm_fimd_get_driver_data(
struct platform_device *pdev)
@@ -165,7 +214,6 @@ static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
DRM_DEBUG_KMS("vblank wait timed out.\n");
}
-
static void fimd_clear_channel(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
@@ -175,17 +223,31 @@ static void fimd_clear_channel(struct exynos_drm_manager *mgr)
/* Check if any channel is enabled. */
for (win = 0; win < WINDOWS_NR; win++) {
- u32 val = readl(ctx->regs + SHADOWCON);
- if (val & SHADOWCON_CHx_ENABLE(win)) {
- val &= ~SHADOWCON_CHx_ENABLE(win);
- writel(val, ctx->regs + SHADOWCON);
+ u32 val = readl(ctx->regs + WINCON(win));
+
+ if (val & WINCONx_ENWIN) {
+ /* wincon */
+ val &= ~WINCONx_ENWIN;
+ writel(val, ctx->regs + WINCON(win));
+
+ /* unprotect windows */
+ if (ctx->driver_data->has_shadowcon) {
+ val = readl(ctx->regs + SHADOWCON);
+ val &= ~SHADOWCON_CHx_ENABLE(win);
+ writel(val, ctx->regs + SHADOWCON);
+ }
ch_enabled = 1;
}
}
/* Wait for vsync, as disable channel takes effect at next vsync */
- if (ch_enabled)
+ if (ch_enabled) {
+ unsigned int state = ctx->suspended;
+
+ ctx->suspended = 0;
fimd_wait_for_vblank(mgr);
+ ctx->suspended = state;
+ }
}
static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
@@ -198,23 +260,6 @@ static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
mgr->drm_dev = ctx->drm_dev = drm_dev;
mgr->pipe = ctx->pipe = priv->pipe++;
- /*
- * enable drm irq mode.
- * - with irq_enabled = true, we can use the vblank feature.
- *
- * P.S. note that we wouldn't use drm irq handler but
- * just specific driver own one instead because
- * drm framework supports only one irq handler.
- */
- drm_dev->irq_enabled = true;
-
- /*
- * with vblank_disable_allowed = true, vblank interrupt will be disabled
- * by drm timer once a current process gives up ownership of
- * vblank event.(after drm_vblank_put function is called)
- */
- drm_dev->vblank_disable_allowed = true;
-
/* attach this sub driver to iommu mapping if supported. */
if (is_drm_iommu_supported(ctx->drm_dev)) {
/*
@@ -243,6 +288,14 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh;
u32 clkdiv;
+ if (ctx->i80_if) {
+ /*
+ * The frame done interrupt should be occurred prior to the
+ * next TE signal.
+ */
+ ideal_clk *= 2;
+ }
+
/* Find the clock divider value that gets us closest to ideal_clk */
clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
@@ -271,11 +324,10 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
struct drm_display_mode *mode = &ctx->mode;
- struct fimd_driver_data *driver_data;
- u32 val, clkdiv, vidcon1;
- int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
+ struct fimd_driver_data *driver_data = ctx->driver_data;
+ void *timing_base = ctx->regs + driver_data->timing_base;
+ u32 val, clkdiv;
- driver_data = ctx->driver_data;
if (ctx->suspended)
return;
@@ -283,33 +335,65 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
if (mode->htotal == 0 || mode->vtotal == 0)
return;
- /* setup polarity values */
- vidcon1 = ctx->vidcon1;
- if (mode->flags & DRM_MODE_FLAG_NVSYNC)
- vidcon1 |= VIDCON1_INV_VSYNC;
- if (mode->flags & DRM_MODE_FLAG_NHSYNC)
- vidcon1 |= VIDCON1_INV_HSYNC;
- writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
-
- /* setup vertical timing values. */
- vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
- vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
- vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
-
- val = VIDTCON0_VBPD(vbpd - 1) |
- VIDTCON0_VFPD(vfpd - 1) |
- VIDTCON0_VSPW(vsync_len - 1);
- writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
-
- /* setup horizontal timing values. */
- hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
- hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
- hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
-
- val = VIDTCON1_HBPD(hbpd - 1) |
- VIDTCON1_HFPD(hfpd - 1) |
- VIDTCON1_HSPW(hsync_len - 1);
- writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
+ if (ctx->i80_if) {
+ val = ctx->i80ifcon | I80IFEN_ENABLE;
+ writel(val, timing_base + I80IFCONFAx(0));
+
+ /* disable auto frame rate */
+ writel(0, timing_base + I80IFCONFBx(0));
+
+ /* set video type selection to I80 interface */
+ if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
+ driver_data->lcdblk_offset,
+ 0x3 << driver_data->lcdblk_vt_shift,
+ 0x1 << driver_data->lcdblk_vt_shift)) {
+ DRM_ERROR("Failed to update sysreg for I80 i/f.\n");
+ return;
+ }
+ } else {
+ int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd;
+ u32 vidcon1;
+
+ /* setup polarity values */
+ vidcon1 = ctx->vidcon1;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ vidcon1 |= VIDCON1_INV_VSYNC;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ vidcon1 |= VIDCON1_INV_HSYNC;
+ writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1);
+
+ /* setup vertical timing values. */
+ vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+ vbpd = mode->crtc_vtotal - mode->crtc_vsync_end;
+ vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay;
+
+ val = VIDTCON0_VBPD(vbpd - 1) |
+ VIDTCON0_VFPD(vfpd - 1) |
+ VIDTCON0_VSPW(vsync_len - 1);
+ writel(val, ctx->regs + driver_data->timing_base + VIDTCON0);
+
+ /* setup horizontal timing values. */
+ hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+ hbpd = mode->crtc_htotal - mode->crtc_hsync_end;
+ hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay;
+
+ val = VIDTCON1_HBPD(hbpd - 1) |
+ VIDTCON1_HFPD(hfpd - 1) |
+ VIDTCON1_HSPW(hsync_len - 1);
+ writel(val, ctx->regs + driver_data->timing_base + VIDTCON1);
+ }
+
+ if (driver_data->has_vidoutcon)
+ writel(ctx->vidout_con, timing_base + VIDOUT_CON);
+
+ /* set bypass selection */
+ if (ctx->sysreg && regmap_update_bits(ctx->sysreg,
+ driver_data->lcdblk_offset,
+ 0x1 << driver_data->lcdblk_bypass_shift,
+ 0x1 << driver_data->lcdblk_bypass_shift)) {
+ DRM_ERROR("Failed to update sysreg for bypass setting.\n");
+ return;
+ }
/* setup horizontal and vertical display size. */
val = VIDTCON2_LINEVAL(mode->vdisplay - 1) |
@@ -322,7 +406,8 @@ static void fimd_commit(struct exynos_drm_manager *mgr)
* fields of register with prefix '_F' would be updated
* at vsync(same as dma start)
*/
- val = VIDCON0_ENVID | VIDCON0_ENVID_F;
+ val = ctx->vidcon0;
+ val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
if (ctx->driver_data->has_clksel)
val |= VIDCON0_CLKSEL_LCD;
@@ -660,6 +745,9 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos)
}
win_data->enabled = true;
+
+ if (ctx->i80_if)
+ atomic_set(&ctx->win_updated, 1);
}
static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
@@ -838,6 +926,58 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
}
}
+static void fimd_trigger(struct device *dev)
+{
+ struct exynos_drm_manager *mgr = get_fimd_manager(dev);
+ struct fimd_context *ctx = mgr->ctx;
+ struct fimd_driver_data *driver_data = ctx->driver_data;
+ void *timing_base = ctx->regs + driver_data->timing_base;
+ u32 reg;
+
+ atomic_set(&ctx->triggering, 1);
+
+ reg = readl(ctx->regs + VIDINTCON0);
+ reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE |
+ VIDINTCON0_INT_SYSMAINCON);
+ writel(reg, ctx->regs + VIDINTCON0);
+
+ reg = readl(timing_base + TRIGCON);
+ reg |= (TRGMODE_I80_RGB_ENABLE_I80 | SWTRGCMD_I80_RGB_ENABLE);
+ writel(reg, timing_base + TRIGCON);
+}
+
+static void fimd_te_handler(struct exynos_drm_manager *mgr)
+{
+ struct fimd_context *ctx = mgr->ctx;
+
+ /* Checks the crtc is detached already from encoder */
+ if (ctx->pipe < 0 || !ctx->drm_dev)
+ return;
+
+ /*
+ * Skips to trigger if in triggering state, because multiple triggering
+ * requests can cause panel reset.
+ */
+ if (atomic_read(&ctx->triggering))
+ return;
+
+ /*
+ * If there is a page flip request, triggers and handles the page flip
+ * event so that current fb can be updated into panel GRAM.
+ */
+ if (atomic_add_unless(&ctx->win_updated, -1, 0))
+ fimd_trigger(ctx->dev);
+
+ /* Wakes up vsync event queue */
+ if (atomic_read(&ctx->wait_vsync_event)) {
+ atomic_set(&ctx->wait_vsync_event, 0);
+ wake_up(&ctx->wait_vsync_queue);
+
+ if (!atomic_read(&ctx->triggering))
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ }
+}
+
static struct exynos_drm_manager_ops fimd_manager_ops = {
.dpms = fimd_dpms,
.mode_fixup = fimd_mode_fixup,
@@ -849,6 +989,7 @@ static struct exynos_drm_manager_ops fimd_manager_ops = {
.win_mode_set = fimd_win_mode_set,
.win_commit = fimd_win_commit,
.win_disable = fimd_win_disable,
+ .te_handler = fimd_te_handler,
};
static struct exynos_drm_manager fimd_manager = {
@@ -859,26 +1000,40 @@ static struct exynos_drm_manager fimd_manager = {
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
{
struct fimd_context *ctx = (struct fimd_context *)dev_id;
- u32 val;
+ u32 val, clear_bit;
val = readl(ctx->regs + VIDINTCON1);
- if (val & VIDINTCON1_INT_FRAME)
- /* VSYNC interrupt */
- writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
+ clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME;
+ if (val & clear_bit)
+ writel(clear_bit, ctx->regs + VIDINTCON1);
/* check the crtc is detached already from encoder */
if (ctx->pipe < 0 || !ctx->drm_dev)
goto out;
- drm_handle_vblank(ctx->drm_dev, ctx->pipe);
- exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+ if (ctx->i80_if) {
+ /* unset I80 frame done interrupt */
+ val = readl(ctx->regs + VIDINTCON0);
+ val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON);
+ writel(val, ctx->regs + VIDINTCON0);
+
+ /* exit triggering mode */
+ atomic_set(&ctx->triggering, 0);
- /* set wait vsync event to zero and wake up queue. */
- if (atomic_read(&ctx->wait_vsync_event)) {
- atomic_set(&ctx->wait_vsync_event, 0);
- wake_up(&ctx->wait_vsync_queue);
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+ } else {
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe);
+
+ /* set wait vsync event to zero and wake up queue. */
+ if (atomic_read(&ctx->wait_vsync_event)) {
+ atomic_set(&ctx->wait_vsync_event, 0);
+ wake_up(&ctx->wait_vsync_queue);
+ }
}
+
out:
return IRQ_HANDLED;
}
@@ -902,7 +1057,6 @@ static void fimd_unbind(struct device *dev, struct device *master,
{
struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
struct fimd_context *ctx = fimd_manager.ctx;
- struct drm_crtc *crtc = mgr->crtc;
fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
@@ -910,8 +1064,6 @@ static void fimd_unbind(struct device *dev, struct device *master,
exynos_dpi_remove(dev);
fimd_mgr_remove(mgr);
-
- crtc->funcs->destroy(crtc);
}
static const struct component_ops fimd_component_ops = {
@@ -923,6 +1075,7 @@ static int fimd_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fimd_context *ctx;
+ struct device_node *i80_if_timings;
struct resource *res;
int ret = -EINVAL;
@@ -944,12 +1097,51 @@ static int fimd_probe(struct platform_device *pdev)
ctx->dev = dev;
ctx->suspended = true;
+ ctx->driver_data = drm_fimd_get_driver_data(pdev);
if (of_property_read_bool(dev->of_node, "samsung,invert-vden"))
ctx->vidcon1 |= VIDCON1_INV_VDEN;
if (of_property_read_bool(dev->of_node, "samsung,invert-vclk"))
ctx->vidcon1 |= VIDCON1_INV_VCLK;
+ i80_if_timings = of_get_child_by_name(dev->of_node, "i80-if-timings");
+ if (i80_if_timings) {
+ u32 val;
+
+ ctx->i80_if = true;
+
+ if (ctx->driver_data->has_vidoutcon)
+ ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0;
+ else
+ ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0;
+ /*
+ * The user manual describes that this "DSI_EN" bit is required
+ * to enable I80 24-bit data interface.
+ */
+ ctx->vidcon0 |= VIDCON0_DSI_EN;
+
+ if (of_property_read_u32(i80_if_timings, "cs-setup", &val))
+ val = 0;
+ ctx->i80ifcon = LCD_CS_SETUP(val);
+ if (of_property_read_u32(i80_if_timings, "wr-setup", &val))
+ val = 0;
+ ctx->i80ifcon |= LCD_WR_SETUP(val);
+ if (of_property_read_u32(i80_if_timings, "wr-active", &val))
+ val = 1;
+ ctx->i80ifcon |= LCD_WR_ACTIVE(val);
+ if (of_property_read_u32(i80_if_timings, "wr-hold", &val))
+ val = 0;
+ ctx->i80ifcon |= LCD_WR_HOLD(val);
+ }
+ of_node_put(i80_if_timings);
+
+ ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,sysreg");
+ if (IS_ERR(ctx->sysreg)) {
+ dev_warn(dev, "failed to get system register.\n");
+ ctx->sysreg = NULL;
+ }
+
ctx->bus_clk = devm_clk_get(dev, "fimd");
if (IS_ERR(ctx->bus_clk)) {
dev_err(dev, "failed to get bus clock\n");
@@ -972,7 +1164,8 @@ static int fimd_probe(struct platform_device *pdev)
goto err_del_component;
}
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ ctx->i80_if ? "lcd_sys" : "vsync");
if (!res) {
dev_err(dev, "irq request failed.\n");
ret = -ENXIO;
@@ -986,7 +1179,6 @@ static int fimd_probe(struct platform_device *pdev)
goto err_del_component;
}
- ctx->driver_data = drm_fimd_get_driver_data(pdev);
init_waitqueue_head(&ctx->wait_vsync_queue);
atomic_set(&ctx->wait_vsync_event, 0);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index 800158714473..6ff8599f6cbf 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -302,9 +302,12 @@ static void g2d_fini_cmdlist(struct g2d_data *g2d)
struct exynos_drm_subdrv *subdrv = &g2d->subdrv;
kfree(g2d->cmdlist_node);
- dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
- g2d->cmdlist_pool_virt,
- g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
+
+ if (g2d->cmdlist_pool_virt && g2d->cmdlist_pool) {
+ dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
+ g2d->cmdlist_pool_virt,
+ g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
+ }
}
static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d)
@@ -1042,8 +1045,23 @@ err:
int exynos_g2d_get_ver_ioctl(struct drm_device *drm_dev, void *data,
struct drm_file *file)
{
+ struct drm_exynos_file_private *file_priv = file->driver_priv;
+ struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
+ struct device *dev;
+ struct g2d_data *g2d;
struct drm_exynos_g2d_get_ver *ver = data;
+ if (!g2d_priv)
+ return -ENODEV;
+
+ dev = g2d_priv->dev;
+ if (!dev)
+ return -ENODEV;
+
+ g2d = dev_get_drvdata(dev);
+ if (!g2d)
+ return -EFAULT;
+
ver->major = G2D_HW_MAJOR_VER;
ver->minor = G2D_HW_MINOR_VER;
@@ -1056,7 +1074,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
{
struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
- struct device *dev = g2d_priv->dev;
+ struct device *dev;
struct g2d_data *g2d;
struct drm_exynos_g2d_set_cmdlist *req = data;
struct drm_exynos_g2d_cmd *cmd;
@@ -1067,6 +1085,10 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
int size;
int ret;
+ if (!g2d_priv)
+ return -ENODEV;
+
+ dev = g2d_priv->dev;
if (!dev)
return -ENODEV;
@@ -1223,13 +1245,17 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
{
struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
- struct device *dev = g2d_priv->dev;
+ struct device *dev;
struct g2d_data *g2d;
struct drm_exynos_g2d_exec *req = data;
struct g2d_runqueue_node *runqueue_node;
struct list_head *run_cmdlist;
struct list_head *event_list;
+ if (!g2d_priv)
+ return -ENODEV;
+
+ dev = g2d_priv->dev;
if (!dev)
return -ENODEV;
@@ -1544,8 +1570,10 @@ static const struct dev_pm_ops g2d_pm_ops = {
static const struct of_device_id exynos_g2d_match[] = {
{ .compatible = "samsung,exynos5250-g2d" },
+ { .compatible = "samsung,exynos4212-g2d" },
{},
};
+MODULE_DEVICE_TABLE(of, exynos_g2d_match);
struct platform_driver g2d_driver = {
.probe = g2d_probe,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index 163a054922cb..0d5b9698d384 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -301,7 +301,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *filp)
{
- struct exynos_drm_gem_obj *exynos_gem_obj;
struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, filp, gem_handle);
@@ -310,8 +309,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
return;
}
- exynos_gem_obj = to_exynos_gem_obj(obj);
-
drm_gem_object_unreference_unlocked(obj);
/*
@@ -321,40 +318,16 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
drm_gem_object_unreference_unlocked(obj);
}
-int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- struct drm_exynos_gem_map_off *args = data;
-
- DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
- args->handle, (unsigned long)args->offset);
-
- if (!(dev->driver->driver_features & DRIVER_GEM)) {
- DRM_ERROR("does not support GEM.\n");
- return -ENODEV;
- }
-
- return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle,
- &args->offset);
-}
-
-int exynos_drm_gem_mmap_buffer(struct file *filp,
+int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem_obj *exynos_gem_obj,
struct vm_area_struct *vma)
{
- struct drm_gem_object *obj = filp->private_data;
- struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
- struct drm_device *drm_dev = obj->dev;
+ struct drm_device *drm_dev = exynos_gem_obj->base.dev;
struct exynos_drm_gem_buf *buffer;
unsigned long vm_size;
int ret;
- WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex));
-
- vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
- vma->vm_private_data = obj;
- vma->vm_ops = drm_dev->driver->gem_vm_ops;
-
- update_vm_cache_attr(exynos_gem_obj, vma);
+ vma->vm_flags &= ~VM_PFNMAP;
+ vma->vm_pgoff = 0;
vm_size = vma->vm_end - vma->vm_start;
@@ -376,60 +349,6 @@ int exynos_drm_gem_mmap_buffer(struct file *filp,
return ret;
}
- /*
- * take a reference to this mapping of the object. And this reference
- * is unreferenced by the corresponding vm_close call.
- */
- drm_gem_object_reference(obj);
-
- drm_vm_open_locked(drm_dev, vma);
-
- return 0;
-}
-
-int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- struct drm_exynos_file_private *exynos_file_priv;
- struct drm_exynos_gem_mmap *args = data;
- struct drm_gem_object *obj;
- struct file *anon_filp;
- unsigned long addr;
-
- if (!(dev->driver->driver_features & DRIVER_GEM)) {
- DRM_ERROR("does not support GEM.\n");
- return -ENODEV;
- }
-
- mutex_lock(&dev->struct_mutex);
-
- obj = drm_gem_object_lookup(dev, file_priv, args->handle);
- if (!obj) {
- DRM_ERROR("failed to lookup gem object.\n");
- mutex_unlock(&dev->struct_mutex);
- return -EINVAL;
- }
-
- exynos_file_priv = file_priv->driver_priv;
- anon_filp = exynos_file_priv->anon_filp;
- anon_filp->private_data = obj;
-
- addr = vm_mmap(anon_filp, 0, args->size, PROT_READ | PROT_WRITE,
- MAP_SHARED, 0);
-
- drm_gem_object_unreference(obj);
-
- if (IS_ERR_VALUE(addr)) {
- mutex_unlock(&dev->struct_mutex);
- return (int)addr;
- }
-
- mutex_unlock(&dev->struct_mutex);
-
- args->mapped = addr;
-
- DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
-
return 0;
}
@@ -713,16 +632,20 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
exynos_gem_obj = to_exynos_gem_obj(obj);
ret = check_gem_flags(exynos_gem_obj->flags);
- if (ret) {
- drm_gem_vm_close(vma);
- drm_gem_free_mmap_offset(obj);
- return ret;
- }
-
- vma->vm_flags &= ~VM_PFNMAP;
- vma->vm_flags |= VM_MIXEDMAP;
+ if (ret)
+ goto err_close_vm;
update_vm_cache_attr(exynos_gem_obj, vma);
+ ret = exynos_drm_gem_mmap_buffer(exynos_gem_obj, vma);
+ if (ret)
+ goto err_close_vm;
+
+ return ret;
+
+err_close_vm:
+ drm_gem_vm_close(vma);
+ drm_gem_free_mmap_offset(obj);
+
return ret;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h
index 1592c0ba7de8..ec58fe9c40df 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h
@@ -12,6 +12,8 @@
#ifndef _EXYNOS_DRM_GEM_H_
#define _EXYNOS_DRM_GEM_H_
+#include <drm/drm_gem.h>
+
#define to_exynos_gem_obj(x) container_of(x,\
struct exynos_drm_gem_obj, base)
@@ -111,20 +113,6 @@ void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *filp);
-/* get buffer offset to map to user space. */
-int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
-
-/*
- * mmap the physically continuous memory that a gem object contains
- * to user space.
- */
-int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
-
-int exynos_drm_gem_mmap_buffer(struct file *filp,
- struct vm_area_struct *vma);
-
/* map user space allocated by malloc to pages. */
int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
index 9e3ff1672965..c6a013fc321c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
@@ -1326,8 +1326,7 @@ static irqreturn_t gsc_irq_handler(int irq, void *dev_id)
buf_id[EXYNOS_DRM_OPS_SRC];
event_work->buf_id[EXYNOS_DRM_OPS_DST] =
buf_id[EXYNOS_DRM_OPS_DST];
- queue_work(ippdrv->event_workq,
- (struct work_struct *)event_work);
+ queue_work(ippdrv->event_workq, &event_work->work);
}
return IRQ_HANDLED;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
index a1888e128f1d..00d74b18f7cb 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
@@ -75,7 +75,6 @@ struct drm_exynos_ipp_mem_node {
u32 prop_id;
u32 buf_id;
struct drm_exynos_ipp_buf_info buf_info;
- struct drm_file *filp;
};
/*
@@ -129,9 +128,6 @@ void exynos_platform_device_ipp_unregister(void)
int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv)
{
- if (!ippdrv)
- return -EINVAL;
-
mutex_lock(&exynos_drm_ippdrv_lock);
list_add_tail(&ippdrv->drv_list, &exynos_drm_ippdrv_list);
mutex_unlock(&exynos_drm_ippdrv_lock);
@@ -141,9 +137,6 @@ int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv)
int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv)
{
- if (!ippdrv)
- return -EINVAL;
-
mutex_lock(&exynos_drm_ippdrv_lock);
list_del(&ippdrv->drv_list);
mutex_unlock(&exynos_drm_ippdrv_lock);
@@ -151,20 +144,15 @@ int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv)
return 0;
}
-static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj,
- u32 *idp)
+static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj)
{
int ret;
- /* do the allocation under our mutexlock */
mutex_lock(lock);
ret = idr_alloc(id_idr, obj, 1, 0, GFP_KERNEL);
mutex_unlock(lock);
- if (ret < 0)
- return ret;
- *idp = ret;
- return 0;
+ return ret;
}
static void ipp_remove_id(struct idr *id_idr, struct mutex *lock, u32 id)
@@ -178,35 +166,25 @@ static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id)
{
void *obj;
- DRM_DEBUG_KMS("id[%d]\n", id);
-
mutex_lock(lock);
-
- /* find object using handle */
obj = idr_find(id_idr, id);
- if (!obj) {
- DRM_ERROR("failed to find object.\n");
- mutex_unlock(lock);
- return ERR_PTR(-ENODEV);
- }
-
mutex_unlock(lock);
return obj;
}
-static inline bool ipp_check_dedicated(struct exynos_drm_ippdrv *ippdrv,
- enum drm_exynos_ipp_cmd cmd)
+static int ipp_check_driver(struct exynos_drm_ippdrv *ippdrv,
+ struct drm_exynos_ipp_property *property)
{
- /*
- * check dedicated flag and WB, OUTPUT operation with
- * power on state.
- */
- if (ippdrv->dedicated || (!ipp_is_m2m_cmd(cmd) &&
- !pm_runtime_suspended(ippdrv->dev)))
- return true;
+ if (ippdrv->dedicated || (!ipp_is_m2m_cmd(property->cmd) &&
+ !pm_runtime_suspended(ippdrv->dev)))
+ return -EBUSY;
- return false;
+ if (ippdrv->check_property &&
+ ippdrv->check_property(ippdrv->dev, property))
+ return -EINVAL;
+
+ return 0;
}
static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx,
@@ -214,62 +192,30 @@ static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx,
{
struct exynos_drm_ippdrv *ippdrv;
u32 ipp_id = property->ipp_id;
-
- DRM_DEBUG_KMS("ipp_id[%d]\n", ipp_id);
+ int ret;
if (ipp_id) {
- /* find ipp driver using idr */
- ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock,
- ipp_id);
- if (IS_ERR(ippdrv)) {
- DRM_ERROR("not found ipp%d driver.\n", ipp_id);
- return ippdrv;
+ ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock, ipp_id);
+ if (!ippdrv) {
+ DRM_DEBUG("ipp%d driver not found\n", ipp_id);
+ return ERR_PTR(-ENODEV);
}
- /*
- * WB, OUTPUT opertion not supported multi-operation.
- * so, make dedicated state at set property ioctl.
- * when ipp driver finished operations, clear dedicated flags.
- */
- if (ipp_check_dedicated(ippdrv, property->cmd)) {
- DRM_ERROR("already used choose device.\n");
- return ERR_PTR(-EBUSY);
- }
-
- /*
- * This is necessary to find correct device in ipp drivers.
- * ipp drivers have different abilities,
- * so need to check property.
- */
- if (ippdrv->check_property &&
- ippdrv->check_property(ippdrv->dev, property)) {
- DRM_ERROR("not support property.\n");
- return ERR_PTR(-EINVAL);
+ ret = ipp_check_driver(ippdrv, property);
+ if (ret < 0) {
+ DRM_DEBUG("ipp%d driver check error %d\n", ipp_id, ret);
+ return ERR_PTR(ret);
}
return ippdrv;
} else {
- /*
- * This case is search all ipp driver for finding.
- * user application don't set ipp_id in this case,
- * so ipp subsystem search correct driver in driver list.
- */
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
- if (ipp_check_dedicated(ippdrv, property->cmd)) {
- DRM_DEBUG_KMS("used device.\n");
- continue;
- }
-
- if (ippdrv->check_property &&
- ippdrv->check_property(ippdrv->dev, property)) {
- DRM_DEBUG_KMS("not support property.\n");
- continue;
- }
-
- return ippdrv;
+ ret = ipp_check_driver(ippdrv, property);
+ if (ret == 0)
+ return ippdrv;
}
- DRM_ERROR("not support ipp driver operations.\n");
+ DRM_DEBUG("cannot find driver suitable for given property.\n");
}
return ERR_PTR(-ENODEV);
@@ -308,8 +254,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
struct drm_file *file)
{
struct drm_exynos_file_private *file_priv = file->driver_priv;
- struct exynos_drm_ipp_private *priv = file_priv->ipp_priv;
- struct device *dev = priv->dev;
+ struct device *dev = file_priv->ipp_dev;
struct ipp_context *ctx = get_ipp_context(dev);
struct drm_exynos_ipp_prop_list *prop_list = data;
struct exynos_drm_ippdrv *ippdrv;
@@ -346,10 +291,10 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
*/
ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock,
prop_list->ipp_id);
- if (IS_ERR(ippdrv)) {
+ if (!ippdrv) {
DRM_ERROR("not found ipp%d driver.\n",
prop_list->ipp_id);
- return PTR_ERR(ippdrv);
+ return -ENODEV;
}
*prop_list = ippdrv->prop_list;
@@ -373,44 +318,6 @@ static void ipp_print_property(struct drm_exynos_ipp_property *property,
sz->hsize, sz->vsize, config->flip, config->degree);
}
-static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property)
-{
- struct exynos_drm_ippdrv *ippdrv;
- struct drm_exynos_ipp_cmd_node *c_node;
- u32 prop_id = property->prop_id;
-
- DRM_DEBUG_KMS("prop_id[%d]\n", prop_id);
-
- ippdrv = ipp_find_drv_by_handle(prop_id);
- if (IS_ERR(ippdrv)) {
- DRM_ERROR("failed to get ipp driver.\n");
- return -EINVAL;
- }
-
- /*
- * Find command node using command list in ippdrv.
- * when we find this command no using prop_id.
- * return property information set in this command node.
- */
- mutex_lock(&ippdrv->cmd_lock);
- list_for_each_entry(c_node, &ippdrv->cmd_list, list) {
- if ((c_node->property.prop_id == prop_id) &&
- (c_node->state == IPP_STATE_STOP)) {
- mutex_unlock(&ippdrv->cmd_lock);
- DRM_DEBUG_KMS("found cmd[%d]ippdrv[0x%x]\n",
- property->cmd, (int)ippdrv);
-
- c_node->property = *property;
- return 0;
- }
- }
- mutex_unlock(&ippdrv->cmd_lock);
-
- DRM_ERROR("failed to search property.\n");
-
- return -EINVAL;
-}
-
static struct drm_exynos_ipp_cmd_work *ipp_create_cmd_work(void)
{
struct drm_exynos_ipp_cmd_work *cmd_work;
@@ -432,7 +339,7 @@ static struct drm_exynos_ipp_event_work *ipp_create_event_work(void)
if (!event_work)
return ERR_PTR(-ENOMEM);
- INIT_WORK((struct work_struct *)event_work, ipp_sched_event);
+ INIT_WORK(&event_work->work, ipp_sched_event);
return event_work;
}
@@ -441,12 +348,12 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
struct drm_file *file)
{
struct drm_exynos_file_private *file_priv = file->driver_priv;
- struct exynos_drm_ipp_private *priv = file_priv->ipp_priv;
- struct device *dev = priv->dev;
+ struct device *dev = file_priv->ipp_dev;
struct ipp_context *ctx = get_ipp_context(dev);
struct drm_exynos_ipp_property *property = data;
struct exynos_drm_ippdrv *ippdrv;
struct drm_exynos_ipp_cmd_node *c_node;
+ u32 prop_id;
int ret, i;
if (!ctx) {
@@ -459,6 +366,8 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
return -EINVAL;
}
+ prop_id = property->prop_id;
+
/*
* This is log print for user application property.
* user application set various property.
@@ -467,14 +376,24 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
ipp_print_property(property, i);
/*
- * set property ioctl generated new prop_id.
- * but in this case already asigned prop_id using old set property.
- * e.g PAUSE state. this case supports find current prop_id and use it
- * instead of allocation.
+ * In case prop_id is not zero try to set existing property.
*/
- if (property->prop_id) {
- DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id);
- return ipp_find_and_set_property(property);
+ if (prop_id) {
+ c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, prop_id);
+
+ if (!c_node || c_node->filp != file) {
+ DRM_DEBUG_KMS("prop_id[%d] not found\n", prop_id);
+ return -EINVAL;
+ }
+
+ if (c_node->state != IPP_STATE_STOP) {
+ DRM_DEBUG_KMS("prop_id[%d] not stopped\n", prop_id);
+ return -EINVAL;
+ }
+
+ c_node->property = *property;
+
+ return 0;
}
/* find ipp driver using ipp id */
@@ -489,21 +408,20 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
if (!c_node)
return -ENOMEM;
- /* create property id */
- ret = ipp_create_id(&ctx->prop_idr, &ctx->prop_lock, c_node,
- &property->prop_id);
- if (ret) {
+ ret = ipp_create_id(&ctx->prop_idr, &ctx->prop_lock, c_node);
+ if (ret < 0) {
DRM_ERROR("failed to create id.\n");
goto err_clear;
}
+ property->prop_id = ret;
DRM_DEBUG_KMS("created prop_id[%d]cmd[%d]ippdrv[0x%x]\n",
property->prop_id, property->cmd, (int)ippdrv);
/* stored property information and ippdrv in private data */
- c_node->priv = priv;
c_node->property = *property;
c_node->state = IPP_STATE_IDLE;
+ c_node->filp = file;
c_node->start_work = ipp_create_cmd_work();
if (IS_ERR(c_node->start_work)) {
@@ -534,7 +452,6 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
INIT_LIST_HEAD(&c_node->mem_list[i]);
INIT_LIST_HEAD(&c_node->event_list);
- list_splice_init(&priv->event_list, &c_node->event_list);
mutex_lock(&ippdrv->cmd_lock);
list_add_tail(&c_node->list, &ippdrv->cmd_list);
mutex_unlock(&ippdrv->cmd_lock);
@@ -556,148 +473,55 @@ err_clear:
return ret;
}
-static void ipp_clean_cmd_node(struct ipp_context *ctx,
- struct drm_exynos_ipp_cmd_node *c_node)
-{
- /* delete list */
- list_del(&c_node->list);
-
- ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock,
- c_node->property.prop_id);
-
- /* destroy mutex */
- mutex_destroy(&c_node->lock);
- mutex_destroy(&c_node->mem_lock);
- mutex_destroy(&c_node->event_lock);
-
- /* free command node */
- kfree(c_node->start_work);
- kfree(c_node->stop_work);
- kfree(c_node->event_work);
- kfree(c_node);
-}
-
-static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node)
-{
- struct drm_exynos_ipp_property *property = &c_node->property;
- struct drm_exynos_ipp_mem_node *m_node;
- struct list_head *head;
- int ret, i, count[EXYNOS_DRM_OPS_MAX] = { 0, };
-
- for_each_ipp_ops(i) {
- /* source/destination memory list */
- head = &c_node->mem_list[i];
-
- /* find memory node entry */
- list_for_each_entry(m_node, head, list) {
- DRM_DEBUG_KMS("%s,count[%d]m_node[0x%x]\n",
- i ? "dst" : "src", count[i], (int)m_node);
- count[i]++;
- }
- }
-
- DRM_DEBUG_KMS("min[%d]max[%d]\n",
- min(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST]),
- max(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST]));
-
- /*
- * M2M operations should be need paired memory address.
- * so, need to check minimum count about src, dst.
- * other case not use paired memory, so use maximum count
- */
- if (ipp_is_m2m_cmd(property->cmd))
- ret = min(count[EXYNOS_DRM_OPS_SRC],
- count[EXYNOS_DRM_OPS_DST]);
- else
- ret = max(count[EXYNOS_DRM_OPS_SRC],
- count[EXYNOS_DRM_OPS_DST]);
-
- return ret;
-}
-
-static struct drm_exynos_ipp_mem_node
- *ipp_find_mem_node(struct drm_exynos_ipp_cmd_node *c_node,
- struct drm_exynos_ipp_queue_buf *qbuf)
-{
- struct drm_exynos_ipp_mem_node *m_node;
- struct list_head *head;
- int count = 0;
-
- DRM_DEBUG_KMS("buf_id[%d]\n", qbuf->buf_id);
-
- /* source/destination memory list */
- head = &c_node->mem_list[qbuf->ops_id];
-
- /* find memory node from memory list */
- list_for_each_entry(m_node, head, list) {
- DRM_DEBUG_KMS("count[%d]m_node[0x%x]\n", count++, (int)m_node);
-
- /* compare buffer id */
- if (m_node->buf_id == qbuf->buf_id)
- return m_node;
- }
-
- return NULL;
-}
-
-static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
+static int ipp_put_mem_node(struct drm_device *drm_dev,
struct drm_exynos_ipp_cmd_node *c_node,
struct drm_exynos_ipp_mem_node *m_node)
{
- struct exynos_drm_ipp_ops *ops = NULL;
- int ret = 0;
+ int i;
DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node);
if (!m_node) {
- DRM_ERROR("invalid queue node.\n");
+ DRM_ERROR("invalid dequeue node.\n");
return -EFAULT;
}
DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
- /* get operations callback */
- ops = ippdrv->ops[m_node->ops_id];
- if (!ops) {
- DRM_ERROR("not support ops.\n");
- return -EFAULT;
+ /* put gem buffer */
+ for_each_ipp_planar(i) {
+ unsigned long handle = m_node->buf_info.handles[i];
+ if (handle)
+ exynos_drm_gem_put_dma_addr(drm_dev, handle,
+ c_node->filp);
}
- /* set address and enable irq */
- if (ops->set_addr) {
- ret = ops->set_addr(ippdrv->dev, &m_node->buf_info,
- m_node->buf_id, IPP_BUF_ENQUEUE);
- if (ret) {
- DRM_ERROR("failed to set addr.\n");
- return ret;
- }
- }
+ list_del(&m_node->list);
+ kfree(m_node);
- return ret;
+ return 0;
}
static struct drm_exynos_ipp_mem_node
*ipp_get_mem_node(struct drm_device *drm_dev,
- struct drm_file *file,
struct drm_exynos_ipp_cmd_node *c_node,
struct drm_exynos_ipp_queue_buf *qbuf)
{
struct drm_exynos_ipp_mem_node *m_node;
- struct drm_exynos_ipp_buf_info buf_info;
- void *addr;
+ struct drm_exynos_ipp_buf_info *buf_info;
int i;
m_node = kzalloc(sizeof(*m_node), GFP_KERNEL);
if (!m_node)
return ERR_PTR(-ENOMEM);
- /* clear base address for error handling */
- memset(&buf_info, 0x0, sizeof(buf_info));
+ buf_info = &m_node->buf_info;
/* operations, buffer id */
m_node->ops_id = qbuf->ops_id;
m_node->prop_id = qbuf->prop_id;
m_node->buf_id = qbuf->buf_id;
+ INIT_LIST_HEAD(&m_node->list);
DRM_DEBUG_KMS("m_node[0x%x]ops_id[%d]\n", (int)m_node, qbuf->ops_id);
DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]\n", qbuf->prop_id, m_node->buf_id);
@@ -707,61 +531,47 @@ static struct drm_exynos_ipp_mem_node
/* get dma address by handle */
if (qbuf->handle[i]) {
+ dma_addr_t *addr;
+
addr = exynos_drm_gem_get_dma_addr(drm_dev,
- qbuf->handle[i], file);
+ qbuf->handle[i], c_node->filp);
if (IS_ERR(addr)) {
DRM_ERROR("failed to get addr.\n");
- goto err_clear;
+ ipp_put_mem_node(drm_dev, c_node, m_node);
+ return ERR_PTR(-EFAULT);
}
- buf_info.handles[i] = qbuf->handle[i];
- buf_info.base[i] = *(dma_addr_t *) addr;
- DRM_DEBUG_KMS("i[%d]base[0x%x]hd[0x%x]\n",
- i, buf_info.base[i], (int)buf_info.handles[i]);
+ buf_info->handles[i] = qbuf->handle[i];
+ buf_info->base[i] = *addr;
+ DRM_DEBUG_KMS("i[%d]base[0x%x]hd[0x%lx]\n", i,
+ buf_info->base[i], buf_info->handles[i]);
}
}
- m_node->filp = file;
- m_node->buf_info = buf_info;
mutex_lock(&c_node->mem_lock);
list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]);
mutex_unlock(&c_node->mem_lock);
return m_node;
-
-err_clear:
- kfree(m_node);
- return ERR_PTR(-EFAULT);
}
-static int ipp_put_mem_node(struct drm_device *drm_dev,
- struct drm_exynos_ipp_cmd_node *c_node,
- struct drm_exynos_ipp_mem_node *m_node)
+static void ipp_clean_mem_nodes(struct drm_device *drm_dev,
+ struct drm_exynos_ipp_cmd_node *c_node, int ops)
{
- int i;
-
- DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node);
+ struct drm_exynos_ipp_mem_node *m_node, *tm_node;
+ struct list_head *head = &c_node->mem_list[ops];
- if (!m_node) {
- DRM_ERROR("invalid dequeue node.\n");
- return -EFAULT;
- }
+ mutex_lock(&c_node->mem_lock);
- DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
+ list_for_each_entry_safe(m_node, tm_node, head, list) {
+ int ret;
- /* put gem buffer */
- for_each_ipp_planar(i) {
- unsigned long handle = m_node->buf_info.handles[i];
- if (handle)
- exynos_drm_gem_put_dma_addr(drm_dev, handle,
- m_node->filp);
+ ret = ipp_put_mem_node(drm_dev, c_node, m_node);
+ if (ret)
+ DRM_ERROR("failed to put m_node.\n");
}
- /* delete list in queue */
- list_del(&m_node->list);
- kfree(m_node);
-
- return 0;
+ mutex_unlock(&c_node->mem_lock);
}
static void ipp_free_event(struct drm_pending_event *event)
@@ -770,7 +580,6 @@ static void ipp_free_event(struct drm_pending_event *event)
}
static int ipp_get_event(struct drm_device *drm_dev,
- struct drm_file *file,
struct drm_exynos_ipp_cmd_node *c_node,
struct drm_exynos_ipp_queue_buf *qbuf)
{
@@ -782,7 +591,7 @@ static int ipp_get_event(struct drm_device *drm_dev,
e = kzalloc(sizeof(*e), GFP_KERNEL);
if (!e) {
spin_lock_irqsave(&drm_dev->event_lock, flags);
- file->event_space += sizeof(e->event);
+ c_node->filp->event_space += sizeof(e->event);
spin_unlock_irqrestore(&drm_dev->event_lock, flags);
return -ENOMEM;
}
@@ -794,7 +603,7 @@ static int ipp_get_event(struct drm_device *drm_dev,
e->event.prop_id = qbuf->prop_id;
e->event.buf_id[EXYNOS_DRM_OPS_DST] = qbuf->buf_id;
e->base.event = &e->event.base;
- e->base.file_priv = file;
+ e->base.file_priv = c_node->filp;
e->base.destroy = ipp_free_event;
mutex_lock(&c_node->event_lock);
list_add_tail(&e->base.link, &c_node->event_list);
@@ -839,6 +648,115 @@ out_unlock:
return;
}
+static void ipp_clean_cmd_node(struct ipp_context *ctx,
+ struct drm_exynos_ipp_cmd_node *c_node)
+{
+ int i;
+
+ /* cancel works */
+ cancel_work_sync(&c_node->start_work->work);
+ cancel_work_sync(&c_node->stop_work->work);
+ cancel_work_sync(&c_node->event_work->work);
+
+ /* put event */
+ ipp_put_event(c_node, NULL);
+
+ for_each_ipp_ops(i)
+ ipp_clean_mem_nodes(ctx->subdrv.drm_dev, c_node, i);
+
+ /* delete list */
+ list_del(&c_node->list);
+
+ ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock,
+ c_node->property.prop_id);
+
+ /* destroy mutex */
+ mutex_destroy(&c_node->lock);
+ mutex_destroy(&c_node->mem_lock);
+ mutex_destroy(&c_node->event_lock);
+
+ /* free command node */
+ kfree(c_node->start_work);
+ kfree(c_node->stop_work);
+ kfree(c_node->event_work);
+ kfree(c_node);
+}
+
+static bool ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node)
+{
+ switch (c_node->property.cmd) {
+ case IPP_CMD_WB:
+ return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]);
+ case IPP_CMD_OUTPUT:
+ return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]);
+ case IPP_CMD_M2M:
+ default:
+ return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]) &&
+ !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]);
+ }
+}
+
+static struct drm_exynos_ipp_mem_node
+ *ipp_find_mem_node(struct drm_exynos_ipp_cmd_node *c_node,
+ struct drm_exynos_ipp_queue_buf *qbuf)
+{
+ struct drm_exynos_ipp_mem_node *m_node;
+ struct list_head *head;
+ int count = 0;
+
+ DRM_DEBUG_KMS("buf_id[%d]\n", qbuf->buf_id);
+
+ /* source/destination memory list */
+ head = &c_node->mem_list[qbuf->ops_id];
+
+ /* find memory node from memory list */
+ list_for_each_entry(m_node, head, list) {
+ DRM_DEBUG_KMS("count[%d]m_node[0x%x]\n", count++, (int)m_node);
+
+ /* compare buffer id */
+ if (m_node->buf_id == qbuf->buf_id)
+ return m_node;
+ }
+
+ return NULL;
+}
+
+static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
+ struct drm_exynos_ipp_cmd_node *c_node,
+ struct drm_exynos_ipp_mem_node *m_node)
+{
+ struct exynos_drm_ipp_ops *ops = NULL;
+ int ret = 0;
+
+ DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node);
+
+ if (!m_node) {
+ DRM_ERROR("invalid queue node.\n");
+ return -EFAULT;
+ }
+
+ DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
+
+ /* get operations callback */
+ ops = ippdrv->ops[m_node->ops_id];
+ if (!ops) {
+ DRM_ERROR("not support ops.\n");
+ return -EFAULT;
+ }
+
+ /* set address and enable irq */
+ if (ops->set_addr) {
+ ret = ops->set_addr(ippdrv->dev, &m_node->buf_info,
+ m_node->buf_id, IPP_BUF_ENQUEUE);
+ if (ret) {
+ DRM_ERROR("failed to set addr.\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
static void ipp_handle_cmd_work(struct device *dev,
struct exynos_drm_ippdrv *ippdrv,
struct drm_exynos_ipp_cmd_work *cmd_work,
@@ -848,7 +766,7 @@ static void ipp_handle_cmd_work(struct device *dev,
cmd_work->ippdrv = ippdrv;
cmd_work->c_node = c_node;
- queue_work(ctx->cmd_workq, (struct work_struct *)cmd_work);
+ queue_work(ctx->cmd_workq, &cmd_work->work);
}
static int ipp_queue_buf_with_run(struct device *dev,
@@ -930,8 +848,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
struct drm_file *file)
{
struct drm_exynos_file_private *file_priv = file->driver_priv;
- struct exynos_drm_ipp_private *priv = file_priv->ipp_priv;
- struct device *dev = priv->dev;
+ struct device *dev = file_priv->ipp_dev;
struct ipp_context *ctx = get_ipp_context(dev);
struct drm_exynos_ipp_queue_buf *qbuf = data;
struct drm_exynos_ipp_cmd_node *c_node;
@@ -955,16 +872,16 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
/* find command node */
c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock,
qbuf->prop_id);
- if (IS_ERR(c_node)) {
+ if (!c_node || c_node->filp != file) {
DRM_ERROR("failed to get command node.\n");
- return PTR_ERR(c_node);
+ return -ENODEV;
}
/* buffer control */
switch (qbuf->buf_type) {
case IPP_BUF_ENQUEUE:
/* get memory node */
- m_node = ipp_get_mem_node(drm_dev, file, c_node, qbuf);
+ m_node = ipp_get_mem_node(drm_dev, c_node, qbuf);
if (IS_ERR(m_node)) {
DRM_ERROR("failed to get m_node.\n");
return PTR_ERR(m_node);
@@ -977,7 +894,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
*/
if (qbuf->ops_id == EXYNOS_DRM_OPS_DST) {
/* get event for destination buffer */
- ret = ipp_get_event(drm_dev, file, c_node, qbuf);
+ ret = ipp_get_event(drm_dev, c_node, qbuf);
if (ret) {
DRM_ERROR("failed to get event.\n");
goto err_clean_node;
@@ -1062,9 +979,8 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
struct drm_file *file)
{
struct drm_exynos_file_private *file_priv = file->driver_priv;
- struct exynos_drm_ipp_private *priv = file_priv->ipp_priv;
struct exynos_drm_ippdrv *ippdrv = NULL;
- struct device *dev = priv->dev;
+ struct device *dev = file_priv->ipp_dev;
struct ipp_context *ctx = get_ipp_context(dev);
struct drm_exynos_ipp_cmd_ctrl *cmd_ctrl = data;
struct drm_exynos_ipp_cmd_work *cmd_work;
@@ -1091,9 +1007,9 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock,
cmd_ctrl->prop_id);
- if (IS_ERR(c_node)) {
+ if (!c_node || c_node->filp != file) {
DRM_ERROR("invalid command node list.\n");
- return PTR_ERR(c_node);
+ return -ENODEV;
}
if (!exynos_drm_ipp_check_valid(ippdrv->dev, cmd_ctrl->ctrl,
@@ -1198,7 +1114,6 @@ static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv,
/* reset h/w block */
if (ippdrv->reset &&
ippdrv->reset(ippdrv->dev)) {
- DRM_ERROR("failed to reset.\n");
return -EINVAL;
}
@@ -1216,30 +1131,24 @@ static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv,
/* set format */
if (ops->set_fmt) {
ret = ops->set_fmt(ippdrv->dev, config->fmt);
- if (ret) {
- DRM_ERROR("not support format.\n");
+ if (ret)
return ret;
- }
}
/* set transform for rotation, flip */
if (ops->set_transf) {
ret = ops->set_transf(ippdrv->dev, config->degree,
config->flip, &swap);
- if (ret) {
- DRM_ERROR("not support tranf.\n");
- return -EINVAL;
- }
+ if (ret)
+ return ret;
}
/* set size */
if (ops->set_size) {
ret = ops->set_size(ippdrv->dev, swap, &config->pos,
&config->sz);
- if (ret) {
- DRM_ERROR("not support size.\n");
+ if (ret)
return ret;
- }
}
}
@@ -1283,11 +1192,6 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
m_node = list_first_entry(head,
struct drm_exynos_ipp_mem_node, list);
- if (!m_node) {
- DRM_ERROR("failed to get node.\n");
- ret = -EFAULT;
- goto err_unlock;
- }
DRM_DEBUG_KMS("m_node[0x%x]\n", (int)m_node);
@@ -1353,80 +1257,39 @@ static int ipp_stop_property(struct drm_device *drm_dev,
struct exynos_drm_ippdrv *ippdrv,
struct drm_exynos_ipp_cmd_node *c_node)
{
- struct drm_exynos_ipp_mem_node *m_node, *tm_node;
struct drm_exynos_ipp_property *property = &c_node->property;
- struct list_head *head;
- int ret = 0, i;
+ int i;
DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id);
- /* put event */
- ipp_put_event(c_node, NULL);
-
- mutex_lock(&c_node->mem_lock);
+ /* stop operations */
+ if (ippdrv->stop)
+ ippdrv->stop(ippdrv->dev, property->cmd);
/* check command */
switch (property->cmd) {
case IPP_CMD_M2M:
- for_each_ipp_ops(i) {
- /* source/destination memory list */
- head = &c_node->mem_list[i];
-
- list_for_each_entry_safe(m_node, tm_node,
- head, list) {
- ret = ipp_put_mem_node(drm_dev, c_node,
- m_node);
- if (ret) {
- DRM_ERROR("failed to put m_node.\n");
- goto err_clear;
- }
- }
- }
+ for_each_ipp_ops(i)
+ ipp_clean_mem_nodes(drm_dev, c_node, i);
break;
case IPP_CMD_WB:
- /* destination memory list */
- head = &c_node->mem_list[EXYNOS_DRM_OPS_DST];
-
- list_for_each_entry_safe(m_node, tm_node, head, list) {
- ret = ipp_put_mem_node(drm_dev, c_node, m_node);
- if (ret) {
- DRM_ERROR("failed to put m_node.\n");
- goto err_clear;
- }
- }
+ ipp_clean_mem_nodes(drm_dev, c_node, EXYNOS_DRM_OPS_DST);
break;
case IPP_CMD_OUTPUT:
- /* source memory list */
- head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC];
-
- list_for_each_entry_safe(m_node, tm_node, head, list) {
- ret = ipp_put_mem_node(drm_dev, c_node, m_node);
- if (ret) {
- DRM_ERROR("failed to put m_node.\n");
- goto err_clear;
- }
- }
+ ipp_clean_mem_nodes(drm_dev, c_node, EXYNOS_DRM_OPS_SRC);
break;
default:
DRM_ERROR("invalid operations.\n");
- ret = -EINVAL;
- goto err_clear;
+ return -EINVAL;
}
-err_clear:
- mutex_unlock(&c_node->mem_lock);
-
- /* stop operations */
- if (ippdrv->stop)
- ippdrv->stop(ippdrv->dev, property->cmd);
-
- return ret;
+ return 0;
}
void ipp_sched_cmd(struct work_struct *work)
{
struct drm_exynos_ipp_cmd_work *cmd_work =
- (struct drm_exynos_ipp_cmd_work *)work;
+ container_of(work, struct drm_exynos_ipp_cmd_work, work);
struct exynos_drm_ippdrv *ippdrv;
struct drm_exynos_ipp_cmd_node *c_node;
struct drm_exynos_ipp_property *property;
@@ -1545,11 +1408,6 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
m_node = list_first_entry(head,
struct drm_exynos_ipp_mem_node, list);
- if (!m_node) {
- DRM_ERROR("empty memory node.\n");
- ret = -ENOMEM;
- goto err_mem_unlock;
- }
tbuf_id[i] = m_node->buf_id;
DRM_DEBUG_KMS("%s buf_id[%d]\n",
@@ -1586,11 +1444,6 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
m_node = list_first_entry(head,
struct drm_exynos_ipp_mem_node, list);
- if (!m_node) {
- DRM_ERROR("empty memory node.\n");
- ret = -ENOMEM;
- goto err_mem_unlock;
- }
tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id;
@@ -1649,7 +1502,7 @@ err_event_unlock:
void ipp_sched_event(struct work_struct *work)
{
struct drm_exynos_ipp_event_work *event_work =
- (struct drm_exynos_ipp_event_work *)work;
+ container_of(work, struct drm_exynos_ipp_event_work, work);
struct exynos_drm_ippdrv *ippdrv;
struct drm_exynos_ipp_cmd_node *c_node;
int ret;
@@ -1704,21 +1557,17 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
/* get ipp driver entry */
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
- u32 ipp_id;
-
ippdrv->drm_dev = drm_dev;
- ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv,
- &ipp_id);
- if (ret || ipp_id == 0) {
+ ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv);
+ if (ret < 0) {
DRM_ERROR("failed to create id.\n");
goto err;
}
+ ippdrv->prop_list.ipp_id = ret;
DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]ipp_id[%d]\n",
- count++, (int)ippdrv, ipp_id);
-
- ippdrv->prop_list.ipp_id = ipp_id;
+ count++, (int)ippdrv, ret);
/* store parent device for node */
ippdrv->parent_dev = dev;
@@ -1756,11 +1605,11 @@ err:
static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{
- struct exynos_drm_ippdrv *ippdrv;
+ struct exynos_drm_ippdrv *ippdrv, *t;
struct ipp_context *ctx = get_ipp_context(dev);
/* get ipp driver entry */
- list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
+ list_for_each_entry_safe(ippdrv, t, &exynos_drm_ippdrv_list, drv_list) {
if (is_drm_iommu_supported(drm_dev))
drm_iommu_detach_device(drm_dev, ippdrv->dev);
@@ -1776,17 +1625,10 @@ static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file)
{
struct drm_exynos_file_private *file_priv = file->driver_priv;
- struct exynos_drm_ipp_private *priv;
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- priv->dev = dev;
- file_priv->ipp_priv = priv;
- INIT_LIST_HEAD(&priv->event_list);
+ file_priv->ipp_dev = dev;
- DRM_DEBUG_KMS("done priv[0x%x]\n", (int)priv);
+ DRM_DEBUG_KMS("done priv[0x%x]\n", (int)dev);
return 0;
}
@@ -1794,15 +1636,11 @@ static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev,
static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file)
{
- struct drm_exynos_file_private *file_priv = file->driver_priv;
- struct exynos_drm_ipp_private *priv = file_priv->ipp_priv;
struct exynos_drm_ippdrv *ippdrv = NULL;
struct ipp_context *ctx = get_ipp_context(dev);
struct drm_exynos_ipp_cmd_node *c_node, *tc_node;
int count = 0;
- DRM_DEBUG_KMS("for priv[0x%x]\n", (int)priv);
-
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
mutex_lock(&ippdrv->cmd_lock);
list_for_each_entry_safe(c_node, tc_node,
@@ -1810,7 +1648,7 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n",
count++, (int)ippdrv);
- if (c_node->priv == priv) {
+ if (c_node->filp == file) {
/*
* userland goto unnormal state. process killed.
* and close the file.
@@ -1832,7 +1670,6 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
mutex_unlock(&ippdrv->cmd_lock);
}
- kfree(priv);
return;
}
@@ -1927,63 +1764,12 @@ static int ipp_remove(struct platform_device *pdev)
return 0;
}
-static int ipp_power_ctrl(struct ipp_context *ctx, bool enable)
-{
- DRM_DEBUG_KMS("enable[%d]\n", enable);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int ipp_suspend(struct device *dev)
-{
- struct ipp_context *ctx = get_ipp_context(dev);
-
- if (pm_runtime_suspended(dev))
- return 0;
-
- return ipp_power_ctrl(ctx, false);
-}
-
-static int ipp_resume(struct device *dev)
-{
- struct ipp_context *ctx = get_ipp_context(dev);
-
- if (!pm_runtime_suspended(dev))
- return ipp_power_ctrl(ctx, true);
-
- return 0;
-}
-#endif
-
-#ifdef CONFIG_PM_RUNTIME
-static int ipp_runtime_suspend(struct device *dev)
-{
- struct ipp_context *ctx = get_ipp_context(dev);
-
- return ipp_power_ctrl(ctx, false);
-}
-
-static int ipp_runtime_resume(struct device *dev)
-{
- struct ipp_context *ctx = get_ipp_context(dev);
-
- return ipp_power_ctrl(ctx, true);
-}
-#endif
-
-static const struct dev_pm_ops ipp_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(ipp_suspend, ipp_resume)
- SET_RUNTIME_PM_OPS(ipp_runtime_suspend, ipp_runtime_resume, NULL)
-};
-
struct platform_driver ipp_driver = {
.probe = ipp_probe,
.remove = ipp_remove,
.driver = {
.name = "exynos-drm-ipp",
.owner = THIS_MODULE,
- .pm = &ipp_pm_ops,
},
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_ipp.h
index 7aaeaae757c2..2a61547a39d0 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_ipp.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.h
@@ -48,7 +48,6 @@ struct drm_exynos_ipp_cmd_work {
/*
* A structure of command node.
*
- * @priv: IPP private information.
* @list: list head to command queue information.
* @event_list: list head of event.
* @mem_list: list head to source,destination memory queue information.
@@ -62,9 +61,9 @@ struct drm_exynos_ipp_cmd_work {
* @stop_work: stop command work structure.
* @event_work: event work structure.
* @state: state of command node.
+ * @filp: associated file pointer.
*/
struct drm_exynos_ipp_cmd_node {
- struct exynos_drm_ipp_private *priv;
struct list_head list;
struct list_head event_list;
struct list_head mem_list[EXYNOS_DRM_OPS_MAX];
@@ -78,6 +77,7 @@ struct drm_exynos_ipp_cmd_node {
struct drm_exynos_ipp_cmd_work *stop_work;
struct drm_exynos_ipp_event_work *event_work;
enum drm_exynos_ipp_state state;
+ struct drm_file *filp;
};
/*
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index 8371cbd7631d..c7045a663763 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -139,6 +139,8 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
overlay->crtc_x, overlay->crtc_y,
overlay->crtc_width, overlay->crtc_height);
+ plane->crtc = crtc;
+
exynos_drm_crtc_plane_mode_set(crtc, overlay);
return 0;
@@ -187,8 +189,6 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
if (ret < 0)
return ret;
- plane->crtc = crtc;
-
exynos_plane_commit(plane);
exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
@@ -254,25 +254,26 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane)
}
struct drm_plane *exynos_plane_init(struct drm_device *dev,
- unsigned long possible_crtcs, bool priv)
+ unsigned long possible_crtcs,
+ enum drm_plane_type type)
{
struct exynos_plane *exynos_plane;
int err;
exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL);
if (!exynos_plane)
- return NULL;
+ return ERR_PTR(-ENOMEM);
- err = drm_plane_init(dev, &exynos_plane->base, possible_crtcs,
- &exynos_plane_funcs, formats, ARRAY_SIZE(formats),
- priv);
+ err = drm_universal_plane_init(dev, &exynos_plane->base, possible_crtcs,
+ &exynos_plane_funcs, formats,
+ ARRAY_SIZE(formats), type);
if (err) {
DRM_ERROR("failed to initialize plane\n");
kfree(exynos_plane);
- return NULL;
+ return ERR_PTR(err);
}
- if (priv)
+ if (type == DRM_PLANE_TYPE_PRIMARY)
exynos_plane->overlay.zpos = DEFAULT_ZPOS;
else
exynos_plane_attach_zpos_property(&exynos_plane->base);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.h b/drivers/gpu/drm/exynos/exynos_drm_plane.h
index 84d464c90d3d..0d1986b115f8 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.h
@@ -17,4 +17,5 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc,
void exynos_plane_commit(struct drm_plane *plane);
void exynos_plane_dpms(struct drm_plane *plane, int mode);
struct drm_plane *exynos_plane_init(struct drm_device *dev,
- unsigned long possible_crtcs, bool priv);
+ unsigned long possible_crtcs,
+ enum drm_plane_type type);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
index f01fbb6dc1f0..b6a37d4f5b13 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
@@ -156,8 +156,7 @@ static irqreturn_t rotator_irq_handler(int irq, void *arg)
event_work->ippdrv = ippdrv;
event_work->buf_id[EXYNOS_DRM_OPS_DST] =
rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
- queue_work(ippdrv->event_workq,
- (struct work_struct *)event_work);
+ queue_work(ippdrv->event_workq, &event_work->work);
} else {
DRM_ERROR("the SFR is set illegally\n");
}
@@ -691,6 +690,7 @@ static const struct of_device_id exynos_rotator_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, exynos_rotator_match);
static int rotator_probe(struct platform_device *pdev)
{
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index 2fb8705d6461..50faf913e574 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -303,23 +303,6 @@ static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
mgr->drm_dev = ctx->drm_dev = drm_dev;
mgr->pipe = ctx->pipe = priv->pipe++;
- /*
- * enable drm irq mode.
- * - with irq_enabled = 1, we can use the vblank feature.
- *
- * P.S. note that we wouldn't use drm irq handler but
- * just specific driver own one instead because
- * drm framework supports only one irq handler.
- */
- drm_dev->irq_enabled = 1;
-
- /*
- * with vblank_disable_allowed = 1, vblank interrupt will be disabled
- * by drm timer once a current process gives up ownership of
- * vblank event.(after drm_vblank_put function is called)
- */
- drm_dev->vblank_disable_allowed = 1;
-
return 0;
}
@@ -562,7 +545,7 @@ static int vidi_create_connector(struct exynos_drm_display *display,
}
drm_connector_helper_add(connector, &vidi_connector_helper_funcs);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
@@ -647,8 +630,6 @@ static int vidi_remove(struct platform_device *pdev)
{
struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
struct vidi_context *ctx = mgr->ctx;
- struct drm_encoder *encoder = ctx->encoder;
- struct drm_crtc *crtc = mgr->crtc;
if (ctx->raw_edid != (struct edid *)fake_edid_info) {
kfree(ctx->raw_edid);
@@ -657,10 +638,6 @@ static int vidi_remove(struct platform_device *pdev)
return -EINVAL;
}
- crtc->funcs->destroy(crtc);
- encoder->funcs->destroy(encoder);
- drm_connector_cleanup(&ctx->connector);
-
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index aa259b0a873a..563a19e62eb2 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -84,6 +84,7 @@ struct hdmi_resources {
struct clk *sclk_hdmiphy;
struct clk *mout_hdmi;
struct regulator_bulk_data *regul_bulk;
+ struct regulator *reg_hdmi_en;
int regul_count;
};
@@ -592,6 +593,13 @@ static struct hdmi_driver_data exynos4212_hdmi_driver_data = {
.is_apb_phy = 0,
};
+static struct hdmi_driver_data exynos4210_hdmi_driver_data = {
+ .type = HDMI_TYPE13,
+ .phy_confs = hdmiphy_v13_configs,
+ .phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
+ .is_apb_phy = 0,
+};
+
static struct hdmi_driver_data exynos5_hdmi_driver_data = {
.type = HDMI_TYPE14,
.phy_confs = hdmiphy_v13_configs,
@@ -1032,6 +1040,8 @@ static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
static void hdmi_connector_destroy(struct drm_connector *connector)
{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
}
static struct drm_connector_funcs hdmi_connector_funcs = {
@@ -1129,7 +1139,7 @@ static int hdmi_create_connector(struct exynos_drm_display *display,
}
drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
@@ -1241,14 +1251,13 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr)
static void hdmi_audio_init(struct hdmi_context *hdata)
{
- u32 sample_rate, bits_per_sample, frame_size_code;
+ u32 sample_rate, bits_per_sample;
u32 data_num, bit_ch, sample_frq;
u32 val;
u8 acr[7];
sample_rate = 44100;
bits_per_sample = 16;
- frame_size_code = 0;
switch (bits_per_sample) {
case 20:
@@ -2168,7 +2177,6 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
struct device *dev = hdata->dev;
struct hdmi_resources *res = &hdata->res;
static char *supply[] = {
- "hdmi-en",
"vdd",
"vdd_osc",
"vdd_pll",
@@ -2228,6 +2236,20 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
}
res->regul_count = ARRAY_SIZE(supply);
+ res->reg_hdmi_en = devm_regulator_get(dev, "hdmi-en");
+ if (IS_ERR(res->reg_hdmi_en) && PTR_ERR(res->reg_hdmi_en) != -ENOENT) {
+ DRM_ERROR("failed to get hdmi-en regulator\n");
+ return PTR_ERR(res->reg_hdmi_en);
+ }
+ if (!IS_ERR(res->reg_hdmi_en)) {
+ ret = regulator_enable(res->reg_hdmi_en);
+ if (ret) {
+ DRM_ERROR("failed to enable hdmi-en regulator\n");
+ return ret;
+ }
+ } else
+ res->reg_hdmi_en = NULL;
+
return ret;
fail:
DRM_ERROR("HDMI resource init - failed\n");
@@ -2263,6 +2285,9 @@ static struct of_device_id hdmi_match_types[] = {
.compatible = "samsung,exynos5-hdmi",
.data = &exynos5_hdmi_driver_data,
}, {
+ .compatible = "samsung,exynos4210-hdmi",
+ .data = &exynos4210_hdmi_driver_data,
+ }, {
.compatible = "samsung,exynos4212-hdmi",
.data = &exynos4212_hdmi_driver_data,
}, {
@@ -2272,6 +2297,7 @@ static struct of_device_id hdmi_match_types[] = {
/* end node */
}
};
+MODULE_DEVICE_TABLE (of, hdmi_match_types);
static int hdmi_bind(struct device *dev, struct device *master, void *data)
{
@@ -2286,12 +2312,6 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data)
static void hdmi_unbind(struct device *dev, struct device *master, void *data)
{
- struct exynos_drm_display *display = get_hdmi_display(dev);
- struct drm_encoder *encoder = display->encoder;
- struct hdmi_context *hdata = display->ctx;
-
- encoder->funcs->destroy(encoder);
- drm_connector_cleanup(&hdata->connector);
}
static const struct component_ops hdmi_component_ops = {
@@ -2494,7 +2514,11 @@ static int hdmi_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&hdata->hotplug_work);
- put_device(&hdata->hdmiphy_port->dev);
+ if (hdata->res.reg_hdmi_en)
+ regulator_disable(hdata->res.reg_hdmi_en);
+
+ if (hdata->hdmiphy_port)
+ put_device(&hdata->hdmiphy_port->dev);
put_device(&hdata->ddc_adpt->dev);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index 7529946d0a74..a41c84ee3a2d 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -76,7 +76,7 @@ struct mixer_resources {
struct clk *vp;
struct clk *sclk_mixer;
struct clk *sclk_hdmi;
- struct clk *sclk_dac;
+ struct clk *mout_mixer;
};
enum mixer_version_id {
@@ -93,6 +93,7 @@ struct mixer_context {
bool interlace;
bool powered;
bool vp_enabled;
+ bool has_sclk;
u32 int_en;
struct mutex mixer_mutex;
@@ -106,6 +107,7 @@ struct mixer_context {
struct mixer_drv_data {
enum mixer_version_id version;
bool is_vp_enabled;
+ bool has_sclk;
};
static const u8 filter_y_horiz_tap8[] = {
@@ -363,6 +365,11 @@ static void mixer_cfg_layer(struct mixer_context *ctx, int win, bool enable)
vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
mixer_reg_writemask(res, MXR_CFG, val,
MXR_CFG_VP_ENABLE);
+
+ /* control blending of graphic layer 0 */
+ mixer_reg_writemask(res, MXR_GRAPHIC_CFG(0), val,
+ MXR_GRP_CFG_BLEND_PRE_MUL |
+ MXR_GRP_CFG_PIXEL_BLEND_EN);
}
break;
}
@@ -809,19 +816,23 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
dev_err(dev, "failed to get clock 'vp'\n");
return -ENODEV;
}
- mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
- if (IS_ERR(mixer_res->sclk_mixer)) {
- dev_err(dev, "failed to get clock 'sclk_mixer'\n");
- return -ENODEV;
- }
- mixer_res->sclk_dac = devm_clk_get(dev, "sclk_dac");
- if (IS_ERR(mixer_res->sclk_dac)) {
- dev_err(dev, "failed to get clock 'sclk_dac'\n");
- return -ENODEV;
- }
- if (mixer_res->sclk_hdmi)
- clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi);
+ if (mixer_ctx->has_sclk) {
+ mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
+ if (IS_ERR(mixer_res->sclk_mixer)) {
+ dev_err(dev, "failed to get clock 'sclk_mixer'\n");
+ return -ENODEV;
+ }
+ mixer_res->mout_mixer = devm_clk_get(dev, "mout_mixer");
+ if (IS_ERR(mixer_res->mout_mixer)) {
+ dev_err(dev, "failed to get clock 'mout_mixer'\n");
+ return -ENODEV;
+ }
+
+ if (mixer_res->sclk_hdmi && mixer_res->mout_mixer)
+ clk_set_parent(mixer_res->mout_mixer,
+ mixer_res->sclk_hdmi);
+ }
res = platform_get_resource(mixer_ctx->pdev, IORESOURCE_MEM, 1);
if (res == NULL) {
@@ -1082,7 +1093,8 @@ static void mixer_poweron(struct exynos_drm_manager *mgr)
clk_prepare_enable(res->mixer);
if (ctx->vp_enabled) {
clk_prepare_enable(res->vp);
- clk_prepare_enable(res->sclk_mixer);
+ if (ctx->has_sclk)
+ clk_prepare_enable(res->sclk_mixer);
}
mutex_lock(&ctx->mixer_mutex);
@@ -1121,7 +1133,8 @@ static void mixer_poweroff(struct exynos_drm_manager *mgr)
clk_disable_unprepare(res->mixer);
if (ctx->vp_enabled) {
clk_disable_unprepare(res->vp);
- clk_disable_unprepare(res->sclk_mixer);
+ if (ctx->has_sclk)
+ clk_disable_unprepare(res->sclk_mixer);
}
pm_runtime_put_sync(ctx->dev);
@@ -1189,9 +1202,15 @@ static struct mixer_drv_data exynos5250_mxr_drv_data = {
.is_vp_enabled = 0,
};
+static struct mixer_drv_data exynos4212_mxr_drv_data = {
+ .version = MXR_VER_0_0_0_16,
+ .is_vp_enabled = 1,
+};
+
static struct mixer_drv_data exynos4210_mxr_drv_data = {
.version = MXR_VER_0_0_0_16,
.is_vp_enabled = 1,
+ .has_sclk = 1,
};
static struct platform_device_id mixer_driver_types[] = {
@@ -1208,6 +1227,12 @@ static struct platform_device_id mixer_driver_types[] = {
static struct of_device_id mixer_match_types[] = {
{
+ .compatible = "samsung,exynos4210-mixer",
+ .data = &exynos4210_mxr_drv_data,
+ }, {
+ .compatible = "samsung,exynos4212-mixer",
+ .data = &exynos4212_mxr_drv_data,
+ }, {
.compatible = "samsung,exynos5-mixer",
.data = &exynos5250_mxr_drv_data,
}, {
@@ -1220,6 +1245,7 @@ static struct of_device_id mixer_match_types[] = {
/* end node */
}
};
+MODULE_DEVICE_TABLE(of, mixer_match_types);
static int mixer_bind(struct device *dev, struct device *manager, void *data)
{
@@ -1251,6 +1277,7 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
ctx->pdev = pdev;
ctx->dev = dev;
ctx->vp_enabled = drv->is_vp_enabled;
+ ctx->has_sclk = drv->has_sclk;
ctx->mxr_ver = drv->version;
init_waitqueue_head(&ctx->wait_vsync_queue);
atomic_set(&ctx->wait_vsync_event, 0);
@@ -1275,15 +1302,12 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
static void mixer_unbind(struct device *dev, struct device *master, void *data)
{
struct exynos_drm_manager *mgr = dev_get_drvdata(dev);
- struct drm_crtc *crtc = mgr->crtc;
dev_info(dev, "remove successful\n");
mixer_mgr_remove(mgr);
pm_runtime_disable(dev);
-
- crtc->funcs->destroy(crtc);
}
static const struct component_ops mixer_component_ops = {
diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c
index c18268cd516e..248c33a35ebf 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_crt.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c
@@ -192,7 +192,7 @@ static void cdv_intel_crt_destroy(struct drm_connector *connector)
struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
psb_intel_i2c_destroy(gma_encoder->ddc_bus);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -304,7 +304,7 @@ void cdv_intel_crt_init(struct drm_device *dev,
drm_connector_helper_add(connector,
&cdv_intel_crt_connector_helper_funcs);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return;
failed_ddc:
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c
index 9ff30c2efadb..9f158eab517a 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -1089,7 +1089,7 @@ static char *link_train_names[] = {
};
#endif
-#define CDV_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200
+#define CDV_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_LEVEL_3
/*
static uint8_t
cdv_intel_dp_pre_emphasis_max(uint8_t voltage_swing)
@@ -1276,7 +1276,7 @@ cdv_intel_dp_set_vswing_premph(struct gma_encoder *encoder, uint8_t signal_level
cdv_sb_write(dev, ddi_reg->VSwing2, dp_vswing_premph_table[index]);
/* ;gfx_dpio_set_reg(0x814c, 0x40802040) */
- if ((vswing + premph) == DP_TRAIN_VOLTAGE_SWING_1200)
+ if ((vswing + premph) == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
cdv_sb_write(dev, ddi_reg->VSwing3, 0x70802040);
else
cdv_sb_write(dev, ddi_reg->VSwing3, 0x40802040);
@@ -1713,7 +1713,7 @@ cdv_intel_dp_destroy(struct drm_connector *connector)
}
}
i2c_del_adapter(&intel_dp->adapter);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -1847,7 +1847,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
/* Set up the DDC bus. */
switch (output_reg) {
diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
index b99084b3f706..4268bf210034 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
@@ -248,7 +248,7 @@ static void cdv_hdmi_destroy(struct drm_connector *connector)
if (gma_encoder->i2c_bus)
psb_intel_i2c_destroy(gma_encoder->i2c_bus);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -356,7 +356,7 @@ void cdv_hdmi_init(struct drm_device *dev,
hdmi_priv->hdmi_i2c_adapter = &(gma_encoder->i2c_bus->adapter);
hdmi_priv->dev = dev;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return;
failed_ddc:
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
index 8ecc920fc26d..0b770396548c 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
@@ -446,7 +446,7 @@ static void cdv_intel_lvds_destroy(struct drm_connector *connector)
if (gma_encoder->i2c_bus)
psb_intel_i2c_destroy(gma_encoder->i2c_bus);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -774,7 +774,7 @@ void cdv_intel_lvds_init(struct drm_device *dev,
out:
mutex_unlock(&dev->mode_config.mutex);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return;
failed_find:
diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
index e7fcc148f333..ddd90ddbc200 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -540,7 +540,8 @@ static void psbfb_gamma_get(struct drm_crtc *crtc, u16 *red,
static int psbfb_probe(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
- struct psb_fbdev *psb_fbdev = (struct psb_fbdev *)helper;
+ struct psb_fbdev *psb_fbdev =
+ container_of(helper, struct psb_fbdev, psb_fb_helper);
struct drm_device *dev = psb_fbdev->psb_fb_helper.dev;
struct drm_psb_private *dev_priv = dev->dev_private;
int bytespp;
@@ -561,7 +562,7 @@ static int psbfb_probe(struct drm_fb_helper *helper,
return psbfb_create(psb_fbdev, sizes);
}
-static struct drm_fb_helper_funcs psb_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs psb_fb_helper_funcs = {
.gamma_set = psbfb_gamma_set,
.gamma_get = psbfb_gamma_get,
.fb_probe = psbfb_probe,
@@ -600,7 +601,8 @@ int psb_fbdev_init(struct drm_device *dev)
}
dev_priv->fbdev = fbdev;
- fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs;
+
+ drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs);
drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
INTELFB_CONN_LIMIT);
diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c
index 592d205a0089..ce015db59dc6 100644
--- a/drivers/gpu/drm/gma500/gtt.c
+++ b/drivers/gpu/drm/gma500/gtt.c
@@ -206,7 +206,7 @@ static int psb_gtt_attach_pages(struct gtt_range *gt)
WARN_ON(gt->pages);
- pages = drm_gem_get_pages(&gt->gem, 0);
+ pages = drm_gem_get_pages(&gt->gem);
if (IS_ERR(pages))
return PTR_ERR(pages);
diff --git a/drivers/gpu/drm/gma500/gtt.h b/drivers/gpu/drm/gma500/gtt.h
index f5860a739bd8..cdbb350c9d5d 100644
--- a/drivers/gpu/drm/gma500/gtt.h
+++ b/drivers/gpu/drm/gma500/gtt.h
@@ -21,6 +21,7 @@
#define _PSB_GTT_H_
#include <drm/drmP.h>
+#include <drm/drm_gem.h>
/* This wants cleaning up with respect to the psb_dev and un-needed stuff */
struct psb_gtt {
diff --git a/drivers/gpu/drm/gma500/intel_bios.c b/drivers/gpu/drm/gma500/intel_bios.c
index d3497348c4d5..63bde4e86c6a 100644
--- a/drivers/gpu/drm/gma500/intel_bios.c
+++ b/drivers/gpu/drm/gma500/intel_bios.c
@@ -116,30 +116,30 @@ parse_edp(struct drm_psb_private *dev_priv, struct bdb_header *bdb)
switch (edp_link_params->preemphasis) {
case 0:
- dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_0;
+ dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_0;
break;
case 1:
- dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5;
+ dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_1;
break;
case 2:
- dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_6;
+ dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_2;
break;
case 3:
- dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5;
+ dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_3;
break;
}
switch (edp_link_params->vswing) {
case 0:
- dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_400;
+ dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
break;
case 1:
- dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_600;
+ dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_1;
break;
case 2:
- dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_800;
+ dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
break;
case 3:
- dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_1200;
+ dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
break;
}
DRM_DEBUG_KMS("VBT reports EDP: VSwing %d, Preemph %d\n",
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
index 6e91b20ce2e5..abf2248da61e 100644
--- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
@@ -318,7 +318,7 @@ static void mdfld_dsi_connector_destroy(struct drm_connector *connector)
if (!dsi_connector)
return;
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
sender = dsi_connector->pkg_sender;
mdfld_dsi_pkg_sender_destroy(sender);
@@ -597,7 +597,7 @@ void mdfld_dsi_output_init(struct drm_device *dev,
dsi_config->encoder = encoder;
encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI :
INTEL_OUTPUT_MIPI2;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return;
/*TODO: add code to destroy outputs on error*/
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c
index cf018ddcc5a6..54f73f50571a 100644
--- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c
+++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c
@@ -665,7 +665,7 @@ void oaktrail_hdmi_init(struct drm_device *dev,
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
dev_info(dev->dev, "HDMI initialised.\n");
return;
@@ -674,7 +674,7 @@ failed_connector:
kfree(gma_encoder);
}
-static DEFINE_PCI_DEVICE_TABLE(hdmi_ids) = {
+static const struct pci_device_id hdmi_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080d) },
{ 0 }
};
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c
index 9b099468a5db..0d39da6e8b7a 100644
--- a/drivers/gpu/drm/gma500/oaktrail_lvds.c
+++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c
@@ -404,7 +404,7 @@ void oaktrail_lvds_init(struct drm_device *dev,
out:
mutex_unlock(&dev->mode_config.mutex);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return;
failed_find:
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 6e8fe9ec02b5..6ec3a905fdd2 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -54,7 +54,7 @@ static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
* PowerVR SGX545 - Cedartrail - Intel GMA 3650, Intel Atom D2550, D2700,
* N2800
*/
-static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
+static const struct pci_device_id pciidlist[] = {
{ 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops },
{ 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops },
#if defined(CONFIG_DRM_GMA600)
@@ -476,6 +476,7 @@ static struct drm_driver driver = {
.unload = psb_driver_unload,
.lastclose = psb_driver_lastclose,
.preclose = psb_driver_preclose,
+ .set_busid = drm_pci_set_busid,
.num_ioctls = ARRAY_SIZE(psb_ioctls),
.device_is_agp = psb_driver_device_is_agp,
diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
index d7778d0472c1..88aad95bde09 100644
--- a/drivers/gpu/drm/gma500/psb_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c
@@ -563,7 +563,7 @@ void psb_intel_lvds_destroy(struct drm_connector *connector)
if (lvds_priv->ddc_bus)
psb_intel_i2c_destroy(lvds_priv->ddc_bus);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -829,7 +829,7 @@ void psb_intel_lvds_init(struct drm_device *dev,
*/
out:
mutex_unlock(&dev->mode_config.mutex);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return;
failed_find:
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
index deeb0829b129..0be96fdb5e28 100644
--- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c
+++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
@@ -1682,7 +1682,7 @@ static void psb_intel_sdvo_destroy(struct drm_connector *connector)
psb_intel_sdvo_connector->tv_format);
psb_intel_sdvo_destroy_enhance_property(connector);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -2071,7 +2071,7 @@ psb_intel_sdvo_connector_init(struct psb_intel_sdvo_connector *connector,
connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
gma_connector_attach_encoder(&connector->base, &encoder->base);
- drm_sysfs_connector_add(&connector->base.base);
+ drm_connector_register(&connector->base.base);
}
static void
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index ac357b02bd35..d4762799351d 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -15,8 +15,7 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-
+#include <linux/component.h>
#include <linux/hdmi.h>
#include <linux/module.h>
#include <linux/irq.h>
@@ -730,12 +729,9 @@ tda998x_configure_audio(struct tda998x_priv *priv,
/* DRM encoder functions */
-static void
-tda998x_encoder_set_config(struct drm_encoder *encoder, void *params)
+static void tda998x_encoder_set_config(struct tda998x_priv *priv,
+ const struct tda998x_encoder_params *p)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
- struct tda998x_encoder_params *p = params;
-
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) |
(p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) |
VIP_CNTRL_0_SWAP_B(p->swap_b) |
@@ -752,11 +748,8 @@ tda998x_encoder_set_config(struct drm_encoder *encoder, void *params)
priv->params = *p;
}
-static void
-tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
+static void tda998x_encoder_dpms(struct tda998x_priv *priv, int mode)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
-
/* we only care about on or off: */
if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
@@ -806,9 +799,8 @@ tda998x_encoder_mode_fixup(struct drm_encoder *encoder,
return true;
}
-static int
-tda998x_encoder_mode_valid(struct drm_encoder *encoder,
- struct drm_display_mode *mode)
+static int tda998x_encoder_mode_valid(struct tda998x_priv *priv,
+ struct drm_display_mode *mode)
{
if (mode->clock > 150000)
return MODE_CLOCK_HIGH;
@@ -820,11 +812,10 @@ tda998x_encoder_mode_valid(struct drm_encoder *encoder,
}
static void
-tda998x_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+tda998x_encoder_mode_set(struct tda998x_priv *priv,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
uint16_t ref_pix, ref_line, n_pix, n_line;
uint16_t hs_pix_s, hs_pix_e;
uint16_t vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e;
@@ -1012,20 +1003,16 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
}
static enum drm_connector_status
-tda998x_encoder_detect(struct drm_encoder *encoder,
- struct drm_connector *connector)
+tda998x_encoder_detect(struct tda998x_priv *priv)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
uint8_t val = cec_read(priv, REG_CEC_RXSHPDLEV);
return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected :
connector_status_disconnected;
}
-static int
-read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk)
+static int read_edid_block(struct tda998x_priv *priv, uint8_t *buf, int blk)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
uint8_t offset, segptr;
int ret, i;
@@ -1079,10 +1066,8 @@ read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk)
return 0;
}
-static uint8_t *
-do_get_edid(struct drm_encoder *encoder)
+static uint8_t *do_get_edid(struct tda998x_priv *priv)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
int j, valid_extensions = 0;
uint8_t *block, *new;
bool print_bad_edid = drm_debug & DRM_UT_KMS;
@@ -1094,7 +1079,7 @@ do_get_edid(struct drm_encoder *encoder)
reg_clear(priv, REG_TX4, TX4_PD_RAM);
/* base block fetch */
- if (read_edid_block(encoder, block, 0))
+ if (read_edid_block(priv, block, 0))
goto fail;
if (!drm_edid_block_valid(block, 0, print_bad_edid))
@@ -1111,7 +1096,7 @@ do_get_edid(struct drm_encoder *encoder)
for (j = 1; j <= block[0x7e]; j++) {
uint8_t *ext_block = block + (valid_extensions + 1) * EDID_LENGTH;
- if (read_edid_block(encoder, ext_block, j))
+ if (read_edid_block(priv, ext_block, j))
goto fail;
if (!drm_edid_block_valid(ext_block, j, print_bad_edid))
@@ -1144,11 +1129,10 @@ fail:
}
static int
-tda998x_encoder_get_modes(struct drm_encoder *encoder,
- struct drm_connector *connector)
+tda998x_encoder_get_modes(struct tda998x_priv *priv,
+ struct drm_connector *connector)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
- struct edid *edid = (struct edid *)do_get_edid(encoder);
+ struct edid *edid = (struct edid *)do_get_edid(priv);
int n = 0;
if (edid) {
@@ -1161,18 +1145,14 @@ tda998x_encoder_get_modes(struct drm_encoder *encoder,
return n;
}
-static int
-tda998x_encoder_create_resources(struct drm_encoder *encoder,
- struct drm_connector *connector)
+static void tda998x_encoder_set_polling(struct tda998x_priv *priv,
+ struct drm_connector *connector)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
-
if (priv->hdmi->irq)
connector->polled = DRM_CONNECTOR_POLL_HPD;
else
connector->polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;
- return 0;
}
static int
@@ -1185,66 +1165,97 @@ tda998x_encoder_set_property(struct drm_encoder *encoder,
return 0;
}
-static void
-tda998x_encoder_destroy(struct drm_encoder *encoder)
+static void tda998x_destroy(struct tda998x_priv *priv)
{
- struct tda998x_priv *priv = to_tda998x_priv(encoder);
-
/* disable all IRQs and free the IRQ handler */
cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
if (priv->hdmi->irq)
free_irq(priv->hdmi->irq, priv);
- if (priv->cec)
- i2c_unregister_device(priv->cec);
+ i2c_unregister_device(priv->cec);
+}
+
+/* Slave encoder support */
+
+static void
+tda998x_encoder_slave_set_config(struct drm_encoder *encoder, void *params)
+{
+ tda998x_encoder_set_config(to_tda998x_priv(encoder), params);
+}
+
+static void tda998x_encoder_slave_destroy(struct drm_encoder *encoder)
+{
+ struct tda998x_priv *priv = to_tda998x_priv(encoder);
+
+ tda998x_destroy(priv);
drm_i2c_encoder_destroy(encoder);
kfree(priv);
}
-static struct drm_encoder_slave_funcs tda998x_encoder_funcs = {
- .set_config = tda998x_encoder_set_config,
- .destroy = tda998x_encoder_destroy,
- .dpms = tda998x_encoder_dpms,
- .save = tda998x_encoder_save,
- .restore = tda998x_encoder_restore,
- .mode_fixup = tda998x_encoder_mode_fixup,
- .mode_valid = tda998x_encoder_mode_valid,
- .mode_set = tda998x_encoder_mode_set,
- .detect = tda998x_encoder_detect,
- .get_modes = tda998x_encoder_get_modes,
- .create_resources = tda998x_encoder_create_resources,
- .set_property = tda998x_encoder_set_property,
-};
+static void tda998x_encoder_slave_dpms(struct drm_encoder *encoder, int mode)
+{
+ tda998x_encoder_dpms(to_tda998x_priv(encoder), mode);
+}
-/* I2C driver functions */
+static int tda998x_encoder_slave_mode_valid(struct drm_encoder *encoder,
+ struct drm_display_mode *mode)
+{
+ return tda998x_encoder_mode_valid(to_tda998x_priv(encoder), mode);
+}
-static int
-tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static void
+tda998x_encoder_slave_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
- return 0;
+ tda998x_encoder_mode_set(to_tda998x_priv(encoder), mode, adjusted_mode);
+}
+
+static enum drm_connector_status
+tda998x_encoder_slave_detect(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ return tda998x_encoder_detect(to_tda998x_priv(encoder));
+}
+
+static int tda998x_encoder_slave_get_modes(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ return tda998x_encoder_get_modes(to_tda998x_priv(encoder), connector);
}
static int
-tda998x_remove(struct i2c_client *client)
+tda998x_encoder_slave_create_resources(struct drm_encoder *encoder,
+ struct drm_connector *connector)
{
+ tda998x_encoder_set_polling(to_tda998x_priv(encoder), connector);
return 0;
}
-static int
-tda998x_encoder_init(struct i2c_client *client,
- struct drm_device *dev,
- struct drm_encoder_slave *encoder_slave)
+static struct drm_encoder_slave_funcs tda998x_encoder_slave_funcs = {
+ .set_config = tda998x_encoder_slave_set_config,
+ .destroy = tda998x_encoder_slave_destroy,
+ .dpms = tda998x_encoder_slave_dpms,
+ .save = tda998x_encoder_save,
+ .restore = tda998x_encoder_restore,
+ .mode_fixup = tda998x_encoder_mode_fixup,
+ .mode_valid = tda998x_encoder_slave_mode_valid,
+ .mode_set = tda998x_encoder_slave_mode_set,
+ .detect = tda998x_encoder_slave_detect,
+ .get_modes = tda998x_encoder_slave_get_modes,
+ .create_resources = tda998x_encoder_slave_create_resources,
+ .set_property = tda998x_encoder_set_property,
+};
+
+/* I2C driver functions */
+
+static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
{
- struct tda998x_priv *priv;
struct device_node *np = client->dev.of_node;
u32 video;
int rev_lo, rev_hi, ret;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);
@@ -1252,17 +1263,11 @@ tda998x_encoder_init(struct i2c_client *client,
priv->current_page = 0xff;
priv->hdmi = client;
priv->cec = i2c_new_dummy(client->adapter, 0x34);
- if (!priv->cec) {
- kfree(priv);
+ if (!priv->cec)
return -ENODEV;
- }
- priv->encoder = &encoder_slave->base;
priv->dpms = DRM_MODE_DPMS_OFF;
- encoder_slave->slave_priv = priv;
- encoder_slave->slave_funcs = &tda998x_encoder_funcs;
-
/* wake up the device: */
cec_write(priv, REG_CEC_ENAMODS,
CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI);
@@ -1365,12 +1370,231 @@ fail:
*/
if (priv->cec)
i2c_unregister_device(priv->cec);
- kfree(priv);
- encoder_slave->slave_priv = NULL;
- encoder_slave->slave_funcs = NULL;
return -ENXIO;
}
+static int tda998x_encoder_init(struct i2c_client *client,
+ struct drm_device *dev,
+ struct drm_encoder_slave *encoder_slave)
+{
+ struct tda998x_priv *priv;
+ int ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->encoder = &encoder_slave->base;
+
+ ret = tda998x_create(client, priv);
+ if (ret) {
+ kfree(priv);
+ return ret;
+ }
+
+ encoder_slave->slave_priv = priv;
+ encoder_slave->slave_funcs = &tda998x_encoder_slave_funcs;
+
+ return 0;
+}
+
+struct tda998x_priv2 {
+ struct tda998x_priv base;
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+};
+
+#define conn_to_tda998x_priv2(x) \
+ container_of(x, struct tda998x_priv2, connector);
+
+#define enc_to_tda998x_priv2(x) \
+ container_of(x, struct tda998x_priv2, encoder);
+
+static void tda998x_encoder2_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder);
+
+ tda998x_encoder_dpms(&priv->base, mode);
+}
+
+static void tda998x_encoder_prepare(struct drm_encoder *encoder)
+{
+ tda998x_encoder2_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static void tda998x_encoder_commit(struct drm_encoder *encoder)
+{
+ tda998x_encoder2_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void tda998x_encoder2_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder);
+
+ tda998x_encoder_mode_set(&priv->base, mode, adjusted_mode);
+}
+
+static const struct drm_encoder_helper_funcs tda998x_encoder_helper_funcs = {
+ .dpms = tda998x_encoder2_dpms,
+ .save = tda998x_encoder_save,
+ .restore = tda998x_encoder_restore,
+ .mode_fixup = tda998x_encoder_mode_fixup,
+ .prepare = tda998x_encoder_prepare,
+ .commit = tda998x_encoder_commit,
+ .mode_set = tda998x_encoder2_mode_set,
+};
+
+static void tda998x_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct tda998x_priv2 *priv = enc_to_tda998x_priv2(encoder);
+
+ tda998x_destroy(&priv->base);
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs tda998x_encoder_funcs = {
+ .destroy = tda998x_encoder_destroy,
+};
+
+static int tda998x_connector_get_modes(struct drm_connector *connector)
+{
+ struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector);
+
+ return tda998x_encoder_get_modes(&priv->base, connector);
+}
+
+static int tda998x_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector);
+
+ return tda998x_encoder_mode_valid(&priv->base, mode);
+}
+
+static struct drm_encoder *
+tda998x_connector_best_encoder(struct drm_connector *connector)
+{
+ struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector);
+
+ return &priv->encoder;
+}
+
+static
+const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = {
+ .get_modes = tda998x_connector_get_modes,
+ .mode_valid = tda998x_connector_mode_valid,
+ .best_encoder = tda998x_connector_best_encoder,
+};
+
+static enum drm_connector_status
+tda998x_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct tda998x_priv2 *priv = conn_to_tda998x_priv2(connector);
+
+ return tda998x_encoder_detect(&priv->base);
+}
+
+static void tda998x_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs tda998x_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = tda998x_connector_detect,
+ .destroy = tda998x_connector_destroy,
+};
+
+static int tda998x_bind(struct device *dev, struct device *master, void *data)
+{
+ struct tda998x_encoder_params *params = dev->platform_data;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct drm_device *drm = data;
+ struct tda998x_priv2 *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+
+ priv->base.encoder = &priv->encoder;
+ priv->connector.interlace_allowed = 1;
+ priv->encoder.possible_crtcs = 1 << 0;
+
+ ret = tda998x_create(client, &priv->base);
+ if (ret)
+ return ret;
+
+ if (!dev->of_node && params)
+ tda998x_encoder_set_config(&priv->base, params);
+
+ tda998x_encoder_set_polling(&priv->base, &priv->connector);
+
+ drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_funcs);
+ ret = drm_encoder_init(drm, &priv->encoder, &tda998x_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS);
+ if (ret)
+ goto err_encoder;
+
+ drm_connector_helper_add(&priv->connector,
+ &tda998x_connector_helper_funcs);
+ ret = drm_connector_init(drm, &priv->connector,
+ &tda998x_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret)
+ goto err_connector;
+
+ ret = drm_connector_register(&priv->connector);
+ if (ret)
+ goto err_sysfs;
+
+ priv->connector.encoder = &priv->encoder;
+ drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder);
+
+ return 0;
+
+err_sysfs:
+ drm_connector_cleanup(&priv->connector);
+err_connector:
+ drm_encoder_cleanup(&priv->encoder);
+err_encoder:
+ tda998x_destroy(&priv->base);
+ return ret;
+}
+
+static void tda998x_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct tda998x_priv2 *priv = dev_get_drvdata(dev);
+
+ drm_connector_cleanup(&priv->connector);
+ drm_encoder_cleanup(&priv->encoder);
+ tda998x_destroy(&priv->base);
+}
+
+static const struct component_ops tda998x_ops = {
+ .bind = tda998x_bind,
+ .unbind = tda998x_unbind,
+};
+
+static int
+tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ return component_add(&client->dev, &tda998x_ops);
+}
+
+static int tda998x_remove(struct i2c_client *client)
+{
+ component_del(&client->dev, &tda998x_ops);
+ return 0;
+}
+
#ifdef CONFIG_OF
static const struct of_device_id tda998x_dt_ids[] = {
{ .compatible = "nxp,tda998x", },
diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c
index e88bac1d781f..d91856779beb 100644
--- a/drivers/gpu/drm/i810/i810_dma.c
+++ b/drivers/gpu/drm/i810/i810_dma.c
@@ -213,7 +213,7 @@ static int i810_dma_cleanup(struct drm_device *dev)
(drm_i810_private_t *) dev->dev_private;
if (dev_priv->ring.virtual_start)
- drm_core_ioremapfree(&dev_priv->ring.map, dev);
+ drm_legacy_ioremapfree(&dev_priv->ring.map, dev);
if (dev_priv->hw_status_page) {
pci_free_consistent(dev->pdev, PAGE_SIZE,
dev_priv->hw_status_page,
@@ -227,7 +227,7 @@ static int i810_dma_cleanup(struct drm_device *dev)
drm_i810_buf_priv_t *buf_priv = buf->dev_private;
if (buf_priv->kernel_virtual && buf->total)
- drm_core_ioremapfree(&buf_priv->map, dev);
+ drm_legacy_ioremapfree(&buf_priv->map, dev);
}
}
return 0;
@@ -306,7 +306,7 @@ static int i810_freelist_init(struct drm_device *dev, drm_i810_private_t *dev_pr
buf_priv->map.flags = 0;
buf_priv->map.mtrr = 0;
- drm_core_ioremap(&buf_priv->map, dev);
+ drm_legacy_ioremap(&buf_priv->map, dev);
buf_priv->kernel_virtual = buf_priv->map.handle;
}
@@ -334,7 +334,7 @@ static int i810_dma_initialize(struct drm_device *dev,
DRM_ERROR("can not find sarea!\n");
return -EINVAL;
}
- dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset);
+ dev_priv->mmio_map = drm_legacy_findmap(dev, init->mmio_offset);
if (!dev_priv->mmio_map) {
dev->dev_private = (void *)dev_priv;
i810_dma_cleanup(dev);
@@ -342,7 +342,7 @@ static int i810_dma_initialize(struct drm_device *dev,
return -EINVAL;
}
dev->agp_buffer_token = init->buffers_offset;
- dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset);
+ dev->agp_buffer_map = drm_legacy_findmap(dev, init->buffers_offset);
if (!dev->agp_buffer_map) {
dev->dev_private = (void *)dev_priv;
i810_dma_cleanup(dev);
@@ -363,7 +363,7 @@ static int i810_dma_initialize(struct drm_device *dev,
dev_priv->ring.map.flags = 0;
dev_priv->ring.map.mtrr = 0;
- drm_core_ioremap(&dev_priv->ring.map, dev);
+ drm_legacy_ioremap(&dev_priv->ring.map, dev);
if (dev_priv->ring.map.handle == NULL) {
dev->dev_private = (void *)dev_priv;
@@ -393,15 +393,14 @@ static int i810_dma_initialize(struct drm_device *dev,
/* Program Hardware Status Page */
dev_priv->hw_status_page =
- pci_alloc_consistent(dev->pdev, PAGE_SIZE,
- &dev_priv->dma_status_page);
+ pci_zalloc_consistent(dev->pdev, PAGE_SIZE,
+ &dev_priv->dma_status_page);
if (!dev_priv->hw_status_page) {
dev->dev_private = (void *)dev_priv;
i810_dma_cleanup(dev);
DRM_ERROR("Can not allocate hardware status page\n");
return -ENOMEM;
}
- memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);
I810_WRITE(0x02080, dev_priv->dma_status_page);
@@ -1216,9 +1215,9 @@ void i810_driver_preclose(struct drm_device *dev, struct drm_file *file_priv)
}
if (file_priv->master && file_priv->master->lock.hw_lock) {
- drm_idlelock_take(&file_priv->master->lock);
+ drm_legacy_idlelock_take(&file_priv->master->lock);
i810_driver_reclaim_buffers(dev, file_priv);
- drm_idlelock_release(&file_priv->master->lock);
+ drm_legacy_idlelock_release(&file_priv->master->lock);
} else {
/* master disappeared, clean up stuff anyway and hope nothing
* goes wrong */
diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c
index 441ccf8f5bdc..44f4a131c8dd 100644
--- a/drivers/gpu/drm/i810/i810_drv.c
+++ b/drivers/gpu/drm/i810/i810_drv.c
@@ -47,7 +47,7 @@ static const struct file_operations i810_driver_fops = {
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
- .mmap = drm_mmap,
+ .mmap = drm_legacy_mmap,
.poll = drm_poll,
#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
@@ -63,6 +63,7 @@ static struct drm_driver driver = {
.load = i810_driver_load,
.lastclose = i810_driver_lastclose,
.preclose = i810_driver_preclose,
+ .set_busid = drm_pci_set_busid,
.device_is_agp = i810_driver_device_is_agp,
.dma_quiescent = i810_driver_dma_quiescent,
.ioctls = i810_ioctls,
diff --git a/drivers/gpu/drm/i810/i810_drv.h b/drivers/gpu/drm/i810/i810_drv.h
index d4d16eddd651..93ec5dc4e7d3 100644
--- a/drivers/gpu/drm/i810/i810_drv.h
+++ b/drivers/gpu/drm/i810/i810_drv.h
@@ -32,6 +32,8 @@
#ifndef _I810_DRV_H_
#define _I810_DRV_H_
+#include <drm/drm_legacy.h>
+
/* General customization:
*/
diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig
index 437e1824d0bf..4e39ab34eb1c 100644
--- a/drivers/gpu/drm/i915/Kconfig
+++ b/drivers/gpu/drm/i915/Kconfig
@@ -69,15 +69,3 @@ config DRM_I915_PRELIMINARY_HW_SUPPORT
option changes the default for that module option.
If in doubt, say "N".
-
-config DRM_I915_UMS
- bool "Enable userspace modesetting on Intel hardware (DEPRECATED)"
- depends on DRM_I915 && BROKEN
- default n
- help
- Choose this option if you still need userspace modesetting.
-
- Userspace modesetting is deprecated for quite some time now, so
- enable this only if you have ancient versions of the DDX drivers.
-
- If in doubt, say "N".
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index cad1683d8bb5..c1dd485aeb6c 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -31,6 +31,7 @@ i915-y += i915_cmd_parser.o \
i915_gpu_error.o \
i915_irq.o \
i915_trace_points.o \
+ intel_lrc.o \
intel_ringbuffer.o \
intel_uncore.o
@@ -59,6 +60,7 @@ i915-y += dvo_ch7017.o \
intel_crt.o \
intel_ddi.o \
intel_dp.o \
+ intel_dp_mst.o \
intel_dsi_cmd.o \
intel_dsi.o \
intel_dsi_pll.o \
diff --git a/drivers/gpu/drm/i915/dvo_ns2501.c b/drivers/gpu/drm/i915/dvo_ns2501.c
index 74f2af7c2d3e..441630434d34 100644
--- a/drivers/gpu/drm/i915/dvo_ns2501.c
+++ b/drivers/gpu/drm/i915/dvo_ns2501.c
@@ -60,16 +60,297 @@
#define NS2501_REGC 0x0c
+enum {
+ MODE_640x480,
+ MODE_800x600,
+ MODE_1024x768,
+};
+
+struct ns2501_reg {
+ uint8_t offset;
+ uint8_t value;
+};
+
+/*
+ * Magic values based on what the BIOS on
+ * Fujitsu-Siemens Lifebook S6010 programs (1024x768 panel).
+ */
+static const struct ns2501_reg regs_1024x768[][86] = {
+ [MODE_640x480] = {
+ [0] = { .offset = 0x0a, .value = 0x81, },
+ [1] = { .offset = 0x18, .value = 0x07, },
+ [2] = { .offset = 0x19, .value = 0x00, },
+ [3] = { .offset = 0x1a, .value = 0x00, },
+ [4] = { .offset = 0x1b, .value = 0x11, },
+ [5] = { .offset = 0x1c, .value = 0x54, },
+ [6] = { .offset = 0x1d, .value = 0x03, },
+ [7] = { .offset = 0x1e, .value = 0x02, },
+ [8] = { .offset = 0xf3, .value = 0x90, },
+ [9] = { .offset = 0xf9, .value = 0x00, },
+ [10] = { .offset = 0xc1, .value = 0x90, },
+ [11] = { .offset = 0xc2, .value = 0x00, },
+ [12] = { .offset = 0xc3, .value = 0x0f, },
+ [13] = { .offset = 0xc4, .value = 0x03, },
+ [14] = { .offset = 0xc5, .value = 0x16, },
+ [15] = { .offset = 0xc6, .value = 0x00, },
+ [16] = { .offset = 0xc7, .value = 0x02, },
+ [17] = { .offset = 0xc8, .value = 0x02, },
+ [18] = { .offset = 0xf4, .value = 0x00, },
+ [19] = { .offset = 0x80, .value = 0xff, },
+ [20] = { .offset = 0x81, .value = 0x07, },
+ [21] = { .offset = 0x82, .value = 0x3d, },
+ [22] = { .offset = 0x83, .value = 0x05, },
+ [23] = { .offset = 0x94, .value = 0x00, },
+ [24] = { .offset = 0x95, .value = 0x00, },
+ [25] = { .offset = 0x96, .value = 0x05, },
+ [26] = { .offset = 0x97, .value = 0x00, },
+ [27] = { .offset = 0x9a, .value = 0x88, },
+ [28] = { .offset = 0x9b, .value = 0x00, },
+ [29] = { .offset = 0x98, .value = 0x00, },
+ [30] = { .offset = 0x99, .value = 0x00, },
+ [31] = { .offset = 0xf7, .value = 0x88, },
+ [32] = { .offset = 0xf8, .value = 0x0a, },
+ [33] = { .offset = 0x9c, .value = 0x24, },
+ [34] = { .offset = 0x9d, .value = 0x00, },
+ [35] = { .offset = 0x9e, .value = 0x25, },
+ [36] = { .offset = 0x9f, .value = 0x03, },
+ [37] = { .offset = 0xa0, .value = 0x28, },
+ [38] = { .offset = 0xa1, .value = 0x01, },
+ [39] = { .offset = 0xa2, .value = 0x28, },
+ [40] = { .offset = 0xa3, .value = 0x05, },
+ [41] = { .offset = 0xb6, .value = 0x09, },
+ [42] = { .offset = 0xb8, .value = 0x00, },
+ [43] = { .offset = 0xb9, .value = 0xa0, },
+ [44] = { .offset = 0xba, .value = 0x00, },
+ [45] = { .offset = 0xbb, .value = 0x20, },
+ [46] = { .offset = 0x10, .value = 0x00, },
+ [47] = { .offset = 0x11, .value = 0xa0, },
+ [48] = { .offset = 0x12, .value = 0x02, },
+ [49] = { .offset = 0x20, .value = 0x00, },
+ [50] = { .offset = 0x22, .value = 0x00, },
+ [51] = { .offset = 0x23, .value = 0x00, },
+ [52] = { .offset = 0x24, .value = 0x00, },
+ [53] = { .offset = 0x25, .value = 0x00, },
+ [54] = { .offset = 0x8c, .value = 0x10, },
+ [55] = { .offset = 0x8d, .value = 0x02, },
+ [56] = { .offset = 0x8e, .value = 0x10, },
+ [57] = { .offset = 0x8f, .value = 0x00, },
+ [58] = { .offset = 0x90, .value = 0xff, },
+ [59] = { .offset = 0x91, .value = 0x07, },
+ [60] = { .offset = 0x92, .value = 0xa0, },
+ [61] = { .offset = 0x93, .value = 0x02, },
+ [62] = { .offset = 0xa5, .value = 0x00, },
+ [63] = { .offset = 0xa6, .value = 0x00, },
+ [64] = { .offset = 0xa7, .value = 0x00, },
+ [65] = { .offset = 0xa8, .value = 0x00, },
+ [66] = { .offset = 0xa9, .value = 0x04, },
+ [67] = { .offset = 0xaa, .value = 0x70, },
+ [68] = { .offset = 0xab, .value = 0x4f, },
+ [69] = { .offset = 0xac, .value = 0x00, },
+ [70] = { .offset = 0xa4, .value = 0x84, },
+ [71] = { .offset = 0x7e, .value = 0x18, },
+ [72] = { .offset = 0x84, .value = 0x00, },
+ [73] = { .offset = 0x85, .value = 0x00, },
+ [74] = { .offset = 0x86, .value = 0x00, },
+ [75] = { .offset = 0x87, .value = 0x00, },
+ [76] = { .offset = 0x88, .value = 0x00, },
+ [77] = { .offset = 0x89, .value = 0x00, },
+ [78] = { .offset = 0x8a, .value = 0x00, },
+ [79] = { .offset = 0x8b, .value = 0x00, },
+ [80] = { .offset = 0x26, .value = 0x00, },
+ [81] = { .offset = 0x27, .value = 0x00, },
+ [82] = { .offset = 0xad, .value = 0x00, },
+ [83] = { .offset = 0x08, .value = 0x30, }, /* 0x31 */
+ [84] = { .offset = 0x41, .value = 0x00, },
+ [85] = { .offset = 0xc0, .value = 0x05, },
+ },
+ [MODE_800x600] = {
+ [0] = { .offset = 0x0a, .value = 0x81, },
+ [1] = { .offset = 0x18, .value = 0x07, },
+ [2] = { .offset = 0x19, .value = 0x00, },
+ [3] = { .offset = 0x1a, .value = 0x00, },
+ [4] = { .offset = 0x1b, .value = 0x19, },
+ [5] = { .offset = 0x1c, .value = 0x64, },
+ [6] = { .offset = 0x1d, .value = 0x02, },
+ [7] = { .offset = 0x1e, .value = 0x02, },
+ [8] = { .offset = 0xf3, .value = 0x90, },
+ [9] = { .offset = 0xf9, .value = 0x00, },
+ [10] = { .offset = 0xc1, .value = 0xd7, },
+ [11] = { .offset = 0xc2, .value = 0x00, },
+ [12] = { .offset = 0xc3, .value = 0xf8, },
+ [13] = { .offset = 0xc4, .value = 0x03, },
+ [14] = { .offset = 0xc5, .value = 0x1a, },
+ [15] = { .offset = 0xc6, .value = 0x00, },
+ [16] = { .offset = 0xc7, .value = 0x73, },
+ [17] = { .offset = 0xc8, .value = 0x02, },
+ [18] = { .offset = 0xf4, .value = 0x00, },
+ [19] = { .offset = 0x80, .value = 0x27, },
+ [20] = { .offset = 0x81, .value = 0x03, },
+ [21] = { .offset = 0x82, .value = 0x41, },
+ [22] = { .offset = 0x83, .value = 0x05, },
+ [23] = { .offset = 0x94, .value = 0x00, },
+ [24] = { .offset = 0x95, .value = 0x00, },
+ [25] = { .offset = 0x96, .value = 0x05, },
+ [26] = { .offset = 0x97, .value = 0x00, },
+ [27] = { .offset = 0x9a, .value = 0x88, },
+ [28] = { .offset = 0x9b, .value = 0x00, },
+ [29] = { .offset = 0x98, .value = 0x00, },
+ [30] = { .offset = 0x99, .value = 0x00, },
+ [31] = { .offset = 0xf7, .value = 0x88, },
+ [32] = { .offset = 0xf8, .value = 0x06, },
+ [33] = { .offset = 0x9c, .value = 0x23, },
+ [34] = { .offset = 0x9d, .value = 0x00, },
+ [35] = { .offset = 0x9e, .value = 0x25, },
+ [36] = { .offset = 0x9f, .value = 0x03, },
+ [37] = { .offset = 0xa0, .value = 0x28, },
+ [38] = { .offset = 0xa1, .value = 0x01, },
+ [39] = { .offset = 0xa2, .value = 0x28, },
+ [40] = { .offset = 0xa3, .value = 0x05, },
+ [41] = { .offset = 0xb6, .value = 0x09, },
+ [42] = { .offset = 0xb8, .value = 0x30, },
+ [43] = { .offset = 0xb9, .value = 0xc8, },
+ [44] = { .offset = 0xba, .value = 0x00, },
+ [45] = { .offset = 0xbb, .value = 0x20, },
+ [46] = { .offset = 0x10, .value = 0x20, },
+ [47] = { .offset = 0x11, .value = 0xc8, },
+ [48] = { .offset = 0x12, .value = 0x02, },
+ [49] = { .offset = 0x20, .value = 0x00, },
+ [50] = { .offset = 0x22, .value = 0x00, },
+ [51] = { .offset = 0x23, .value = 0x00, },
+ [52] = { .offset = 0x24, .value = 0x00, },
+ [53] = { .offset = 0x25, .value = 0x00, },
+ [54] = { .offset = 0x8c, .value = 0x10, },
+ [55] = { .offset = 0x8d, .value = 0x02, },
+ [56] = { .offset = 0x8e, .value = 0x04, },
+ [57] = { .offset = 0x8f, .value = 0x00, },
+ [58] = { .offset = 0x90, .value = 0xff, },
+ [59] = { .offset = 0x91, .value = 0x07, },
+ [60] = { .offset = 0x92, .value = 0xa0, },
+ [61] = { .offset = 0x93, .value = 0x02, },
+ [62] = { .offset = 0xa5, .value = 0x00, },
+ [63] = { .offset = 0xa6, .value = 0x00, },
+ [64] = { .offset = 0xa7, .value = 0x00, },
+ [65] = { .offset = 0xa8, .value = 0x00, },
+ [66] = { .offset = 0xa9, .value = 0x83, },
+ [67] = { .offset = 0xaa, .value = 0x40, },
+ [68] = { .offset = 0xab, .value = 0x32, },
+ [69] = { .offset = 0xac, .value = 0x00, },
+ [70] = { .offset = 0xa4, .value = 0x80, },
+ [71] = { .offset = 0x7e, .value = 0x18, },
+ [72] = { .offset = 0x84, .value = 0x00, },
+ [73] = { .offset = 0x85, .value = 0x00, },
+ [74] = { .offset = 0x86, .value = 0x00, },
+ [75] = { .offset = 0x87, .value = 0x00, },
+ [76] = { .offset = 0x88, .value = 0x00, },
+ [77] = { .offset = 0x89, .value = 0x00, },
+ [78] = { .offset = 0x8a, .value = 0x00, },
+ [79] = { .offset = 0x8b, .value = 0x00, },
+ [80] = { .offset = 0x26, .value = 0x00, },
+ [81] = { .offset = 0x27, .value = 0x00, },
+ [82] = { .offset = 0xad, .value = 0x00, },
+ [83] = { .offset = 0x08, .value = 0x30, }, /* 0x31 */
+ [84] = { .offset = 0x41, .value = 0x00, },
+ [85] = { .offset = 0xc0, .value = 0x07, },
+ },
+ [MODE_1024x768] = {
+ [0] = { .offset = 0x0a, .value = 0x81, },
+ [1] = { .offset = 0x18, .value = 0x07, },
+ [2] = { .offset = 0x19, .value = 0x00, },
+ [3] = { .offset = 0x1a, .value = 0x00, },
+ [4] = { .offset = 0x1b, .value = 0x11, },
+ [5] = { .offset = 0x1c, .value = 0x54, },
+ [6] = { .offset = 0x1d, .value = 0x03, },
+ [7] = { .offset = 0x1e, .value = 0x02, },
+ [8] = { .offset = 0xf3, .value = 0x90, },
+ [9] = { .offset = 0xf9, .value = 0x00, },
+ [10] = { .offset = 0xc1, .value = 0x90, },
+ [11] = { .offset = 0xc2, .value = 0x00, },
+ [12] = { .offset = 0xc3, .value = 0x0f, },
+ [13] = { .offset = 0xc4, .value = 0x03, },
+ [14] = { .offset = 0xc5, .value = 0x16, },
+ [15] = { .offset = 0xc6, .value = 0x00, },
+ [16] = { .offset = 0xc7, .value = 0x02, },
+ [17] = { .offset = 0xc8, .value = 0x02, },
+ [18] = { .offset = 0xf4, .value = 0x00, },
+ [19] = { .offset = 0x80, .value = 0xff, },
+ [20] = { .offset = 0x81, .value = 0x07, },
+ [21] = { .offset = 0x82, .value = 0x3d, },
+ [22] = { .offset = 0x83, .value = 0x05, },
+ [23] = { .offset = 0x94, .value = 0x00, },
+ [24] = { .offset = 0x95, .value = 0x00, },
+ [25] = { .offset = 0x96, .value = 0x05, },
+ [26] = { .offset = 0x97, .value = 0x00, },
+ [27] = { .offset = 0x9a, .value = 0x88, },
+ [28] = { .offset = 0x9b, .value = 0x00, },
+ [29] = { .offset = 0x98, .value = 0x00, },
+ [30] = { .offset = 0x99, .value = 0x00, },
+ [31] = { .offset = 0xf7, .value = 0x88, },
+ [32] = { .offset = 0xf8, .value = 0x0a, },
+ [33] = { .offset = 0x9c, .value = 0x24, },
+ [34] = { .offset = 0x9d, .value = 0x00, },
+ [35] = { .offset = 0x9e, .value = 0x25, },
+ [36] = { .offset = 0x9f, .value = 0x03, },
+ [37] = { .offset = 0xa0, .value = 0x28, },
+ [38] = { .offset = 0xa1, .value = 0x01, },
+ [39] = { .offset = 0xa2, .value = 0x28, },
+ [40] = { .offset = 0xa3, .value = 0x05, },
+ [41] = { .offset = 0xb6, .value = 0x09, },
+ [42] = { .offset = 0xb8, .value = 0x00, },
+ [43] = { .offset = 0xb9, .value = 0xa0, },
+ [44] = { .offset = 0xba, .value = 0x00, },
+ [45] = { .offset = 0xbb, .value = 0x20, },
+ [46] = { .offset = 0x10, .value = 0x00, },
+ [47] = { .offset = 0x11, .value = 0xa0, },
+ [48] = { .offset = 0x12, .value = 0x02, },
+ [49] = { .offset = 0x20, .value = 0x00, },
+ [50] = { .offset = 0x22, .value = 0x00, },
+ [51] = { .offset = 0x23, .value = 0x00, },
+ [52] = { .offset = 0x24, .value = 0x00, },
+ [53] = { .offset = 0x25, .value = 0x00, },
+ [54] = { .offset = 0x8c, .value = 0x10, },
+ [55] = { .offset = 0x8d, .value = 0x02, },
+ [56] = { .offset = 0x8e, .value = 0x10, },
+ [57] = { .offset = 0x8f, .value = 0x00, },
+ [58] = { .offset = 0x90, .value = 0xff, },
+ [59] = { .offset = 0x91, .value = 0x07, },
+ [60] = { .offset = 0x92, .value = 0xa0, },
+ [61] = { .offset = 0x93, .value = 0x02, },
+ [62] = { .offset = 0xa5, .value = 0x00, },
+ [63] = { .offset = 0xa6, .value = 0x00, },
+ [64] = { .offset = 0xa7, .value = 0x00, },
+ [65] = { .offset = 0xa8, .value = 0x00, },
+ [66] = { .offset = 0xa9, .value = 0x04, },
+ [67] = { .offset = 0xaa, .value = 0x70, },
+ [68] = { .offset = 0xab, .value = 0x4f, },
+ [69] = { .offset = 0xac, .value = 0x00, },
+ [70] = { .offset = 0xa4, .value = 0x84, },
+ [71] = { .offset = 0x7e, .value = 0x18, },
+ [72] = { .offset = 0x84, .value = 0x00, },
+ [73] = { .offset = 0x85, .value = 0x00, },
+ [74] = { .offset = 0x86, .value = 0x00, },
+ [75] = { .offset = 0x87, .value = 0x00, },
+ [76] = { .offset = 0x88, .value = 0x00, },
+ [77] = { .offset = 0x89, .value = 0x00, },
+ [78] = { .offset = 0x8a, .value = 0x00, },
+ [79] = { .offset = 0x8b, .value = 0x00, },
+ [80] = { .offset = 0x26, .value = 0x00, },
+ [81] = { .offset = 0x27, .value = 0x00, },
+ [82] = { .offset = 0xad, .value = 0x00, },
+ [83] = { .offset = 0x08, .value = 0x34, }, /* 0x35 */
+ [84] = { .offset = 0x41, .value = 0x00, },
+ [85] = { .offset = 0xc0, .value = 0x01, },
+ },
+};
+
+static const struct ns2501_reg regs_init[] = {
+ [0] = { .offset = 0x35, .value = 0xff, },
+ [1] = { .offset = 0x34, .value = 0x00, },
+ [2] = { .offset = 0x08, .value = 0x30, },
+};
+
struct ns2501_priv {
- //I2CDevRec d;
bool quiet;
- int reg_8_shadow;
- int reg_8_set;
- // Shadow registers for i915
- int dvoc;
- int pll_a;
- int srcdim;
- int fw_blc;
+ const struct ns2501_reg *regs;
};
#define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr))
@@ -205,11 +486,9 @@ static bool ns2501_init(struct intel_dvo_device *dvo,
goto out;
}
ns->quiet = false;
- ns->reg_8_set = 0;
- ns->reg_8_shadow =
- NS2501_8_PD | NS2501_8_BPAS | NS2501_8_VEN | NS2501_8_HEN;
DRM_DEBUG_KMS("init ns2501 dvo controller successfully!\n");
+
return true;
out:
@@ -242,9 +521,9 @@ static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo,
* of the panel in here so we could always accept it
* by disabling the scaler.
*/
- if ((mode->hdisplay == 800 && mode->vdisplay == 600) ||
- (mode->hdisplay == 640 && mode->vdisplay == 480) ||
- (mode->hdisplay == 1024 && mode->vdisplay == 768)) {
+ if ((mode->hdisplay == 640 && mode->vdisplay == 480 && mode->clock == 25175) ||
+ (mode->hdisplay == 800 && mode->vdisplay == 600 && mode->clock == 40000) ||
+ (mode->hdisplay == 1024 && mode->vdisplay == 768 && mode->clock == 65000)) {
return MODE_OK;
} else {
return MODE_ONE_SIZE; /* Is this a reasonable error? */
@@ -255,180 +534,30 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- bool ok;
- int retries = 10;
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
+ int mode_idx, i;
DRM_DEBUG_KMS
("set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n",
mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal);
- /*
- * Where do I find the native resolution for which scaling is not required???
- *
- * First trigger the DVO on as otherwise the chip does not appear on the i2c
- * bus.
- */
- do {
- ok = true;
-
- if (mode->hdisplay == 800 && mode->vdisplay == 600) {
- /* mode 277 */
- ns->reg_8_shadow &= ~NS2501_8_BPAS;
- DRM_DEBUG_KMS("switching to 800x600\n");
-
- /*
- * No, I do not know where this data comes from.
- * It is just what the video bios left in the DVO, so
- * I'm just copying it here over.
- * This also means that I cannot support any other modes
- * except the ones supported by the bios.
- */
- ok &= ns2501_writeb(dvo, 0x11, 0xc8); // 0xc7 also works.
- ok &= ns2501_writeb(dvo, 0x1b, 0x19);
- ok &= ns2501_writeb(dvo, 0x1c, 0x62); // VBIOS left 0x64 here, but 0x62 works nicer
- ok &= ns2501_writeb(dvo, 0x1d, 0x02);
-
- ok &= ns2501_writeb(dvo, 0x34, 0x03);
- ok &= ns2501_writeb(dvo, 0x35, 0xff);
+ if (mode->hdisplay == 640 && mode->vdisplay == 480)
+ mode_idx = MODE_640x480;
+ else if (mode->hdisplay == 800 && mode->vdisplay == 600)
+ mode_idx = MODE_800x600;
+ else if (mode->hdisplay == 1024 && mode->vdisplay == 768)
+ mode_idx = MODE_1024x768;
+ else
+ return;
- ok &= ns2501_writeb(dvo, 0x80, 0x27);
- ok &= ns2501_writeb(dvo, 0x81, 0x03);
- ok &= ns2501_writeb(dvo, 0x82, 0x41);
- ok &= ns2501_writeb(dvo, 0x83, 0x05);
+ /* Hopefully doing it every time won't hurt... */
+ for (i = 0; i < ARRAY_SIZE(regs_init); i++)
+ ns2501_writeb(dvo, regs_init[i].offset, regs_init[i].value);
- ok &= ns2501_writeb(dvo, 0x8d, 0x02);
- ok &= ns2501_writeb(dvo, 0x8e, 0x04);
- ok &= ns2501_writeb(dvo, 0x8f, 0x00);
+ ns->regs = regs_1024x768[mode_idx];
- ok &= ns2501_writeb(dvo, 0x90, 0xfe); /* vertical. VBIOS left 0xff here, but 0xfe works better */
- ok &= ns2501_writeb(dvo, 0x91, 0x07);
- ok &= ns2501_writeb(dvo, 0x94, 0x00);
- ok &= ns2501_writeb(dvo, 0x95, 0x00);
-
- ok &= ns2501_writeb(dvo, 0x96, 0x00);
-
- ok &= ns2501_writeb(dvo, 0x99, 0x00);
- ok &= ns2501_writeb(dvo, 0x9a, 0x88);
-
- ok &= ns2501_writeb(dvo, 0x9c, 0x23); /* Looks like first and last line of the image. */
- ok &= ns2501_writeb(dvo, 0x9d, 0x00);
- ok &= ns2501_writeb(dvo, 0x9e, 0x25);
- ok &= ns2501_writeb(dvo, 0x9f, 0x03);
-
- ok &= ns2501_writeb(dvo, 0xa4, 0x80);
-
- ok &= ns2501_writeb(dvo, 0xb6, 0x00);
-
- ok &= ns2501_writeb(dvo, 0xb9, 0xc8); /* horizontal? */
- ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */
-
- ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */
- ok &= ns2501_writeb(dvo, 0xc1, 0xd7);
-
- ok &= ns2501_writeb(dvo, 0xc2, 0x00);
- ok &= ns2501_writeb(dvo, 0xc3, 0xf8);
-
- ok &= ns2501_writeb(dvo, 0xc4, 0x03);
- ok &= ns2501_writeb(dvo, 0xc5, 0x1a);
-
- ok &= ns2501_writeb(dvo, 0xc6, 0x00);
- ok &= ns2501_writeb(dvo, 0xc7, 0x73);
- ok &= ns2501_writeb(dvo, 0xc8, 0x02);
-
- } else if (mode->hdisplay == 640 && mode->vdisplay == 480) {
- /* mode 274 */
- DRM_DEBUG_KMS("switching to 640x480\n");
- /*
- * No, I do not know where this data comes from.
- * It is just what the video bios left in the DVO, so
- * I'm just copying it here over.
- * This also means that I cannot support any other modes
- * except the ones supported by the bios.
- */
- ns->reg_8_shadow &= ~NS2501_8_BPAS;
-
- ok &= ns2501_writeb(dvo, 0x11, 0xa0);
- ok &= ns2501_writeb(dvo, 0x1b, 0x11);
- ok &= ns2501_writeb(dvo, 0x1c, 0x54);
- ok &= ns2501_writeb(dvo, 0x1d, 0x03);
-
- ok &= ns2501_writeb(dvo, 0x34, 0x03);
- ok &= ns2501_writeb(dvo, 0x35, 0xff);
-
- ok &= ns2501_writeb(dvo, 0x80, 0xff);
- ok &= ns2501_writeb(dvo, 0x81, 0x07);
- ok &= ns2501_writeb(dvo, 0x82, 0x3d);
- ok &= ns2501_writeb(dvo, 0x83, 0x05);
-
- ok &= ns2501_writeb(dvo, 0x8d, 0x02);
- ok &= ns2501_writeb(dvo, 0x8e, 0x10);
- ok &= ns2501_writeb(dvo, 0x8f, 0x00);
-
- ok &= ns2501_writeb(dvo, 0x90, 0xff); /* vertical */
- ok &= ns2501_writeb(dvo, 0x91, 0x07);
- ok &= ns2501_writeb(dvo, 0x94, 0x00);
- ok &= ns2501_writeb(dvo, 0x95, 0x00);
-
- ok &= ns2501_writeb(dvo, 0x96, 0x05);
-
- ok &= ns2501_writeb(dvo, 0x99, 0x00);
- ok &= ns2501_writeb(dvo, 0x9a, 0x88);
-
- ok &= ns2501_writeb(dvo, 0x9c, 0x24);
- ok &= ns2501_writeb(dvo, 0x9d, 0x00);
- ok &= ns2501_writeb(dvo, 0x9e, 0x25);
- ok &= ns2501_writeb(dvo, 0x9f, 0x03);
-
- ok &= ns2501_writeb(dvo, 0xa4, 0x84);
-
- ok &= ns2501_writeb(dvo, 0xb6, 0x09);
-
- ok &= ns2501_writeb(dvo, 0xb9, 0xa0); /* horizontal? */
- ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */
-
- ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */
- ok &= ns2501_writeb(dvo, 0xc1, 0x90);
-
- ok &= ns2501_writeb(dvo, 0xc2, 0x00);
- ok &= ns2501_writeb(dvo, 0xc3, 0x0f);
-
- ok &= ns2501_writeb(dvo, 0xc4, 0x03);
- ok &= ns2501_writeb(dvo, 0xc5, 0x16);
-
- ok &= ns2501_writeb(dvo, 0xc6, 0x00);
- ok &= ns2501_writeb(dvo, 0xc7, 0x02);
- ok &= ns2501_writeb(dvo, 0xc8, 0x02);
-
- } else if (mode->hdisplay == 1024 && mode->vdisplay == 768) {
- /* mode 280 */
- DRM_DEBUG_KMS("switching to 1024x768\n");
- /*
- * This might or might not work, actually. I'm silently
- * assuming here that the native panel resolution is
- * 1024x768. If not, then this leaves the scaler disabled
- * generating a picture that is likely not the expected.
- *
- * Problem is that I do not know where to take the panel
- * dimensions from.
- *
- * Enable the bypass, scaling not required.
- *
- * The scaler registers are irrelevant here....
- *
- */
- ns->reg_8_shadow |= NS2501_8_BPAS;
- ok &= ns2501_writeb(dvo, 0x37, 0x44);
- } else {
- /*
- * Data not known. Bummer!
- * Hopefully, the code should not go here
- * as mode_OK delivered no other modes.
- */
- ns->reg_8_shadow |= NS2501_8_BPAS;
- }
- ok &= ns2501_writeb(dvo, NS2501_REG8, ns->reg_8_shadow);
- } while (!ok && retries--);
+ for (i = 0; i < 84; i++)
+ ns2501_writeb(dvo, ns->regs[i].offset, ns->regs[i].value);
}
/* set the NS2501 power state */
@@ -439,60 +568,46 @@ static bool ns2501_get_hw_state(struct intel_dvo_device *dvo)
if (!ns2501_readb(dvo, NS2501_REG8, &ch))
return false;
- if (ch & NS2501_8_PD)
- return true;
- else
- return false;
+ return ch & NS2501_8_PD;
}
/* set the NS2501 power state */
static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable)
{
- bool ok;
- int retries = 10;
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
- unsigned char ch;
DRM_DEBUG_KMS("Trying set the dpms of the DVO to %i\n", enable);
- ch = ns->reg_8_shadow;
+ if (enable) {
+ if (WARN_ON(ns->regs[83].offset != 0x08 ||
+ ns->regs[84].offset != 0x41 ||
+ ns->regs[85].offset != 0xc0))
+ return;
- if (enable)
- ch |= NS2501_8_PD;
- else
- ch &= ~NS2501_8_PD;
-
- if (ns->reg_8_set == 0 || ns->reg_8_shadow != ch) {
- ns->reg_8_set = 1;
- ns->reg_8_shadow = ch;
-
- do {
- ok = true;
- ok &= ns2501_writeb(dvo, NS2501_REG8, ch);
- ok &=
- ns2501_writeb(dvo, 0x34,
- enable ? 0x03 : 0x00);
- ok &=
- ns2501_writeb(dvo, 0x35,
- enable ? 0xff : 0x00);
- } while (!ok && retries--);
- }
-}
+ ns2501_writeb(dvo, 0xc0, ns->regs[85].value | 0x08);
-static void ns2501_dump_regs(struct intel_dvo_device *dvo)
-{
- uint8_t val;
-
- ns2501_readb(dvo, NS2501_FREQ_LO, &val);
- DRM_DEBUG_KMS("NS2501_FREQ_LO: 0x%02x\n", val);
- ns2501_readb(dvo, NS2501_FREQ_HI, &val);
- DRM_DEBUG_KMS("NS2501_FREQ_HI: 0x%02x\n", val);
- ns2501_readb(dvo, NS2501_REG8, &val);
- DRM_DEBUG_KMS("NS2501_REG8: 0x%02x\n", val);
- ns2501_readb(dvo, NS2501_REG9, &val);
- DRM_DEBUG_KMS("NS2501_REG9: 0x%02x\n", val);
- ns2501_readb(dvo, NS2501_REGC, &val);
- DRM_DEBUG_KMS("NS2501_REGC: 0x%02x\n", val);
+ ns2501_writeb(dvo, 0x41, ns->regs[84].value);
+
+ ns2501_writeb(dvo, 0x34, 0x01);
+ msleep(15);
+
+ ns2501_writeb(dvo, 0x08, 0x35);
+ if (!(ns->regs[83].value & NS2501_8_BPAS))
+ ns2501_writeb(dvo, 0x08, 0x31);
+ msleep(200);
+
+ ns2501_writeb(dvo, 0x34, 0x03);
+
+ ns2501_writeb(dvo, 0xc0, ns->regs[85].value);
+ } else {
+ ns2501_writeb(dvo, 0x34, 0x01);
+ msleep(200);
+
+ ns2501_writeb(dvo, 0x08, 0x34);
+ msleep(15);
+
+ ns2501_writeb(dvo, 0x34, 0x00);
+ }
}
static void ns2501_destroy(struct intel_dvo_device *dvo)
@@ -512,6 +627,5 @@ struct intel_dvo_dev_ops ns2501_ops = {
.mode_set = ns2501_mode_set,
.dpms = ns2501_dpms,
.get_hw_state = ns2501_get_hw_state,
- .dump_regs = ns2501_dump_regs,
.destroy = ns2501_destroy,
};
diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c
index 9d7954366bd2..593b657d3e59 100644
--- a/drivers/gpu/drm/i915/i915_cmd_parser.c
+++ b/drivers/gpu/drm/i915/i915_cmd_parser.c
@@ -426,6 +426,9 @@ static const u32 gen7_render_regs[] = {
GEN7_SO_WRITE_OFFSET(1),
GEN7_SO_WRITE_OFFSET(2),
GEN7_SO_WRITE_OFFSET(3),
+ GEN7_L3SQCREG1,
+ GEN7_L3CNTLREG2,
+ GEN7_L3CNTLREG3,
};
static const u32 gen7_blt_regs[] = {
@@ -706,11 +709,13 @@ int i915_cmd_parser_init_ring(struct intel_engine_cs *ring)
BUG_ON(!validate_cmds_sorted(ring, cmd_tables, cmd_table_count));
BUG_ON(!validate_regs_sorted(ring));
- ret = init_hash_table(ring, cmd_tables, cmd_table_count);
- if (ret) {
- DRM_ERROR("CMD: cmd_parser_init failed!\n");
- fini_hash_table(ring);
- return ret;
+ if (hash_empty(ring->cmd_hash)) {
+ ret = init_hash_table(ring, cmd_tables, cmd_table_count);
+ if (ret) {
+ DRM_ERROR("CMD: cmd_parser_init failed!\n");
+ fini_hash_table(ring);
+ return ret;
+ }
}
ring->needs_cmd_parser = true;
@@ -839,8 +844,6 @@ finish:
*/
bool i915_needs_cmd_parser(struct intel_engine_cs *ring)
{
- struct drm_i915_private *dev_priv = ring->dev->dev_private;
-
if (!ring->needs_cmd_parser)
return false;
@@ -849,7 +852,7 @@ bool i915_needs_cmd_parser(struct intel_engine_cs *ring)
* disabled. That will cause all of the parser's PPGTT checks to
* fail. For now, disable parsing when PPGTT is off.
*/
- if (!dev_priv->mm.aliasing_ppgtt)
+ if (USES_PPGTT(ring->dev))
return false;
return (i915.enable_cmd_parser == 1);
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index b8c689202c40..063b44817e08 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -136,7 +136,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
obj->last_read_seqno,
obj->last_write_seqno,
obj->last_fenced_seqno,
- i915_cache_level_str(obj->cache_level),
+ i915_cache_level_str(to_i915(obj->base.dev), obj->cache_level),
obj->dirty ? " dirty" : "",
obj->madv == I915_MADV_DONTNEED ? " purgeable" : "");
if (obj->base.name)
@@ -170,11 +170,13 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
}
if (obj->ring != NULL)
seq_printf(m, " (%s)", obj->ring->name);
+ if (obj->frontbuffer_bits)
+ seq_printf(m, " (frontbuffer: 0x%03x)", obj->frontbuffer_bits);
}
static void describe_ctx(struct seq_file *m, struct intel_context *ctx)
{
- seq_putc(m, ctx->is_initialized ? 'I' : 'i');
+ seq_putc(m, ctx->legacy_hw_ctx.initialized ? 'I' : 'i');
seq_putc(m, ctx->remap_slice ? 'R' : 'r');
seq_putc(m, ' ');
}
@@ -331,7 +333,7 @@ static int per_file_stats(int id, void *ptr, void *data)
}
ppgtt = container_of(vma->vm, struct i915_hw_ppgtt, base);
- if (ppgtt->ctx && ppgtt->ctx->file_priv != stats->file_priv)
+ if (ppgtt->file_priv != stats->file_priv)
continue;
if (obj->ring) /* XXX per-vma statistic */
@@ -513,8 +515,14 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long flags;
struct intel_crtc *crtc;
+ int ret;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
for_each_intel_crtc(dev, crtc) {
const char pipe = pipe_name(crtc->pipe);
@@ -527,6 +535,8 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data)
seq_printf(m, "No flip due on pipe %c (plane %c)\n",
pipe, plane);
} else {
+ u32 addr;
+
if (atomic_read(&work->pending) < INTEL_FLIP_COMPLETE) {
seq_printf(m, "Flip queued on pipe %c (plane %c)\n",
pipe, plane);
@@ -534,28 +544,42 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data)
seq_printf(m, "Flip pending (waiting for vsync) on pipe %c (plane %c)\n",
pipe, plane);
}
+ if (work->flip_queued_ring) {
+ seq_printf(m, "Flip queued on %s at seqno %u, next seqno %u [current breadcrumb %u], completed? %d\n",
+ work->flip_queued_ring->name,
+ work->flip_queued_seqno,
+ dev_priv->next_seqno,
+ work->flip_queued_ring->get_seqno(work->flip_queued_ring, true),
+ i915_seqno_passed(work->flip_queued_ring->get_seqno(work->flip_queued_ring, true),
+ work->flip_queued_seqno));
+ } else
+ seq_printf(m, "Flip not associated with any ring\n");
+ seq_printf(m, "Flip queued on frame %d, (was ready on frame %d), now %d\n",
+ work->flip_queued_vblank,
+ work->flip_ready_vblank,
+ drm_vblank_count(dev, crtc->pipe));
if (work->enable_stall_check)
seq_puts(m, "Stall check enabled, ");
else
seq_puts(m, "Stall check waiting for page flip ioctl, ");
seq_printf(m, "%d prepares\n", atomic_read(&work->pending));
- if (work->old_fb_obj) {
- struct drm_i915_gem_object *obj = work->old_fb_obj;
- if (obj)
- seq_printf(m, "Old framebuffer gtt_offset 0x%08lx\n",
- i915_gem_obj_ggtt_offset(obj));
- }
+ if (INTEL_INFO(dev)->gen >= 4)
+ addr = I915_HI_DISPBASE(I915_READ(DSPSURF(crtc->plane)));
+ else
+ addr = I915_READ(DSPADDR(crtc->plane));
+ seq_printf(m, "Current scanout address 0x%08x\n", addr);
+
if (work->pending_flip_obj) {
- struct drm_i915_gem_object *obj = work->pending_flip_obj;
- if (obj)
- seq_printf(m, "New framebuffer gtt_offset 0x%08lx\n",
- i915_gem_obj_ggtt_offset(obj));
+ seq_printf(m, "New framebuffer address 0x%08lx\n", (long)work->gtt_offset);
+ seq_printf(m, "MMIO update completed? %d\n", addr == work->gtt_offset);
}
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
+ mutex_unlock(&dev->struct_mutex);
+
return 0;
}
@@ -641,7 +665,6 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
intel_runtime_pm_get(dev_priv);
if (IS_CHERRYVIEW(dev)) {
- int i;
seq_printf(m, "Master Interrupt Control:\t%08x\n",
I915_READ(GEN8_MASTER_IRQ));
@@ -653,7 +676,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
I915_READ(VLV_IIR_RW));
seq_printf(m, "Display IMR:\t%08x\n",
I915_READ(VLV_IMR));
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
seq_printf(m, "Pipe %c stat:\t%08x\n",
pipe_name(pipe),
I915_READ(PIPESTAT(pipe)));
@@ -693,7 +716,13 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
i, I915_READ(GEN8_GT_IER(i)));
}
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
+ if (!intel_display_power_enabled(dev_priv,
+ POWER_DOMAIN_PIPE(pipe))) {
+ seq_printf(m, "Pipe %c power disabled\n",
+ pipe_name(pipe));
+ continue;
+ }
seq_printf(m, "Pipe %c IMR:\t%08x\n",
pipe_name(pipe),
I915_READ(GEN8_DE_PIPE_IMR(pipe)));
@@ -734,7 +763,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
I915_READ(VLV_IIR_RW));
seq_printf(m, "Display IMR:\t%08x\n",
I915_READ(VLV_IMR));
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
seq_printf(m, "Pipe %c stat:\t%08x\n",
pipe_name(pipe),
I915_READ(PIPESTAT(pipe)));
@@ -770,7 +799,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
I915_READ(IIR));
seq_printf(m, "Interrupt mask: %08x\n",
I915_READ(IMR));
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
seq_printf(m, "Pipe %c stat: %08x\n",
pipe_name(pipe),
I915_READ(PIPESTAT(pipe)));
@@ -918,7 +947,7 @@ static ssize_t i915_error_state_read(struct file *file, char __user *userbuf,
ssize_t ret_count = 0;
int ret;
- ret = i915_error_state_buf_init(&error_str, count, *pos);
+ ret = i915_error_state_buf_init(&error_str, to_i915(error_priv->dev), count, *pos);
if (ret)
return ret;
@@ -985,29 +1014,6 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_next_seqno_fops,
i915_next_seqno_get, i915_next_seqno_set,
"0x%llx\n");
-static int i915_rstdby_delays(struct seq_file *m, void *unused)
-{
- struct drm_info_node *node = m->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- u16 crstanddelay;
- int ret;
-
- ret = mutex_lock_interruptible(&dev->struct_mutex);
- if (ret)
- return ret;
- intel_runtime_pm_get(dev_priv);
-
- crstanddelay = I915_READ16(CRSTANDVID);
-
- intel_runtime_pm_put(dev_priv);
- mutex_unlock(&dev->struct_mutex);
-
- seq_printf(m, "w/ctx: %d, w/o ctx: %d\n", (crstanddelay >> 8) & 0x3f, (crstanddelay & 0x3f));
-
- return 0;
-}
-
static int i915_frequency_info(struct seq_file *m, void *unused)
{
struct drm_info_node *node = m->private;
@@ -1029,7 +1035,8 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
MEMSTAT_VID_SHIFT);
seq_printf(m, "Current P-state: %d\n",
(rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
- } else if ((IS_GEN6(dev) || IS_GEN7(dev)) && !IS_VALLEYVIEW(dev)) {
+ } else if (IS_GEN6(dev) || (IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) ||
+ IS_BROADWELL(dev)) {
u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS);
u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
@@ -1037,6 +1044,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
u32 rpstat, cagf, reqf;
u32 rpupei, rpcurup, rpprevup;
u32 rpdownei, rpcurdown, rpprevdown;
+ u32 pm_ier, pm_imr, pm_isr, pm_iir, pm_mask;
int max_freq;
/* RPSTAT1 is in the GT power well */
@@ -1048,7 +1056,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
reqf = I915_READ(GEN6_RPNSWREQ);
reqf &= ~GEN6_TURBO_DISABLE;
- if (IS_HASWELL(dev))
+ if (IS_HASWELL(dev) || IS_BROADWELL(dev))
reqf >>= 24;
else
reqf >>= 25;
@@ -1065,7 +1073,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
rpdownei = I915_READ(GEN6_RP_CUR_DOWN_EI);
rpcurdown = I915_READ(GEN6_RP_CUR_DOWN);
rpprevdown = I915_READ(GEN6_RP_PREV_DOWN);
- if (IS_HASWELL(dev))
+ if (IS_HASWELL(dev) || IS_BROADWELL(dev))
cagf = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT;
else
cagf = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT;
@@ -1074,12 +1082,21 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL);
mutex_unlock(&dev->struct_mutex);
+ if (IS_GEN6(dev) || IS_GEN7(dev)) {
+ pm_ier = I915_READ(GEN6_PMIER);
+ pm_imr = I915_READ(GEN6_PMIMR);
+ pm_isr = I915_READ(GEN6_PMISR);
+ pm_iir = I915_READ(GEN6_PMIIR);
+ pm_mask = I915_READ(GEN6_PMINTRMSK);
+ } else {
+ pm_ier = I915_READ(GEN8_GT_IER(2));
+ pm_imr = I915_READ(GEN8_GT_IMR(2));
+ pm_isr = I915_READ(GEN8_GT_ISR(2));
+ pm_iir = I915_READ(GEN8_GT_IIR(2));
+ pm_mask = I915_READ(GEN6_PMINTRMSK);
+ }
seq_printf(m, "PM IER=0x%08x IMR=0x%08x ISR=0x%08x IIR=0x%08x, MASK=0x%08x\n",
- I915_READ(GEN6_PMIER),
- I915_READ(GEN6_PMIMR),
- I915_READ(GEN6_PMISR),
- I915_READ(GEN6_PMIIR),
- I915_READ(GEN6_PMINTRMSK));
+ pm_ier, pm_imr, pm_isr, pm_iir, pm_mask);
seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status);
seq_printf(m, "Render p-state ratio: %d\n",
(gt_perf_status & 0xff00) >> 8);
@@ -1121,20 +1138,21 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
seq_printf(m, "Max overclocked frequency: %dMHz\n",
dev_priv->rps.max_freq * GT_FREQUENCY_MULTIPLIER);
} else if (IS_VALLEYVIEW(dev)) {
- u32 freq_sts, val;
+ u32 freq_sts;
mutex_lock(&dev_priv->rps.hw_lock);
freq_sts = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
seq_printf(m, "PUNIT_REG_GPU_FREQ_STS: 0x%08x\n", freq_sts);
seq_printf(m, "DDR freq: %d MHz\n", dev_priv->mem_freq);
- val = valleyview_rps_max_freq(dev_priv);
seq_printf(m, "max GPU freq: %d MHz\n",
- vlv_gpu_freq(dev_priv, val));
+ vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq));
- val = valleyview_rps_min_freq(dev_priv);
seq_printf(m, "min GPU freq: %d MHz\n",
- vlv_gpu_freq(dev_priv, val));
+ vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq));
+
+ seq_printf(m, "efficient (RPe) frequency: %d MHz\n",
+ vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq));
seq_printf(m, "current GPU freq: %d MHz\n",
vlv_gpu_freq(dev_priv, (freq_sts >> 8) & 0xff));
@@ -1148,61 +1166,6 @@ out:
return ret;
}
-static int i915_delayfreq_table(struct seq_file *m, void *unused)
-{
- struct drm_info_node *node = m->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- u32 delayfreq;
- int ret, i;
-
- ret = mutex_lock_interruptible(&dev->struct_mutex);
- if (ret)
- return ret;
- intel_runtime_pm_get(dev_priv);
-
- for (i = 0; i < 16; i++) {
- delayfreq = I915_READ(PXVFREQ_BASE + i * 4);
- seq_printf(m, "P%02dVIDFREQ: 0x%08x (VID: %d)\n", i, delayfreq,
- (delayfreq & PXVFREQ_PX_MASK) >> PXVFREQ_PX_SHIFT);
- }
-
- intel_runtime_pm_put(dev_priv);
-
- mutex_unlock(&dev->struct_mutex);
-
- return 0;
-}
-
-static inline int MAP_TO_MV(int map)
-{
- return 1250 - (map * 25);
-}
-
-static int i915_inttoext_table(struct seq_file *m, void *unused)
-{
- struct drm_info_node *node = m->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- u32 inttoext;
- int ret, i;
-
- ret = mutex_lock_interruptible(&dev->struct_mutex);
- if (ret)
- return ret;
- intel_runtime_pm_get(dev_priv);
-
- for (i = 1; i <= 32; i++) {
- inttoext = I915_READ(INTTOEXT_BASE_ILK + i * 4);
- seq_printf(m, "INTTOEXT%02d: 0x%08x\n", i, inttoext);
- }
-
- intel_runtime_pm_put(dev_priv);
- mutex_unlock(&dev->struct_mutex);
-
- return 0;
-}
-
static int ironlake_drpc_info(struct seq_file *m)
{
struct drm_info_node *node = m->private;
@@ -1432,7 +1395,7 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
if (IS_VALLEYVIEW(dev))
return vlv_drpc_info(m);
- else if (IS_GEN6(dev) || IS_GEN7(dev))
+ else if (INTEL_INFO(dev)->gen >= 6)
return gen6_drpc_info(m);
else
return ironlake_drpc_info(m);
@@ -1500,6 +1463,47 @@ static int i915_fbc_status(struct seq_file *m, void *unused)
return 0;
}
+static int i915_fbc_fc_get(void *data, u64 *val)
+{
+ struct drm_device *dev = data;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (INTEL_INFO(dev)->gen < 7 || !HAS_FBC(dev))
+ return -ENODEV;
+
+ drm_modeset_lock_all(dev);
+ *val = dev_priv->fbc.false_color;
+ drm_modeset_unlock_all(dev);
+
+ return 0;
+}
+
+static int i915_fbc_fc_set(void *data, u64 val)
+{
+ struct drm_device *dev = data;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 reg;
+
+ if (INTEL_INFO(dev)->gen < 7 || !HAS_FBC(dev))
+ return -ENODEV;
+
+ drm_modeset_lock_all(dev);
+
+ reg = I915_READ(ILK_DPFC_CONTROL);
+ dev_priv->fbc.false_color = val;
+
+ I915_WRITE(ILK_DPFC_CONTROL, val ?
+ (reg | FBC_CTL_FALSE_COLOR) :
+ (reg & ~FBC_CTL_FALSE_COLOR));
+
+ drm_modeset_unlock_all(dev);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(i915_fbc_fc_fops,
+ i915_fbc_fc_get, i915_fbc_fc_set,
+ "%llu\n");
+
static int i915_ips_status(struct seq_file *m, void *unused)
{
struct drm_info_node *node = m->private;
@@ -1513,10 +1517,17 @@ static int i915_ips_status(struct seq_file *m, void *unused)
intel_runtime_pm_get(dev_priv);
- if (IS_BROADWELL(dev) || I915_READ(IPS_CTL) & IPS_ENABLE)
- seq_puts(m, "enabled\n");
- else
- seq_puts(m, "disabled\n");
+ seq_printf(m, "Enabled by kernel parameter: %s\n",
+ yesno(i915.enable_ips));
+
+ if (INTEL_INFO(dev)->gen >= 8) {
+ seq_puts(m, "Currently: unknown\n");
+ } else {
+ if (I915_READ(IPS_CTL) & IPS_ENABLE)
+ seq_puts(m, "Currently: enabled\n");
+ else
+ seq_puts(m, "Currently: disabled\n");
+ }
intel_runtime_pm_put(dev_priv);
@@ -1620,26 +1631,6 @@ out:
return ret;
}
-static int i915_gfxec(struct seq_file *m, void *unused)
-{
- struct drm_info_node *node = m->private;
- struct drm_device *dev = node->minor->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- int ret;
-
- ret = mutex_lock_interruptible(&dev->struct_mutex);
- if (ret)
- return ret;
- intel_runtime_pm_get(dev_priv);
-
- seq_printf(m, "GFXEC: %ld\n", (unsigned long)I915_READ(0x112f4));
- intel_runtime_pm_put(dev_priv);
-
- mutex_unlock(&dev->struct_mutex);
-
- return 0;
-}
-
static int i915_opregion(struct seq_file *m, void *unused)
{
struct drm_info_node *node = m->private;
@@ -1677,9 +1668,6 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
#ifdef CONFIG_DRM_I915_FBDEV
struct drm_i915_private *dev_priv = dev->dev_private;
- int ret = mutex_lock_interruptible(&dev->mode_config.mutex);
- if (ret)
- return ret;
ifbdev = dev_priv->fbdev;
fb = to_intel_framebuffer(ifbdev->helper.fb);
@@ -1692,7 +1680,6 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
atomic_read(&fb->base.refcount.refcount));
describe_obj(m, fb->obj);
seq_putc(m, '\n');
- mutex_unlock(&dev->mode_config.mutex);
#endif
mutex_lock(&dev->mode_config.fb_lock);
@@ -1714,6 +1701,14 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
return 0;
}
+static void describe_ctx_ringbuf(struct seq_file *m,
+ struct intel_ringbuffer *ringbuf)
+{
+ seq_printf(m, " (ringbuffer, space: %d, head: %u, tail: %u, last head: %d)",
+ ringbuf->space, ringbuf->head, ringbuf->tail,
+ ringbuf->last_retired_head);
+}
+
static int i915_context_status(struct seq_file *m, void *unused)
{
struct drm_info_node *node = m->private;
@@ -1723,7 +1718,7 @@ static int i915_context_status(struct seq_file *m, void *unused)
struct intel_context *ctx;
int ret, i;
- ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
if (ret)
return ret;
@@ -1740,20 +1735,172 @@ static int i915_context_status(struct seq_file *m, void *unused)
}
list_for_each_entry(ctx, &dev_priv->context_list, link) {
- if (ctx->obj == NULL)
+ if (!i915.enable_execlists &&
+ ctx->legacy_hw_ctx.rcs_state == NULL)
continue;
seq_puts(m, "HW context ");
describe_ctx(m, ctx);
- for_each_ring(ring, dev_priv, i)
+ for_each_ring(ring, dev_priv, i) {
if (ring->default_context == ctx)
- seq_printf(m, "(default context %s) ", ring->name);
+ seq_printf(m, "(default context %s) ",
+ ring->name);
+ }
+
+ if (i915.enable_execlists) {
+ seq_putc(m, '\n');
+ for_each_ring(ring, dev_priv, i) {
+ struct drm_i915_gem_object *ctx_obj =
+ ctx->engine[i].state;
+ struct intel_ringbuffer *ringbuf =
+ ctx->engine[i].ringbuf;
+
+ seq_printf(m, "%s: ", ring->name);
+ if (ctx_obj)
+ describe_obj(m, ctx_obj);
+ if (ringbuf)
+ describe_ctx_ringbuf(m, ringbuf);
+ seq_putc(m, '\n');
+ }
+ } else {
+ describe_obj(m, ctx->legacy_hw_ctx.rcs_state);
+ }
+
+ seq_putc(m, '\n');
+ }
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+static int i915_dump_lrc(struct seq_file *m, void *unused)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring;
+ struct intel_context *ctx;
+ int ret, i;
+
+ if (!i915.enable_execlists) {
+ seq_printf(m, "Logical Ring Contexts are disabled\n");
+ return 0;
+ }
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(ctx, &dev_priv->context_list, link) {
+ for_each_ring(ring, dev_priv, i) {
+ struct drm_i915_gem_object *ctx_obj = ctx->engine[i].state;
+
+ if (ring->default_context == ctx)
+ continue;
+
+ if (ctx_obj) {
+ struct page *page = i915_gem_object_get_page(ctx_obj, 1);
+ uint32_t *reg_state = kmap_atomic(page);
+ int j;
+
+ seq_printf(m, "CONTEXT: %s %u\n", ring->name,
+ intel_execlists_ctx_id(ctx_obj));
+
+ for (j = 0; j < 0x600 / sizeof(u32) / 4; j += 4) {
+ seq_printf(m, "\t[0x%08lx] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ i915_gem_obj_ggtt_offset(ctx_obj) + 4096 + (j * 4),
+ reg_state[j], reg_state[j + 1],
+ reg_state[j + 2], reg_state[j + 3]);
+ }
+ kunmap_atomic(reg_state);
+
+ seq_putc(m, '\n');
+ }
+ }
+ }
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+static int i915_execlists(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *)m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring;
+ u32 status_pointer;
+ u8 read_pointer;
+ u8 write_pointer;
+ u32 status;
+ u32 ctx_id;
+ struct list_head *cursor;
+ int ring_id, i;
+ int ret;
+
+ if (!i915.enable_execlists) {
+ seq_puts(m, "Logical Ring Contexts are disabled\n");
+ return 0;
+ }
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ for_each_ring(ring, dev_priv, ring_id) {
+ struct intel_ctx_submit_request *head_req = NULL;
+ int count = 0;
+ unsigned long flags;
+
+ seq_printf(m, "%s\n", ring->name);
+
+ status = I915_READ(RING_EXECLIST_STATUS(ring));
+ ctx_id = I915_READ(RING_EXECLIST_STATUS(ring) + 4);
+ seq_printf(m, "\tExeclist status: 0x%08X, context: %u\n",
+ status, ctx_id);
+
+ status_pointer = I915_READ(RING_CONTEXT_STATUS_PTR(ring));
+ seq_printf(m, "\tStatus pointer: 0x%08X\n", status_pointer);
+
+ read_pointer = ring->next_context_status_buffer;
+ write_pointer = status_pointer & 0x07;
+ if (read_pointer > write_pointer)
+ write_pointer += 6;
+ seq_printf(m, "\tRead pointer: 0x%08X, write pointer 0x%08X\n",
+ read_pointer, write_pointer);
+
+ for (i = 0; i < 6; i++) {
+ status = I915_READ(RING_CONTEXT_STATUS_BUF(ring) + 8*i);
+ ctx_id = I915_READ(RING_CONTEXT_STATUS_BUF(ring) + 8*i + 4);
+
+ seq_printf(m, "\tStatus buffer %d: 0x%08X, context: %u\n",
+ i, status, ctx_id);
+ }
+
+ spin_lock_irqsave(&ring->execlist_lock, flags);
+ list_for_each(cursor, &ring->execlist_queue)
+ count++;
+ head_req = list_first_entry_or_null(&ring->execlist_queue,
+ struct intel_ctx_submit_request, execlist_link);
+ spin_unlock_irqrestore(&ring->execlist_lock, flags);
+
+ seq_printf(m, "\t%d requests in queue\n", count);
+ if (head_req) {
+ struct drm_i915_gem_object *ctx_obj;
+
+ ctx_obj = head_req->ctx->engine[ring_id].state;
+ seq_printf(m, "\tHead request id: %u\n",
+ intel_execlists_ctx_id(ctx_obj));
+ seq_printf(m, "\tHead request tail: %u\n",
+ head_req->tail);
+ }
- describe_obj(m, ctx->obj);
seq_putc(m, '\n');
}
- mutex_unlock(&dev->mode_config.mutex);
+ mutex_unlock(&dev->struct_mutex);
return 0;
}
@@ -1858,12 +2005,18 @@ static int per_file_ctx(int id, void *ptr, void *data)
{
struct intel_context *ctx = ptr;
struct seq_file *m = data;
- struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(ctx);
+ struct i915_hw_ppgtt *ppgtt = ctx->ppgtt;
+
+ if (!ppgtt) {
+ seq_printf(m, " no ppgtt for context %d\n",
+ ctx->user_handle);
+ return 0;
+ }
if (i915_gem_context_is_default(ctx))
seq_puts(m, " default context:\n");
else
- seq_printf(m, " context %d:\n", ctx->id);
+ seq_printf(m, " context %d:\n", ctx->user_handle);
ppgtt->debug_dump(ppgtt, m);
return 0;
@@ -1918,8 +2071,7 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
seq_printf(m, "pd gtt offset: 0x%08x\n", ppgtt->pd_offset);
ppgtt->debug_dump(ppgtt, m);
- } else
- return;
+ }
list_for_each_entry_reverse(file, &dev->filelist, lhead) {
struct drm_i915_file_private *file_priv = file->driver_priv;
@@ -1976,17 +2128,25 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
intel_runtime_pm_get(dev_priv);
+ mutex_lock(&dev_priv->psr.lock);
seq_printf(m, "Sink_Support: %s\n", yesno(dev_priv->psr.sink_support));
seq_printf(m, "Source_OK: %s\n", yesno(dev_priv->psr.source_ok));
+ seq_printf(m, "Enabled: %s\n", yesno((bool)dev_priv->psr.enabled));
+ seq_printf(m, "Active: %s\n", yesno(dev_priv->psr.active));
+ seq_printf(m, "Busy frontbuffer bits: 0x%03x\n",
+ dev_priv->psr.busy_frontbuffer_bits);
+ seq_printf(m, "Re-enable work scheduled: %s\n",
+ yesno(work_busy(&dev_priv->psr.work.work)));
enabled = HAS_PSR(dev) &&
I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
- seq_printf(m, "Enabled: %s\n", yesno(enabled));
+ seq_printf(m, "HW Enabled & Active bit: %s\n", yesno(enabled));
if (HAS_PSR(dev))
psrperf = I915_READ(EDP_PSR_PERF_CNT(dev)) &
EDP_PSR_PERF_CNT_MASK;
seq_printf(m, "Performance_Counter: %u\n", psrperf);
+ mutex_unlock(&dev_priv->psr.lock);
intel_runtime_pm_put(dev_priv);
return 0;
@@ -2072,7 +2232,7 @@ static int i915_pc8_status(struct seq_file *m, void *unused)
seq_printf(m, "GPU idle: %s\n", yesno(!dev_priv->mm.busy));
seq_printf(m, "IRQs disabled: %s\n",
- yesno(dev_priv->pm.irqs_disabled));
+ yesno(!intel_irqs_enabled(dev_priv)));
return 0;
}
@@ -2126,6 +2286,8 @@ static const char *power_domain_str(enum intel_display_power_domain domain)
return "VGA";
case POWER_DOMAIN_AUDIO:
return "AUDIO";
+ case POWER_DOMAIN_PLLS:
+ return "PLLS";
case POWER_DOMAIN_INIT:
return "INIT";
default:
@@ -2223,9 +2385,12 @@ static void intel_crtc_info(struct seq_file *m, struct intel_crtc *intel_crtc)
struct drm_crtc *crtc = &intel_crtc->base;
struct intel_encoder *intel_encoder;
- seq_printf(m, "\tfb: %d, pos: %dx%d, size: %dx%d\n",
- crtc->primary->fb->base.id, crtc->x, crtc->y,
- crtc->primary->fb->width, crtc->primary->fb->height);
+ if (crtc->primary->fb)
+ seq_printf(m, "\tfb: %d, pos: %dx%d, size: %dx%d\n",
+ crtc->primary->fb->base.id, crtc->x, crtc->y,
+ crtc->primary->fb->width, crtc->primary->fb->height);
+ else
+ seq_puts(m, "\tprimary plane disabled\n");
for_each_encoder_on_crtc(dev, crtc, intel_encoder)
intel_encoder_info(m, intel_crtc, intel_encoder);
}
@@ -2287,13 +2452,15 @@ static void intel_connector_info(struct seq_file *m,
seq_printf(m, "\tCEA rev: %d\n",
connector->display_info.cea_rev);
}
- if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT ||
- intel_encoder->type == INTEL_OUTPUT_EDP)
- intel_dp_info(m, intel_connector);
- else if (intel_encoder->type == INTEL_OUTPUT_HDMI)
- intel_hdmi_info(m, intel_connector);
- else if (intel_encoder->type == INTEL_OUTPUT_LVDS)
- intel_lvds_info(m, intel_connector);
+ if (intel_encoder) {
+ if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT ||
+ intel_encoder->type == INTEL_OUTPUT_EDP)
+ intel_dp_info(m, intel_connector);
+ else if (intel_encoder->type == INTEL_OUTPUT_HDMI)
+ intel_hdmi_info(m, intel_connector);
+ else if (intel_encoder->type == INTEL_OUTPUT_LVDS)
+ intel_lvds_info(m, intel_connector);
+ }
seq_printf(m, "\tmodes:\n");
list_for_each_entry(mode, &connector->modes, head)
@@ -2347,17 +2514,17 @@ static int i915_display_info(struct seq_file *m, void *unused)
bool active;
int x, y;
- seq_printf(m, "CRTC %d: pipe: %c, active: %s\n",
+ seq_printf(m, "CRTC %d: pipe: %c, active=%s (size=%dx%d)\n",
crtc->base.base.id, pipe_name(crtc->pipe),
- yesno(crtc->active));
+ yesno(crtc->active), crtc->config.pipe_src_w, crtc->config.pipe_src_h);
if (crtc->active) {
intel_crtc_info(m, crtc);
active = cursor_position(dev, crtc->pipe, &x, &y);
- seq_printf(m, "\tcursor visible? %s, position (%d, %d), addr 0x%08x, active? %s\n",
+ seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x, active? %s\n",
yesno(crtc->cursor_base),
- x, y, crtc->cursor_addr,
- yesno(active));
+ x, y, crtc->cursor_width, crtc->cursor_height,
+ crtc->cursor_addr, yesno(active));
}
seq_printf(m, "\tunderrun reporting: cpu=%s pch=%s \n",
@@ -2377,12 +2544,166 @@ static int i915_display_info(struct seq_file *m, void *unused)
return 0;
}
+static int i915_semaphore_status(struct seq_file *m, void *unused)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring;
+ int num_rings = hweight32(INTEL_INFO(dev)->ring_mask);
+ int i, j, ret;
+
+ if (!i915_semaphore_is_enabled(dev)) {
+ seq_puts(m, "Semaphores are disabled\n");
+ return 0;
+ }
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+ intel_runtime_pm_get(dev_priv);
+
+ if (IS_BROADWELL(dev)) {
+ struct page *page;
+ uint64_t *seqno;
+
+ page = i915_gem_object_get_page(dev_priv->semaphore_obj, 0);
+
+ seqno = (uint64_t *)kmap_atomic(page);
+ for_each_ring(ring, dev_priv, i) {
+ uint64_t offset;
+
+ seq_printf(m, "%s\n", ring->name);
+
+ seq_puts(m, " Last signal:");
+ for (j = 0; j < num_rings; j++) {
+ offset = i * I915_NUM_RINGS + j;
+ seq_printf(m, "0x%08llx (0x%02llx) ",
+ seqno[offset], offset * 8);
+ }
+ seq_putc(m, '\n');
+
+ seq_puts(m, " Last wait: ");
+ for (j = 0; j < num_rings; j++) {
+ offset = i + (j * I915_NUM_RINGS);
+ seq_printf(m, "0x%08llx (0x%02llx) ",
+ seqno[offset], offset * 8);
+ }
+ seq_putc(m, '\n');
+
+ }
+ kunmap_atomic(seqno);
+ } else {
+ seq_puts(m, " Last signal:");
+ for_each_ring(ring, dev_priv, i)
+ for (j = 0; j < num_rings; j++)
+ seq_printf(m, "0x%08x\n",
+ I915_READ(ring->semaphore.mbox.signal[j]));
+ seq_putc(m, '\n');
+ }
+
+ seq_puts(m, "\nSync seqno:\n");
+ for_each_ring(ring, dev_priv, i) {
+ for (j = 0; j < num_rings; j++) {
+ seq_printf(m, " 0x%08x ", ring->semaphore.sync_seqno[j]);
+ }
+ seq_putc(m, '\n');
+ }
+ seq_putc(m, '\n');
+
+ intel_runtime_pm_put(dev_priv);
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+}
+
+static int i915_shared_dplls_info(struct seq_file *m, void *unused)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int i;
+
+ drm_modeset_lock_all(dev);
+ for (i = 0; i < dev_priv->num_shared_dpll; i++) {
+ struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i];
+
+ seq_printf(m, "DPLL%i: %s, id: %i\n", i, pll->name, pll->id);
+ seq_printf(m, " refcount: %i, active: %i, on: %s\n", pll->refcount,
+ pll->active, yesno(pll->on));
+ seq_printf(m, " tracked hardware state:\n");
+ seq_printf(m, " dpll: 0x%08x\n", pll->hw_state.dpll);
+ seq_printf(m, " dpll_md: 0x%08x\n", pll->hw_state.dpll_md);
+ seq_printf(m, " fp0: 0x%08x\n", pll->hw_state.fp0);
+ seq_printf(m, " fp1: 0x%08x\n", pll->hw_state.fp1);
+ seq_printf(m, " wrpll: 0x%08x\n", pll->hw_state.wrpll);
+ }
+ drm_modeset_unlock_all(dev);
+
+ return 0;
+}
+
+static int i915_wa_registers(struct seq_file *m, void *unused)
+{
+ int i;
+ int ret;
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+
+ intel_runtime_pm_get(dev_priv);
+
+ seq_printf(m, "Workarounds applied: %d\n", dev_priv->num_wa_regs);
+ for (i = 0; i < dev_priv->num_wa_regs; ++i) {
+ u32 addr, mask;
+
+ addr = dev_priv->intel_wa_regs[i].addr;
+ mask = dev_priv->intel_wa_regs[i].mask;
+ dev_priv->intel_wa_regs[i].value = I915_READ(addr) | mask;
+ if (dev_priv->intel_wa_regs[i].addr)
+ seq_printf(m, "0x%X: 0x%08X, mask: 0x%08X\n",
+ dev_priv->intel_wa_regs[i].addr,
+ dev_priv->intel_wa_regs[i].value,
+ dev_priv->intel_wa_regs[i].mask);
+ }
+
+ intel_runtime_pm_put(dev_priv);
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
struct pipe_crc_info {
const char *name;
struct drm_device *dev;
enum pipe pipe;
};
+static int i915_dp_mst_info(struct seq_file *m, void *unused)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_encoder *encoder;
+ struct intel_encoder *intel_encoder;
+ struct intel_digital_port *intel_dig_port;
+ drm_modeset_lock_all(dev);
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ intel_encoder = to_intel_encoder(encoder);
+ if (intel_encoder->type != INTEL_OUTPUT_DISPLAYPORT)
+ continue;
+ intel_dig_port = enc_to_dig_port(encoder);
+ if (!intel_dig_port->dp.can_mst)
+ continue;
+
+ drm_dp_mst_dump_topology(m, &intel_dig_port->dp.mst_mgr);
+ }
+ drm_modeset_unlock_all(dev);
+ return 0;
+}
+
static int i915_pipe_crc_open(struct inode *inode, struct file *filep)
{
struct pipe_crc_info *info = inode->i_private;
@@ -2616,8 +2937,7 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
*source = INTEL_PIPE_CRC_SOURCE_PIPE;
drm_modeset_lock_all(dev);
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
if (!encoder->base.crtc)
continue;
@@ -2849,7 +3169,60 @@ static int ilk_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
return 0;
}
-static int ivb_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
+static void hsw_trans_edp_pipe_A_crc_wa(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *crtc =
+ to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_A]);
+
+ drm_modeset_lock_all(dev);
+ /*
+ * If we use the eDP transcoder we need to make sure that we don't
+ * bypass the pfit, since otherwise the pipe CRC source won't work. Only
+ * relevant on hsw with pipe A when using the always-on power well
+ * routing.
+ */
+ if (crtc->config.cpu_transcoder == TRANSCODER_EDP &&
+ !crtc->config.pch_pfit.enabled) {
+ crtc->config.pch_pfit.force_thru = true;
+
+ intel_display_power_get(dev_priv,
+ POWER_DOMAIN_PIPE_PANEL_FITTER(PIPE_A));
+
+ dev_priv->display.crtc_disable(&crtc->base);
+ dev_priv->display.crtc_enable(&crtc->base);
+ }
+ drm_modeset_unlock_all(dev);
+}
+
+static void hsw_undo_trans_edp_pipe_A_crc_wa(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *crtc =
+ to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_A]);
+
+ drm_modeset_lock_all(dev);
+ /*
+ * If we use the eDP transcoder we need to make sure that we don't
+ * bypass the pfit, since otherwise the pipe CRC source won't work. Only
+ * relevant on hsw with pipe A when using the always-on power well
+ * routing.
+ */
+ if (crtc->config.pch_pfit.force_thru) {
+ crtc->config.pch_pfit.force_thru = false;
+
+ dev_priv->display.crtc_disable(&crtc->base);
+ dev_priv->display.crtc_enable(&crtc->base);
+
+ intel_display_power_put(dev_priv,
+ POWER_DOMAIN_PIPE_PANEL_FITTER(PIPE_A));
+ }
+ drm_modeset_unlock_all(dev);
+}
+
+static int ivb_pipe_crc_ctl_reg(struct drm_device *dev,
+ enum pipe pipe,
+ enum intel_pipe_crc_source *source,
uint32_t *val)
{
if (*source == INTEL_PIPE_CRC_SOURCE_AUTO)
@@ -2863,6 +3236,9 @@ static int ivb_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
*val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_IVB;
break;
case INTEL_PIPE_CRC_SOURCE_PF:
+ if (IS_HASWELL(dev) && pipe == PIPE_A)
+ hsw_trans_edp_pipe_A_crc_wa(dev);
+
*val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PF_IVB;
break;
case INTEL_PIPE_CRC_SOURCE_NONE:
@@ -2895,11 +3271,11 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
else if (INTEL_INFO(dev)->gen < 5)
ret = i9xx_pipe_crc_ctl_reg(dev, pipe, &source, &val);
else if (IS_VALLEYVIEW(dev))
- ret = vlv_pipe_crc_ctl_reg(dev,pipe, &source, &val);
+ ret = vlv_pipe_crc_ctl_reg(dev, pipe, &source, &val);
else if (IS_GEN5(dev) || IS_GEN6(dev))
ret = ilk_pipe_crc_ctl_reg(&source, &val);
else
- ret = ivb_pipe_crc_ctl_reg(&source, &val);
+ ret = ivb_pipe_crc_ctl_reg(dev, pipe, &source, &val);
if (ret != 0)
return ret;
@@ -2929,11 +3305,16 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
/* real source -> none transition */
if (source == INTEL_PIPE_CRC_SOURCE_NONE) {
struct intel_pipe_crc_entry *entries;
+ struct intel_crtc *crtc =
+ to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
DRM_DEBUG_DRIVER("stopping CRCs for pipe %c\n",
pipe_name(pipe));
- intel_wait_for_vblank(dev, pipe);
+ drm_modeset_lock(&crtc->base.mutex, NULL);
+ if (crtc->active)
+ intel_wait_for_vblank(dev, pipe);
+ drm_modeset_unlock(&crtc->base.mutex);
spin_lock_irq(&pipe_crc->lock);
entries = pipe_crc->entries;
@@ -2946,6 +3327,8 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
g4x_undo_pipe_scramble_reset(dev, pipe);
else if (IS_VALLEYVIEW(dev))
vlv_undo_pipe_scramble_reset(dev, pipe);
+ else if (IS_HASWELL(dev) && pipe == PIPE_A)
+ hsw_undo_trans_edp_pipe_A_crc_wa(dev);
}
return 0;
@@ -3177,7 +3560,7 @@ static int pri_wm_latency_open(struct inode *inode, struct file *file)
{
struct drm_device *dev = inode->i_private;
- if (!HAS_PCH_SPLIT(dev))
+ if (HAS_GMCH_DISPLAY(dev))
return -ENODEV;
return single_open(file, pri_wm_latency_show, dev);
@@ -3187,7 +3570,7 @@ static int spr_wm_latency_open(struct inode *inode, struct file *file)
{
struct drm_device *dev = inode->i_private;
- if (!HAS_PCH_SPLIT(dev))
+ if (HAS_GMCH_DISPLAY(dev))
return -ENODEV;
return single_open(file, spr_wm_latency_show, dev);
@@ -3197,7 +3580,7 @@ static int cur_wm_latency_open(struct inode *inode, struct file *file)
{
struct drm_device *dev = inode->i_private;
- if (!HAS_PCH_SPLIT(dev))
+ if (HAS_GMCH_DISPLAY(dev))
return -ENODEV;
return single_open(file, cur_wm_latency_show, dev);
@@ -3443,9 +3826,6 @@ i915_drop_caches_set(void *data, u64 val)
{
struct drm_device *dev = data;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_i915_gem_object *obj, *next;
- struct i915_address_space *vm;
- struct i915_vma *vma, *x;
int ret;
DRM_DEBUG("Dropping caches: 0x%08llx\n", val);
@@ -3465,29 +3845,11 @@ i915_drop_caches_set(void *data, u64 val)
if (val & (DROP_RETIRE | DROP_ACTIVE))
i915_gem_retire_requests(dev);
- if (val & DROP_BOUND) {
- list_for_each_entry(vm, &dev_priv->vm_list, global_link) {
- list_for_each_entry_safe(vma, x, &vm->inactive_list,
- mm_list) {
- if (vma->pin_count)
- continue;
+ if (val & DROP_BOUND)
+ i915_gem_shrink(dev_priv, LONG_MAX, I915_SHRINK_BOUND);
- ret = i915_vma_unbind(vma);
- if (ret)
- goto unlock;
- }
- }
- }
-
- if (val & DROP_UNBOUND) {
- list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list,
- global_list)
- if (obj->pages_pin_count == 0) {
- ret = i915_gem_object_put_pages(obj);
- if (ret)
- goto unlock;
- }
- }
+ if (val & DROP_UNBOUND)
+ i915_gem_shrink(dev_priv, LONG_MAX, I915_SHRINK_UNBOUND);
unlock:
mutex_unlock(&dev->struct_mutex);
@@ -3506,7 +3868,7 @@ i915_max_freq_get(void *data, u64 *val)
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
- if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+ if (INTEL_INFO(dev)->gen < 6)
return -ENODEV;
flush_delayed_work(&dev_priv->rps.delayed_resume_work);
@@ -3532,7 +3894,7 @@ i915_max_freq_set(void *data, u64 val)
u32 rp_state_cap, hw_max, hw_min;
int ret;
- if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+ if (INTEL_INFO(dev)->gen < 6)
return -ENODEV;
flush_delayed_work(&dev_priv->rps.delayed_resume_work);
@@ -3549,8 +3911,8 @@ i915_max_freq_set(void *data, u64 val)
if (IS_VALLEYVIEW(dev)) {
val = vlv_freq_opcode(dev_priv, val);
- hw_max = valleyview_rps_max_freq(dev_priv);
- hw_min = valleyview_rps_min_freq(dev_priv);
+ hw_max = dev_priv->rps.max_freq;
+ hw_min = dev_priv->rps.min_freq;
} else {
do_div(val, GT_FREQUENCY_MULTIPLIER);
@@ -3587,7 +3949,7 @@ i915_min_freq_get(void *data, u64 *val)
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
- if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+ if (INTEL_INFO(dev)->gen < 6)
return -ENODEV;
flush_delayed_work(&dev_priv->rps.delayed_resume_work);
@@ -3613,7 +3975,7 @@ i915_min_freq_set(void *data, u64 val)
u32 rp_state_cap, hw_max, hw_min;
int ret;
- if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+ if (INTEL_INFO(dev)->gen < 6)
return -ENODEV;
flush_delayed_work(&dev_priv->rps.delayed_resume_work);
@@ -3630,8 +3992,8 @@ i915_min_freq_set(void *data, u64 val)
if (IS_VALLEYVIEW(dev)) {
val = vlv_freq_opcode(dev_priv, val);
- hw_max = valleyview_rps_max_freq(dev_priv);
- hw_min = valleyview_rps_min_freq(dev_priv);
+ hw_max = dev_priv->rps.max_freq;
+ hw_min = dev_priv->rps.min_freq;
} else {
do_div(val, GT_FREQUENCY_MULTIPLIER);
@@ -3799,20 +4161,18 @@ static const struct drm_info_list i915_debugfs_list[] = {
{"i915_gem_hws_blt", i915_hws_info, 0, (void *)BCS},
{"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS},
{"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS},
- {"i915_rstdby_delays", i915_rstdby_delays, 0},
{"i915_frequency_info", i915_frequency_info, 0},
- {"i915_delayfreq_table", i915_delayfreq_table, 0},
- {"i915_inttoext_table", i915_inttoext_table, 0},
{"i915_drpc_info", i915_drpc_info, 0},
{"i915_emon_status", i915_emon_status, 0},
{"i915_ring_freq_table", i915_ring_freq_table, 0},
- {"i915_gfxec", i915_gfxec, 0},
{"i915_fbc_status", i915_fbc_status, 0},
{"i915_ips_status", i915_ips_status, 0},
{"i915_sr_status", i915_sr_status, 0},
{"i915_opregion", i915_opregion, 0},
{"i915_gem_framebuffer", i915_gem_framebuffer_info, 0},
{"i915_context_status", i915_context_status, 0},
+ {"i915_dump_lrc", i915_dump_lrc, 0},
+ {"i915_execlists", i915_execlists, 0},
{"i915_gen6_forcewake_count", i915_gen6_forcewake_count_info, 0},
{"i915_swizzle_info", i915_swizzle_info, 0},
{"i915_ppgtt_info", i915_ppgtt_info, 0},
@@ -3823,6 +4183,10 @@ static const struct drm_info_list i915_debugfs_list[] = {
{"i915_pc8_status", i915_pc8_status, 0},
{"i915_power_domain_info", i915_power_domain_info, 0},
{"i915_display_info", i915_display_info, 0},
+ {"i915_semaphore_status", i915_semaphore_status, 0},
+ {"i915_shared_dplls_info", i915_shared_dplls_info, 0},
+ {"i915_dp_mst_info", i915_dp_mst_info, 0},
+ {"i915_wa_registers", i915_wa_registers, 0},
};
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
@@ -3844,6 +4208,7 @@ static const struct i915_debugfs_files {
{"i915_pri_wm_latency", &i915_pri_wm_latency_fops},
{"i915_spr_wm_latency", &i915_spr_wm_latency_fops},
{"i915_cur_wm_latency", &i915_cur_wm_latency_fops},
+ {"i915_fbc_false_color", &i915_fbc_fc_fops},
};
void intel_display_crc_init(struct drm_device *dev)
@@ -3851,7 +4216,7 @@ void intel_display_crc_init(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
enum pipe pipe;
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
pipe_crc->opened = false;
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index d44344140627..1403b01e8216 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -28,9 +28,11 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/async.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
+#include <drm/drm_legacy.h>
#include "intel_drv.h"
#include <drm/i915_drm.h>
#include "i915_drv.h"
@@ -138,7 +140,7 @@ static void i915_free_hws(struct drm_device *dev)
I915_WRITE(HWS_PGA, 0x1ffff000);
}
-void i915_kernel_lost_context(struct drm_device * dev)
+void i915_kernel_lost_context(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv;
@@ -166,7 +168,7 @@ void i915_kernel_lost_context(struct drm_device * dev)
master_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
}
-static int i915_dma_cleanup(struct drm_device * dev)
+static int i915_dma_cleanup(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int i;
@@ -190,13 +192,13 @@ static int i915_dma_cleanup(struct drm_device * dev)
return 0;
}
-static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
+static int i915_initialize(struct drm_device *dev, drm_i915_init_t *init)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
int ret;
- master_priv->sarea = drm_getsarea(dev);
+ master_priv->sarea = drm_legacy_getsarea(dev);
if (master_priv->sarea) {
master_priv->sarea_priv = (drm_i915_sarea_t *)
((u8 *)master_priv->sarea->handle + init->sarea_priv_offset);
@@ -235,7 +237,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
return 0;
}
-static int i915_dma_resume(struct drm_device * dev)
+static int i915_dma_resume(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_engine_cs *ring = LP_RING(dev_priv);
@@ -359,7 +361,7 @@ static int validate_cmd(int cmd)
return 0;
}
-static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords)
+static int i915_emit_cmds(struct drm_device *dev, int *buffer, int dwords)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int i, ret;
@@ -369,6 +371,7 @@ static int i915_emit_cmds(struct drm_device * dev, int *buffer, int dwords)
for (i = 0; i < dwords;) {
int sz = validate_cmd(buffer[i]);
+
if (sz == 0 || i + sz > dwords)
return -EINVAL;
i += sz;
@@ -453,7 +456,7 @@ static void i915_emit_breadcrumb(struct drm_device *dev)
}
}
-static int i915_dispatch_cmdbuffer(struct drm_device * dev,
+static int i915_dispatch_cmdbuffer(struct drm_device *dev,
drm_i915_cmdbuffer_t *cmd,
struct drm_clip_rect *cliprects,
void *cmdbuf)
@@ -487,8 +490,8 @@ static int i915_dispatch_cmdbuffer(struct drm_device * dev,
return 0;
}
-static int i915_dispatch_batchbuffer(struct drm_device * dev,
- drm_i915_batchbuffer_t * batch,
+static int i915_dispatch_batchbuffer(struct drm_device *dev,
+ drm_i915_batchbuffer_t *batch,
struct drm_clip_rect *cliprects)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -549,7 +552,7 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,
return 0;
}
-static int i915_dispatch_flip(struct drm_device * dev)
+static int i915_dispatch_flip(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv =
@@ -755,7 +758,7 @@ fail_batch_free:
return ret;
}
-static int i915_emit_irq(struct drm_device * dev)
+static int i915_emit_irq(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
@@ -781,7 +784,7 @@ static int i915_emit_irq(struct drm_device * dev)
return dev_priv->dri1.counter;
}
-static int i915_wait_irq(struct drm_device * dev, int irq_nr)
+static int i915_wait_irq(struct drm_device *dev, int irq_nr)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
@@ -998,7 +1001,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
value = HAS_WT(dev);
break;
case I915_PARAM_HAS_ALIASING_PPGTT:
- value = dev_priv->mm.aliasing_ppgtt || USES_FULL_PPGTT(dev);
+ value = USES_PPGTT(dev);
break;
case I915_PARAM_HAS_WAIT_TIMEOUT:
value = 1;
@@ -1266,6 +1269,7 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_
{
struct drm_device *dev = pci_get_drvdata(pdev);
pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
+
if (state == VGA_SWITCHEROO_ON) {
pr_info("switched on\n");
dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
@@ -1334,6 +1338,13 @@ static int i915_load_modeset_init(struct drm_device *dev)
intel_power_domains_init_hw(dev_priv);
+ /*
+ * We enable some interrupt sources in our postinstall hooks, so mark
+ * interrupts as enabled _before_ actually enabling them to avoid
+ * special cases in our ordering checks.
+ */
+ dev_priv->pm._irqs_disabled = false;
+
ret = drm_irq_install(dev, dev->pdev->irq);
if (ret)
goto cleanup_gem_stolen;
@@ -1346,8 +1357,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
if (ret)
goto cleanup_irq;
- INIT_WORK(&dev_priv->console_resume_work, intel_console_resume);
-
intel_modeset_gem_init(dev);
/* Always safe in the mode setting case. */
@@ -1373,10 +1382,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
* scanning against hotplug events. Hence do this first and ignore the
* tiny window where we will loose hotplug notifactions.
*/
- intel_fbdev_initial_config(dev);
-
- /* Only enable hotplug handling once the fbdev is fully set up. */
- dev_priv->enable_hotplug_processing = true;
+ async_schedule(intel_fbdev_initial_config, dev_priv);
drm_kms_helper_poll_init(dev);
@@ -1387,7 +1393,6 @@ cleanup_gem:
i915_gem_cleanup_ringbuffer(dev);
i915_gem_context_fini(dev);
mutex_unlock(&dev->struct_mutex);
- WARN_ON(dev_priv->mm.aliasing_ppgtt);
cleanup_irq:
drm_irq_uninstall(dev);
cleanup_gem_stolen:
@@ -1425,15 +1430,16 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master)
}
#if IS_ENABLED(CONFIG_FB)
-static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
+static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
{
struct apertures_struct *ap;
struct pci_dev *pdev = dev_priv->dev->pdev;
bool primary;
+ int ret;
ap = alloc_apertures(1);
if (!ap)
- return;
+ return -ENOMEM;
ap->ranges[0].base = dev_priv->gtt.mappable_base;
ap->ranges[0].size = dev_priv->gtt.mappable_end;
@@ -1441,13 +1447,16 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
primary =
pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
- remove_conflicting_framebuffers(ap, "inteldrmfb", primary);
+ ret = remove_conflicting_framebuffers(ap, "inteldrmfb", primary);
kfree(ap);
+
+ return ret;
}
#else
-static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
+static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
{
+ return 0;
}
#endif
@@ -1492,10 +1501,11 @@ static void i915_dump_device_info(struct drm_i915_private *dev_priv)
#define SEP_EMPTY
#define PRINT_FLAG(name) info->name ? #name "," : ""
#define SEP_COMMA ,
- DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x flags="
+ DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x rev=0x%02x flags="
DEV_INFO_FOR_EACH_FLAG(PRINT_S, SEP_EMPTY),
info->gen,
dev_priv->dev->pdev->device,
+ dev_priv->dev->pdev->revision,
DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_COMMA));
#undef PRINT_S
#undef SEP_EMPTY
@@ -1525,10 +1535,10 @@ static void intel_device_info_runtime_init(struct drm_device *dev)
info = (struct intel_device_info *)&dev_priv->info;
if (IS_VALLEYVIEW(dev))
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
info->num_sprites[pipe] = 2;
else
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
info->num_sprites[pipe] = 1;
if (i915.disable_display) {
@@ -1594,18 +1604,20 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (dev_priv == NULL)
return -ENOMEM;
- dev->dev_private = (void *)dev_priv;
+ dev->dev_private = dev_priv;
dev_priv->dev = dev;
- /* copy initial configuration to dev_priv->info */
+ /* Setup the write-once "constant" device info */
device_info = (struct intel_device_info *)&dev_priv->info;
- *device_info = *info;
+ memcpy(device_info, info, sizeof(dev_priv->info));
+ device_info->device_id = dev->pdev->device;
spin_lock_init(&dev_priv->irq_lock);
spin_lock_init(&dev_priv->gpu_error.lock);
spin_lock_init(&dev_priv->backlight_lock);
spin_lock_init(&dev_priv->uncore.lock);
spin_lock_init(&dev_priv->mm.object_stat_lock);
+ spin_lock_init(&dev_priv->mmio_flip_lock);
mutex_init(&dev_priv->dpio_lock);
mutex_init(&dev_priv->modeset_restore_lock);
@@ -1664,7 +1676,11 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
goto out_gtt;
}
- i915_kick_out_firmware_fb(dev_priv);
+ ret = i915_kick_out_firmware_fb(dev_priv);
+ if (ret) {
+ DRM_ERROR("failed to remove conflicting framebuffer drivers\n");
+ goto out_gtt;
+ }
}
pci_set_master(dev->pdev);
@@ -1717,6 +1733,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
goto out_mtrrfree;
}
+ dev_priv->dp_wq = alloc_ordered_workqueue("i915-dp", 0);
+ if (dev_priv->dp_wq == NULL) {
+ DRM_ERROR("Failed to create our dp workqueue.\n");
+ ret = -ENOMEM;
+ goto out_freewq;
+ }
+
intel_irq_init(dev);
intel_uncore_sanitize(dev);
@@ -1792,12 +1815,14 @@ out_gem_unload:
intel_teardown_gmbus(dev);
intel_teardown_mchbar(dev);
pm_qos_remove_request(&dev_priv->pm_qos);
+ destroy_workqueue(dev_priv->dp_wq);
+out_freewq:
destroy_workqueue(dev_priv->wq);
out_mtrrfree:
arch_phys_wc_del(dev_priv->gtt.mtrr);
io_mapping_free(dev_priv->gtt.mappable);
out_gtt:
- dev_priv->gtt.base.cleanup(&dev_priv->gtt.base);
+ i915_global_gtt_cleanup(dev);
out_regs:
intel_uncore_fini(dev);
pci_iounmap(dev->pdev, dev_priv->regs);
@@ -1844,7 +1869,6 @@ int i915_driver_unload(struct drm_device *dev)
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
intel_fbdev_fini(dev);
intel_modeset_cleanup(dev);
- cancel_work_sync(&dev_priv->console_resume_work);
/*
* free the memory space allocated for the child device
@@ -1877,7 +1901,6 @@ int i915_driver_unload(struct drm_device *dev)
mutex_lock(&dev->struct_mutex);
i915_gem_cleanup_ringbuffer(dev);
i915_gem_context_fini(dev);
- WARN_ON(dev_priv->mm.aliasing_ppgtt);
mutex_unlock(&dev->struct_mutex);
i915_gem_cleanup_stolen(dev);
@@ -1885,17 +1908,16 @@ int i915_driver_unload(struct drm_device *dev)
i915_free_hws(dev);
}
- WARN_ON(!list_empty(&dev_priv->vm_list));
-
drm_vblank_cleanup(dev);
intel_teardown_gmbus(dev);
intel_teardown_mchbar(dev);
+ destroy_workqueue(dev_priv->dp_wq);
destroy_workqueue(dev_priv->wq);
pm_qos_remove_request(&dev_priv->pm_qos);
- dev_priv->gtt.base.cleanup(&dev_priv->gtt.base);
+ i915_global_gtt_cleanup(dev);
intel_uncore_fini(dev);
if (dev_priv->regs != NULL)
@@ -1933,7 +1955,7 @@ int i915_driver_open(struct drm_device *dev, struct drm_file *file)
* and DMA structures, since the kernel won't be using them, and clea
* up any GEM state.
*/
-void i915_driver_lastclose(struct drm_device * dev)
+void i915_driver_lastclose(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1954,12 +1976,15 @@ void i915_driver_lastclose(struct drm_device * dev)
i915_dma_cleanup(dev);
}
-void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv)
+void i915_driver_preclose(struct drm_device *dev, struct drm_file *file)
{
mutex_lock(&dev->struct_mutex);
- i915_gem_context_close(dev, file_priv);
- i915_gem_release(dev, file_priv);
+ i915_gem_context_close(dev, file);
+ i915_gem_release(dev, file);
mutex_unlock(&dev->struct_mutex);
+
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ intel_modeset_preclose(dev, file);
}
void i915_driver_postclose(struct drm_device *dev, struct drm_file *file)
@@ -2031,7 +2056,7 @@ int i915_max_ioctl = ARRAY_SIZE(i915_ioctls);
* manage the gtt, we need to claim that all intel devices are agp. For
* otherwise the drm core refuses to initialize the agp support code.
*/
-int i915_driver_device_is_agp(struct drm_device * dev)
+int i915_driver_device_is_agp(struct drm_device *dev)
{
return 1;
}
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 651e65e051c0..2318b4c7a8f8 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -28,6 +28,7 @@
*/
#include <linux/device.h>
+#include <linux/acpi.h>
#include <drm/drmP.h>
#include <drm/i915_drm.h>
#include "i915_drv.h"
@@ -46,8 +47,6 @@ static struct drm_driver driver;
PIPE_C_OFFSET, PIPE_EDP_OFFSET }, \
.trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \
TRANSCODER_C_OFFSET, TRANSCODER_EDP_OFFSET }, \
- .dpll_offsets = { DPLL_A_OFFSET, DPLL_B_OFFSET }, \
- .dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET }, \
.palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET }
#define GEN_CHV_PIPEOFFSETS \
@@ -55,10 +54,6 @@ static struct drm_driver driver;
CHV_PIPE_C_OFFSET }, \
.trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \
CHV_TRANSCODER_C_OFFSET, }, \
- .dpll_offsets = { DPLL_A_OFFSET, DPLL_B_OFFSET, \
- CHV_DPLL_C_OFFSET }, \
- .dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET, \
- CHV_DPLL_C_MD_OFFSET }, \
.palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET, \
CHV_PALETTE_C_OFFSET }
@@ -308,6 +303,7 @@ static const struct intel_device_info intel_broadwell_d_info = {
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
.has_llc = 1,
.has_ddi = 1,
+ .has_fpga_dbg = 1,
.has_fbc = 1,
GEN_DEFAULT_PIPEOFFSETS,
IVB_CURSOR_OFFSETS,
@@ -319,6 +315,7 @@ static const struct intel_device_info intel_broadwell_m_info = {
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
.has_llc = 1,
.has_ddi = 1,
+ .has_fpga_dbg = 1,
.has_fbc = 1,
GEN_DEFAULT_PIPEOFFSETS,
IVB_CURSOR_OFFSETS,
@@ -330,6 +327,7 @@ static const struct intel_device_info intel_broadwell_gt3d_info = {
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
.has_llc = 1,
.has_ddi = 1,
+ .has_fpga_dbg = 1,
.has_fbc = 1,
GEN_DEFAULT_PIPEOFFSETS,
IVB_CURSOR_OFFSETS,
@@ -341,6 +339,7 @@ static const struct intel_device_info intel_broadwell_gt3m_info = {
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
.has_llc = 1,
.has_ddi = 1,
+ .has_fpga_dbg = 1,
.has_fbc = 1,
GEN_DEFAULT_PIPEOFFSETS,
IVB_CURSOR_OFFSETS,
@@ -482,6 +481,10 @@ bool i915_semaphore_is_enabled(struct drm_device *dev)
if (i915.semaphores >= 0)
return i915.semaphores;
+ /* TODO: make semaphores and Execlists play nicely together */
+ if (i915.enable_execlists)
+ return false;
+
/* Until we get further testing... */
if (IS_GEN8(dev))
return false;
@@ -495,12 +498,45 @@ bool i915_semaphore_is_enabled(struct drm_device *dev)
return true;
}
+void intel_hpd_cancel_work(struct drm_i915_private *dev_priv)
+{
+ spin_lock_irq(&dev_priv->irq_lock);
+
+ dev_priv->long_hpd_port_mask = 0;
+ dev_priv->short_hpd_port_mask = 0;
+ dev_priv->hpd_event_bits = 0;
+
+ spin_unlock_irq(&dev_priv->irq_lock);
+
+ cancel_work_sync(&dev_priv->dig_port_work);
+ cancel_work_sync(&dev_priv->hotplug_work);
+ cancel_delayed_work_sync(&dev_priv->hotplug_reenable_work);
+}
+
+static void intel_suspend_encoders(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = dev_priv->dev;
+ struct drm_encoder *encoder;
+
+ drm_modeset_lock_all(dev);
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+
+ if (intel_encoder->suspend)
+ intel_encoder->suspend(intel_encoder);
+ }
+ drm_modeset_unlock_all(dev);
+}
+
+static int intel_suspend_complete(struct drm_i915_private *dev_priv);
+static int intel_resume_prepare(struct drm_i915_private *dev_priv,
+ bool rpm_resume);
+
static int i915_drm_freeze(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc;
-
- intel_runtime_pm_get(dev_priv);
+ pci_power_t opregion_target_state;
/* ignore lid events during suspend */
mutex_lock(&dev_priv->modeset_restore_lock);
@@ -526,21 +562,26 @@ static int i915_drm_freeze(struct drm_device *dev)
return error;
}
- drm_irq_uninstall(dev);
- dev_priv->enable_hotplug_processing = false;
-
- intel_disable_gt_powersave(dev);
-
/*
* Disable CRTCs directly since we want to preserve sw state
- * for _thaw.
+ * for _thaw. Also, power gate the CRTC power wells.
*/
drm_modeset_lock_all(dev);
- for_each_crtc(dev, crtc) {
- dev_priv->display.crtc_disable(crtc);
- }
+ for_each_crtc(dev, crtc)
+ intel_crtc_control(crtc, false);
drm_modeset_unlock_all(dev);
+ intel_dp_mst_suspend(dev);
+
+ flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
+ intel_runtime_pm_disable_interrupts(dev);
+ intel_hpd_cancel_work(dev_priv);
+
+ intel_suspend_encoders(dev_priv);
+
+ intel_suspend_gt_powersave(dev);
+
intel_modeset_suspend_hw(dev);
}
@@ -548,15 +589,22 @@ static int i915_drm_freeze(struct drm_device *dev)
i915_save_state(dev);
+ opregion_target_state = PCI_D3cold;
+#if IS_ENABLED(CONFIG_ACPI_SLEEP)
+ if (acpi_target_system_state() < ACPI_STATE_S3)
+ opregion_target_state = PCI_D1;
+#endif
+ intel_opregion_notify_adapter(dev, opregion_target_state);
+
+ intel_uncore_forcewake_reset(dev, false);
intel_opregion_fini(dev);
- intel_uncore_fini(dev);
- console_lock();
- intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED);
- console_unlock();
+ intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED, true);
dev_priv->suspend_count++;
+ intel_display_set_init_power(dev_priv, false);
+
return 0;
}
@@ -590,27 +638,20 @@ int i915_suspend(struct drm_device *dev, pm_message_t state)
return 0;
}
-void intel_console_resume(struct work_struct *work)
-{
- struct drm_i915_private *dev_priv =
- container_of(work, struct drm_i915_private,
- console_resume_work);
- struct drm_device *dev = dev_priv->dev;
-
- console_lock();
- intel_fbdev_set_suspend(dev, FBINFO_STATE_RUNNING);
- console_unlock();
-}
-
static int i915_drm_thaw_early(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
- intel_uncore_early_sanitize(dev);
+ ret = intel_resume_prepare(dev_priv, false);
+ if (ret)
+ DRM_ERROR("Resume prepare failed: %d,Continuing resume\n", ret);
+
+ intel_uncore_early_sanitize(dev, true);
intel_uncore_sanitize(dev);
intel_power_domains_init_hw(dev_priv);
- return 0;
+ return ret;
}
static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
@@ -639,11 +680,19 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
}
mutex_unlock(&dev->struct_mutex);
- /* We need working interrupts for modeset enabling ... */
- drm_irq_install(dev, dev->pdev->irq);
+ intel_runtime_pm_restore_interrupts(dev);
intel_modeset_init_hw(dev);
+ {
+ unsigned long irqflags;
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ if (dev_priv->display.hpd_irq_setup)
+ dev_priv->display.hpd_irq_setup(dev);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+ }
+
+ intel_dp_mst_resume(dev);
drm_modeset_lock_all(dev);
intel_modeset_setup_hw_state(dev, true);
drm_modeset_unlock_all(dev);
@@ -655,30 +704,20 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
* notifications.
* */
intel_hpd_init(dev);
- dev_priv->enable_hotplug_processing = true;
/* Config may have changed between suspend and resume */
drm_helper_hpd_irq_event(dev);
}
intel_opregion_init(dev);
- /*
- * The console lock can be pretty contented on resume due
- * to all the printk activity. Try to keep it out of the hot
- * path of resume if possible.
- */
- if (console_trylock()) {
- intel_fbdev_set_suspend(dev, FBINFO_STATE_RUNNING);
- console_unlock();
- } else {
- schedule_work(&dev_priv->console_resume_work);
- }
+ intel_fbdev_set_suspend(dev, FBINFO_STATE_RUNNING, false);
mutex_lock(&dev_priv->modeset_restore_lock);
dev_priv->modeset_restore = MODESET_DONE;
mutex_unlock(&dev_priv->modeset_restore_lock);
- intel_runtime_pm_put(dev_priv);
+ intel_opregion_notify_adapter(dev, PCI_D0);
+
return 0;
}
@@ -805,7 +844,13 @@ int i915_reset(struct drm_device *dev)
!dev_priv->ums.mm_suspended) {
dev_priv->ums.mm_suspended = 0;
+ /* Used to prevent gem_check_wedged returning -EAGAIN during gpu reset */
+ dev_priv->gpu_error.reload_in_reset = true;
+
ret = i915_gem_init_hw(dev);
+
+ dev_priv->gpu_error.reload_in_reset = false;
+
mutex_unlock(&dev->struct_mutex);
if (ret) {
DRM_ERROR("Failed hw init on reset %d\n", ret);
@@ -826,8 +871,6 @@ int i915_reset(struct drm_device *dev)
*/
if (INTEL_INFO(dev)->gen > 5)
intel_reset_gt_powersave(dev);
-
- intel_hpd_init(dev);
} else {
mutex_unlock(&dev->struct_mutex);
}
@@ -887,6 +930,8 @@ static int i915_pm_suspend_late(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ struct drm_i915_private *dev_priv = drm_dev->dev_private;
+ int ret;
/*
* We have a suspedn ordering issue with the snd-hda driver also
@@ -900,10 +945,16 @@ static int i915_pm_suspend_late(struct device *dev)
if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
return 0;
- pci_disable_device(pdev);
- pci_set_power_state(pdev, PCI_D3hot);
+ ret = intel_suspend_complete(dev_priv);
- return 0;
+ if (ret)
+ DRM_ERROR("Suspend complete failed: %d\n", ret);
+ else {
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+ }
+
+ return ret;
}
static int i915_pm_resume_early(struct device *dev)
@@ -935,6 +986,15 @@ static int i915_pm_freeze(struct device *dev)
return i915_drm_freeze(drm_dev);
}
+static int i915_pm_freeze_late(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ struct drm_i915_private *dev_priv = drm_dev->dev_private;
+
+ return intel_suspend_complete(dev_priv);
+}
+
static int i915_pm_thaw_early(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -959,23 +1019,26 @@ static int i915_pm_poweroff(struct device *dev)
return i915_drm_freeze(drm_dev);
}
-static int hsw_runtime_suspend(struct drm_i915_private *dev_priv)
+static int hsw_suspend_complete(struct drm_i915_private *dev_priv)
{
hsw_enable_pc8(dev_priv);
return 0;
}
-static int snb_runtime_resume(struct drm_i915_private *dev_priv)
+static int snb_resume_prepare(struct drm_i915_private *dev_priv,
+ bool rpm_resume)
{
struct drm_device *dev = dev_priv->dev;
- intel_init_pch_refclk(dev);
+ if (rpm_resume)
+ intel_init_pch_refclk(dev);
return 0;
}
-static int hsw_runtime_resume(struct drm_i915_private *dev_priv)
+static int hsw_resume_prepare(struct drm_i915_private *dev_priv,
+ bool rpm_resume)
{
hsw_disable_pc8(dev_priv);
@@ -1271,7 +1334,7 @@ static void vlv_check_no_gt_access(struct drm_i915_private *dev_priv)
I915_WRITE(VLV_GTLC_PW_STATUS, VLV_GTLC_ALLOWWAKEERR);
}
-static int vlv_runtime_suspend(struct drm_i915_private *dev_priv)
+static int vlv_suspend_complete(struct drm_i915_private *dev_priv)
{
u32 mask;
int err;
@@ -1311,7 +1374,8 @@ err1:
return err;
}
-static int vlv_runtime_resume(struct drm_i915_private *dev_priv)
+static int vlv_resume_prepare(struct drm_i915_private *dev_priv,
+ bool rpm_resume)
{
struct drm_device *dev = dev_priv->dev;
int err;
@@ -1336,8 +1400,10 @@ static int vlv_runtime_resume(struct drm_i915_private *dev_priv)
vlv_check_no_gt_access(dev_priv);
- intel_init_clock_gating(dev);
- i915_gem_restore_fences(dev);
+ if (rpm_resume) {
+ intel_init_clock_gating(dev);
+ i915_gem_restore_fences(dev);
+ }
return ret;
}
@@ -1352,7 +1418,9 @@ static int intel_runtime_suspend(struct device *device)
if (WARN_ON_ONCE(!(dev_priv->rps.enabled && intel_enable_rc6(dev))))
return -ENODEV;
- WARN_ON(!HAS_RUNTIME_PM(dev));
+ if (WARN_ON_ONCE(!HAS_RUNTIME_PM(dev)))
+ return -ENODEV;
+
assert_force_wake_inactive(dev_priv);
DRM_DEBUG_KMS("Suspending device\n");
@@ -1389,17 +1457,7 @@ static int intel_runtime_suspend(struct device *device)
cancel_work_sync(&dev_priv->rps.work);
intel_runtime_pm_disable_interrupts(dev);
- if (IS_GEN6(dev)) {
- ret = 0;
- } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
- ret = hsw_runtime_suspend(dev_priv);
- } else if (IS_VALLEYVIEW(dev)) {
- ret = vlv_runtime_suspend(dev_priv);
- } else {
- ret = -ENODEV;
- WARN_ON(1);
- }
-
+ ret = intel_suspend_complete(dev_priv);
if (ret) {
DRM_ERROR("Runtime suspend failed, disabling it (%d)\n", ret);
intel_runtime_pm_restore_interrupts(dev);
@@ -1411,13 +1469,29 @@ static int intel_runtime_suspend(struct device *device)
dev_priv->pm.suspended = true;
/*
- * current versions of firmware which depend on this opregion
- * notification have repurposed the D1 definition to mean
- * "runtime suspended" vs. what you would normally expect (D3)
- * to distinguish it from notifications that might be sent
- * via the suspend path.
+ * FIXME: We really should find a document that references the arguments
+ * used below!
*/
- intel_opregion_notify_adapter(dev, PCI_D1);
+ if (IS_HASWELL(dev)) {
+ /*
+ * current versions of firmware which depend on this opregion
+ * notification have repurposed the D1 definition to mean
+ * "runtime suspended" vs. what you would normally expect (D3)
+ * to distinguish it from notifications that might be sent via
+ * the suspend path.
+ */
+ intel_opregion_notify_adapter(dev, PCI_D1);
+ } else {
+ /*
+ * On Broadwell, if we use PCI_D1 the PCH DDI ports will stop
+ * being detected, and the call we do at intel_runtime_resume()
+ * won't be able to restore them. Since PCI_D3hot matches the
+ * actual specification and appears to be working, use it. Let's
+ * assume the other non-Haswell platforms will stay the same as
+ * Broadwell.
+ */
+ intel_opregion_notify_adapter(dev, PCI_D3hot);
+ }
DRM_DEBUG_KMS("Device suspended\n");
return 0;
@@ -1430,24 +1504,15 @@ static int intel_runtime_resume(struct device *device)
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
- WARN_ON(!HAS_RUNTIME_PM(dev));
+ if (WARN_ON_ONCE(!HAS_RUNTIME_PM(dev)))
+ return -ENODEV;
DRM_DEBUG_KMS("Resuming device\n");
intel_opregion_notify_adapter(dev, PCI_D0);
dev_priv->pm.suspended = false;
- if (IS_GEN6(dev)) {
- ret = snb_runtime_resume(dev_priv);
- } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
- ret = hsw_runtime_resume(dev_priv);
- } else if (IS_VALLEYVIEW(dev)) {
- ret = vlv_runtime_resume(dev_priv);
- } else {
- WARN_ON(1);
- ret = -ENODEV;
- }
-
+ ret = intel_resume_prepare(dev_priv, true);
/*
* No point of rolling back things in case of an error, as the best
* we can do is to hope that things will still work (and disable RPM).
@@ -1466,12 +1531,55 @@ static int intel_runtime_resume(struct device *device)
return ret;
}
+/*
+ * This function implements common functionality of runtime and system
+ * suspend sequence.
+ */
+static int intel_suspend_complete(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = dev_priv->dev;
+ int ret;
+
+ if (IS_HASWELL(dev) || IS_BROADWELL(dev))
+ ret = hsw_suspend_complete(dev_priv);
+ else if (IS_VALLEYVIEW(dev))
+ ret = vlv_suspend_complete(dev_priv);
+ else
+ ret = 0;
+
+ return ret;
+}
+
+/*
+ * This function implements common functionality of runtime and system
+ * resume sequence. Variable rpm_resume used for implementing different
+ * code paths.
+ */
+static int intel_resume_prepare(struct drm_i915_private *dev_priv,
+ bool rpm_resume)
+{
+ struct drm_device *dev = dev_priv->dev;
+ int ret;
+
+ if (IS_GEN6(dev))
+ ret = snb_resume_prepare(dev_priv, rpm_resume);
+ else if (IS_HASWELL(dev) || IS_BROADWELL(dev))
+ ret = hsw_resume_prepare(dev_priv, rpm_resume);
+ else if (IS_VALLEYVIEW(dev))
+ ret = vlv_resume_prepare(dev_priv, rpm_resume);
+ else
+ ret = 0;
+
+ return ret;
+}
+
static const struct dev_pm_ops i915_pm_ops = {
.suspend = i915_pm_suspend,
.suspend_late = i915_pm_suspend_late,
.resume_early = i915_pm_resume_early,
.resume = i915_pm_resume,
.freeze = i915_pm_freeze,
+ .freeze_late = i915_pm_freeze_late,
.thaw_early = i915_pm_thaw_early,
.thaw = i915_pm_thaw,
.poweroff = i915_pm_poweroff,
@@ -1515,6 +1623,7 @@ static struct drm_driver driver = {
.lastclose = i915_driver_lastclose,
.preclose = i915_driver_preclose,
.postclose = i915_driver_postclose,
+ .set_busid = drm_pci_set_busid,
/* Used in place of i915_pm_ops for non-DRIVER_MODESET */
.suspend = i915_suspend,
@@ -1606,6 +1715,8 @@ static void __exit i915_exit(void)
module_init(i915_init);
module_exit(i915_exit);
-MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_AUTHOR("Tungsten Graphics, Inc.");
+MODULE_AUTHOR("Intel Corporation");
+
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 1f7700897dfc..16a6f6d187a1 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -35,11 +35,15 @@
#include "i915_reg.h"
#include "intel_bios.h"
#include "intel_ringbuffer.h"
+#include "intel_lrc.h"
#include "i915_gem_gtt.h"
+#include "i915_gem_render_state.h"
#include <linux/io-mapping.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <drm/intel-gtt.h>
+#include <drm/drm_legacy.h> /* for struct drm_dma_handle */
+#include <drm/drm_gem.h>
#include <linux/backlight.h>
#include <linux/hashtable.h>
#include <linux/intel-iommu.h>
@@ -49,11 +53,9 @@
/* General customization:
*/
-#define DRIVER_AUTHOR "Tungsten Graphics, Inc."
-
#define DRIVER_NAME "i915"
#define DRIVER_DESC "Intel Graphics"
-#define DRIVER_DATE "20080730"
+#define DRIVER_DATE "20140905"
enum pipe {
INVALID_PIPE = -1,
@@ -129,6 +131,7 @@ enum intel_display_power_domain {
POWER_DOMAIN_PORT_OTHER,
POWER_DOMAIN_VGA,
POWER_DOMAIN_AUDIO,
+ POWER_DOMAIN_PLLS,
POWER_DOMAIN_INIT,
POWER_DOMAIN_NUM,
@@ -161,7 +164,10 @@ enum hpd_pin {
I915_GEM_DOMAIN_INSTRUCTION | \
I915_GEM_DOMAIN_VERTEX)
-#define for_each_pipe(p) for ((p) = 0; (p) < INTEL_INFO(dev)->num_pipes; (p)++)
+#define for_each_pipe(__dev_priv, __p) \
+ for ((__p) = 0; (__p) < INTEL_INFO(__dev_priv)->num_pipes; (__p)++)
+#define for_each_plane(pipe, p) \
+ for ((p) = 0; (p) < INTEL_INFO(dev)->num_sprites[(pipe)] + 1; (p)++)
#define for_each_sprite(p, s) for ((s) = 0; (s) < INTEL_INFO(dev)->num_sprites[(p)]; (s)++)
#define for_each_crtc(dev, crtc) \
@@ -170,6 +176,11 @@ enum hpd_pin {
#define for_each_intel_crtc(dev, intel_crtc) \
list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head)
+#define for_each_intel_encoder(dev, intel_encoder) \
+ list_for_each_entry(intel_encoder, \
+ &(dev)->mode_config.encoder_list, \
+ base.head)
+
#define for_each_encoder_on_crtc(dev, __crtc, intel_encoder) \
list_for_each_entry((intel_encoder), &(dev)->mode_config.encoder_list, base.head) \
if ((intel_encoder)->base.crtc == (__crtc))
@@ -178,22 +189,33 @@ enum hpd_pin {
list_for_each_entry((intel_connector), &(dev)->mode_config.connector_list, base.head) \
if ((intel_connector)->base.encoder == (__encoder))
+#define for_each_power_domain(domain, mask) \
+ for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++) \
+ if ((1 << (domain)) & (mask))
+
struct drm_i915_private;
+struct i915_mm_struct;
struct i915_mmu_object;
enum intel_dpll_id {
DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */
/* real shared dpll ids must be >= 0 */
- DPLL_ID_PCH_PLL_A,
- DPLL_ID_PCH_PLL_B,
+ DPLL_ID_PCH_PLL_A = 0,
+ DPLL_ID_PCH_PLL_B = 1,
+ DPLL_ID_WRPLL1 = 0,
+ DPLL_ID_WRPLL2 = 1,
};
#define I915_NUM_PLLS 2
struct intel_dpll_hw_state {
+ /* i9xx, pch plls */
uint32_t dpll;
uint32_t dpll_md;
uint32_t fp0;
uint32_t fp1;
+
+ /* hsw, bdw */
+ uint32_t wrpll;
};
struct intel_shared_dpll {
@@ -204,6 +226,8 @@ struct intel_shared_dpll {
/* should match the index in the dev_priv->shared_dplls array */
enum intel_dpll_id id;
struct intel_dpll_hw_state hw_state;
+ /* The mode_set hook is optional and should be used together with the
+ * intel_prepare_shared_dpll function. */
void (*mode_set)(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll);
void (*enable)(struct drm_i915_private *dev_priv,
@@ -228,12 +252,6 @@ void intel_link_compute_m_n(int bpp, int nlanes,
int pixel_clock, int link_clock,
struct intel_link_m_n *m_n);
-struct intel_ddi_plls {
- int spll_refcount;
- int wrpll1_refcount;
- int wrpll2_refcount;
-};
-
/* Interface history:
*
* 1.1: Original.
@@ -272,8 +290,10 @@ struct intel_opregion {
struct intel_overlay;
struct intel_overlay_error_state;
+struct drm_local_map;
+
struct drm_i915_master_private {
- drm_local_map_t *sarea;
+ struct drm_local_map *sarea;
struct _drm_i915_sarea *sarea_priv;
};
#define I915_FENCE_REG_NONE -1
@@ -310,6 +330,7 @@ struct drm_i915_error_state {
u32 eir;
u32 pgtbl_er;
u32 ier;
+ u32 gtier[4];
u32 ccid;
u32 derrmr;
u32 forcewake;
@@ -324,6 +345,7 @@ struct drm_i915_error_state {
u64 fence[I915_MAX_NUM_FENCES];
struct intel_overlay_error_state *overlay;
struct intel_display_error_state *display;
+ struct drm_i915_error_object *semaphore_obj;
struct drm_i915_error_ring {
bool valid;
@@ -381,6 +403,7 @@ struct drm_i915_error_state {
pid_t pid;
char comm[TASK_COMM_LEN];
} ring[I915_NUM_RINGS];
+
struct drm_i915_error_buffer {
u32 size;
u32 name;
@@ -399,6 +422,7 @@ struct drm_i915_error_state {
} **active_bo, **pinned_bo;
u32 *active_bo_count, *pinned_bo_count;
+ u32 vm_count;
};
struct intel_connector;
@@ -435,8 +459,8 @@ struct drm_i915_display_funcs {
void (*update_wm)(struct drm_crtc *crtc);
void (*update_sprite_wm)(struct drm_plane *plane,
struct drm_crtc *crtc,
- uint32_t sprite_width, int pixel_size,
- bool enable, bool scaled);
+ uint32_t sprite_width, uint32_t sprite_height,
+ int pixel_size, bool enable, bool scaled);
void (*modeset_global_resources)(struct drm_device *dev);
/* Returns the active state of the crtc, and if the crtc is active,
* fills out the pipe-config with the hw state. */
@@ -544,6 +568,7 @@ struct intel_uncore {
struct intel_device_info {
u32 display_mmio_offset;
+ u16 device_id;
u8 num_pipes:3;
u8 num_sprites[I915_MAX_PIPES];
u8 gen;
@@ -552,8 +577,6 @@ struct intel_device_info {
/* Register offsets for the various display pipes and transcoders */
int pipe_offsets[I915_MAX_TRANSCODERS];
int trans_offsets[I915_MAX_TRANSCODERS];
- int dpll_offsets[I915_MAX_PIPES];
- int dpll_md_offsets[I915_MAX_PIPES];
int palette_offsets[I915_MAX_PIPES];
int cursor_offsets[I915_MAX_PIPES];
};
@@ -586,30 +609,60 @@ struct i915_ctx_hang_stats {
};
/* This must match up with the value previously used for execbuf2.rsvd1. */
-#define DEFAULT_CONTEXT_ID 0
+#define DEFAULT_CONTEXT_HANDLE 0
+/**
+ * struct intel_context - as the name implies, represents a context.
+ * @ref: reference count.
+ * @user_handle: userspace tracking identity for this context.
+ * @remap_slice: l3 row remapping information.
+ * @file_priv: filp associated with this context (NULL for global default
+ * context).
+ * @hang_stats: information about the role of this context in possible GPU
+ * hangs.
+ * @vm: virtual memory space used by this context.
+ * @legacy_hw_ctx: render context backing object and whether it is correctly
+ * initialized (legacy ring submission mechanism only).
+ * @link: link in the global list of contexts.
+ *
+ * Contexts are memory images used by the hardware to store copies of their
+ * internal state.
+ */
struct intel_context {
struct kref ref;
- int id;
- bool is_initialized;
+ int user_handle;
uint8_t remap_slice;
struct drm_i915_file_private *file_priv;
- struct intel_engine_cs *last_ring;
- struct drm_i915_gem_object *obj;
struct i915_ctx_hang_stats hang_stats;
- struct i915_address_space *vm;
+ struct i915_hw_ppgtt *ppgtt;
+
+ /* Legacy ring buffer submission */
+ struct {
+ struct drm_i915_gem_object *rcs_state;
+ bool initialized;
+ } legacy_hw_ctx;
+
+ /* Execlists */
+ bool rcs_initialized;
+ struct {
+ struct drm_i915_gem_object *state;
+ struct intel_ringbuffer *ringbuf;
+ } engine[I915_NUM_RINGS];
struct list_head link;
};
struct i915_fbc {
unsigned long size;
+ unsigned threshold;
unsigned int fb_id;
enum plane plane;
int y;
- struct drm_mm_node *compressed_fb;
+ struct drm_mm_node compressed_fb;
struct drm_mm_node *compressed_llb;
+ bool false_color;
+
struct intel_fbc_work {
struct delayed_work work;
struct drm_crtc *crtc;
@@ -635,9 +688,15 @@ struct i915_drrs {
struct intel_connector *connector;
};
+struct intel_dp;
struct i915_psr {
+ struct mutex lock;
bool sink_support;
bool source_ok;
+ struct intel_dp *enabled;
+ bool active;
+ struct delayed_work work;
+ unsigned busy_frontbuffer_bits;
};
enum intel_pch {
@@ -657,6 +716,7 @@ enum intel_sbi_destination {
#define QUIRK_LVDS_SSC_DISABLE (1<<1)
#define QUIRK_INVERT_BRIGHTNESS (1<<2)
#define QUIRK_BACKLIGHT_PRESENT (1<<3)
+#define QUIRK_PIPEB_FORCE (1<<4)
struct intel_fbdev;
struct intel_fbc_work;
@@ -880,6 +940,12 @@ struct vlv_s0ix_state {
u32 clock_gate_dis2;
};
+struct intel_rps_ei {
+ u32 cz_clock;
+ u32 render_c0;
+ u32 media_c0;
+};
+
struct intel_gen6_power_mgmt {
/* work and pm_iir are protected by dev_priv->irq_lock */
struct work_struct work;
@@ -903,6 +969,9 @@ struct intel_gen6_power_mgmt {
u8 efficient_freq; /* AKA RPe. Pre-determined balanced frequency */
u8 rp1_freq; /* "less than" RP0 power/freqency */
u8 rp0_freq; /* Non-overclocked max frequency. */
+ u32 cz_freq;
+
+ u32 ei_interrupt_count;
int last_adj;
enum { LOW_POWER, BETWEEN, HIGH_POWER } power;
@@ -910,6 +979,9 @@ struct intel_gen6_power_mgmt {
bool enabled;
struct delayed_work delayed_resume_work;
+ /* manual wa residency calculations */
+ struct intel_rps_ei up_ei, down_ei;
+
/*
* Protects RPS/RC6 register access and PCU communication.
* Must be taken after struct_mutex if nested.
@@ -1104,6 +1176,7 @@ struct i915_gem_mm {
};
struct drm_i915_error_state_buf {
+ struct drm_i915_private *i915;
unsigned bytes;
unsigned size;
int err;
@@ -1176,6 +1249,9 @@ struct i915_gpu_error {
/* For missed irq/seqno simulation. */
unsigned int test_irq_rings;
+
+ /* Used to prevent gem_check_wedged returning -EAGAIN during gpu reset */
+ bool reload_in_reset;
};
enum modeset_restore {
@@ -1185,6 +1261,12 @@ enum modeset_restore {
};
struct ddi_vbt_port_info {
+ /*
+ * This is an index in the HDMI/DVI DDI buffer translation table.
+ * The special value HDMI_LEVEL_SHIFT_UNKNOWN means the VBT didn't
+ * populate this field.
+ */
+#define HDMI_LEVEL_SHIFT_UNKNOWN 0xff
uint8_t hdmi_level_shift;
uint8_t supports_dvi:1;
@@ -1230,6 +1312,7 @@ struct intel_vbt_data {
u16 pwm_freq_hz;
bool present;
bool active_low_pwm;
+ u8 min_brightness; /* min_brightness/255 of max */
} backlight;
/* MIPI DSI */
@@ -1299,7 +1382,7 @@ struct ilk_wm_values {
*/
struct i915_runtime_pm {
bool suspended;
- bool irqs_disabled;
+ bool _irqs_disabled;
};
enum intel_pipe_crc_source {
@@ -1332,6 +1415,17 @@ struct intel_pipe_crc {
wait_queue_head_t wq;
};
+struct i915_frontbuffer_tracking {
+ struct mutex lock;
+
+ /*
+ * Tracking bits for delayed frontbuffer flushing du to gpu activity or
+ * scheduled flips.
+ */
+ unsigned busy_bits;
+ unsigned flip_bits;
+};
+
struct drm_i915_private {
struct drm_device *dev;
struct kmem_cache *slab;
@@ -1363,14 +1457,18 @@ struct drm_i915_private {
struct pci_dev *bridge_dev;
struct intel_engine_cs ring[I915_NUM_RINGS];
+ struct drm_i915_gem_object *semaphore_obj;
uint32_t last_seqno, next_seqno;
- drm_dma_handle_t *status_page_dmah;
+ struct drm_dma_handle *status_page_dmah;
struct resource mch_res;
/* protects the irq masks */
spinlock_t irq_lock;
+ /* protects the mmio flip data */
+ spinlock_t mmio_flip_lock;
+
bool display_irqs_enabled;
/* To control wakeup latency, e.g. for irq-driven dp aux transfers. */
@@ -1390,7 +1488,6 @@ struct drm_i915_private {
u32 pipestat_irq_mask[I915_MAX_PIPES];
struct work_struct hotplug_work;
- bool enable_hotplug_processing;
struct {
unsigned long hpd_last_jiffies;
int hpd_cnt;
@@ -1401,7 +1498,7 @@ struct drm_i915_private {
} hpd_mark;
} hpd_stats[HPD_NUM_PINS];
u32 hpd_event_bits;
- struct timer_list hotplug_reenable_timer;
+ struct delayed_work hotplug_reenable_work;
struct i915_fbc fbc;
struct i915_drrs drrs;
@@ -1417,6 +1514,9 @@ struct drm_i915_private {
/* LVDS info */
bool no_aux_handshake;
+ /* protects panel power sequencer state */
+ struct mutex pps_mutex;
+
struct drm_i915_fence_reg fence_regs[I915_MAX_NUM_FENCES]; /* assume 965 */
int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
int num_fence_regs; /* 8 on pre-965, 16 otherwise */
@@ -1449,9 +1549,8 @@ struct drm_i915_private {
struct i915_gtt gtt; /* VM representing the global address space */
struct i915_gem_mm mm;
-#if defined(CONFIG_MMU_NOTIFIER)
- DECLARE_HASHTABLE(mmu_notifiers, 7);
-#endif
+ DECLARE_HASHTABLE(mm_structs, 7);
+ struct mutex mm_lock;
/* Kernel Modesetting */
@@ -1467,14 +1566,30 @@ struct drm_i915_private {
int num_shared_dpll;
struct intel_shared_dpll shared_dplls[I915_NUM_PLLS];
- struct intel_ddi_plls ddi_plls;
int dpio_phy_iosf_port[I915_NUM_PHYS_VLV];
+ /*
+ * workarounds are currently applied at different places and
+ * changes are being done to consolidate them so exact count is
+ * not clear at this point, use a max value for now.
+ */
+#define I915_MAX_WA_REGS 16
+ struct {
+ u32 addr;
+ u32 value;
+ /* bitmask representing WA bits */
+ u32 mask;
+ } intel_wa_regs[I915_MAX_WA_REGS];
+ u32 num_wa_regs;
+
/* Reclocking support */
bool render_reclock_avail;
bool lvds_downclock_avail;
/* indicates the reduced downclock for LVDS*/
int lvds_downclock;
+
+ struct i915_frontbuffer_tracking fb_tracking;
+
u16 orig_clock;
bool mchbar_need_disable;
@@ -1502,14 +1617,9 @@ struct drm_i915_private {
#ifdef CONFIG_DRM_I915_FBDEV
/* list of fbdev register on this device */
struct intel_fbdev *fbdev;
+ struct work_struct fbdev_suspend_work;
#endif
- /*
- * The console may be contended at resume, but we don't
- * want it to block on it.
- */
- struct work_struct console_resume_work;
-
struct drm_property *broadcast_rgb_property;
struct drm_property *force_audio_property;
@@ -1541,12 +1651,42 @@ struct drm_i915_private {
struct i915_runtime_pm pm;
+ struct intel_digital_port *hpd_irq_port[I915_MAX_PORTS];
+ u32 long_hpd_port_mask;
+ u32 short_hpd_port_mask;
+ struct work_struct dig_port_work;
+
+ /*
+ * if we get a HPD irq from DP and a HPD irq from non-DP
+ * the non-DP HPD could block the workqueue on a mode config
+ * mutex getting, that userspace may have taken. However
+ * userspace is waiting on the DP workqueue to run which is
+ * blocked behind the non-DP one.
+ */
+ struct workqueue_struct *dp_wq;
+
+ uint32_t bios_vgacntr;
+
/* Old dri1 support infrastructure, beware the dragons ya fools entering
* here! */
struct i915_dri1_state dri1;
/* Old ums support infrastructure, same warning applies. */
struct i915_ums_state ums;
+ /* Abstract the submission mechanism (legacy ringbuffer or execlists) away */
+ struct {
+ int (*do_execbuf)(struct drm_device *dev, struct drm_file *file,
+ struct intel_engine_cs *ring,
+ struct intel_context *ctx,
+ struct drm_i915_gem_execbuffer2 *args,
+ struct list_head *vmas,
+ struct drm_i915_gem_object *batch_obj,
+ u64 exec_start, u32 flags);
+ int (*init_rings)(struct drm_device *dev);
+ void (*cleanup_ring)(struct intel_engine_cs *ring);
+ void (*stop_ring)(struct intel_engine_cs *ring);
+ } gt;
+
/*
* NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
* will be rejected. Instead look for a better place.
@@ -1592,6 +1732,28 @@ struct drm_i915_gem_object_ops {
void (*release)(struct drm_i915_gem_object *);
};
+/*
+ * Frontbuffer tracking bits. Set in obj->frontbuffer_bits while a gem bo is
+ * considered to be the frontbuffer for the given plane interface-vise. This
+ * doesn't mean that the hw necessarily already scans it out, but that any
+ * rendering (by the cpu or gpu) will land in the frontbuffer eventually.
+ *
+ * We have one bit per pipe and per scanout plane type.
+ */
+#define INTEL_FRONTBUFFER_BITS_PER_PIPE 4
+#define INTEL_FRONTBUFFER_BITS \
+ (INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES)
+#define INTEL_FRONTBUFFER_PRIMARY(pipe) \
+ (1 << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
+#define INTEL_FRONTBUFFER_CURSOR(pipe) \
+ (1 << (1 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
+#define INTEL_FRONTBUFFER_SPRITE(pipe) \
+ (1 << (2 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
+#define INTEL_FRONTBUFFER_OVERLAY(pipe) \
+ (1 << (3 +(INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))))
+#define INTEL_FRONTBUFFER_ALL_MASK(pipe) \
+ (0xf << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
+
struct drm_i915_gem_object {
struct drm_gem_object base;
@@ -1662,17 +1824,18 @@ struct drm_i915_gem_object {
unsigned int pin_display:1;
/*
- * Is the GPU currently using a fence to access this buffer,
+ * Is the object to be mapped as read-only to the GPU
+ * Only honoured if hardware has relevant pte bit
*/
- unsigned int pending_fenced_gpu_access:1;
- unsigned int fenced_gpu_access:1;
-
+ unsigned long gt_ro:1;
unsigned int cache_level:3;
unsigned int has_aliasing_ppgtt_mapping:1;
unsigned int has_global_gtt_mapping:1;
unsigned int has_dma_mapping:1;
+ unsigned int frontbuffer_bits:INTEL_FRONTBUFFER_BITS;
+
struct sg_table *pages;
int pages_pin_count;
@@ -1702,7 +1865,7 @@ struct drm_i915_gem_object {
struct drm_file *pin_filp;
/** for phy allocated objects */
- drm_dma_handle_t *phys_handle;
+ struct drm_dma_handle *phys_handle;
union {
struct i915_gem_userptr {
@@ -1711,14 +1874,18 @@ struct drm_i915_gem_object {
unsigned workers :4;
#define I915_GEM_USERPTR_MAX_WORKERS 15
- struct mm_struct *mm;
- struct i915_mmu_object *mn;
+ struct i915_mm_struct *mm;
+ struct i915_mmu_object *mmu_object;
struct work_struct *work;
} userptr;
};
};
#define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base)
+void i915_gem_track_fb(struct drm_i915_gem_object *old,
+ struct drm_i915_gem_object *new,
+ unsigned frontbuffer_bits);
+
/**
* Request queue structure.
*
@@ -1864,51 +2031,63 @@ struct drm_i915_cmd_table {
int count;
};
-#define INTEL_INFO(dev) (&to_i915(dev)->info)
-
-#define IS_I830(dev) ((dev)->pdev->device == 0x3577)
-#define IS_845G(dev) ((dev)->pdev->device == 0x2562)
+/* Note that the (struct drm_i915_private *) cast is just to shut up gcc. */
+#define __I915__(p) ({ \
+ struct drm_i915_private *__p; \
+ if (__builtin_types_compatible_p(typeof(*p), struct drm_i915_private)) \
+ __p = (struct drm_i915_private *)p; \
+ else if (__builtin_types_compatible_p(typeof(*p), struct drm_device)) \
+ __p = to_i915((struct drm_device *)p); \
+ else \
+ BUILD_BUG(); \
+ __p; \
+})
+#define INTEL_INFO(p) (&__I915__(p)->info)
+#define INTEL_DEVID(p) (INTEL_INFO(p)->device_id)
+
+#define IS_I830(dev) (INTEL_DEVID(dev) == 0x3577)
+#define IS_845G(dev) (INTEL_DEVID(dev) == 0x2562)
#define IS_I85X(dev) (INTEL_INFO(dev)->is_i85x)
-#define IS_I865G(dev) ((dev)->pdev->device == 0x2572)
+#define IS_I865G(dev) (INTEL_DEVID(dev) == 0x2572)
#define IS_I915G(dev) (INTEL_INFO(dev)->is_i915g)
-#define IS_I915GM(dev) ((dev)->pdev->device == 0x2592)
-#define IS_I945G(dev) ((dev)->pdev->device == 0x2772)
+#define IS_I915GM(dev) (INTEL_DEVID(dev) == 0x2592)
+#define IS_I945G(dev) (INTEL_DEVID(dev) == 0x2772)
#define IS_I945GM(dev) (INTEL_INFO(dev)->is_i945gm)
#define IS_BROADWATER(dev) (INTEL_INFO(dev)->is_broadwater)
#define IS_CRESTLINE(dev) (INTEL_INFO(dev)->is_crestline)
-#define IS_GM45(dev) ((dev)->pdev->device == 0x2A42)
+#define IS_GM45(dev) (INTEL_DEVID(dev) == 0x2A42)
#define IS_G4X(dev) (INTEL_INFO(dev)->is_g4x)
-#define IS_PINEVIEW_G(dev) ((dev)->pdev->device == 0xa001)
-#define IS_PINEVIEW_M(dev) ((dev)->pdev->device == 0xa011)
+#define IS_PINEVIEW_G(dev) (INTEL_DEVID(dev) == 0xa001)
+#define IS_PINEVIEW_M(dev) (INTEL_DEVID(dev) == 0xa011)
#define IS_PINEVIEW(dev) (INTEL_INFO(dev)->is_pineview)
#define IS_G33(dev) (INTEL_INFO(dev)->is_g33)
-#define IS_IRONLAKE_M(dev) ((dev)->pdev->device == 0x0046)
+#define IS_IRONLAKE_M(dev) (INTEL_DEVID(dev) == 0x0046)
#define IS_IVYBRIDGE(dev) (INTEL_INFO(dev)->is_ivybridge)
-#define IS_IVB_GT1(dev) ((dev)->pdev->device == 0x0156 || \
- (dev)->pdev->device == 0x0152 || \
- (dev)->pdev->device == 0x015a)
-#define IS_SNB_GT1(dev) ((dev)->pdev->device == 0x0102 || \
- (dev)->pdev->device == 0x0106 || \
- (dev)->pdev->device == 0x010A)
+#define IS_IVB_GT1(dev) (INTEL_DEVID(dev) == 0x0156 || \
+ INTEL_DEVID(dev) == 0x0152 || \
+ INTEL_DEVID(dev) == 0x015a)
+#define IS_SNB_GT1(dev) (INTEL_DEVID(dev) == 0x0102 || \
+ INTEL_DEVID(dev) == 0x0106 || \
+ INTEL_DEVID(dev) == 0x010A)
#define IS_VALLEYVIEW(dev) (INTEL_INFO(dev)->is_valleyview)
#define IS_CHERRYVIEW(dev) (INTEL_INFO(dev)->is_valleyview && IS_GEN8(dev))
#define IS_HASWELL(dev) (INTEL_INFO(dev)->is_haswell)
#define IS_BROADWELL(dev) (!INTEL_INFO(dev)->is_valleyview && IS_GEN8(dev))
#define IS_MOBILE(dev) (INTEL_INFO(dev)->is_mobile)
#define IS_HSW_EARLY_SDV(dev) (IS_HASWELL(dev) && \
- ((dev)->pdev->device & 0xFF00) == 0x0C00)
+ (INTEL_DEVID(dev) & 0xFF00) == 0x0C00)
#define IS_BDW_ULT(dev) (IS_BROADWELL(dev) && \
- (((dev)->pdev->device & 0xf) == 0x2 || \
- ((dev)->pdev->device & 0xf) == 0x6 || \
- ((dev)->pdev->device & 0xf) == 0xe))
+ ((INTEL_DEVID(dev) & 0xf) == 0x2 || \
+ (INTEL_DEVID(dev) & 0xf) == 0x6 || \
+ (INTEL_DEVID(dev) & 0xf) == 0xe))
#define IS_HSW_ULT(dev) (IS_HASWELL(dev) && \
- ((dev)->pdev->device & 0xFF00) == 0x0A00)
+ (INTEL_DEVID(dev) & 0xFF00) == 0x0A00)
#define IS_ULT(dev) (IS_HSW_ULT(dev) || IS_BDW_ULT(dev))
#define IS_HSW_GT3(dev) (IS_HASWELL(dev) && \
- ((dev)->pdev->device & 0x00F0) == 0x0020)
+ (INTEL_DEVID(dev) & 0x00F0) == 0x0020)
/* ULX machines are also considered ULT. */
-#define IS_HSW_ULX(dev) ((dev)->pdev->device == 0x0A0E || \
- (dev)->pdev->device == 0x0A1E)
+#define IS_HSW_ULX(dev) (INTEL_DEVID(dev) == 0x0A0E || \
+ INTEL_DEVID(dev) == 0x0A1E)
#define IS_PRELIMINARY_HW(intel_info) ((intel_info)->is_preliminary)
/*
@@ -1940,12 +2119,11 @@ struct drm_i915_cmd_table {
#define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
#define HAS_HW_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 6)
-#define HAS_ALIASING_PPGTT(dev) (INTEL_INFO(dev)->gen >= 6 && \
- (!IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)))
-#define HAS_PPGTT(dev) (INTEL_INFO(dev)->gen >= 7 \
- && !IS_GEN8(dev))
-#define USES_PPGTT(dev) intel_enable_ppgtt(dev, false)
-#define USES_FULL_PPGTT(dev) intel_enable_ppgtt(dev, true)
+#define HAS_LOGICAL_RING_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 8)
+#define HAS_ALIASING_PPGTT(dev) (INTEL_INFO(dev)->gen >= 6)
+#define HAS_PPGTT(dev) (INTEL_INFO(dev)->gen >= 7 && !IS_GEN8(dev))
+#define USES_PPGTT(dev) (i915.enable_ppgtt)
+#define USES_FULL_PPGTT(dev) (i915.enable_ppgtt == 2)
#define HAS_OVERLAY(dev) (INTEL_INFO(dev)->has_overlay)
#define OVERLAY_NEEDS_PHYSICAL(dev) (INTEL_INFO(dev)->overlay_needs_physical)
@@ -1998,6 +2176,8 @@ struct drm_i915_cmd_table {
#define HAS_PCH_NOP(dev) (INTEL_PCH_TYPE(dev) == PCH_NOP)
#define HAS_PCH_SPLIT(dev) (INTEL_PCH_TYPE(dev) != PCH_NONE)
+#define HAS_GMCH_DISPLAY(dev) (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev))
+
/* DPF == dynamic parity feature */
#define HAS_L3_DPF(dev) (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
#define NUM_L3_SLICES(dev) (IS_HSW_GT3(dev) ? 2 : HAS_L3_DPF(dev))
@@ -2027,6 +2207,7 @@ struct i915_params {
int enable_rc6;
int enable_fbc;
int enable_ppgtt;
+ int enable_execlists;
int enable_psr;
unsigned int preliminary_hw_support;
int disable_power_well;
@@ -2040,6 +2221,8 @@ struct i915_params {
bool reset;
bool disable_display;
bool disable_vtd_wa;
+ int use_mmio_flip;
+ bool mmio_debug;
};
extern struct i915_params i915 __read_mostly;
@@ -2048,12 +2231,12 @@ void i915_update_dri1_breadcrumb(struct drm_device *dev);
extern void i915_kernel_lost_context(struct drm_device * dev);
extern int i915_driver_load(struct drm_device *, unsigned long flags);
extern int i915_driver_unload(struct drm_device *);
-extern int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv);
+extern int i915_driver_open(struct drm_device *dev, struct drm_file *file);
extern void i915_driver_lastclose(struct drm_device * dev);
extern void i915_driver_preclose(struct drm_device *dev,
- struct drm_file *file_priv);
+ struct drm_file *file);
extern void i915_driver_postclose(struct drm_device *dev,
- struct drm_file *file_priv);
+ struct drm_file *file);
extern int i915_driver_device_is_agp(struct drm_device * dev);
#ifdef CONFIG_COMPAT
extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
@@ -2069,8 +2252,7 @@ extern unsigned long i915_mch_val(struct drm_i915_private *dev_priv);
extern unsigned long i915_gfx_val(struct drm_i915_private *dev_priv);
extern void i915_update_gfx_val(struct drm_i915_private *dev_priv);
int vlv_force_gfx_clock(struct drm_i915_private *dev_priv, bool on);
-
-extern void intel_console_resume(struct work_struct *work);
+void intel_hpd_cancel_work(struct drm_i915_private *dev_priv);
/* i915_irq.c */
void i915_queue_hangcheck(struct drm_device *dev);
@@ -2084,10 +2266,12 @@ extern void intel_irq_init(struct drm_device *dev);
extern void intel_hpd_init(struct drm_device *dev);
extern void intel_uncore_sanitize(struct drm_device *dev);
-extern void intel_uncore_early_sanitize(struct drm_device *dev);
+extern void intel_uncore_early_sanitize(struct drm_device *dev,
+ bool restore_forcewake);
extern void intel_uncore_init(struct drm_device *dev);
extern void intel_uncore_check_errors(struct drm_device *dev);
extern void intel_uncore_fini(struct drm_device *dev);
+extern void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore);
void
i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
@@ -2117,6 +2301,20 @@ int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+void i915_gem_execbuffer_move_to_active(struct list_head *vmas,
+ struct intel_engine_cs *ring);
+void i915_gem_execbuffer_retire_commands(struct drm_device *dev,
+ struct drm_file *file,
+ struct intel_engine_cs *ring,
+ struct drm_i915_gem_object *obj);
+int i915_gem_ringbuffer_submission(struct drm_device *dev,
+ struct drm_file *file,
+ struct intel_engine_cs *ring,
+ struct intel_context *ctx,
+ struct drm_i915_gem_execbuffer2 *args,
+ struct list_head *vmas,
+ struct drm_i915_gem_object *batch_obj,
+ u64 exec_start, u32 flags);
int i915_gem_execbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_execbuffer2(struct drm_device *dev, void *data,
@@ -2151,6 +2349,12 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
int i915_gem_wait_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
void i915_gem_load(struct drm_device *dev);
+unsigned long i915_gem_shrink(struct drm_i915_private *dev_priv,
+ long target,
+ unsigned flags);
+#define I915_SHRINK_PURGEABLE 0x1
+#define I915_SHRINK_UNBOUND 0x2
+#define I915_SHRINK_BOUND 0x4
void *i915_gem_object_alloc(struct drm_device *dev);
void i915_gem_object_free(struct drm_i915_gem_object *obj);
void i915_gem_object_init(struct drm_i915_gem_object *obj,
@@ -2235,6 +2439,8 @@ bool i915_gem_retire_requests(struct drm_device *dev);
void i915_gem_retire_requests_ring(struct intel_engine_cs *ring);
int __must_check i915_gem_check_wedge(struct i915_gpu_error *error,
bool interruptible);
+int __must_check i915_gem_check_olr(struct intel_engine_cs *ring, u32 seqno);
+
static inline bool i915_reset_in_progress(struct i915_gpu_error *error)
{
return unlikely(atomic_read(&error->reset_counter)
@@ -2267,6 +2473,7 @@ void i915_gem_reset(struct drm_device *dev);
bool i915_gem_clflush_object(struct drm_i915_gem_object *obj, bool force);
int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj);
int __must_check i915_gem_init(struct drm_device *dev);
+int i915_gem_init_rings(struct drm_device *dev);
int __must_check i915_gem_init_hw(struct drm_device *dev);
int i915_gem_l3_remap(struct intel_engine_cs *ring, int slice);
void i915_gem_init_swizzling(struct drm_device *dev);
@@ -2337,7 +2544,7 @@ static inline bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj) {
}
/* Some GGTT VM helpers */
-#define obj_to_ggtt(obj) \
+#define i915_obj_to_ggtt(obj) \
(&((struct drm_i915_private *)(obj)->base.dev->dev_private)->gtt.base)
static inline bool i915_is_ggtt(struct i915_address_space *vm)
{
@@ -2346,21 +2553,30 @@ static inline bool i915_is_ggtt(struct i915_address_space *vm)
return vm == ggtt;
}
+static inline struct i915_hw_ppgtt *
+i915_vm_to_ppgtt(struct i915_address_space *vm)
+{
+ WARN_ON(i915_is_ggtt(vm));
+
+ return container_of(vm, struct i915_hw_ppgtt, base);
+}
+
+
static inline bool i915_gem_obj_ggtt_bound(struct drm_i915_gem_object *obj)
{
- return i915_gem_obj_bound(obj, obj_to_ggtt(obj));
+ return i915_gem_obj_bound(obj, i915_obj_to_ggtt(obj));
}
static inline unsigned long
i915_gem_obj_ggtt_offset(struct drm_i915_gem_object *obj)
{
- return i915_gem_obj_offset(obj, obj_to_ggtt(obj));
+ return i915_gem_obj_offset(obj, i915_obj_to_ggtt(obj));
}
static inline unsigned long
i915_gem_obj_ggtt_size(struct drm_i915_gem_object *obj)
{
- return i915_gem_obj_size(obj, obj_to_ggtt(obj));
+ return i915_gem_obj_size(obj, i915_obj_to_ggtt(obj));
}
static inline int __must_check
@@ -2368,7 +2584,8 @@ i915_gem_obj_ggtt_pin(struct drm_i915_gem_object *obj,
uint32_t alignment,
unsigned flags)
{
- return i915_gem_object_pin(obj, obj_to_ggtt(obj), alignment, flags | PIN_GLOBAL);
+ return i915_gem_object_pin(obj, i915_obj_to_ggtt(obj),
+ alignment, flags | PIN_GLOBAL);
}
static inline int
@@ -2380,7 +2597,6 @@ i915_gem_object_ggtt_unbind(struct drm_i915_gem_object *obj)
void i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj);
/* i915_gem_context.c */
-#define ctx_to_ppgtt(ctx) container_of((ctx)->vm, struct i915_hw_ppgtt, base)
int __must_check i915_gem_context_init(struct drm_device *dev);
void i915_gem_context_fini(struct drm_device *dev);
void i915_gem_context_reset(struct drm_device *dev);
@@ -2392,6 +2608,8 @@ int i915_switch_context(struct intel_engine_cs *ring,
struct intel_context *
i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id);
void i915_gem_context_free(struct kref *ctx_ref);
+struct drm_i915_gem_object *
+i915_gem_alloc_context_obj(struct drm_device *dev, size_t size);
static inline void i915_gem_context_reference(struct intel_context *ctx)
{
kref_get(&ctx->ref);
@@ -2404,7 +2622,7 @@ static inline void i915_gem_context_unreference(struct intel_context *ctx)
static inline bool i915_gem_context_is_default(const struct intel_context *c)
{
- return c->id == DEFAULT_CONTEXT_ID;
+ return c->user_handle == DEFAULT_CONTEXT_HANDLE;
}
int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
@@ -2412,8 +2630,6 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
struct drm_file *file);
-/* i915_gem_render_state.c */
-int i915_gem_render_state_init(struct intel_engine_cs *ring);
/* i915_gem_evict.c */
int __must_check i915_gem_evict_something(struct drm_device *dev,
struct i915_address_space *vm,
@@ -2435,7 +2651,7 @@ static inline void i915_gem_chipset_flush(struct drm_device *dev)
/* i915_gem_stolen.c */
int i915_gem_init_stolen(struct drm_device *dev);
-int i915_gem_stolen_setup_compression(struct drm_device *dev, int size);
+int i915_gem_stolen_setup_compression(struct drm_device *dev, int size, int fb_cpp);
void i915_gem_stolen_cleanup_compression(struct drm_device *dev);
void i915_gem_cleanup_stolen(struct drm_device *dev);
struct drm_i915_gem_object *
@@ -2445,7 +2661,6 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
u32 stolen_offset,
u32 gtt_offset,
u32 size);
-void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj);
/* i915_gem_tiling.c */
static inline bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj)
@@ -2482,6 +2697,7 @@ void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...);
int i915_error_state_to_str(struct drm_i915_error_state_buf *estr,
const struct i915_error_state_file_priv *error);
int i915_error_state_buf_init(struct drm_i915_error_state_buf *eb,
+ struct drm_i915_private *i915,
size_t count, loff_t pos);
static inline void i915_error_state_buf_release(
struct drm_i915_error_state_buf *eb)
@@ -2496,7 +2712,7 @@ void i915_error_state_put(struct i915_error_state_file_priv *error_priv);
void i915_destroy_error_state(struct drm_device *dev);
void i915_get_extra_instdone(struct drm_device *dev, uint32_t *instdone);
-const char *i915_cache_level_str(int type);
+const char *i915_cache_level_str(struct drm_i915_private *i915, int type);
/* i915_cmd_parser.c */
int i915_cmd_parser_get_version(void);
@@ -2588,13 +2804,14 @@ extern void intel_modeset_setup_hw_state(struct drm_device *dev,
extern void i915_redisable_vga(struct drm_device *dev);
extern void i915_redisable_vga_power_on(struct drm_device *dev);
extern bool intel_fbc_enabled(struct drm_device *dev);
+extern void gen8_fbc_sw_flush(struct drm_device *dev, u32 value);
extern void intel_disable_fbc(struct drm_device *dev);
extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
extern void intel_init_pch_refclk(struct drm_device *dev);
extern void gen6_set_rps(struct drm_device *dev, u8 val);
extern void valleyview_set_rps(struct drm_device *dev, u8 val);
-extern int valleyview_rps_max_freq(struct drm_i915_private *dev_priv);
-extern int valleyview_rps_min_freq(struct drm_i915_private *dev_priv);
+extern void intel_set_memory_cxsr(struct drm_i915_private *dev_priv,
+ bool enable);
extern void intel_detect_pch(struct drm_device *dev);
extern int intel_trans_dp_port_sel(struct drm_crtc *crtc);
extern int intel_enable_rc6(const struct drm_device *dev);
@@ -2605,6 +2822,8 @@ int i915_reg_read_ioctl(struct drm_device *dev, void *data,
int i915_get_reset_stats_ioctl(struct drm_device *dev, void *data,
struct drm_file *file);
+void intel_notify_mmio_flip(struct intel_engine_cs *ring);
+
/* overlay */
extern struct intel_overlay_error_state *intel_overlay_capture_error_state(struct drm_device *dev);
extern void intel_overlay_print_error_state(struct drm_i915_error_state_buf *e,
@@ -2700,10 +2919,10 @@ int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val);
static inline uint32_t i915_vgacntrl_reg(struct drm_device *dev)
{
- if (HAS_PCH_SPLIT(dev))
- return CPU_VGACNTRL;
- else if (IS_VALLEYVIEW(dev))
+ if (IS_VALLEYVIEW(dev))
return VLV_VGACNTRL;
+ else if (INTEL_INFO(dev)->gen >= 5)
+ return CPU_VGACNTRL;
else
return VGACNTRL;
}
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index f247d922e44a..28f91df2604d 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -60,7 +60,6 @@ static unsigned long i915_gem_shrinker_scan(struct shrinker *shrinker,
static int i915_gem_shrinker_oom(struct notifier_block *nb,
unsigned long event,
void *ptr);
-static unsigned long i915_gem_purge(struct drm_i915_private *dev_priv, long target);
static unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv);
static bool cpu_cache_is_coherent(struct drm_device *dev,
@@ -1085,7 +1084,13 @@ i915_gem_check_wedge(struct i915_gpu_error *error,
if (i915_terminally_wedged(error))
return -EIO;
- return -EAGAIN;
+ /*
+ * Check if GPU Reset is in progress - we need intel_ring_begin
+ * to work properly to reinit the hw state while the gpu is
+ * still marked as reset-in-progress. Handle this with a flag.
+ */
+ if (!error->reload_in_reset)
+ return -EAGAIN;
}
return 0;
@@ -1095,7 +1100,7 @@ i915_gem_check_wedge(struct i915_gpu_error *error,
* Compare seqno against outstanding lazy request. Emit a request if they are
* equal.
*/
-static int
+int
i915_gem_check_olr(struct intel_engine_cs *ring, u32 seqno)
{
int ret;
@@ -1161,14 +1166,14 @@ static int __wait_seqno(struct intel_engine_cs *ring, u32 seqno,
s64 before, now;
int ret;
- WARN(dev_priv->pm.irqs_disabled, "IRQs disabled\n");
+ WARN(!intel_irqs_enabled(dev_priv), "IRQs disabled");
if (i915_seqno_passed(ring->get_seqno(ring, true), seqno))
return 0;
timeout_expire = timeout ? jiffies + nsecs_to_jiffies((u64)*timeout) : 0;
- if (INTEL_INFO(dev)->gen >= 6 && can_wait_boost(file_priv)) {
+ if (INTEL_INFO(dev)->gen >= 6 && ring->id == RCS && can_wait_boost(file_priv)) {
gen6_rps_boost(dev_priv);
if (file_priv)
mod_delayed_work(dev_priv->wq,
@@ -1560,14 +1565,29 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
if (ret)
goto unpin;
- obj->fault_mappable = true;
-
+ /* Finally, remap it using the new GTT offset */
pfn = dev_priv->gtt.mappable_base + i915_gem_obj_ggtt_offset(obj);
pfn >>= PAGE_SHIFT;
- pfn += page_offset;
- /* Finally, remap it using the new GTT offset */
- ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
+ if (!obj->fault_mappable) {
+ unsigned long size = min_t(unsigned long,
+ vma->vm_end - vma->vm_start,
+ obj->base.size);
+ int i;
+
+ for (i = 0; i < size >> PAGE_SHIFT; i++) {
+ ret = vm_insert_pfn(vma,
+ (unsigned long)vma->vm_start + i * PAGE_SIZE,
+ pfn + i);
+ if (ret)
+ break;
+ }
+
+ obj->fault_mappable = true;
+ } else
+ ret = vm_insert_pfn(vma,
+ (unsigned long)vmf->virtual_address,
+ pfn + page_offset);
unpin:
i915_gem_object_ggtt_unpin(obj);
unlock:
@@ -1575,10 +1595,13 @@ unlock:
out:
switch (ret) {
case -EIO:
- /* If this -EIO is due to a gpu hang, give the reset code a
- * chance to clean up the mess. Otherwise return the proper
- * SIGBUS. */
- if (i915_terminally_wedged(&dev_priv->gpu_error)) {
+ /*
+ * We eat errors when the gpu is terminally wedged to avoid
+ * userspace unduly crashing (gl has no provisions for mmaps to
+ * fail). But any other -EIO isn't ours (e.g. swap in failure)
+ * and so needs to be reported.
+ */
+ if (!i915_terminally_wedged(&dev_priv->gpu_error)) {
ret = VM_FAULT_SIGBUS;
break;
}
@@ -1717,7 +1740,11 @@ static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj)
* offsets on purgeable objects by truncating it and marking it purged,
* which prevents userspace from ever using that object again.
*/
- i915_gem_purge(dev_priv, obj->base.size >> PAGE_SHIFT);
+ i915_gem_shrink(dev_priv,
+ obj->base.size >> PAGE_SHIFT,
+ I915_SHRINK_BOUND |
+ I915_SHRINK_UNBOUND |
+ I915_SHRINK_PURGEABLE);
ret = drm_gem_create_mmap_offset(&obj->base);
if (ret != -ENOSPC)
goto out;
@@ -1914,12 +1941,11 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
return 0;
}
-static unsigned long
-__i915_gem_shrink(struct drm_i915_private *dev_priv, long target,
- bool purgeable_only)
+unsigned long
+i915_gem_shrink(struct drm_i915_private *dev_priv,
+ long target, unsigned flags)
{
- struct list_head still_in_list;
- struct drm_i915_gem_object *obj;
+ const bool purgeable_only = flags & I915_SHRINK_PURGEABLE;
unsigned long count = 0;
/*
@@ -1941,62 +1967,68 @@ __i915_gem_shrink(struct drm_i915_private *dev_priv, long target,
* dev->struct_mutex and so we won't ever be able to observe an
* object on the bound_list with a reference count equals 0.
*/
- INIT_LIST_HEAD(&still_in_list);
- while (count < target && !list_empty(&dev_priv->mm.unbound_list)) {
- obj = list_first_entry(&dev_priv->mm.unbound_list,
- typeof(*obj), global_list);
- list_move_tail(&obj->global_list, &still_in_list);
+ if (flags & I915_SHRINK_UNBOUND) {
+ struct list_head still_in_list;
- if (!i915_gem_object_is_purgeable(obj) && purgeable_only)
- continue;
+ INIT_LIST_HEAD(&still_in_list);
+ while (count < target && !list_empty(&dev_priv->mm.unbound_list)) {
+ struct drm_i915_gem_object *obj;
- drm_gem_object_reference(&obj->base);
+ obj = list_first_entry(&dev_priv->mm.unbound_list,
+ typeof(*obj), global_list);
+ list_move_tail(&obj->global_list, &still_in_list);
- if (i915_gem_object_put_pages(obj) == 0)
- count += obj->base.size >> PAGE_SHIFT;
+ if (!i915_gem_object_is_purgeable(obj) && purgeable_only)
+ continue;
+
+ drm_gem_object_reference(&obj->base);
- drm_gem_object_unreference(&obj->base);
+ if (i915_gem_object_put_pages(obj) == 0)
+ count += obj->base.size >> PAGE_SHIFT;
+
+ drm_gem_object_unreference(&obj->base);
+ }
+ list_splice(&still_in_list, &dev_priv->mm.unbound_list);
}
- list_splice(&still_in_list, &dev_priv->mm.unbound_list);
- INIT_LIST_HEAD(&still_in_list);
- while (count < target && !list_empty(&dev_priv->mm.bound_list)) {
- struct i915_vma *vma, *v;
+ if (flags & I915_SHRINK_BOUND) {
+ struct list_head still_in_list;
- obj = list_first_entry(&dev_priv->mm.bound_list,
- typeof(*obj), global_list);
- list_move_tail(&obj->global_list, &still_in_list);
+ INIT_LIST_HEAD(&still_in_list);
+ while (count < target && !list_empty(&dev_priv->mm.bound_list)) {
+ struct drm_i915_gem_object *obj;
+ struct i915_vma *vma, *v;
- if (!i915_gem_object_is_purgeable(obj) && purgeable_only)
- continue;
+ obj = list_first_entry(&dev_priv->mm.bound_list,
+ typeof(*obj), global_list);
+ list_move_tail(&obj->global_list, &still_in_list);
- drm_gem_object_reference(&obj->base);
+ if (!i915_gem_object_is_purgeable(obj) && purgeable_only)
+ continue;
- list_for_each_entry_safe(vma, v, &obj->vma_list, vma_link)
- if (i915_vma_unbind(vma))
- break;
+ drm_gem_object_reference(&obj->base);
- if (i915_gem_object_put_pages(obj) == 0)
- count += obj->base.size >> PAGE_SHIFT;
+ list_for_each_entry_safe(vma, v, &obj->vma_list, vma_link)
+ if (i915_vma_unbind(vma))
+ break;
- drm_gem_object_unreference(&obj->base);
+ if (i915_gem_object_put_pages(obj) == 0)
+ count += obj->base.size >> PAGE_SHIFT;
+
+ drm_gem_object_unreference(&obj->base);
+ }
+ list_splice(&still_in_list, &dev_priv->mm.bound_list);
}
- list_splice(&still_in_list, &dev_priv->mm.bound_list);
return count;
}
static unsigned long
-i915_gem_purge(struct drm_i915_private *dev_priv, long target)
-{
- return __i915_gem_shrink(dev_priv, target, true);
-}
-
-static unsigned long
i915_gem_shrink_all(struct drm_i915_private *dev_priv)
{
i915_gem_evict_everything(dev_priv->dev);
- return __i915_gem_shrink(dev_priv, LONG_MAX, false);
+ return i915_gem_shrink(dev_priv, LONG_MAX,
+ I915_SHRINK_BOUND | I915_SHRINK_UNBOUND);
}
static int
@@ -2043,7 +2075,11 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
for (i = 0; i < page_count; i++) {
page = shmem_read_mapping_page_gfp(mapping, i, gfp);
if (IS_ERR(page)) {
- i915_gem_purge(dev_priv, page_count);
+ i915_gem_shrink(dev_priv,
+ page_count,
+ I915_SHRINK_BOUND |
+ I915_SHRINK_UNBOUND |
+ I915_SHRINK_PURGEABLE);
page = shmem_read_mapping_page_gfp(mapping, i, gfp);
}
if (IS_ERR(page)) {
@@ -2051,16 +2087,10 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
* our own buffer, now let the real VM do its job and
* go down in flames if truly OOM.
*/
- gfp &= ~(__GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD);
- gfp |= __GFP_IO | __GFP_WAIT;
-
i915_gem_shrink_all(dev_priv);
- page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+ page = shmem_read_mapping_page(mapping, i);
if (IS_ERR(page))
goto err_pages;
-
- gfp |= __GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD;
- gfp &= ~(__GFP_IO | __GFP_WAIT);
}
#ifdef CONFIG_SWIOTLB
if (swiotlb_nr_tbl()) {
@@ -2151,8 +2181,6 @@ static void
i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
struct intel_engine_cs *ring)
{
- struct drm_device *dev = obj->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
u32 seqno = intel_ring_get_seqno(ring);
BUG_ON(ring == NULL);
@@ -2171,19 +2199,6 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
list_move_tail(&obj->ring_list, &ring->active_list);
obj->last_read_seqno = seqno;
-
- if (obj->fenced_gpu_access) {
- obj->last_fenced_seqno = seqno;
-
- /* Bump MRU to take account of the delayed flush */
- if (obj->fence_reg != I915_FENCE_REG_NONE) {
- struct drm_i915_fence_reg *reg;
-
- reg = &dev_priv->fence_regs[obj->fence_reg];
- list_move_tail(&reg->lru_list,
- &dev_priv->mm.fence_list);
- }
- }
}
void i915_vma_move_to_active(struct i915_vma *vma,
@@ -2209,6 +2224,8 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
list_move_tail(&vma->mm_list, &vm->inactive_list);
}
+ intel_fb_obj_flush(obj, true);
+
list_del_init(&obj->ring_list);
obj->ring = NULL;
@@ -2217,7 +2234,6 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
obj->base.write_domain = 0;
obj->last_fenced_seqno = 0;
- obj->fenced_gpu_access = false;
obj->active = 0;
drm_gem_object_unreference(&obj->base);
@@ -2315,10 +2331,21 @@ int __i915_add_request(struct intel_engine_cs *ring,
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
struct drm_i915_gem_request *request;
+ struct intel_ringbuffer *ringbuf;
u32 request_ring_position, request_start;
int ret;
- request_start = intel_ring_get_tail(ring);
+ request = ring->preallocated_lazy_request;
+ if (WARN_ON(request == NULL))
+ return -ENOMEM;
+
+ if (i915.enable_execlists) {
+ struct intel_context *ctx = request->ctx;
+ ringbuf = ctx->engine[ring->id].ringbuf;
+ } else
+ ringbuf = ring->buffer;
+
+ request_start = intel_ring_get_tail(ringbuf);
/*
* Emit any outstanding flushes - execbuf can fail to emit the flush
* after having emitted the batchbuffer command. Hence we need to fix
@@ -2326,24 +2353,32 @@ int __i915_add_request(struct intel_engine_cs *ring,
* is that the flush _must_ happen before the next request, no matter
* what.
*/
- ret = intel_ring_flush_all_caches(ring);
- if (ret)
- return ret;
-
- request = ring->preallocated_lazy_request;
- if (WARN_ON(request == NULL))
- return -ENOMEM;
+ if (i915.enable_execlists) {
+ ret = logical_ring_flush_all_caches(ringbuf);
+ if (ret)
+ return ret;
+ } else {
+ ret = intel_ring_flush_all_caches(ring);
+ if (ret)
+ return ret;
+ }
/* Record the position of the start of the request so that
* should we detect the updated seqno part-way through the
* GPU processing the request, we never over-estimate the
* position of the head.
*/
- request_ring_position = intel_ring_get_tail(ring);
+ request_ring_position = intel_ring_get_tail(ringbuf);
- ret = ring->add_request(ring);
- if (ret)
- return ret;
+ if (i915.enable_execlists) {
+ ret = ring->emit_request(ringbuf);
+ if (ret)
+ return ret;
+ } else {
+ ret = ring->add_request(ring);
+ if (ret)
+ return ret;
+ }
request->seqno = intel_ring_get_seqno(ring);
request->ring = ring;
@@ -2358,12 +2393,14 @@ int __i915_add_request(struct intel_engine_cs *ring,
*/
request->batch_obj = obj;
- /* Hold a reference to the current context so that we can inspect
- * it later in case a hangcheck error event fires.
- */
- request->ctx = ring->last_context;
- if (request->ctx)
- i915_gem_context_reference(request->ctx);
+ if (!i915.enable_execlists) {
+ /* Hold a reference to the current context so that we can inspect
+ * it later in case a hangcheck error event fires.
+ */
+ request->ctx = ring->last_context;
+ if (request->ctx)
+ i915_gem_context_reference(request->ctx);
+ }
request->emitted_jiffies = jiffies;
list_add_tail(&request->list, &ring->request_list);
@@ -2534,6 +2571,18 @@ static void i915_gem_reset_ring_cleanup(struct drm_i915_private *dev_priv,
i915_gem_free_request(request);
}
+ while (!list_empty(&ring->execlist_queue)) {
+ struct intel_ctx_submit_request *submit_req;
+
+ submit_req = list_first_entry(&ring->execlist_queue,
+ struct intel_ctx_submit_request,
+ execlist_link);
+ list_del(&submit_req->execlist_link);
+ intel_runtime_pm_put(dev_priv);
+ i915_gem_context_unreference(submit_req->ctx);
+ kfree(submit_req);
+ }
+
/* These may not have been flush before the reset, do so now */
kfree(ring->preallocated_lazy_request);
ring->preallocated_lazy_request = NULL;
@@ -2618,6 +2667,7 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring)
while (!list_empty(&ring->request_list)) {
struct drm_i915_gem_request *request;
+ struct intel_ringbuffer *ringbuf;
request = list_first_entry(&ring->request_list,
struct drm_i915_gem_request,
@@ -2627,12 +2677,24 @@ i915_gem_retire_requests_ring(struct intel_engine_cs *ring)
break;
trace_i915_gem_request_retire(ring, request->seqno);
+
+ /* This is one of the few common intersection points
+ * between legacy ringbuffer submission and execlists:
+ * we need to tell them apart in order to find the correct
+ * ringbuffer to which the request belongs to.
+ */
+ if (i915.enable_execlists) {
+ struct intel_context *ctx = request->ctx;
+ ringbuf = ctx->engine[ring->id].ringbuf;
+ } else
+ ringbuf = ring->buffer;
+
/* We know the GPU must have read the request to have
* sent us the seqno + interrupt, so use the position
* of tail of the request to update the last known position
* of the GPU head.
*/
- ring->buffer->last_retired_head = request->tail;
+ ringbuf->last_retired_head = request->tail;
i915_gem_free_request(request);
}
@@ -2822,6 +2884,8 @@ i915_gem_object_sync(struct drm_i915_gem_object *obj,
idx = intel_ring_sync_index(from, to);
seqno = obj->last_read_seqno;
+ /* Optimization: Avoid semaphore sync when we are sure we already
+ * waited for an object with higher seqno */
if (seqno <= from->semaphore.sync_seqno[idx])
return 0;
@@ -2892,6 +2956,9 @@ int i915_vma_unbind(struct i915_vma *vma)
* cause memory corruption through use-after-free.
*/
+ /* Throw away the active reference before moving to the unbound list */
+ i915_gem_object_retire(obj);
+
if (i915_is_ggtt(vma->vm)) {
i915_gem_object_finish_gtt(obj);
@@ -2905,20 +2972,19 @@ int i915_vma_unbind(struct i915_vma *vma)
vma->unbind_vma(vma);
- i915_gem_gtt_finish_object(obj);
-
list_del_init(&vma->mm_list);
- /* Avoid an unnecessary call to unbind on rebind. */
if (i915_is_ggtt(vma->vm))
- obj->map_and_fenceable = true;
+ obj->map_and_fenceable = false;
drm_mm_remove_node(&vma->node);
i915_gem_vma_destroy(vma);
/* Since the unbound list is global, only move to that list if
* no more VMAs exist. */
- if (list_empty(&obj->vma_list))
+ if (list_empty(&obj->vma_list)) {
+ i915_gem_gtt_finish_object(obj);
list_move_tail(&obj->global_list, &dev_priv->mm.unbound_list);
+ }
/* And finally now the object is completely decoupled from this vma,
* we can drop its hold on the backing storage and allow it to be
@@ -2937,9 +3003,11 @@ int i915_gpu_idle(struct drm_device *dev)
/* Flush everything onto the inactive list. */
for_each_ring(ring, dev_priv, i) {
- ret = i915_switch_context(ring, ring->default_context);
- if (ret)
- return ret;
+ if (!i915.enable_execlists) {
+ ret = i915_switch_context(ring, ring->default_context);
+ if (ret)
+ return ret;
+ }
ret = intel_ring_idle(ring);
if (ret)
@@ -3153,7 +3221,6 @@ i915_gem_object_wait_fence(struct drm_i915_gem_object *obj)
obj->last_fenced_seqno = 0;
}
- obj->fenced_gpu_access = false;
return 0;
}
@@ -3260,6 +3327,9 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj)
return 0;
}
} else if (enable) {
+ if (WARN_ON(!obj->map_and_fenceable))
+ return -EINVAL;
+
reg = i915_find_fence_reg(dev);
if (IS_ERR(reg))
return PTR_ERR(reg);
@@ -3281,17 +3351,20 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj)
return 0;
}
-static bool i915_gem_valid_gtt_space(struct drm_device *dev,
- struct drm_mm_node *gtt_space,
+static bool i915_gem_valid_gtt_space(struct i915_vma *vma,
unsigned long cache_level)
{
+ struct drm_mm_node *gtt_space = &vma->node;
struct drm_mm_node *other;
- /* On non-LLC machines we have to be careful when putting differing
- * types of snoopable memory together to avoid the prefetcher
- * crossing memory domains and dying.
+ /*
+ * On some machines we have to be careful when putting differing types
+ * of snoopable memory together to avoid the prefetcher crossing memory
+ * domains and dying. During vm initialisation, we decide whether or not
+ * these constraints apply and set the drm_mm.color_adjust
+ * appropriately.
*/
- if (HAS_LLC(dev))
+ if (vma->vm->mm.color_adjust == NULL)
return true;
if (!drm_mm_node_allocated(gtt_space))
@@ -3429,8 +3502,7 @@ search_free:
goto err_free_vma;
}
- if (WARN_ON(!i915_gem_valid_gtt_space(dev, &vma->node,
- obj->cache_level))) {
+ if (WARN_ON(!i915_gem_valid_gtt_space(vma, obj->cache_level))) {
ret = -EINVAL;
goto err_remove_node;
}
@@ -3530,6 +3602,8 @@ i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj)
old_write_domain = obj->base.write_domain;
obj->base.write_domain = 0;
+ intel_fb_obj_flush(obj, false);
+
trace_i915_gem_object_change_domain(obj,
obj->base.read_domains,
old_write_domain);
@@ -3551,6 +3625,8 @@ i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj,
old_write_domain = obj->base.write_domain;
obj->base.write_domain = 0;
+ intel_fb_obj_flush(obj, false);
+
trace_i915_gem_object_change_domain(obj,
obj->base.read_domains,
old_write_domain);
@@ -3566,11 +3642,12 @@ int
i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
{
struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
+ struct i915_vma *vma = i915_gem_obj_to_ggtt(obj);
uint32_t old_write_domain, old_read_domains;
int ret;
/* Not valid to be called on unbound objects. */
- if (!i915_gem_obj_bound_any(obj))
+ if (vma == NULL)
return -EINVAL;
if (obj->base.write_domain == I915_GEM_DOMAIN_GTT)
@@ -3604,18 +3681,17 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
obj->dirty = 1;
}
+ if (write)
+ intel_fb_obj_invalidate(obj, NULL);
+
trace_i915_gem_object_change_domain(obj,
old_read_domains,
old_write_domain);
/* And bump the LRU for this access */
- if (i915_gem_object_is_inactive(obj)) {
- struct i915_vma *vma = i915_gem_obj_to_ggtt(obj);
- if (vma)
- list_move_tail(&vma->mm_list,
- &dev_priv->gtt.base.inactive_list);
-
- }
+ if (i915_gem_object_is_inactive(obj))
+ list_move_tail(&vma->mm_list,
+ &dev_priv->gtt.base.inactive_list);
return 0;
}
@@ -3636,7 +3712,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
}
list_for_each_entry_safe(vma, next, &obj->vma_list, vma_link) {
- if (!i915_gem_valid_gtt_space(dev, &vma->node, cache_level)) {
+ if (!i915_gem_valid_gtt_space(vma, cache_level)) {
ret = i915_vma_unbind(vma);
if (ret)
return ret;
@@ -3779,9 +3855,6 @@ static bool is_pin_display(struct drm_i915_gem_object *obj)
{
struct i915_vma *vma;
- if (list_empty(&obj->vma_list))
- return false;
-
vma = i915_gem_obj_to_ggtt(obj);
if (!vma)
return false;
@@ -3940,6 +4013,9 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write)
obj->base.write_domain = I915_GEM_DOMAIN_CPU;
}
+ if (write)
+ intel_fb_obj_invalidate(obj, NULL);
+
trace_i915_gem_object_change_domain(obj,
old_read_domains,
old_write_domain);
@@ -4305,8 +4381,6 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
obj->fence_reg = I915_FENCE_REG_NONE;
obj->madv = I915_MADV_WILLNEED;
- /* Avoid an unnecessary call to unbind on the first bind. */
- obj->map_and_fenceable = true;
i915_gem_info_add_obj(obj->base.dev->dev_private, obj->base.size);
}
@@ -4428,13 +4502,14 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
if (obj->stolen)
i915_gem_object_unpin_pages(obj);
+ WARN_ON(obj->frontbuffer_bits);
+
if (WARN_ON(obj->pages_pin_count))
obj->pages_pin_count = 0;
if (discard_backing_storage(obj))
obj->madv = I915_MADV_DONTNEED;
i915_gem_object_put_pages(obj);
i915_gem_object_free_mmap_offset(obj);
- i915_gem_object_release_stolen(obj);
BUG_ON(obj->pages);
@@ -4466,12 +4541,18 @@ struct i915_vma *i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
void i915_gem_vma_destroy(struct i915_vma *vma)
{
+ struct i915_address_space *vm = NULL;
WARN_ON(vma->node.allocated);
/* Keep the vma as a placeholder in the execbuffer reservation lists */
if (!list_empty(&vma->exec_list))
return;
+ vm = vma->vm;
+
+ if (!i915_is_ggtt(vm))
+ i915_ppgtt_put(i915_vm_to_ppgtt(vm));
+
list_del(&vma->vma_link);
kfree(vma);
@@ -4485,7 +4566,7 @@ i915_gem_stop_ringbuffers(struct drm_device *dev)
int i;
for_each_ring(ring, dev_priv, i)
- intel_stop_ring_buffer(ring);
+ dev_priv->gt.stop_ring(ring);
}
int
@@ -4521,7 +4602,7 @@ i915_gem_suspend(struct drm_device *dev)
del_timer_sync(&dev_priv->gpu_error.hangcheck_timer);
cancel_delayed_work_sync(&dev_priv->mm.retire_work);
- cancel_delayed_work_sync(&dev_priv->mm.idle_work);
+ flush_delayed_work(&dev_priv->mm.idle_work);
return 0;
@@ -4602,11 +4683,46 @@ intel_enable_blt(struct drm_device *dev)
return true;
}
-static int i915_gem_init_rings(struct drm_device *dev)
+static void init_unused_ring(struct drm_device *dev, u32 base)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(RING_CTL(base), 0);
+ I915_WRITE(RING_HEAD(base), 0);
+ I915_WRITE(RING_TAIL(base), 0);
+ I915_WRITE(RING_START(base), 0);
+}
+
+static void init_unused_rings(struct drm_device *dev)
+{
+ if (IS_I830(dev)) {
+ init_unused_ring(dev, PRB1_BASE);
+ init_unused_ring(dev, SRB0_BASE);
+ init_unused_ring(dev, SRB1_BASE);
+ init_unused_ring(dev, SRB2_BASE);
+ init_unused_ring(dev, SRB3_BASE);
+ } else if (IS_GEN2(dev)) {
+ init_unused_ring(dev, SRB0_BASE);
+ init_unused_ring(dev, SRB1_BASE);
+ } else if (IS_GEN3(dev)) {
+ init_unused_ring(dev, PRB1_BASE);
+ init_unused_ring(dev, PRB2_BASE);
+ }
+}
+
+int i915_gem_init_rings(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
+ /*
+ * At least 830 can leave some of the unused rings
+ * "active" (ie. head != tail) after resume which
+ * will prevent c3 entry. Makes sure all unused rings
+ * are totally idle.
+ */
+ init_unused_rings(dev);
+
ret = intel_init_render_ring_buffer(dev);
if (ret)
return ret;
@@ -4685,7 +4801,7 @@ i915_gem_init_hw(struct drm_device *dev)
i915_gem_init_swizzling(dev);
- ret = i915_gem_init_rings(dev);
+ ret = dev_priv->gt.init_rings(dev);
if (ret)
return ret;
@@ -4703,6 +4819,14 @@ i915_gem_init_hw(struct drm_device *dev)
if (ret && ret != -EIO) {
DRM_ERROR("Context enable failed %d\n", ret);
i915_gem_cleanup_ringbuffer(dev);
+
+ return ret;
+ }
+
+ ret = i915_ppgtt_init_hw(dev);
+ if (ret && ret != -EIO) {
+ DRM_ERROR("PPGTT enable failed %d\n", ret);
+ i915_gem_cleanup_ringbuffer(dev);
}
return ret;
@@ -4713,6 +4837,9 @@ int i915_gem_init(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
+ i915.enable_execlists = intel_sanitize_enable_execlists(dev,
+ i915.enable_execlists);
+
mutex_lock(&dev->struct_mutex);
if (IS_VALLEYVIEW(dev)) {
@@ -4723,7 +4850,24 @@ int i915_gem_init(struct drm_device *dev)
DRM_DEBUG_DRIVER("allow wake ack timed out\n");
}
- i915_gem_init_userptr(dev);
+ if (!i915.enable_execlists) {
+ dev_priv->gt.do_execbuf = i915_gem_ringbuffer_submission;
+ dev_priv->gt.init_rings = i915_gem_init_rings;
+ dev_priv->gt.cleanup_ring = intel_cleanup_ring_buffer;
+ dev_priv->gt.stop_ring = intel_stop_ring_buffer;
+ } else {
+ dev_priv->gt.do_execbuf = intel_execlists_submission;
+ dev_priv->gt.init_rings = intel_logical_rings_init;
+ dev_priv->gt.cleanup_ring = intel_logical_ring_cleanup;
+ dev_priv->gt.stop_ring = intel_logical_ring_stop;
+ }
+
+ ret = i915_gem_init_userptr(dev);
+ if (ret) {
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
+
i915_gem_init_global_gtt(dev);
ret = i915_gem_context_init(dev);
@@ -4758,7 +4902,7 @@ i915_gem_cleanup_ringbuffer(struct drm_device *dev)
int i;
for_each_ring(ring, dev_priv, i)
- intel_cleanup_ring_buffer(ring);
+ dev_priv->gt.cleanup_ring(ring);
}
int
@@ -4912,6 +5056,8 @@ i915_gem_load(struct drm_device *dev)
dev_priv->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom;
register_oom_notifier(&dev_priv->mm.oom_notifier);
+
+ mutex_init(&dev_priv->fb_tracking.lock);
}
void i915_gem_release(struct drm_device *dev, struct drm_file *file)
@@ -4973,6 +5119,23 @@ int i915_gem_open(struct drm_device *dev, struct drm_file *file)
return ret;
}
+void i915_gem_track_fb(struct drm_i915_gem_object *old,
+ struct drm_i915_gem_object *new,
+ unsigned frontbuffer_bits)
+{
+ if (old) {
+ WARN_ON(!mutex_is_locked(&old->base.dev->struct_mutex));
+ WARN_ON(!(old->frontbuffer_bits & frontbuffer_bits));
+ old->frontbuffer_bits &= ~frontbuffer_bits;
+ }
+
+ if (new) {
+ WARN_ON(!mutex_is_locked(&new->base.dev->struct_mutex));
+ WARN_ON(new->frontbuffer_bits & frontbuffer_bits);
+ new->frontbuffer_bits |= frontbuffer_bits;
+ }
+}
+
static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
{
if (!mutex_is_locked(mutex))
@@ -5051,16 +5214,15 @@ unsigned long i915_gem_obj_offset(struct drm_i915_gem_object *o,
struct drm_i915_private *dev_priv = o->base.dev->dev_private;
struct i915_vma *vma;
- if (!dev_priv->mm.aliasing_ppgtt ||
- vm == &dev_priv->mm.aliasing_ppgtt->base)
- vm = &dev_priv->gtt.base;
+ WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base);
- BUG_ON(list_empty(&o->vma_list));
list_for_each_entry(vma, &o->vma_list, vma_link) {
if (vma->vm == vm)
return vma->node.start;
}
+ WARN(1, "%s vma for this object not found.\n",
+ i915_is_ggtt(vm) ? "global" : "ppgtt");
return -1;
}
@@ -5093,9 +5255,7 @@ unsigned long i915_gem_obj_size(struct drm_i915_gem_object *o,
struct drm_i915_private *dev_priv = o->base.dev->dev_private;
struct i915_vma *vma;
- if (!dev_priv->mm.aliasing_ppgtt ||
- vm == &dev_priv->mm.aliasing_ppgtt->base)
- vm = &dev_priv->gtt.base;
+ WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base);
BUG_ON(list_empty(&o->vma_list));
@@ -5118,11 +5278,16 @@ i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
if (!i915_gem_shrinker_lock(dev, &unlock))
return SHRINK_STOP;
- freed = i915_gem_purge(dev_priv, sc->nr_to_scan);
+ freed = i915_gem_shrink(dev_priv,
+ sc->nr_to_scan,
+ I915_SHRINK_BOUND |
+ I915_SHRINK_UNBOUND |
+ I915_SHRINK_PURGEABLE);
if (freed < sc->nr_to_scan)
- freed += __i915_gem_shrink(dev_priv,
- sc->nr_to_scan - freed,
- false);
+ freed += i915_gem_shrink(dev_priv,
+ sc->nr_to_scan - freed,
+ I915_SHRINK_BOUND |
+ I915_SHRINK_UNBOUND);
if (unlock)
mutex_unlock(&dev->struct_mutex);
@@ -5141,8 +5306,11 @@ i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr)
bool was_interruptible;
bool unlock;
- while (!i915_gem_shrinker_lock(dev, &unlock) && --timeout)
+ while (!i915_gem_shrinker_lock(dev, &unlock) && --timeout) {
schedule_timeout_killable(1);
+ if (fatal_signal_pending(current))
+ return NOTIFY_DONE;
+ }
if (timeout == 0) {
pr_err("Unable to purge GPU memory due lock contention.\n");
return NOTIFY_DONE;
@@ -5197,14 +5365,8 @@ struct i915_vma *i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj)
{
struct i915_vma *vma;
- /* This WARN has probably outlived its usefulness (callers already
- * WARN if they don't find the GGTT vma they expect). When removing,
- * remember to remove the pre-check in is_pin_display() as well */
- if (WARN_ON(list_empty(&obj->vma_list)))
- return NULL;
-
vma = list_first_entry(&obj->vma_list, typeof(*vma), vma_link);
- if (vma->vm != obj_to_ggtt(obj))
+ if (vma->vm != i915_obj_to_ggtt(obj))
return NULL;
return vma;
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index a5ddf3bce9c3..a5221d8f1580 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -96,50 +96,6 @@
#define GEN6_CONTEXT_ALIGN (64<<10)
#define GEN7_CONTEXT_ALIGN 4096
-static void do_ppgtt_cleanup(struct i915_hw_ppgtt *ppgtt)
-{
- struct drm_device *dev = ppgtt->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct i915_address_space *vm = &ppgtt->base;
-
- if (ppgtt == dev_priv->mm.aliasing_ppgtt ||
- (list_empty(&vm->active_list) && list_empty(&vm->inactive_list))) {
- ppgtt->base.cleanup(&ppgtt->base);
- return;
- }
-
- /*
- * Make sure vmas are unbound before we take down the drm_mm
- *
- * FIXME: Proper refcounting should take care of this, this shouldn't be
- * needed at all.
- */
- if (!list_empty(&vm->active_list)) {
- struct i915_vma *vma;
-
- list_for_each_entry(vma, &vm->active_list, mm_list)
- if (WARN_ON(list_empty(&vma->vma_link) ||
- list_is_singular(&vma->vma_link)))
- break;
-
- i915_gem_evict_vm(&ppgtt->base, true);
- } else {
- i915_gem_retire_requests(dev);
- i915_gem_evict_vm(&ppgtt->base, false);
- }
-
- ppgtt->base.cleanup(&ppgtt->base);
-}
-
-static void ppgtt_release(struct kref *kref)
-{
- struct i915_hw_ppgtt *ppgtt =
- container_of(kref, struct i915_hw_ppgtt, ref);
-
- do_ppgtt_cleanup(ppgtt);
- kfree(ppgtt);
-}
-
static size_t get_context_alignment(struct drm_device *dev)
{
if (IS_GEN6(dev))
@@ -179,48 +135,52 @@ static int get_context_size(struct drm_device *dev)
void i915_gem_context_free(struct kref *ctx_ref)
{
struct intel_context *ctx = container_of(ctx_ref,
- typeof(*ctx), ref);
- struct i915_hw_ppgtt *ppgtt = NULL;
+ typeof(*ctx), ref);
- if (ctx->obj) {
- /* We refcount even the aliasing PPGTT to keep the code symmetric */
- if (USES_PPGTT(ctx->obj->base.dev))
- ppgtt = ctx_to_ppgtt(ctx);
+ if (i915.enable_execlists)
+ intel_lr_context_free(ctx);
- /* XXX: Free up the object before tearing down the address space, in
- * case we're bound in the PPGTT */
- drm_gem_object_unreference(&ctx->obj->base);
- }
+ i915_ppgtt_put(ctx->ppgtt);
- if (ppgtt)
- kref_put(&ppgtt->ref, ppgtt_release);
+ if (ctx->legacy_hw_ctx.rcs_state)
+ drm_gem_object_unreference(&ctx->legacy_hw_ctx.rcs_state->base);
list_del(&ctx->link);
kfree(ctx);
}
-static struct i915_hw_ppgtt *
-create_vm_for_ctx(struct drm_device *dev, struct intel_context *ctx)
+struct drm_i915_gem_object *
+i915_gem_alloc_context_obj(struct drm_device *dev, size_t size)
{
- struct i915_hw_ppgtt *ppgtt;
+ struct drm_i915_gem_object *obj;
int ret;
- ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
- if (!ppgtt)
+ obj = i915_gem_alloc_object(dev, size);
+ if (obj == NULL)
return ERR_PTR(-ENOMEM);
- ret = i915_gem_init_ppgtt(dev, ppgtt);
- if (ret) {
- kfree(ppgtt);
- return ERR_PTR(ret);
+ /*
+ * Try to make the context utilize L3 as well as LLC.
+ *
+ * On VLV we don't have L3 controls in the PTEs so we
+ * shouldn't touch the cache level, especially as that
+ * would make the object snooped which might have a
+ * negative performance impact.
+ */
+ if (INTEL_INFO(dev)->gen >= 7 && !IS_VALLEYVIEW(dev)) {
+ ret = i915_gem_object_set_cache_level(obj, I915_CACHE_L3_LLC);
+ /* Failure shouldn't ever happen this early */
+ if (WARN_ON(ret)) {
+ drm_gem_object_unreference(&obj->base);
+ return ERR_PTR(ret);
+ }
}
- ppgtt->ctx = ctx;
- return ppgtt;
+ return obj;
}
static struct intel_context *
__create_hw_context(struct drm_device *dev,
- struct drm_i915_file_private *file_priv)
+ struct drm_i915_file_private *file_priv)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_context *ctx;
@@ -234,40 +194,26 @@ __create_hw_context(struct drm_device *dev,
list_add_tail(&ctx->link, &dev_priv->context_list);
if (dev_priv->hw_context_size) {
- ctx->obj = i915_gem_alloc_object(dev, dev_priv->hw_context_size);
- if (ctx->obj == NULL) {
- ret = -ENOMEM;
+ struct drm_i915_gem_object *obj =
+ i915_gem_alloc_context_obj(dev, dev_priv->hw_context_size);
+ if (IS_ERR(obj)) {
+ ret = PTR_ERR(obj);
goto err_out;
}
-
- /*
- * Try to make the context utilize L3 as well as LLC.
- *
- * On VLV we don't have L3 controls in the PTEs so we
- * shouldn't touch the cache level, especially as that
- * would make the object snooped which might have a
- * negative performance impact.
- */
- if (INTEL_INFO(dev)->gen >= 7 && !IS_VALLEYVIEW(dev)) {
- ret = i915_gem_object_set_cache_level(ctx->obj,
- I915_CACHE_L3_LLC);
- /* Failure shouldn't ever happen this early */
- if (WARN_ON(ret))
- goto err_out;
- }
+ ctx->legacy_hw_ctx.rcs_state = obj;
}
/* Default context will never have a file_priv */
if (file_priv != NULL) {
ret = idr_alloc(&file_priv->context_idr, ctx,
- DEFAULT_CONTEXT_ID, 0, GFP_KERNEL);
+ DEFAULT_CONTEXT_HANDLE, 0, GFP_KERNEL);
if (ret < 0)
goto err_out;
} else
- ret = DEFAULT_CONTEXT_ID;
+ ret = DEFAULT_CONTEXT_HANDLE;
ctx->file_priv = file_priv;
- ctx->id = ret;
+ ctx->user_handle = ret;
/* NB: Mark all slices as needing a remap so that when the context first
* loads it will restore whatever remap state already exists. If there
* is no remap info, it will be a NOP. */
@@ -287,11 +233,9 @@ err_out:
*/
static struct intel_context *
i915_gem_create_context(struct drm_device *dev,
- struct drm_i915_file_private *file_priv,
- bool create_vm)
+ struct drm_i915_file_private *file_priv)
{
const bool is_global_default_ctx = file_priv == NULL;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_context *ctx;
int ret = 0;
@@ -301,7 +245,7 @@ i915_gem_create_context(struct drm_device *dev,
if (IS_ERR(ctx))
return ctx;
- if (is_global_default_ctx && ctx->obj) {
+ if (is_global_default_ctx && ctx->legacy_hw_ctx.rcs_state) {
/* We may need to do things with the shrinker which
* require us to immediately switch back to the default
* context. This can cause a problem as pinning the
@@ -309,7 +253,7 @@ i915_gem_create_context(struct drm_device *dev,
* be available. To avoid this we always pin the default
* context.
*/
- ret = i915_gem_obj_ggtt_pin(ctx->obj,
+ ret = i915_gem_obj_ggtt_pin(ctx->legacy_hw_ctx.rcs_state,
get_context_alignment(dev), 0);
if (ret) {
DRM_DEBUG_DRIVER("Couldn't pin %d\n", ret);
@@ -317,40 +261,24 @@ i915_gem_create_context(struct drm_device *dev,
}
}
- if (create_vm) {
- struct i915_hw_ppgtt *ppgtt = create_vm_for_ctx(dev, ctx);
+ if (USES_FULL_PPGTT(dev)) {
+ struct i915_hw_ppgtt *ppgtt = i915_ppgtt_create(dev, file_priv);
if (IS_ERR_OR_NULL(ppgtt)) {
DRM_DEBUG_DRIVER("PPGTT setup failed (%ld)\n",
PTR_ERR(ppgtt));
ret = PTR_ERR(ppgtt);
goto err_unpin;
- } else
- ctx->vm = &ppgtt->base;
-
- /* This case is reserved for the global default context and
- * should only happen once. */
- if (is_global_default_ctx) {
- if (WARN_ON(dev_priv->mm.aliasing_ppgtt)) {
- ret = -EEXIST;
- goto err_unpin;
- }
-
- dev_priv->mm.aliasing_ppgtt = ppgtt;
}
- } else if (USES_PPGTT(dev)) {
- /* For platforms which only have aliasing PPGTT, we fake the
- * address space and refcounting. */
- ctx->vm = &dev_priv->mm.aliasing_ppgtt->base;
- kref_get(&dev_priv->mm.aliasing_ppgtt->ref);
- } else
- ctx->vm = &dev_priv->gtt.base;
+
+ ctx->ppgtt = ppgtt;
+ }
return ctx;
err_unpin:
- if (is_global_default_ctx && ctx->obj)
- i915_gem_object_ggtt_unpin(ctx->obj);
+ if (is_global_default_ctx && ctx->legacy_hw_ctx.rcs_state)
+ i915_gem_object_ggtt_unpin(ctx->legacy_hw_ctx.rcs_state);
err_destroy:
i915_gem_context_unreference(ctx);
return ERR_PTR(ret);
@@ -361,30 +289,23 @@ void i915_gem_context_reset(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int i;
- /* Prevent the hardware from restoring the last context (which hung) on
- * the next switch */
+ /* In execlists mode we will unreference the context when the execlist
+ * queue is cleared and the requests destroyed.
+ */
+ if (i915.enable_execlists)
+ return;
+
for (i = 0; i < I915_NUM_RINGS; i++) {
struct intel_engine_cs *ring = &dev_priv->ring[i];
- struct intel_context *dctx = ring->default_context;
-
- /* Do a fake switch to the default context */
- if (ring->last_context == dctx)
- continue;
+ struct intel_context *lctx = ring->last_context;
- if (!ring->last_context)
- continue;
+ if (lctx) {
+ if (lctx->legacy_hw_ctx.rcs_state && i == RCS)
+ i915_gem_object_ggtt_unpin(lctx->legacy_hw_ctx.rcs_state);
- if (dctx->obj && i == RCS) {
- WARN_ON(i915_gem_obj_ggtt_pin(dctx->obj,
- get_context_alignment(dev), 0));
- /* Fake a finish/inactive */
- dctx->obj->base.write_domain = 0;
- dctx->obj->active = 0;
+ i915_gem_context_unreference(lctx);
+ ring->last_context = NULL;
}
-
- i915_gem_context_unreference(ring->last_context);
- i915_gem_context_reference(dctx);
- ring->last_context = dctx;
}
}
@@ -399,7 +320,11 @@ int i915_gem_context_init(struct drm_device *dev)
if (WARN_ON(dev_priv->ring[RCS].default_context))
return 0;
- if (HAS_HW_CONTEXTS(dev)) {
+ if (i915.enable_execlists) {
+ /* NB: intentionally left blank. We will allocate our own
+ * backing objects as we need them, thank you very much */
+ dev_priv->hw_context_size = 0;
+ } else if (HAS_HW_CONTEXTS(dev)) {
dev_priv->hw_context_size = round_up(get_context_size(dev), 4096);
if (dev_priv->hw_context_size > (1<<20)) {
DRM_DEBUG_DRIVER("Disabling HW Contexts; invalid size %d\n",
@@ -408,18 +333,23 @@ int i915_gem_context_init(struct drm_device *dev)
}
}
- ctx = i915_gem_create_context(dev, NULL, USES_PPGTT(dev));
+ ctx = i915_gem_create_context(dev, NULL);
if (IS_ERR(ctx)) {
DRM_ERROR("Failed to create default global context (error %ld)\n",
PTR_ERR(ctx));
return PTR_ERR(ctx);
}
- /* NB: RCS will hold a ref for all rings */
- for (i = 0; i < I915_NUM_RINGS; i++)
- dev_priv->ring[i].default_context = ctx;
+ for (i = 0; i < I915_NUM_RINGS; i++) {
+ struct intel_engine_cs *ring = &dev_priv->ring[i];
+
+ /* NB: RCS will hold a ref for all rings */
+ ring->default_context = ctx;
+ }
- DRM_DEBUG_DRIVER("%s context support initialized\n", dev_priv->hw_context_size ? "HW" : "fake");
+ DRM_DEBUG_DRIVER("%s context support initialized\n",
+ i915.enable_execlists ? "LR" :
+ dev_priv->hw_context_size ? "HW" : "fake");
return 0;
}
@@ -429,7 +359,7 @@ void i915_gem_context_fini(struct drm_device *dev)
struct intel_context *dctx = dev_priv->ring[RCS].default_context;
int i;
- if (dctx->obj) {
+ if (dctx->legacy_hw_ctx.rcs_state) {
/* The only known way to stop the gpu from accessing the hw context is
* to reset it. Do this as the very last operation to avoid confusing
* other code, leading to spurious errors. */
@@ -444,13 +374,13 @@ void i915_gem_context_fini(struct drm_device *dev)
WARN_ON(!dev_priv->ring[RCS].last_context);
if (dev_priv->ring[RCS].last_context == dctx) {
/* Fake switch to NULL context */
- WARN_ON(dctx->obj->active);
- i915_gem_object_ggtt_unpin(dctx->obj);
+ WARN_ON(dctx->legacy_hw_ctx.rcs_state->active);
+ i915_gem_object_ggtt_unpin(dctx->legacy_hw_ctx.rcs_state);
i915_gem_context_unreference(dctx);
dev_priv->ring[RCS].last_context = NULL;
}
- i915_gem_object_ggtt_unpin(dctx->obj);
+ i915_gem_object_ggtt_unpin(dctx->legacy_hw_ctx.rcs_state);
}
for (i = 0; i < I915_NUM_RINGS; i++) {
@@ -471,19 +401,11 @@ int i915_gem_context_enable(struct drm_i915_private *dev_priv)
struct intel_engine_cs *ring;
int ret, i;
- /* This is the only place the aliasing PPGTT gets enabled, which means
- * it has to happen before we bail on reset */
- if (dev_priv->mm.aliasing_ppgtt) {
- struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
- ppgtt->enable(ppgtt);
- }
+ BUG_ON(!dev_priv->ring[RCS].default_context);
- /* FIXME: We should make this work, even in reset */
- if (i915_reset_in_progress(&dev_priv->gpu_error))
+ if (i915.enable_execlists)
return 0;
- BUG_ON(!dev_priv->ring[RCS].default_context);
-
for_each_ring(ring, dev_priv, i) {
ret = i915_switch_context(ring, ring->default_context);
if (ret)
@@ -509,7 +431,7 @@ int i915_gem_context_open(struct drm_device *dev, struct drm_file *file)
idr_init(&file_priv->context_idr);
mutex_lock(&dev->struct_mutex);
- ctx = i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev));
+ ctx = i915_gem_create_context(dev, file_priv);
mutex_unlock(&dev->struct_mutex);
if (IS_ERR(ctx)) {
@@ -545,6 +467,7 @@ mi_set_context(struct intel_engine_cs *ring,
struct intel_context *new_context,
u32 hw_flags)
{
+ u32 flags = hw_flags | MI_MM_SPACE_GTT;
int ret;
/* w/a: If Flush TLB Invalidation Mode is enabled, driver must do a TLB
@@ -558,6 +481,10 @@ mi_set_context(struct intel_engine_cs *ring,
return ret;
}
+ /* These flags are for resource streamer on HSW+ */
+ if (!IS_HASWELL(ring->dev) && INTEL_INFO(ring->dev)->gen < 8)
+ flags |= (MI_SAVE_EXT_STATE_EN | MI_RESTORE_EXT_STATE_EN);
+
ret = intel_ring_begin(ring, 6);
if (ret)
return ret;
@@ -570,11 +497,8 @@ mi_set_context(struct intel_engine_cs *ring,
intel_ring_emit(ring, MI_NOOP);
intel_ring_emit(ring, MI_SET_CONTEXT);
- intel_ring_emit(ring, i915_gem_obj_ggtt_offset(new_context->obj) |
- MI_MM_SPACE_GTT |
- MI_SAVE_EXT_STATE_EN |
- MI_RESTORE_EXT_STATE_EN |
- hw_flags);
+ intel_ring_emit(ring, i915_gem_obj_ggtt_offset(new_context->legacy_hw_ctx.rcs_state) |
+ flags);
/*
* w/a: MI_SET_CONTEXT must always be followed by MI_NOOP
* WaMiSetContext_Hang:snb,ivb,vlv
@@ -596,22 +520,21 @@ static int do_switch(struct intel_engine_cs *ring,
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
struct intel_context *from = ring->last_context;
- struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(to);
u32 hw_flags = 0;
bool uninitialized = false;
int ret, i;
if (from != NULL && ring == &dev_priv->ring[RCS]) {
- BUG_ON(from->obj == NULL);
- BUG_ON(!i915_gem_obj_is_pinned(from->obj));
+ BUG_ON(from->legacy_hw_ctx.rcs_state == NULL);
+ BUG_ON(!i915_gem_obj_is_pinned(from->legacy_hw_ctx.rcs_state));
}
- if (from == to && from->last_ring == ring && !to->remap_slice)
+ if (from == to && !to->remap_slice)
return 0;
/* Trying to pin first makes error handling easier. */
if (ring == &dev_priv->ring[RCS]) {
- ret = i915_gem_obj_ggtt_pin(to->obj,
+ ret = i915_gem_obj_ggtt_pin(to->legacy_hw_ctx.rcs_state,
get_context_alignment(ring->dev), 0);
if (ret)
return ret;
@@ -624,8 +547,8 @@ static int do_switch(struct intel_engine_cs *ring,
*/
from = ring->last_context;
- if (USES_FULL_PPGTT(ring->dev)) {
- ret = ppgtt->switch_mm(ppgtt, ring, false);
+ if (to->ppgtt) {
+ ret = to->ppgtt->switch_mm(to->ppgtt, ring);
if (ret)
goto unpin_out;
}
@@ -644,17 +567,17 @@ static int do_switch(struct intel_engine_cs *ring,
*
* XXX: We need a real interface to do this instead of trickery.
*/
- ret = i915_gem_object_set_to_gtt_domain(to->obj, false);
+ ret = i915_gem_object_set_to_gtt_domain(to->legacy_hw_ctx.rcs_state, false);
if (ret)
goto unpin_out;
- if (!to->obj->has_global_gtt_mapping) {
- struct i915_vma *vma = i915_gem_obj_to_vma(to->obj,
+ if (!to->legacy_hw_ctx.rcs_state->has_global_gtt_mapping) {
+ struct i915_vma *vma = i915_gem_obj_to_vma(to->legacy_hw_ctx.rcs_state,
&dev_priv->gtt.base);
- vma->bind_vma(vma, to->obj->cache_level, GLOBAL_BIND);
+ vma->bind_vma(vma, to->legacy_hw_ctx.rcs_state->cache_level, GLOBAL_BIND);
}
- if (!to->is_initialized || i915_gem_context_is_default(to))
+ if (!to->legacy_hw_ctx.initialized || i915_gem_context_is_default(to))
hw_flags |= MI_RESTORE_INHIBIT;
ret = mi_set_context(ring, to, hw_flags);
@@ -680,8 +603,8 @@ static int do_switch(struct intel_engine_cs *ring,
* MI_SET_CONTEXT instead of when the next seqno has completed.
*/
if (from != NULL) {
- from->obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
- i915_vma_move_to_active(i915_gem_obj_to_ggtt(from->obj), ring);
+ from->legacy_hw_ctx.rcs_state->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
+ i915_vma_move_to_active(i915_gem_obj_to_ggtt(from->legacy_hw_ctx.rcs_state), ring);
/* As long as MI_SET_CONTEXT is serializing, ie. it flushes the
* whole damn pipeline, we don't need to explicitly mark the
* object dirty. The only exception is that the context must be
@@ -689,23 +612,28 @@ static int do_switch(struct intel_engine_cs *ring,
* able to defer doing this until we know the object would be
* swapped, but there is no way to do that yet.
*/
- from->obj->dirty = 1;
- BUG_ON(from->obj->ring != ring);
+ from->legacy_hw_ctx.rcs_state->dirty = 1;
+ BUG_ON(from->legacy_hw_ctx.rcs_state->ring != ring);
/* obj is kept alive until the next request by its active ref */
- i915_gem_object_ggtt_unpin(from->obj);
+ i915_gem_object_ggtt_unpin(from->legacy_hw_ctx.rcs_state);
i915_gem_context_unreference(from);
}
- uninitialized = !to->is_initialized && from == NULL;
- to->is_initialized = true;
+ uninitialized = !to->legacy_hw_ctx.initialized && from == NULL;
+ to->legacy_hw_ctx.initialized = true;
done:
i915_gem_context_reference(to);
ring->last_context = to;
- to->last_ring = ring;
if (uninitialized) {
+ if (ring->init_context) {
+ ret = ring->init_context(ring);
+ if (ret)
+ DRM_ERROR("ring init context: %d\n", ret);
+ }
+
ret = i915_gem_render_state_init(ring);
if (ret)
DRM_ERROR("init render state: %d\n", ret);
@@ -715,7 +643,7 @@ done:
unpin_out:
if (ring->id == RCS)
- i915_gem_object_ggtt_unpin(to->obj);
+ i915_gem_object_ggtt_unpin(to->legacy_hw_ctx.rcs_state);
return ret;
}
@@ -726,17 +654,22 @@ unpin_out:
*
* The context life cycle is simple. The context refcount is incremented and
* decremented by 1 and create and destroy. If the context is in use by the GPU,
- * it will have a refoucnt > 1. This allows us to destroy the context abstract
+ * it will have a refcount > 1. This allows us to destroy the context abstract
* object while letting the normal object tracking destroy the backing BO.
+ *
+ * This function should not be used in execlists mode. Instead the context is
+ * switched by writing to the ELSP and requests keep a reference to their
+ * context.
*/
int i915_switch_context(struct intel_engine_cs *ring,
struct intel_context *to)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ WARN_ON(i915.enable_execlists);
WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
- if (to->obj == NULL) { /* We have the fake context */
+ if (to->legacy_hw_ctx.rcs_state == NULL) { /* We have the fake context */
if (to != ring->last_context) {
i915_gem_context_reference(to);
if (ring->last_context)
@@ -749,9 +682,9 @@ int i915_switch_context(struct intel_engine_cs *ring,
return do_switch(ring, to);
}
-static bool hw_context_enabled(struct drm_device *dev)
+static bool contexts_enabled(struct drm_device *dev)
{
- return to_i915(dev)->hw_context_size;
+ return i915.enable_execlists || to_i915(dev)->hw_context_size;
}
int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
@@ -762,19 +695,19 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
struct intel_context *ctx;
int ret;
- if (!hw_context_enabled(dev))
+ if (!contexts_enabled(dev))
return -ENODEV;
ret = i915_mutex_lock_interruptible(dev);
if (ret)
return ret;
- ctx = i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev));
+ ctx = i915_gem_create_context(dev, file_priv);
mutex_unlock(&dev->struct_mutex);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- args->ctx_id = ctx->id;
+ args->ctx_id = ctx->user_handle;
DRM_DEBUG_DRIVER("HW context %d created\n", args->ctx_id);
return 0;
@@ -788,7 +721,7 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
struct intel_context *ctx;
int ret;
- if (args->ctx_id == DEFAULT_CONTEXT_ID)
+ if (args->ctx_id == DEFAULT_CONTEXT_HANDLE)
return -ENOENT;
ret = i915_mutex_lock_interruptible(dev);
@@ -801,7 +734,7 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
return PTR_ERR(ctx);
}
- idr_remove(&ctx->file_priv->context_idr, ctx->id);
+ idr_remove(&ctx->file_priv->context_idr, ctx->user_handle);
i915_gem_context_unreference(ctx);
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index bbf4b12d842e..886ff2ee7a28 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -243,7 +243,7 @@ int
i915_gem_evict_everything(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct i915_address_space *vm;
+ struct i915_address_space *vm, *v;
bool lists_empty = true;
int ret;
@@ -270,7 +270,7 @@ i915_gem_evict_everything(struct drm_device *dev)
i915_gem_retire_requests(dev);
/* Having flushed everything, unbind() should never raise an error */
- list_for_each_entry(vm, &dev_priv->vm_list, global_link)
+ list_for_each_entry_safe(vm, v, &dev_priv->vm_list, global_link)
WARN_ON(i915_gem_evict_vm(vm, false));
return 0;
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 3a30133f93e8..1a0611bb576b 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -35,6 +35,7 @@
#define __EXEC_OBJECT_HAS_PIN (1<<31)
#define __EXEC_OBJECT_HAS_FENCE (1<<30)
+#define __EXEC_OBJECT_NEEDS_MAP (1<<29)
#define __EXEC_OBJECT_NEEDS_BIAS (1<<28)
#define BATCH_OFFSET_BIAS (256*1024)
@@ -94,7 +95,6 @@ eb_lookup_vmas(struct eb_vmas *eb,
struct i915_address_space *vm,
struct drm_file *file)
{
- struct drm_i915_private *dev_priv = vm->dev->dev_private;
struct drm_i915_gem_object *obj;
struct list_head objects;
int i, ret;
@@ -129,20 +129,6 @@ eb_lookup_vmas(struct eb_vmas *eb,
i = 0;
while (!list_empty(&objects)) {
struct i915_vma *vma;
- struct i915_address_space *bind_vm = vm;
-
- if (exec[i].flags & EXEC_OBJECT_NEEDS_GTT &&
- USES_FULL_PPGTT(vm->dev)) {
- ret = -EINVAL;
- goto err;
- }
-
- /* If we have secure dispatch, or the userspace assures us that
- * they know what they're doing, use the GGTT VM.
- */
- if (((args->flags & I915_EXEC_SECURE) &&
- (i == (args->buffer_count - 1))))
- bind_vm = &dev_priv->gtt.base;
obj = list_first_entry(&objects,
struct drm_i915_gem_object,
@@ -156,7 +142,7 @@ eb_lookup_vmas(struct eb_vmas *eb,
* from the (obj, vm) we don't run the risk of creating
* duplicated vmas for the same vm.
*/
- vma = i915_gem_obj_lookup_or_create_vma(obj, bind_vm);
+ vma = i915_gem_obj_lookup_or_create_vma(obj, vm);
if (IS_ERR(vma)) {
DRM_DEBUG("Failed to lookup VMA\n");
ret = PTR_ERR(vma);
@@ -307,7 +293,7 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj,
struct drm_device *dev = obj->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
uint64_t delta = reloc->delta + target_offset;
- uint32_t __iomem *reloc_entry;
+ uint64_t offset;
void __iomem *reloc_page;
int ret;
@@ -320,25 +306,24 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj,
return ret;
/* Map the page containing the relocation we're going to perform. */
- reloc->offset += i915_gem_obj_ggtt_offset(obj);
+ offset = i915_gem_obj_ggtt_offset(obj);
+ offset += reloc->offset;
reloc_page = io_mapping_map_atomic_wc(dev_priv->gtt.mappable,
- reloc->offset & PAGE_MASK);
- reloc_entry = (uint32_t __iomem *)
- (reloc_page + offset_in_page(reloc->offset));
- iowrite32(lower_32_bits(delta), reloc_entry);
+ offset & PAGE_MASK);
+ iowrite32(lower_32_bits(delta), reloc_page + offset_in_page(offset));
if (INTEL_INFO(dev)->gen >= 8) {
- reloc_entry += 1;
+ offset += sizeof(uint32_t);
- if (offset_in_page(reloc->offset + sizeof(uint32_t)) == 0) {
+ if (offset_in_page(offset) == 0) {
io_mapping_unmap_atomic(reloc_page);
- reloc_page = io_mapping_map_atomic_wc(
- dev_priv->gtt.mappable,
- reloc->offset + sizeof(uint32_t));
- reloc_entry = reloc_page;
+ reloc_page =
+ io_mapping_map_atomic_wc(dev_priv->gtt.mappable,
+ offset);
}
- iowrite32(upper_32_bits(delta), reloc_entry);
+ iowrite32(upper_32_bits(delta),
+ reloc_page + offset_in_page(offset));
}
io_mapping_unmap_atomic(reloc_page);
@@ -535,34 +520,18 @@ i915_gem_execbuffer_relocate(struct eb_vmas *eb)
}
static int
-need_reloc_mappable(struct i915_vma *vma)
-{
- struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
- return entry->relocation_count && !use_cpu_reloc(vma->obj) &&
- i915_is_ggtt(vma->vm);
-}
-
-static int
i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
struct intel_engine_cs *ring,
bool *need_reloc)
{
struct drm_i915_gem_object *obj = vma->obj;
struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
- bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
- bool need_fence;
uint64_t flags;
int ret;
flags = 0;
-
- need_fence =
- has_fenced_gpu_access &&
- entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
- obj->tiling_mode != I915_TILING_NONE;
- if (need_fence || need_reloc_mappable(vma))
+ if (entry->flags & __EXEC_OBJECT_NEEDS_MAP)
flags |= PIN_MAPPABLE;
-
if (entry->flags & EXEC_OBJECT_NEEDS_GTT)
flags |= PIN_GLOBAL;
if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS)
@@ -574,17 +543,13 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
entry->flags |= __EXEC_OBJECT_HAS_PIN;
- if (has_fenced_gpu_access) {
- if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) {
- ret = i915_gem_object_get_fence(obj);
- if (ret)
- return ret;
-
- if (i915_gem_object_pin_fence(obj))
- entry->flags |= __EXEC_OBJECT_HAS_FENCE;
+ if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) {
+ ret = i915_gem_object_get_fence(obj);
+ if (ret)
+ return ret;
- obj->pending_fenced_gpu_access = true;
- }
+ if (i915_gem_object_pin_fence(obj))
+ entry->flags |= __EXEC_OBJECT_HAS_FENCE;
}
if (entry->offset != vma->node.start) {
@@ -601,26 +566,40 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
}
static bool
-eb_vma_misplaced(struct i915_vma *vma, bool has_fenced_gpu_access)
+need_reloc_mappable(struct i915_vma *vma)
{
struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
- struct drm_i915_gem_object *obj = vma->obj;
- bool need_fence, need_mappable;
- need_fence =
- has_fenced_gpu_access &&
- entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
- obj->tiling_mode != I915_TILING_NONE;
- need_mappable = need_fence || need_reloc_mappable(vma);
+ if (entry->relocation_count == 0)
+ return false;
+
+ if (!i915_is_ggtt(vma->vm))
+ return false;
- WARN_ON((need_mappable || need_fence) &&
+ /* See also use_cpu_reloc() */
+ if (HAS_LLC(vma->obj->base.dev))
+ return false;
+
+ if (vma->obj->base.write_domain == I915_GEM_DOMAIN_CPU)
+ return false;
+
+ return true;
+}
+
+static bool
+eb_vma_misplaced(struct i915_vma *vma)
+{
+ struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
+ struct drm_i915_gem_object *obj = vma->obj;
+
+ WARN_ON(entry->flags & __EXEC_OBJECT_NEEDS_MAP &&
!i915_is_ggtt(vma->vm));
if (entry->alignment &&
vma->node.start & (entry->alignment - 1))
return true;
- if (need_mappable && !obj->map_and_fenceable)
+ if (entry->flags & __EXEC_OBJECT_NEEDS_MAP && !obj->map_and_fenceable)
return true;
if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS &&
@@ -642,9 +621,6 @@ i915_gem_execbuffer_reserve(struct intel_engine_cs *ring,
bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
int retry;
- if (list_empty(vmas))
- return 0;
-
i915_gem_retire_requests_ring(ring);
vm = list_first_entry(vmas, struct i915_vma, exec_list)->vm;
@@ -658,20 +634,21 @@ i915_gem_execbuffer_reserve(struct intel_engine_cs *ring,
obj = vma->obj;
entry = vma->exec_entry;
+ if (!has_fenced_gpu_access)
+ entry->flags &= ~EXEC_OBJECT_NEEDS_FENCE;
need_fence =
- has_fenced_gpu_access &&
entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
obj->tiling_mode != I915_TILING_NONE;
need_mappable = need_fence || need_reloc_mappable(vma);
- if (need_mappable)
+ if (need_mappable) {
+ entry->flags |= __EXEC_OBJECT_NEEDS_MAP;
list_move(&vma->exec_list, &ordered_vmas);
- else
+ } else
list_move_tail(&vma->exec_list, &ordered_vmas);
obj->base.pending_read_domains = I915_GEM_GPU_DOMAINS & ~I915_GEM_DOMAIN_COMMAND;
obj->base.pending_write_domain = 0;
- obj->pending_fenced_gpu_access = false;
}
list_splice(&ordered_vmas, vmas);
@@ -696,7 +673,7 @@ i915_gem_execbuffer_reserve(struct intel_engine_cs *ring,
if (!drm_mm_node_allocated(&vma->node))
continue;
- if (eb_vma_misplaced(vma, has_fenced_gpu_access))
+ if (eb_vma_misplaced(vma))
ret = i915_vma_unbind(vma);
else
ret = i915_gem_execbuffer_reserve_vma(vma, ring, need_relocs);
@@ -744,9 +721,6 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev,
int i, total, ret;
unsigned count = args->buffer_count;
- if (WARN_ON(list_empty(&eb->vmas)))
- return 0;
-
vm = list_first_entry(&eb->vmas, struct i915_vma, exec_list)->vm;
/* We may process another execbuffer during the unlock... */
@@ -890,18 +864,24 @@ i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec)
}
static int
-validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
+validate_exec_list(struct drm_device *dev,
+ struct drm_i915_gem_exec_object2 *exec,
int count)
{
- int i;
unsigned relocs_total = 0;
unsigned relocs_max = UINT_MAX / sizeof(struct drm_i915_gem_relocation_entry);
+ unsigned invalid_flags;
+ int i;
+
+ invalid_flags = __EXEC_OBJECT_UNKNOWN_FLAGS;
+ if (USES_FULL_PPGTT(dev))
+ invalid_flags |= EXEC_OBJECT_NEEDS_GTT;
for (i = 0; i < count; i++) {
char __user *ptr = to_user_ptr(exec[i].relocs_ptr);
int length; /* limited by fault_in_pages_readable() */
- if (exec[i].flags & __EXEC_OBJECT_UNKNOWN_FLAGS)
+ if (exec[i].flags & invalid_flags)
return -EINVAL;
/* First check for malicious input causing overflow in
@@ -938,7 +918,7 @@ i915_gem_validate_context(struct drm_device *dev, struct drm_file *file,
struct intel_context *ctx = NULL;
struct i915_ctx_hang_stats *hs;
- if (ring->id != RCS && ctx_id != DEFAULT_CONTEXT_ID)
+ if (ring->id != RCS && ctx_id != DEFAULT_CONTEXT_HANDLE)
return ERR_PTR(-EINVAL);
ctx = i915_gem_context_get(file->driver_priv, ctx_id);
@@ -951,16 +931,26 @@ i915_gem_validate_context(struct drm_device *dev, struct drm_file *file,
return ERR_PTR(-EIO);
}
+ if (i915.enable_execlists && !ctx->engine[ring->id].state) {
+ int ret = intel_lr_context_deferred_create(ctx, ring);
+ if (ret) {
+ DRM_DEBUG("Could not create LRC %u: %d\n", ctx_id, ret);
+ return ERR_PTR(ret);
+ }
+ }
+
return ctx;
}
-static void
+void
i915_gem_execbuffer_move_to_active(struct list_head *vmas,
struct intel_engine_cs *ring)
{
+ u32 seqno = intel_ring_get_seqno(ring);
struct i915_vma *vma;
list_for_each_entry(vma, vmas, exec_list) {
+ struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
struct drm_i915_gem_object *obj = vma->obj;
u32 old_read = obj->base.read_domains;
u32 old_write = obj->base.write_domain;
@@ -969,26 +959,31 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas,
if (obj->base.write_domain == 0)
obj->base.pending_read_domains |= obj->base.read_domains;
obj->base.read_domains = obj->base.pending_read_domains;
- obj->fenced_gpu_access = obj->pending_fenced_gpu_access;
i915_vma_move_to_active(vma, ring);
if (obj->base.write_domain) {
obj->dirty = 1;
- obj->last_write_seqno = intel_ring_get_seqno(ring);
- /* check for potential scanout */
- if (i915_gem_obj_ggtt_bound(obj) &&
- i915_gem_obj_to_ggtt(obj)->pin_count)
- intel_mark_fb_busy(obj, ring);
+ obj->last_write_seqno = seqno;
+
+ intel_fb_obj_invalidate(obj, ring);
/* update for the implicit flush after a batch */
obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
}
+ if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) {
+ obj->last_fenced_seqno = seqno;
+ if (entry->flags & __EXEC_OBJECT_HAS_FENCE) {
+ struct drm_i915_private *dev_priv = to_i915(ring->dev);
+ list_move_tail(&dev_priv->fence_regs[obj->fence_reg].lru_list,
+ &dev_priv->mm.fence_list);
+ }
+ }
trace_i915_gem_object_change_domain(obj, old_read, old_write);
}
}
-static void
+void
i915_gem_execbuffer_retire_commands(struct drm_device *dev,
struct drm_file *file,
struct intel_engine_cs *ring,
@@ -1028,6 +1023,163 @@ i915_reset_gen7_sol_offsets(struct drm_device *dev,
return 0;
}
+int
+i915_gem_ringbuffer_submission(struct drm_device *dev, struct drm_file *file,
+ struct intel_engine_cs *ring,
+ struct intel_context *ctx,
+ struct drm_i915_gem_execbuffer2 *args,
+ struct list_head *vmas,
+ struct drm_i915_gem_object *batch_obj,
+ u64 exec_start, u32 flags)
+{
+ struct drm_clip_rect *cliprects = NULL;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u64 exec_len;
+ int instp_mode;
+ u32 instp_mask;
+ int i, ret = 0;
+
+ if (args->num_cliprects != 0) {
+ if (ring != &dev_priv->ring[RCS]) {
+ DRM_DEBUG("clip rectangles are only valid with the render ring\n");
+ return -EINVAL;
+ }
+
+ if (INTEL_INFO(dev)->gen >= 5) {
+ DRM_DEBUG("clip rectangles are only valid on pre-gen5\n");
+ return -EINVAL;
+ }
+
+ if (args->num_cliprects > UINT_MAX / sizeof(*cliprects)) {
+ DRM_DEBUG("execbuf with %u cliprects\n",
+ args->num_cliprects);
+ return -EINVAL;
+ }
+
+ cliprects = kcalloc(args->num_cliprects,
+ sizeof(*cliprects),
+ GFP_KERNEL);
+ if (cliprects == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (copy_from_user(cliprects,
+ to_user_ptr(args->cliprects_ptr),
+ sizeof(*cliprects)*args->num_cliprects)) {
+ ret = -EFAULT;
+ goto error;
+ }
+ } else {
+ if (args->DR4 == 0xffffffff) {
+ DRM_DEBUG("UXA submitting garbage DR4, fixing up\n");
+ args->DR4 = 0;
+ }
+
+ if (args->DR1 || args->DR4 || args->cliprects_ptr) {
+ DRM_DEBUG("0 cliprects but dirt in cliprects fields\n");
+ return -EINVAL;
+ }
+ }
+
+ ret = i915_gem_execbuffer_move_to_gpu(ring, vmas);
+ if (ret)
+ goto error;
+
+ ret = i915_switch_context(ring, ctx);
+ if (ret)
+ goto error;
+
+ instp_mode = args->flags & I915_EXEC_CONSTANTS_MASK;
+ instp_mask = I915_EXEC_CONSTANTS_MASK;
+ switch (instp_mode) {
+ case I915_EXEC_CONSTANTS_REL_GENERAL:
+ case I915_EXEC_CONSTANTS_ABSOLUTE:
+ case I915_EXEC_CONSTANTS_REL_SURFACE:
+ if (instp_mode != 0 && ring != &dev_priv->ring[RCS]) {
+ DRM_DEBUG("non-0 rel constants mode on non-RCS\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (instp_mode != dev_priv->relative_constants_mode) {
+ if (INTEL_INFO(dev)->gen < 4) {
+ DRM_DEBUG("no rel constants on pre-gen4\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (INTEL_INFO(dev)->gen > 5 &&
+ instp_mode == I915_EXEC_CONSTANTS_REL_SURFACE) {
+ DRM_DEBUG("rel surface constants mode invalid on gen5+\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* The HW changed the meaning on this bit on gen6 */
+ if (INTEL_INFO(dev)->gen >= 6)
+ instp_mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE;
+ }
+ break;
+ default:
+ DRM_DEBUG("execbuf with unknown constants: %d\n", instp_mode);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (ring == &dev_priv->ring[RCS] &&
+ instp_mode != dev_priv->relative_constants_mode) {
+ ret = intel_ring_begin(ring, 4);
+ if (ret)
+ goto error;
+
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+ intel_ring_emit(ring, INSTPM);
+ intel_ring_emit(ring, instp_mask << 16 | instp_mode);
+ intel_ring_advance(ring);
+
+ dev_priv->relative_constants_mode = instp_mode;
+ }
+
+ if (args->flags & I915_EXEC_GEN7_SOL_RESET) {
+ ret = i915_reset_gen7_sol_offsets(dev, ring);
+ if (ret)
+ goto error;
+ }
+
+ exec_len = args->batch_len;
+ if (cliprects) {
+ for (i = 0; i < args->num_cliprects; i++) {
+ ret = i915_emit_box(dev, &cliprects[i],
+ args->DR1, args->DR4);
+ if (ret)
+ goto error;
+
+ ret = ring->dispatch_execbuffer(ring,
+ exec_start, exec_len,
+ flags);
+ if (ret)
+ goto error;
+ }
+ } else {
+ ret = ring->dispatch_execbuffer(ring,
+ exec_start, exec_len,
+ flags);
+ if (ret)
+ return ret;
+ }
+
+ trace_i915_gem_ring_dispatch(ring, intel_ring_get_seqno(ring), flags);
+
+ i915_gem_execbuffer_move_to_active(vmas, ring);
+ i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj);
+
+error:
+ kfree(cliprects);
+ return ret;
+}
+
/**
* Find one BSD ring to dispatch the corresponding BSD command.
* The Ring ID is returned.
@@ -1087,20 +1239,19 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
struct drm_i915_private *dev_priv = dev->dev_private;
struct eb_vmas *eb;
struct drm_i915_gem_object *batch_obj;
- struct drm_clip_rect *cliprects = NULL;
struct intel_engine_cs *ring;
struct intel_context *ctx;
struct i915_address_space *vm;
const u32 ctx_id = i915_execbuffer2_get_context_id(*args);
- u64 exec_start = args->batch_start_offset, exec_len;
- u32 mask, flags;
- int ret, mode, i;
+ u64 exec_start = args->batch_start_offset;
+ u32 flags;
+ int ret;
bool need_relocs;
if (!i915_gem_check_execbuffer(args))
return -EINVAL;
- ret = validate_exec_list(exec, args->buffer_count);
+ ret = validate_exec_list(dev, exec, args->buffer_count);
if (ret)
return ret;
@@ -1138,87 +1289,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
return -EINVAL;
}
- mode = args->flags & I915_EXEC_CONSTANTS_MASK;
- mask = I915_EXEC_CONSTANTS_MASK;
- switch (mode) {
- case I915_EXEC_CONSTANTS_REL_GENERAL:
- case I915_EXEC_CONSTANTS_ABSOLUTE:
- case I915_EXEC_CONSTANTS_REL_SURFACE:
- if (mode != 0 && ring != &dev_priv->ring[RCS]) {
- DRM_DEBUG("non-0 rel constants mode on non-RCS\n");
- return -EINVAL;
- }
-
- if (mode != dev_priv->relative_constants_mode) {
- if (INTEL_INFO(dev)->gen < 4) {
- DRM_DEBUG("no rel constants on pre-gen4\n");
- return -EINVAL;
- }
-
- if (INTEL_INFO(dev)->gen > 5 &&
- mode == I915_EXEC_CONSTANTS_REL_SURFACE) {
- DRM_DEBUG("rel surface constants mode invalid on gen5+\n");
- return -EINVAL;
- }
-
- /* The HW changed the meaning on this bit on gen6 */
- if (INTEL_INFO(dev)->gen >= 6)
- mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE;
- }
- break;
- default:
- DRM_DEBUG("execbuf with unknown constants: %d\n", mode);
- return -EINVAL;
- }
-
if (args->buffer_count < 1) {
DRM_DEBUG("execbuf with %d buffers\n", args->buffer_count);
return -EINVAL;
}
- if (args->num_cliprects != 0) {
- if (ring != &dev_priv->ring[RCS]) {
- DRM_DEBUG("clip rectangles are only valid with the render ring\n");
- return -EINVAL;
- }
-
- if (INTEL_INFO(dev)->gen >= 5) {
- DRM_DEBUG("clip rectangles are only valid on pre-gen5\n");
- return -EINVAL;
- }
-
- if (args->num_cliprects > UINT_MAX / sizeof(*cliprects)) {
- DRM_DEBUG("execbuf with %u cliprects\n",
- args->num_cliprects);
- return -EINVAL;
- }
-
- cliprects = kcalloc(args->num_cliprects,
- sizeof(*cliprects),
- GFP_KERNEL);
- if (cliprects == NULL) {
- ret = -ENOMEM;
- goto pre_mutex_err;
- }
-
- if (copy_from_user(cliprects,
- to_user_ptr(args->cliprects_ptr),
- sizeof(*cliprects)*args->num_cliprects)) {
- ret = -EFAULT;
- goto pre_mutex_err;
- }
- } else {
- if (args->DR4 == 0xffffffff) {
- DRM_DEBUG("UXA submitting garbage DR4, fixing up\n");
- args->DR4 = 0;
- }
-
- if (args->DR1 || args->DR4 || args->cliprects_ptr) {
- DRM_DEBUG("0 cliprects but dirt in cliprects fields\n");
- return -EINVAL;
- }
- }
-
intel_runtime_pm_get(dev_priv);
ret = i915_mutex_lock_interruptible(dev);
@@ -1240,8 +1315,9 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
i915_gem_context_reference(ctx);
- vm = ctx->vm;
- if (!USES_FULL_PPGTT(dev))
+ if (ctx->ppgtt)
+ vm = &ctx->ppgtt->base;
+ else
vm = &dev_priv->gtt.base;
eb = eb_create(args);
@@ -1308,77 +1384,36 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
/* snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure
* batch" bit. Hence we need to pin secure batches into the global gtt.
* hsw should have this fixed, but bdw mucks it up again. */
- if (flags & I915_DISPATCH_SECURE &&
- !batch_obj->has_global_gtt_mapping) {
- /* When we have multiple VMs, we'll need to make sure that we
- * allocate space first */
- struct i915_vma *vma = i915_gem_obj_to_ggtt(batch_obj);
- BUG_ON(!vma);
- vma->bind_vma(vma, batch_obj->cache_level, GLOBAL_BIND);
- }
-
- if (flags & I915_DISPATCH_SECURE)
- exec_start += i915_gem_obj_ggtt_offset(batch_obj);
- else
- exec_start += i915_gem_obj_offset(batch_obj, vm);
-
- ret = i915_gem_execbuffer_move_to_gpu(ring, &eb->vmas);
- if (ret)
- goto err;
-
- ret = i915_switch_context(ring, ctx);
- if (ret)
- goto err;
-
- if (ring == &dev_priv->ring[RCS] &&
- mode != dev_priv->relative_constants_mode) {
- ret = intel_ring_begin(ring, 4);
- if (ret)
- goto err;
-
- intel_ring_emit(ring, MI_NOOP);
- intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
- intel_ring_emit(ring, INSTPM);
- intel_ring_emit(ring, mask << 16 | mode);
- intel_ring_advance(ring);
-
- dev_priv->relative_constants_mode = mode;
- }
-
- if (args->flags & I915_EXEC_GEN7_SOL_RESET) {
- ret = i915_reset_gen7_sol_offsets(dev, ring);
- if (ret)
- goto err;
- }
-
-
- exec_len = args->batch_len;
- if (cliprects) {
- for (i = 0; i < args->num_cliprects; i++) {
- ret = i915_emit_box(dev, &cliprects[i],
- args->DR1, args->DR4);
- if (ret)
- goto err;
-
- ret = ring->dispatch_execbuffer(ring,
- exec_start, exec_len,
- flags);
- if (ret)
- goto err;
- }
- } else {
- ret = ring->dispatch_execbuffer(ring,
- exec_start, exec_len,
- flags);
+ if (flags & I915_DISPATCH_SECURE) {
+ /*
+ * So on first glance it looks freaky that we pin the batch here
+ * outside of the reservation loop. But:
+ * - The batch is already pinned into the relevant ppgtt, so we
+ * already have the backing storage fully allocated.
+ * - No other BO uses the global gtt (well contexts, but meh),
+ * so we don't really have issues with mutliple objects not
+ * fitting due to fragmentation.
+ * So this is actually safe.
+ */
+ ret = i915_gem_obj_ggtt_pin(batch_obj, 0, 0);
if (ret)
goto err;
- }
- trace_i915_gem_ring_dispatch(ring, intel_ring_get_seqno(ring), flags);
+ exec_start += i915_gem_obj_ggtt_offset(batch_obj);
+ } else
+ exec_start += i915_gem_obj_offset(batch_obj, vm);
- i915_gem_execbuffer_move_to_active(&eb->vmas, ring);
- i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj);
+ ret = dev_priv->gt.do_execbuf(dev, file, ring, ctx, args,
+ &eb->vmas, batch_obj, exec_start, flags);
+ /*
+ * FIXME: We crucially rely upon the active tracking for the (ppgtt)
+ * batch vma for correctness. For less ugly and less fragility this
+ * needs to be adjusted to also track the ggtt batch vma properly as
+ * active.
+ */
+ if (flags & I915_DISPATCH_SECURE)
+ i915_gem_object_ggtt_unpin(batch_obj);
err:
/* the request owns the ref now */
i915_gem_context_unreference(ctx);
@@ -1387,8 +1422,6 @@ err:
mutex_unlock(&dev->struct_mutex);
pre_mutex_err:
- kfree(cliprects);
-
/* intel_gpu_busy should also get a ref, so it will free when the device
* is really idle. */
intel_runtime_pm_put(dev_priv);
@@ -1525,7 +1558,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
ret = i915_gem_do_execbuffer(dev, data, file, args, exec2_list);
if (!ret) {
/* Copy the new buffer offsets back to the user's exec list. */
- struct drm_i915_gem_exec_object2 *user_exec_list =
+ struct drm_i915_gem_exec_object2 __user *user_exec_list =
to_user_ptr(args->buffers_ptr);
int i;
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 8b3cde703364..728938f02341 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -33,17 +33,6 @@
static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv);
static void chv_setup_private_ppat(struct drm_i915_private *dev_priv);
-bool intel_enable_ppgtt(struct drm_device *dev, bool full)
-{
- if (i915.enable_ppgtt == 0)
- return false;
-
- if (i915.enable_ppgtt == 1 && full)
- return false;
-
- return true;
-}
-
static int sanitize_enable_ppgtt(struct drm_device *dev, int enable_ppgtt)
{
if (enable_ppgtt == 0 || !HAS_ALIASING_PPGTT(dev))
@@ -63,6 +52,13 @@ static int sanitize_enable_ppgtt(struct drm_device *dev, int enable_ppgtt)
}
#endif
+ /* Early VLV doesn't have this */
+ if (IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev) &&
+ dev->pdev->revision < 0xb) {
+ DRM_DEBUG_DRIVER("disabling PPGTT on pre-B3 step VLV\n");
+ return 0;
+ }
+
return HAS_ALIASING_PPGTT(dev) ? 1 : 0;
}
@@ -71,7 +67,6 @@ static void ppgtt_bind_vma(struct i915_vma *vma,
enum i915_cache_level cache_level,
u32 flags);
static void ppgtt_unbind_vma(struct i915_vma *vma);
-static int gen8_ppgtt_enable(struct i915_hw_ppgtt *ppgtt);
static inline gen8_gtt_pte_t gen8_pte_encode(dma_addr_t addr,
enum i915_cache_level level,
@@ -110,7 +105,7 @@ static inline gen8_ppgtt_pde_t gen8_pde_encode(struct drm_device *dev,
static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr,
enum i915_cache_level level,
- bool valid)
+ bool valid, u32 unused)
{
gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
pte |= GEN6_PTE_ADDR_ENCODE(addr);
@@ -132,7 +127,7 @@ static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr,
static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr,
enum i915_cache_level level,
- bool valid)
+ bool valid, u32 unused)
{
gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
pte |= GEN6_PTE_ADDR_ENCODE(addr);
@@ -156,7 +151,7 @@ static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr,
static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
enum i915_cache_level level,
- bool valid)
+ bool valid, u32 flags)
{
gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
pte |= GEN6_PTE_ADDR_ENCODE(addr);
@@ -164,7 +159,8 @@ static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
/* Mark the page as writeable. Other platforms don't have a
* setting for read-only/writable, so this matches that behavior.
*/
- pte |= BYT_PTE_WRITEABLE;
+ if (!(flags & PTE_READ_ONLY))
+ pte |= BYT_PTE_WRITEABLE;
if (level != I915_CACHE_NONE)
pte |= BYT_PTE_SNOOPED_BY_CPU_CACHES;
@@ -174,7 +170,7 @@ static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr,
enum i915_cache_level level,
- bool valid)
+ bool valid, u32 unused)
{
gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
pte |= HSW_PTE_ADDR_ENCODE(addr);
@@ -187,7 +183,7 @@ static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr,
static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr,
enum i915_cache_level level,
- bool valid)
+ bool valid, u32 unused)
{
gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
pte |= HSW_PTE_ADDR_ENCODE(addr);
@@ -208,19 +204,12 @@ static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr,
/* Broadwell Page Directory Pointer Descriptors */
static int gen8_write_pdp(struct intel_engine_cs *ring, unsigned entry,
- uint64_t val, bool synchronous)
+ uint64_t val)
{
- struct drm_i915_private *dev_priv = ring->dev->dev_private;
int ret;
BUG_ON(entry >= 4);
- if (synchronous) {
- I915_WRITE(GEN8_RING_PDP_UDW(ring, entry), val >> 32);
- I915_WRITE(GEN8_RING_PDP_LDW(ring, entry), (u32)val);
- return 0;
- }
-
ret = intel_ring_begin(ring, 6);
if (ret)
return ret;
@@ -237,8 +226,7 @@ static int gen8_write_pdp(struct intel_engine_cs *ring, unsigned entry,
}
static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt,
- struct intel_engine_cs *ring,
- bool synchronous)
+ struct intel_engine_cs *ring)
{
int i, ret;
@@ -247,7 +235,7 @@ static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt,
for (i = used_pd - 1; i >= 0; i--) {
dma_addr_t addr = ppgtt->pd_dma_addr[i];
- ret = gen8_write_pdp(ring, i, addr, synchronous);
+ ret = gen8_write_pdp(ring, i, addr);
if (ret)
return ret;
}
@@ -301,7 +289,7 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
struct sg_table *pages,
uint64_t start,
- enum i915_cache_level cache_level)
+ enum i915_cache_level cache_level, u32 unused)
{
struct i915_hw_ppgtt *ppgtt =
container_of(vm, struct i915_hw_ppgtt, base);
@@ -395,9 +383,6 @@ static void gen8_ppgtt_cleanup(struct i915_address_space *vm)
struct i915_hw_ppgtt *ppgtt =
container_of(vm, struct i915_hw_ppgtt, base);
- list_del(&vm->global_link);
- drm_mm_takedown(&vm->mm);
-
gen8_ppgtt_unmap_pages(ppgtt);
gen8_ppgtt_free(ppgtt);
}
@@ -607,7 +592,6 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
kunmap_atomic(pd_vaddr);
}
- ppgtt->enable = gen8_ppgtt_enable;
ppgtt->switch_mm = gen8_mm_switch;
ppgtt->base.clear_range = gen8_ppgtt_clear_range;
ppgtt->base.insert_entries = gen8_ppgtt_insert_entries;
@@ -639,7 +623,7 @@ static void gen6_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m)
uint32_t pd_entry;
int pte, pde;
- scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true);
+ scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true, 0);
pd_addr = (gen6_gtt_pte_t __iomem *)dev_priv->gtt.gsm +
ppgtt->pd_offset / sizeof(gen6_gtt_pte_t);
@@ -716,29 +700,10 @@ static uint32_t get_pd_offset(struct i915_hw_ppgtt *ppgtt)
}
static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt,
- struct intel_engine_cs *ring,
- bool synchronous)
+ struct intel_engine_cs *ring)
{
- struct drm_device *dev = ppgtt->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
- /* If we're in reset, we can assume the GPU is sufficiently idle to
- * manually frob these bits. Ideally we could use the ring functions,
- * except our error handling makes it quite difficult (can't use
- * intel_ring_begin, ring->flush, or intel_ring_advance)
- *
- * FIXME: We should try not to special case reset
- */
- if (synchronous ||
- i915_reset_in_progress(&dev_priv->gpu_error)) {
- WARN_ON(ppgtt != dev_priv->mm.aliasing_ppgtt);
- I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G);
- I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt));
- POSTING_READ(RING_PP_DIR_BASE(ring));
- return 0;
- }
-
/* NB: TLBs must be flushed and invalidated before a switch */
ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
if (ret)
@@ -760,29 +725,10 @@ static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt,
}
static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt,
- struct intel_engine_cs *ring,
- bool synchronous)
+ struct intel_engine_cs *ring)
{
- struct drm_device *dev = ppgtt->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
- /* If we're in reset, we can assume the GPU is sufficiently idle to
- * manually frob these bits. Ideally we could use the ring functions,
- * except our error handling makes it quite difficult (can't use
- * intel_ring_begin, ring->flush, or intel_ring_advance)
- *
- * FIXME: We should try not to special case reset
- */
- if (synchronous ||
- i915_reset_in_progress(&dev_priv->gpu_error)) {
- WARN_ON(ppgtt != dev_priv->mm.aliasing_ppgtt);
- I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G);
- I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt));
- POSTING_READ(RING_PP_DIR_BASE(ring));
- return 0;
- }
-
/* NB: TLBs must be flushed and invalidated before a switch */
ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
if (ret)
@@ -811,14 +757,11 @@ static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt,
}
static int gen6_mm_switch(struct i915_hw_ppgtt *ppgtt,
- struct intel_engine_cs *ring,
- bool synchronous)
+ struct intel_engine_cs *ring)
{
struct drm_device *dev = ppgtt->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- if (!synchronous)
- return 0;
I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G);
I915_WRITE(RING_PP_DIR_BASE(ring), get_pd_offset(ppgtt));
@@ -828,39 +771,20 @@ static int gen6_mm_switch(struct i915_hw_ppgtt *ppgtt,
return 0;
}
-static int gen8_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
+static void gen8_ppgtt_enable(struct drm_device *dev)
{
- struct drm_device *dev = ppgtt->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_engine_cs *ring;
- int j, ret;
+ int j;
for_each_ring(ring, dev_priv, j) {
I915_WRITE(RING_MODE_GEN7(ring),
_MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
-
- /* We promise to do a switch later with FULL PPGTT. If this is
- * aliasing, this is the one and only switch we'll do */
- if (USES_FULL_PPGTT(dev))
- continue;
-
- ret = ppgtt->switch_mm(ppgtt, ring, true);
- if (ret)
- goto err_out;
}
-
- return 0;
-
-err_out:
- for_each_ring(ring, dev_priv, j)
- I915_WRITE(RING_MODE_GEN7(ring),
- _MASKED_BIT_DISABLE(GFX_PPGTT_ENABLE));
- return ret;
}
-static int gen7_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
+static void gen7_ppgtt_enable(struct drm_device *dev)
{
- struct drm_device *dev = ppgtt->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_engine_cs *ring;
uint32_t ecochk, ecobits;
@@ -879,31 +803,16 @@ static int gen7_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
I915_WRITE(GAM_ECOCHK, ecochk);
for_each_ring(ring, dev_priv, i) {
- int ret;
/* GFX_MODE is per-ring on gen7+ */
I915_WRITE(RING_MODE_GEN7(ring),
_MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
-
- /* We promise to do a switch later with FULL PPGTT. If this is
- * aliasing, this is the one and only switch we'll do */
- if (USES_FULL_PPGTT(dev))
- continue;
-
- ret = ppgtt->switch_mm(ppgtt, ring, true);
- if (ret)
- return ret;
}
-
- return 0;
}
-static int gen6_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
+static void gen6_ppgtt_enable(struct drm_device *dev)
{
- struct drm_device *dev = ppgtt->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_engine_cs *ring;
uint32_t ecochk, gab_ctl, ecobits;
- int i;
ecobits = I915_READ(GAC_ECO_BITS);
I915_WRITE(GAC_ECO_BITS, ecobits | ECOBITS_SNB_BIT |
@@ -916,14 +825,6 @@ static int gen6_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
I915_WRITE(GAM_ECOCHK, ecochk | ECOCHK_SNB_BIT | ECOCHK_PPGTT_CACHE64B);
I915_WRITE(GFX_MODE, _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
-
- for_each_ring(ring, dev_priv, i) {
- int ret = ppgtt->switch_mm(ppgtt, ring, true);
- if (ret)
- return ret;
- }
-
- return 0;
}
/* PPGTT support for Sandybdrige/Gen6 and later */
@@ -941,7 +842,7 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
unsigned last_pte, i;
- scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true);
+ scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true, 0);
while (num_entries) {
last_pte = first_pte + num_entries;
@@ -964,7 +865,7 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
struct sg_table *pages,
uint64_t start,
- enum i915_cache_level cache_level)
+ enum i915_cache_level cache_level, u32 flags)
{
struct i915_hw_ppgtt *ppgtt =
container_of(vm, struct i915_hw_ppgtt, base);
@@ -981,7 +882,8 @@ static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
pt_vaddr[act_pte] =
vm->pte_encode(sg_page_iter_dma_address(&sg_iter),
- cache_level, true);
+ cache_level, true, flags);
+
if (++act_pte == I915_PPGTT_PT_ENTRIES) {
kunmap_atomic(pt_vaddr);
pt_vaddr = NULL;
@@ -1020,8 +922,6 @@ static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
struct i915_hw_ppgtt *ppgtt =
container_of(vm, struct i915_hw_ppgtt, base);
- list_del(&vm->global_link);
- drm_mm_takedown(&ppgtt->base.mm);
drm_mm_remove_node(&ppgtt->node);
gen6_ppgtt_unmap_pages(ppgtt);
@@ -1142,13 +1042,10 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
ppgtt->base.pte_encode = dev_priv->gtt.base.pte_encode;
if (IS_GEN6(dev)) {
- ppgtt->enable = gen6_ppgtt_enable;
ppgtt->switch_mm = gen6_mm_switch;
} else if (IS_HASWELL(dev)) {
- ppgtt->enable = gen7_ppgtt_enable;
ppgtt->switch_mm = hsw_mm_switch;
} else if (IS_GEN7(dev)) {
- ppgtt->enable = gen7_ppgtt_enable;
ppgtt->switch_mm = gen7_mm_switch;
} else
BUG();
@@ -1179,47 +1076,126 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
ppgtt->node.size >> 20,
ppgtt->node.start / PAGE_SIZE);
+ gen6_write_pdes(ppgtt);
+ DRM_DEBUG("Adding PPGTT at offset %x\n",
+ ppgtt->pd_offset << 10);
+
return 0;
}
-int i915_gem_init_ppgtt(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
+static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- int ret = 0;
ppgtt->base.dev = dev;
ppgtt->base.scratch = dev_priv->gtt.base.scratch;
if (INTEL_INFO(dev)->gen < 8)
- ret = gen6_ppgtt_init(ppgtt);
+ return gen6_ppgtt_init(ppgtt);
else if (IS_GEN8(dev))
- ret = gen8_ppgtt_init(ppgtt, dev_priv->gtt.base.total);
+ return gen8_ppgtt_init(ppgtt, dev_priv->gtt.base.total);
else
BUG();
+}
+int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret = 0;
- if (!ret) {
- struct drm_i915_private *dev_priv = dev->dev_private;
+ ret = __hw_ppgtt_init(dev, ppgtt);
+ if (ret == 0) {
kref_init(&ppgtt->ref);
drm_mm_init(&ppgtt->base.mm, ppgtt->base.start,
ppgtt->base.total);
i915_init_vm(dev_priv, &ppgtt->base);
- if (INTEL_INFO(dev)->gen < 8) {
- gen6_write_pdes(ppgtt);
- DRM_DEBUG("Adding PPGTT at offset %x\n",
- ppgtt->pd_offset << 10);
+ }
+
+ return ret;
+}
+
+int i915_ppgtt_init_hw(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring;
+ struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
+ int i, ret = 0;
+
+ /* In the case of execlists, PPGTT is enabled by the context descriptor
+ * and the PDPs are contained within the context itself. We don't
+ * need to do anything here. */
+ if (i915.enable_execlists)
+ return 0;
+
+ if (!USES_PPGTT(dev))
+ return 0;
+
+ if (IS_GEN6(dev))
+ gen6_ppgtt_enable(dev);
+ else if (IS_GEN7(dev))
+ gen7_ppgtt_enable(dev);
+ else if (INTEL_INFO(dev)->gen >= 8)
+ gen8_ppgtt_enable(dev);
+ else
+ WARN_ON(1);
+
+ if (ppgtt) {
+ for_each_ring(ring, dev_priv, i) {
+ ret = ppgtt->switch_mm(ppgtt, ring);
+ if (ret != 0)
+ return ret;
}
}
return ret;
}
+struct i915_hw_ppgtt *
+i915_ppgtt_create(struct drm_device *dev, struct drm_i915_file_private *fpriv)
+{
+ struct i915_hw_ppgtt *ppgtt;
+ int ret;
+
+ ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
+ if (!ppgtt)
+ return ERR_PTR(-ENOMEM);
+
+ ret = i915_ppgtt_init(dev, ppgtt);
+ if (ret) {
+ kfree(ppgtt);
+ return ERR_PTR(ret);
+ }
+
+ ppgtt->file_priv = fpriv;
+
+ return ppgtt;
+}
+
+void i915_ppgtt_release(struct kref *kref)
+{
+ struct i915_hw_ppgtt *ppgtt =
+ container_of(kref, struct i915_hw_ppgtt, ref);
+
+ /* vmas should already be unbound */
+ WARN_ON(!list_empty(&ppgtt->base.active_list));
+ WARN_ON(!list_empty(&ppgtt->base.inactive_list));
+
+ list_del(&ppgtt->base.global_link);
+ drm_mm_takedown(&ppgtt->base.mm);
+
+ ppgtt->base.cleanup(&ppgtt->base);
+ kfree(ppgtt);
+}
static void
ppgtt_bind_vma(struct i915_vma *vma,
enum i915_cache_level cache_level,
u32 flags)
{
+ /* Currently applicable only to VLV */
+ if (vma->obj->gt_ro)
+ flags |= PTE_READ_ONLY;
+
vma->vm->insert_entries(vma->vm, vma->obj->pages, vma->node.start,
- cache_level);
+ cache_level, flags);
}
static void ppgtt_unbind_vma(struct i915_vma *vma)
@@ -1297,6 +1273,16 @@ void i915_check_and_clear_faults(struct drm_device *dev)
POSTING_READ(RING_FAULT_REG(&dev_priv->ring[RCS]));
}
+static void i915_ggtt_flush(struct drm_i915_private *dev_priv)
+{
+ if (INTEL_INFO(dev_priv->dev)->gen < 6) {
+ intel_gtt_chipset_flush();
+ } else {
+ I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN);
+ POSTING_READ(GFX_FLSH_CNTL_GEN6);
+ }
+}
+
void i915_gem_suspend_gtt_mappings(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1313,6 +1299,8 @@ void i915_gem_suspend_gtt_mappings(struct drm_device *dev)
dev_priv->gtt.base.start,
dev_priv->gtt.base.total,
true);
+
+ i915_ggtt_flush(dev_priv);
}
void i915_gem_restore_gtt_mappings(struct drm_device *dev)
@@ -1365,7 +1353,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
gen6_write_pdes(container_of(vm, struct i915_hw_ppgtt, base));
}
- i915_gem_chipset_flush(dev);
+ i915_ggtt_flush(dev_priv);
}
int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
@@ -1394,7 +1382,7 @@ static inline void gen8_set_pte(void __iomem *addr, gen8_gtt_pte_t pte)
static void gen8_ggtt_insert_entries(struct i915_address_space *vm,
struct sg_table *st,
uint64_t start,
- enum i915_cache_level level)
+ enum i915_cache_level level, u32 unused)
{
struct drm_i915_private *dev_priv = vm->dev->dev_private;
unsigned first_entry = start >> PAGE_SHIFT;
@@ -1402,7 +1390,7 @@ static void gen8_ggtt_insert_entries(struct i915_address_space *vm,
(gen8_gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
int i = 0;
struct sg_page_iter sg_iter;
- dma_addr_t addr = 0;
+ dma_addr_t addr = 0; /* shut up gcc */
for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) {
addr = sg_dma_address(sg_iter.sg) +
@@ -1440,7 +1428,7 @@ static void gen8_ggtt_insert_entries(struct i915_address_space *vm,
static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
struct sg_table *st,
uint64_t start,
- enum i915_cache_level level)
+ enum i915_cache_level level, u32 flags)
{
struct drm_i915_private *dev_priv = vm->dev->dev_private;
unsigned first_entry = start >> PAGE_SHIFT;
@@ -1448,11 +1436,11 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
(gen6_gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
int i = 0;
struct sg_page_iter sg_iter;
- dma_addr_t addr;
+ dma_addr_t addr = 0;
for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) {
addr = sg_page_iter_dma_address(&sg_iter);
- iowrite32(vm->pte_encode(addr, level, true), &gtt_entries[i]);
+ iowrite32(vm->pte_encode(addr, level, true, flags), &gtt_entries[i]);
i++;
}
@@ -1462,9 +1450,10 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
* of NUMA access patterns. Therefore, even with the way we assume
* hardware should work, we must keep this posting read for paranoia.
*/
- if (i != 0)
- WARN_ON(readl(&gtt_entries[i-1]) !=
- vm->pte_encode(addr, level, true));
+ if (i != 0) {
+ unsigned long gtt = readl(&gtt_entries[i-1]);
+ WARN_ON(gtt != vm->pte_encode(addr, level, true, flags));
+ }
/* This next bit makes the above posting read even more important. We
* want to flush the TLBs only after we're certain all the PTE updates
@@ -1518,7 +1507,7 @@ static void gen6_ggtt_clear_range(struct i915_address_space *vm,
first_entry, num_entries, max_entries))
num_entries = max_entries;
- scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, use_scratch);
+ scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, use_scratch, 0);
for (i = 0; i < num_entries; i++)
iowrite32(scratch_pte, &gtt_base[i]);
@@ -1567,6 +1556,10 @@ static void ggtt_bind_vma(struct i915_vma *vma,
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj = vma->obj;
+ /* Currently applicable only to VLV */
+ if (obj->gt_ro)
+ flags |= PTE_READ_ONLY;
+
/* If there is no aliasing PPGTT, or the caller needs a global mapping,
* or we have a global mapping already but the cacheability flags have
* changed, set the global PTEs.
@@ -1583,7 +1576,7 @@ static void ggtt_bind_vma(struct i915_vma *vma,
(cache_level != obj->cache_level)) {
vma->vm->insert_entries(vma->vm, obj->pages,
vma->node.start,
- cache_level);
+ cache_level, flags);
obj->has_global_gtt_mapping = 1;
}
}
@@ -1595,7 +1588,7 @@ static void ggtt_bind_vma(struct i915_vma *vma,
appgtt->base.insert_entries(&appgtt->base,
vma->obj->pages,
vma->node.start,
- cache_level);
+ cache_level, flags);
vma->obj->has_aliasing_ppgtt_mapping = 1;
}
}
@@ -1657,10 +1650,10 @@ static void i915_gtt_color_adjust(struct drm_mm_node *node,
}
}
-void i915_gem_setup_global_gtt(struct drm_device *dev,
- unsigned long start,
- unsigned long mappable_end,
- unsigned long end)
+int i915_gem_setup_global_gtt(struct drm_device *dev,
+ unsigned long start,
+ unsigned long mappable_end,
+ unsigned long end)
{
/* Let GEM Manage all of the aperture.
*
@@ -1676,6 +1669,7 @@ void i915_gem_setup_global_gtt(struct drm_device *dev,
struct drm_mm_node *entry;
struct drm_i915_gem_object *obj;
unsigned long hole_start, hole_end;
+ int ret;
BUG_ON(mappable_end > end);
@@ -1687,14 +1681,16 @@ void i915_gem_setup_global_gtt(struct drm_device *dev,
/* Mark any preallocated objects as occupied */
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
struct i915_vma *vma = i915_gem_obj_to_vma(obj, ggtt_vm);
- int ret;
+
DRM_DEBUG_KMS("reserving preallocated space: %lx + %zx\n",
i915_gem_obj_ggtt_offset(obj), obj->base.size);
WARN_ON(i915_gem_obj_ggtt_bound(obj));
ret = drm_mm_reserve_node(&ggtt_vm->mm, &vma->node);
- if (ret)
- DRM_DEBUG_KMS("Reservation failed\n");
+ if (ret) {
+ DRM_DEBUG_KMS("Reservation failed: %i\n", ret);
+ return ret;
+ }
obj->has_global_gtt_mapping = 1;
}
@@ -1711,6 +1707,22 @@ void i915_gem_setup_global_gtt(struct drm_device *dev,
/* And finally clear the reserved guard page */
ggtt_vm->clear_range(ggtt_vm, end - PAGE_SIZE, PAGE_SIZE, true);
+
+ if (USES_PPGTT(dev) && !USES_FULL_PPGTT(dev)) {
+ struct i915_hw_ppgtt *ppgtt;
+
+ ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
+ if (!ppgtt)
+ return -ENOMEM;
+
+ ret = __hw_ppgtt_init(dev, ppgtt);
+ if (ret != 0)
+ return ret;
+
+ dev_priv->mm.aliasing_ppgtt = ppgtt;
+ }
+
+ return 0;
}
void i915_gem_init_global_gtt(struct drm_device *dev)
@@ -1724,6 +1736,25 @@ void i915_gem_init_global_gtt(struct drm_device *dev)
i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size);
}
+void i915_global_gtt_cleanup(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct i915_address_space *vm = &dev_priv->gtt.base;
+
+ if (dev_priv->mm.aliasing_ppgtt) {
+ struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
+
+ ppgtt->base.cleanup(&ppgtt->base);
+ }
+
+ if (drm_mm_initialized(&vm->mm)) {
+ drm_mm_takedown(&vm->mm);
+ list_del(&vm->global_link);
+ }
+
+ vm->cleanup(vm);
+}
+
static int setup_scratch_page(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1871,6 +1902,22 @@ static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv)
GEN8_PPAT(6, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2)) |
GEN8_PPAT(7, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
+ if (!USES_PPGTT(dev_priv->dev))
+ /* Spec: "For GGTT, there is NO pat_sel[2:0] from the entry,
+ * so RTL will always use the value corresponding to
+ * pat_sel = 000".
+ * So let's disable cache for GGTT to avoid screen corruptions.
+ * MOCS still can be used though.
+ * - System agent ggtt writes (i.e. cpu gtt mmaps) already work
+ * before this patch, i.e. the same uncached + snooping access
+ * like on gen6/7 seems to be in effect.
+ * - So this just fixes blitter/render access. Again it looks
+ * like it's not just uncached access, but uncached + snooping.
+ * So we can still hold onto all our assumptions wrt cpu
+ * clflushing on LLC machines.
+ */
+ pat = GEN8_PPAT(0, GEN8_PPAT_UC);
+
/* XXX: spec defines this as 2 distinct registers. It's unclear if a 64b
* write would work. */
I915_WRITE(GEN8_PRIVATE_PAT, pat);
@@ -1992,10 +2039,6 @@ static void gen6_gmch_remove(struct i915_address_space *vm)
struct i915_gtt *gtt = container_of(vm, struct i915_gtt, base);
- if (drm_mm_initialized(&vm->mm)) {
- drm_mm_takedown(&vm->mm);
- list_del(&vm->global_link);
- }
iounmap(gtt->gsm);
teardown_scratch_page(vm->dev);
}
@@ -2028,10 +2071,6 @@ static int i915_gmch_probe(struct drm_device *dev,
static void i915_gmch_remove(struct i915_address_space *vm)
{
- if (drm_mm_initialized(&vm->mm)) {
- drm_mm_takedown(&vm->mm);
- list_del(&vm->global_link);
- }
intel_gmch_remove();
}
@@ -2130,8 +2169,10 @@ static struct i915_vma *__i915_gem_vma_create(struct drm_i915_gem_object *obj,
/* Keep GGTT vmas first to make debug easier */
if (i915_is_ggtt(vm))
list_add(&vma->vma_link, &obj->vma_list);
- else
+ else {
list_add_tail(&vma->vma_link, &obj->vma_list);
+ i915_ppgtt_get(i915_vm_to_ppgtt(vm));
+ }
return vma;
}
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
index 1b96a06be3cb..d5c14af51e99 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.h
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
@@ -34,6 +34,8 @@
#ifndef __I915_GEM_GTT_H__
#define __I915_GEM_GTT_H__
+struct drm_i915_file_private;
+
typedef uint32_t gen6_gtt_pte_t;
typedef uint64_t gen8_gtt_pte_t;
typedef gen8_gtt_pte_t gen8_ppgtt_pde_t;
@@ -154,6 +156,7 @@ struct i915_vma {
void (*unbind_vma)(struct i915_vma *vma);
/* Map an object into an address space with the given cache flags. */
#define GLOBAL_BIND (1<<0)
+#define PTE_READ_ONLY (1<<1)
void (*bind_vma)(struct i915_vma *vma,
enum i915_cache_level cache_level,
u32 flags);
@@ -197,7 +200,7 @@ struct i915_address_space {
/* FIXME: Need a more generic return type */
gen6_gtt_pte_t (*pte_encode)(dma_addr_t addr,
enum i915_cache_level level,
- bool valid); /* Create a valid PTE */
+ bool valid, u32 flags); /* Create a valid PTE */
void (*clear_range)(struct i915_address_space *vm,
uint64_t start,
uint64_t length,
@@ -205,7 +208,7 @@ struct i915_address_space {
void (*insert_entries)(struct i915_address_space *vm,
struct sg_table *st,
uint64_t start,
- enum i915_cache_level cache_level);
+ enum i915_cache_level cache_level, u32 flags);
void (*cleanup)(struct i915_address_space *vm);
};
@@ -257,22 +260,36 @@ struct i915_hw_ppgtt {
dma_addr_t *gen8_pt_dma_addr[4];
};
- struct intel_context *ctx;
+ struct drm_i915_file_private *file_priv;
int (*enable)(struct i915_hw_ppgtt *ppgtt);
int (*switch_mm)(struct i915_hw_ppgtt *ppgtt,
- struct intel_engine_cs *ring,
- bool synchronous);
+ struct intel_engine_cs *ring);
void (*debug_dump)(struct i915_hw_ppgtt *ppgtt, struct seq_file *m);
};
int i915_gem_gtt_init(struct drm_device *dev);
void i915_gem_init_global_gtt(struct drm_device *dev);
-void i915_gem_setup_global_gtt(struct drm_device *dev, unsigned long start,
- unsigned long mappable_end, unsigned long end);
-
-bool intel_enable_ppgtt(struct drm_device *dev, bool full);
-int i915_gem_init_ppgtt(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt);
+int i915_gem_setup_global_gtt(struct drm_device *dev, unsigned long start,
+ unsigned long mappable_end, unsigned long end);
+void i915_global_gtt_cleanup(struct drm_device *dev);
+
+
+int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt);
+int i915_ppgtt_init_hw(struct drm_device *dev);
+void i915_ppgtt_release(struct kref *kref);
+struct i915_hw_ppgtt *i915_ppgtt_create(struct drm_device *dev,
+ struct drm_i915_file_private *fpriv);
+static inline void i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt)
+{
+ if (ppgtt)
+ kref_get(&ppgtt->ref);
+}
+static inline void i915_ppgtt_put(struct i915_hw_ppgtt *ppgtt)
+{
+ if (ppgtt)
+ kref_put(&ppgtt->ref, i915_ppgtt_release);
+}
void i915_check_and_clear_faults(struct drm_device *dev);
void i915_gem_suspend_gtt_mappings(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c
index 34894b573064..a9a62d75aa57 100644
--- a/drivers/gpu/drm/i915/i915_gem_render_state.c
+++ b/drivers/gpu/drm/i915/i915_gem_render_state.c
@@ -28,64 +28,6 @@
#include "i915_drv.h"
#include "intel_renderstate.h"
-struct i915_render_state {
- struct drm_i915_gem_object *obj;
- unsigned long ggtt_offset;
- u32 *batch;
- u32 size;
- u32 len;
-};
-
-static struct i915_render_state *render_state_alloc(struct drm_device *dev)
-{
- struct i915_render_state *so;
- struct page *page;
- int ret;
-
- so = kzalloc(sizeof(*so), GFP_KERNEL);
- if (!so)
- return ERR_PTR(-ENOMEM);
-
- so->obj = i915_gem_alloc_object(dev, 4096);
- if (so->obj == NULL) {
- ret = -ENOMEM;
- goto free;
- }
- so->size = 4096;
-
- ret = i915_gem_obj_ggtt_pin(so->obj, 4096, 0);
- if (ret)
- goto free_gem;
-
- BUG_ON(so->obj->pages->nents != 1);
- page = sg_page(so->obj->pages->sgl);
-
- so->batch = kmap(page);
- if (!so->batch) {
- ret = -ENOMEM;
- goto unpin;
- }
-
- so->ggtt_offset = i915_gem_obj_ggtt_offset(so->obj);
-
- return so;
-unpin:
- i915_gem_object_ggtt_unpin(so->obj);
-free_gem:
- drm_gem_object_unreference(&so->obj->base);
-free:
- kfree(so);
- return ERR_PTR(ret);
-}
-
-static void render_state_free(struct i915_render_state *so)
-{
- kunmap(kmap_to_page(so->batch));
- i915_gem_object_ggtt_unpin(so->obj);
- drm_gem_object_unreference(&so->obj->base);
- kfree(so);
-}
-
static const struct intel_renderstate_rodata *
render_state_get_rodata(struct drm_device *dev, const int gen)
{
@@ -101,98 +43,137 @@ render_state_get_rodata(struct drm_device *dev, const int gen)
return NULL;
}
-static int render_state_setup(const int gen,
- const struct intel_renderstate_rodata *rodata,
- struct i915_render_state *so)
+static int render_state_init(struct render_state *so, struct drm_device *dev)
{
- const u64 goffset = i915_gem_obj_ggtt_offset(so->obj);
- u32 reloc_index = 0;
- u32 * const d = so->batch;
- unsigned int i = 0;
int ret;
- if (!rodata || rodata->batch_items * 4 > so->size)
+ so->gen = INTEL_INFO(dev)->gen;
+ so->rodata = render_state_get_rodata(dev, so->gen);
+ if (so->rodata == NULL)
+ return 0;
+
+ if (so->rodata->batch_items * 4 > 4096)
return -EINVAL;
+ so->obj = i915_gem_alloc_object(dev, 4096);
+ if (so->obj == NULL)
+ return -ENOMEM;
+
+ ret = i915_gem_obj_ggtt_pin(so->obj, 4096, 0);
+ if (ret)
+ goto free_gem;
+
+ so->ggtt_offset = i915_gem_obj_ggtt_offset(so->obj);
+ return 0;
+
+free_gem:
+ drm_gem_object_unreference(&so->obj->base);
+ return ret;
+}
+
+static int render_state_setup(struct render_state *so)
+{
+ const struct intel_renderstate_rodata *rodata = so->rodata;
+ unsigned int i = 0, reloc_index = 0;
+ struct page *page;
+ u32 *d;
+ int ret;
+
ret = i915_gem_object_set_to_cpu_domain(so->obj, true);
if (ret)
return ret;
+ page = sg_page(so->obj->pages->sgl);
+ d = kmap(page);
+
while (i < rodata->batch_items) {
u32 s = rodata->batch[i];
- if (reloc_index < rodata->reloc_items &&
- i * 4 == rodata->reloc[reloc_index]) {
-
- s += goffset & 0xffffffff;
-
- /* We keep batch offsets max 32bit */
- if (gen >= 8) {
+ if (i * 4 == rodata->reloc[reloc_index]) {
+ u64 r = s + so->ggtt_offset;
+ s = lower_32_bits(r);
+ if (so->gen >= 8) {
if (i + 1 >= rodata->batch_items ||
rodata->batch[i + 1] != 0)
return -EINVAL;
- d[i] = s;
- i++;
- s = (goffset & 0xffffffff00000000ull) >> 32;
+ d[i++] = s;
+ s = upper_32_bits(r);
}
reloc_index++;
}
- d[i] = s;
- i++;
+ d[i++] = s;
}
+ kunmap(page);
ret = i915_gem_object_set_to_gtt_domain(so->obj, false);
if (ret)
return ret;
- if (rodata->reloc_items != reloc_index) {
- DRM_ERROR("not all relocs resolved, %d out of %d\n",
- reloc_index, rodata->reloc_items);
+ if (rodata->reloc[reloc_index] != -1) {
+ DRM_ERROR("only %d relocs resolved\n", reloc_index);
return -EINVAL;
}
- so->len = rodata->batch_items * 4;
-
return 0;
}
-int i915_gem_render_state_init(struct intel_engine_cs *ring)
+void i915_gem_render_state_fini(struct render_state *so)
+{
+ i915_gem_object_ggtt_unpin(so->obj);
+ drm_gem_object_unreference(&so->obj->base);
+}
+
+int i915_gem_render_state_prepare(struct intel_engine_cs *ring,
+ struct render_state *so)
{
- const int gen = INTEL_INFO(ring->dev)->gen;
- struct i915_render_state *so;
- const struct intel_renderstate_rodata *rodata;
int ret;
if (WARN_ON(ring->id != RCS))
return -ENOENT;
- rodata = render_state_get_rodata(ring->dev, gen);
- if (rodata == NULL)
+ ret = render_state_init(so, ring->dev);
+ if (ret)
+ return ret;
+
+ if (so->rodata == NULL)
return 0;
- so = render_state_alloc(ring->dev);
- if (IS_ERR(so))
- return PTR_ERR(so);
+ ret = render_state_setup(so);
+ if (ret) {
+ i915_gem_render_state_fini(so);
+ return ret;
+ }
+
+ return 0;
+}
+
+int i915_gem_render_state_init(struct intel_engine_cs *ring)
+{
+ struct render_state so;
+ int ret;
- ret = render_state_setup(gen, rodata, so);
+ ret = i915_gem_render_state_prepare(ring, &so);
if (ret)
- goto out;
+ return ret;
+
+ if (so.rodata == NULL)
+ return 0;
ret = ring->dispatch_execbuffer(ring,
- i915_gem_obj_ggtt_offset(so->obj),
- so->len,
+ so.ggtt_offset,
+ so.rodata->batch_items * 4,
I915_DISPATCH_SECURE);
if (ret)
goto out;
- i915_vma_move_to_active(i915_gem_obj_to_ggtt(so->obj), ring);
+ i915_vma_move_to_active(i915_gem_obj_to_ggtt(so.obj), ring);
- ret = __i915_add_request(ring, NULL, so->obj, NULL);
+ ret = __i915_add_request(ring, NULL, so.obj, NULL);
/* __i915_add_request moves object to inactive if it fails */
out:
- render_state_free(so);
+ i915_gem_render_state_fini(&so);
return ret;
}
diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.h b/drivers/gpu/drm/i915/i915_gem_render_state.h
new file mode 100644
index 000000000000..c44961ed3fad
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_gem_render_state.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ */
+
+#ifndef _I915_GEM_RENDER_STATE_H_
+#define _I915_GEM_RENDER_STATE_H_
+
+#include <linux/types.h>
+
+struct intel_renderstate_rodata {
+ const u32 *reloc;
+ const u32 *batch;
+ const u32 batch_items;
+};
+
+struct render_state {
+ const struct intel_renderstate_rodata *rodata;
+ struct drm_i915_gem_object *obj;
+ u64 ggtt_offset;
+ int gen;
+};
+
+int i915_gem_render_state_init(struct intel_engine_cs *ring);
+void i915_gem_render_state_fini(struct render_state *so);
+int i915_gem_render_state_prepare(struct intel_engine_cs *ring,
+ struct render_state *so);
+
+#endif /* _I915_GEM_RENDER_STATE_H_ */
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index 7465ab0fd396..85fda6b803e4 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -147,30 +147,68 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
return base;
}
-static int i915_setup_compression(struct drm_device *dev, int size)
+static int find_compression_threshold(struct drm_device *dev,
+ struct drm_mm_node *node,
+ int size,
+ int fb_cpp)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb);
+ int compression_threshold = 1;
int ret;
- compressed_fb = kzalloc(sizeof(*compressed_fb), GFP_KERNEL);
- if (!compressed_fb)
- goto err_llb;
+ /* HACK: This code depends on what we will do in *_enable_fbc. If that
+ * code changes, this code needs to change as well.
+ *
+ * The enable_fbc code will attempt to use one of our 2 compression
+ * thresholds, therefore, in that case, we only have 1 resort.
+ */
- /* Try to over-allocate to reduce reallocations and fragmentation */
- ret = drm_mm_insert_node(&dev_priv->mm.stolen, compressed_fb,
+ /* Try to over-allocate to reduce reallocations and fragmentation. */
+ ret = drm_mm_insert_node(&dev_priv->mm.stolen, node,
size <<= 1, 4096, DRM_MM_SEARCH_DEFAULT);
- if (ret)
- ret = drm_mm_insert_node(&dev_priv->mm.stolen, compressed_fb,
- size >>= 1, 4096,
- DRM_MM_SEARCH_DEFAULT);
- if (ret)
+ if (ret == 0)
+ return compression_threshold;
+
+again:
+ /* HW's ability to limit the CFB is 1:4 */
+ if (compression_threshold > 4 ||
+ (fb_cpp == 2 && compression_threshold == 2))
+ return 0;
+
+ ret = drm_mm_insert_node(&dev_priv->mm.stolen, node,
+ size >>= 1, 4096,
+ DRM_MM_SEARCH_DEFAULT);
+ if (ret && INTEL_INFO(dev)->gen <= 4) {
+ return 0;
+ } else if (ret) {
+ compression_threshold <<= 1;
+ goto again;
+ } else {
+ return compression_threshold;
+ }
+}
+
+static int i915_setup_compression(struct drm_device *dev, int size, int fb_cpp)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_mm_node *uninitialized_var(compressed_llb);
+ int ret;
+
+ ret = find_compression_threshold(dev, &dev_priv->fbc.compressed_fb,
+ size, fb_cpp);
+ if (!ret)
goto err_llb;
+ else if (ret > 1) {
+ DRM_INFO("Reducing the compressed framebuffer size. This may lead to less power savings than a non-reduced-size. Try to increase stolen memory size if available in BIOS.\n");
+
+ }
+
+ dev_priv->fbc.threshold = ret;
if (HAS_PCH_SPLIT(dev))
- I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start);
+ I915_WRITE(ILK_DPFC_CB_BASE, dev_priv->fbc.compressed_fb.start);
else if (IS_GM45(dev)) {
- I915_WRITE(DPFC_CB_BASE, compressed_fb->start);
+ I915_WRITE(DPFC_CB_BASE, dev_priv->fbc.compressed_fb.start);
} else {
compressed_llb = kzalloc(sizeof(*compressed_llb), GFP_KERNEL);
if (!compressed_llb)
@@ -184,13 +222,12 @@ static int i915_setup_compression(struct drm_device *dev, int size)
dev_priv->fbc.compressed_llb = compressed_llb;
I915_WRITE(FBC_CFB_BASE,
- dev_priv->mm.stolen_base + compressed_fb->start);
+ dev_priv->mm.stolen_base + dev_priv->fbc.compressed_fb.start);
I915_WRITE(FBC_LL_BASE,
dev_priv->mm.stolen_base + compressed_llb->start);
}
- dev_priv->fbc.compressed_fb = compressed_fb;
- dev_priv->fbc.size = size;
+ dev_priv->fbc.size = size / dev_priv->fbc.threshold;
DRM_DEBUG_KMS("reserved %d bytes of contiguous stolen space for FBC\n",
size);
@@ -199,14 +236,13 @@ static int i915_setup_compression(struct drm_device *dev, int size)
err_fb:
kfree(compressed_llb);
- drm_mm_remove_node(compressed_fb);
+ drm_mm_remove_node(&dev_priv->fbc.compressed_fb);
err_llb:
- kfree(compressed_fb);
pr_info_once("drm: not enough stolen space for compressed buffer (need %d more bytes), disabling. Hint: you may be able to increase stolen memory size in the BIOS to avoid this.\n", size);
return -ENOSPC;
}
-int i915_gem_stolen_setup_compression(struct drm_device *dev, int size)
+int i915_gem_stolen_setup_compression(struct drm_device *dev, int size, int fb_cpp)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -219,7 +255,7 @@ int i915_gem_stolen_setup_compression(struct drm_device *dev, int size)
/* Release any current block */
i915_gem_stolen_cleanup_compression(dev);
- return i915_setup_compression(dev, size);
+ return i915_setup_compression(dev, size, fb_cpp);
}
void i915_gem_stolen_cleanup_compression(struct drm_device *dev)
@@ -229,10 +265,7 @@ void i915_gem_stolen_cleanup_compression(struct drm_device *dev)
if (dev_priv->fbc.size == 0)
return;
- if (dev_priv->fbc.compressed_fb) {
- drm_mm_remove_node(dev_priv->fbc.compressed_fb);
- kfree(dev_priv->fbc.compressed_fb);
- }
+ drm_mm_remove_node(&dev_priv->fbc.compressed_fb);
if (dev_priv->fbc.compressed_llb) {
drm_mm_remove_node(dev_priv->fbc.compressed_llb);
@@ -256,6 +289,7 @@ void i915_gem_cleanup_stolen(struct drm_device *dev)
int i915_gem_init_stolen(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 tmp;
int bios_reserved = 0;
#ifdef CONFIG_INTEL_IOMMU
@@ -275,8 +309,16 @@ int i915_gem_init_stolen(struct drm_device *dev)
DRM_DEBUG_KMS("found %zd bytes of stolen memory at %08lx\n",
dev_priv->gtt.stolen_size, dev_priv->mm.stolen_base);
- if (IS_VALLEYVIEW(dev))
- bios_reserved = 1024*1024; /* top 1M on VLV/BYT */
+ if (INTEL_INFO(dev)->gen >= 8) {
+ tmp = I915_READ(GEN7_BIOS_RESERVED);
+ tmp >>= GEN8_BIOS_RESERVED_SHIFT;
+ tmp &= GEN8_BIOS_RESERVED_MASK;
+ bios_reserved = (1024*1024) << tmp;
+ } else if (IS_GEN7(dev)) {
+ tmp = I915_READ(GEN7_BIOS_RESERVED);
+ bios_reserved = tmp & GEN7_BIOS_RESERVED_256K ?
+ 256*1024 : 1024*1024;
+ }
if (WARN_ON(bios_reserved > dev_priv->gtt.stolen_size))
return 0;
@@ -336,9 +378,20 @@ static void i915_gem_object_put_pages_stolen(struct drm_i915_gem_object *obj)
kfree(obj->pages);
}
+
+static void
+i915_gem_object_release_stolen(struct drm_i915_gem_object *obj)
+{
+ if (obj->stolen) {
+ drm_mm_remove_node(obj->stolen);
+ kfree(obj->stolen);
+ obj->stolen = NULL;
+ }
+}
static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = {
.get_pages = i915_gem_object_get_pages_stolen,
.put_pages = i915_gem_object_put_pages_stolen,
+ .release = i915_gem_object_release_stolen,
};
static struct drm_i915_gem_object *
@@ -496,13 +549,3 @@ err_out:
drm_gem_object_unreference(&obj->base);
return NULL;
}
-
-void
-i915_gem_object_release_stolen(struct drm_i915_gem_object *obj)
-{
- if (obj->stolen) {
- drm_mm_remove_node(obj->stolen);
- kfree(obj->stolen);
- obj->stolen = NULL;
- }
-}
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index cb150e8b4336..2b1eaa29ada4 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -91,7 +91,14 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
- if (IS_VALLEYVIEW(dev)) {
+ if (INTEL_INFO(dev)->gen >= 8 || IS_VALLEYVIEW(dev)) {
+ /*
+ * On BDW+, swizzling is not used. We leave the CPU memory
+ * controller in charge of optimizing memory accesses without
+ * the extra address manipulation GPU side.
+ *
+ * VLV and CHV don't have GPU swizzling.
+ */
swizzle_x = I915_BIT_6_SWIZZLE_NONE;
swizzle_y = I915_BIT_6_SWIZZLE_NONE;
} else if (INTEL_INFO(dev)->gen >= 6) {
@@ -357,26 +364,13 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
* has to also include the unfenced register the GPU uses
* whilst executing a fenced command for an untiled object.
*/
-
- obj->map_and_fenceable =
- !i915_gem_obj_ggtt_bound(obj) ||
- (i915_gem_obj_ggtt_offset(obj) +
- obj->base.size <= dev_priv->gtt.mappable_end &&
- i915_gem_object_fence_ok(obj, args->tiling_mode));
-
- /* Rebind if we need a change of alignment */
- if (!obj->map_and_fenceable) {
- u32 unfenced_align =
- i915_gem_get_gtt_alignment(dev, obj->base.size,
- args->tiling_mode,
- false);
- if (i915_gem_obj_ggtt_offset(obj) & (unfenced_align - 1))
- ret = i915_gem_object_ggtt_unbind(obj);
- }
+ if (obj->map_and_fenceable &&
+ !i915_gem_object_fence_ok(obj, args->tiling_mode))
+ ret = i915_gem_object_ggtt_unbind(obj);
if (ret == 0) {
obj->fence_dirty =
- obj->fenced_gpu_access ||
+ obj->last_fenced_seqno ||
obj->fence_reg != I915_FENCE_REG_NONE;
obj->tiling_mode = args->tiling_mode;
diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c
index 21ea92886a56..d182058383a9 100644
--- a/drivers/gpu/drm/i915/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/i915_gem_userptr.c
@@ -32,6 +32,15 @@
#include <linux/mempolicy.h>
#include <linux/swap.h>
+struct i915_mm_struct {
+ struct mm_struct *mm;
+ struct drm_device *dev;
+ struct i915_mmu_notifier *mn;
+ struct hlist_node node;
+ struct kref kref;
+ struct work_struct work;
+};
+
#if defined(CONFIG_MMU_NOTIFIER)
#include <linux/interval_tree.h>
@@ -40,19 +49,83 @@ struct i915_mmu_notifier {
struct hlist_node node;
struct mmu_notifier mn;
struct rb_root objects;
- struct drm_device *dev;
- struct mm_struct *mm;
- struct work_struct work;
- unsigned long count;
+ struct list_head linear;
unsigned long serial;
+ bool has_linear;
};
struct i915_mmu_object {
- struct i915_mmu_notifier *mmu;
+ struct i915_mmu_notifier *mn;
struct interval_tree_node it;
+ struct list_head link;
struct drm_i915_gem_object *obj;
+ bool is_linear;
};
+static unsigned long cancel_userptr(struct drm_i915_gem_object *obj)
+{
+ struct drm_device *dev = obj->base.dev;
+ unsigned long end;
+
+ mutex_lock(&dev->struct_mutex);
+ /* Cancel any active worker and force us to re-evaluate gup */
+ obj->userptr.work = NULL;
+
+ if (obj->pages != NULL) {
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ struct i915_vma *vma, *tmp;
+ bool was_interruptible;
+
+ was_interruptible = dev_priv->mm.interruptible;
+ dev_priv->mm.interruptible = false;
+
+ list_for_each_entry_safe(vma, tmp, &obj->vma_list, vma_link) {
+ int ret = i915_vma_unbind(vma);
+ WARN_ON(ret && ret != -EIO);
+ }
+ WARN_ON(i915_gem_object_put_pages(obj));
+
+ dev_priv->mm.interruptible = was_interruptible;
+ }
+
+ end = obj->userptr.ptr + obj->base.size;
+
+ drm_gem_object_unreference(&obj->base);
+ mutex_unlock(&dev->struct_mutex);
+
+ return end;
+}
+
+static void *invalidate_range__linear(struct i915_mmu_notifier *mn,
+ struct mm_struct *mm,
+ unsigned long start,
+ unsigned long end)
+{
+ struct i915_mmu_object *mo;
+ unsigned long serial;
+
+restart:
+ serial = mn->serial;
+ list_for_each_entry(mo, &mn->linear, link) {
+ struct drm_i915_gem_object *obj;
+
+ if (mo->it.last < start || mo->it.start > end)
+ continue;
+
+ obj = mo->obj;
+ drm_gem_object_reference(&obj->base);
+ spin_unlock(&mn->lock);
+
+ cancel_userptr(obj);
+
+ spin_lock(&mn->lock);
+ if (serial != mn->serial)
+ goto restart;
+ }
+
+ return NULL;
+}
+
static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
struct mm_struct *mm,
unsigned long start,
@@ -60,16 +133,18 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
{
struct i915_mmu_notifier *mn = container_of(_mn, struct i915_mmu_notifier, mn);
struct interval_tree_node *it = NULL;
+ unsigned long next = start;
unsigned long serial = 0;
end--; /* interval ranges are inclusive, but invalidate range is exclusive */
- while (start < end) {
- struct drm_i915_gem_object *obj;
+ while (next < end) {
+ struct drm_i915_gem_object *obj = NULL;
- obj = NULL;
spin_lock(&mn->lock);
- if (serial == mn->serial)
- it = interval_tree_iter_next(it, start, end);
+ if (mn->has_linear)
+ it = invalidate_range__linear(mn, mm, start, end);
+ else if (serial == mn->serial)
+ it = interval_tree_iter_next(it, next, end);
else
it = interval_tree_iter_first(&mn->objects, start, end);
if (it != NULL) {
@@ -81,31 +156,7 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
if (obj == NULL)
return;
- mutex_lock(&mn->dev->struct_mutex);
- /* Cancel any active worker and force us to re-evaluate gup */
- obj->userptr.work = NULL;
-
- if (obj->pages != NULL) {
- struct drm_i915_private *dev_priv = to_i915(mn->dev);
- struct i915_vma *vma, *tmp;
- bool was_interruptible;
-
- was_interruptible = dev_priv->mm.interruptible;
- dev_priv->mm.interruptible = false;
-
- list_for_each_entry_safe(vma, tmp, &obj->vma_list, vma_link) {
- int ret = i915_vma_unbind(vma);
- WARN_ON(ret && ret != -EIO);
- }
- WARN_ON(i915_gem_object_put_pages(obj));
-
- dev_priv->mm.interruptible = was_interruptible;
- }
-
- start = obj->userptr.ptr + obj->base.size;
-
- drm_gem_object_unreference(&obj->base);
- mutex_unlock(&mn->dev->struct_mutex);
+ next = cancel_userptr(obj);
}
}
@@ -114,113 +165,47 @@ static const struct mmu_notifier_ops i915_gem_userptr_notifier = {
};
static struct i915_mmu_notifier *
-__i915_mmu_notifier_lookup(struct drm_device *dev, struct mm_struct *mm)
+i915_mmu_notifier_create(struct mm_struct *mm)
{
- struct drm_i915_private *dev_priv = to_i915(dev);
- struct i915_mmu_notifier *mmu;
-
- /* Protected by dev->struct_mutex */
- hash_for_each_possible(dev_priv->mmu_notifiers, mmu, node, (unsigned long)mm)
- if (mmu->mm == mm)
- return mmu;
-
- return NULL;
-}
-
-static struct i915_mmu_notifier *
-i915_mmu_notifier_get(struct drm_device *dev, struct mm_struct *mm)
-{
- struct drm_i915_private *dev_priv = to_i915(dev);
- struct i915_mmu_notifier *mmu;
+ struct i915_mmu_notifier *mn;
int ret;
- lockdep_assert_held(&dev->struct_mutex);
-
- mmu = __i915_mmu_notifier_lookup(dev, mm);
- if (mmu)
- return mmu;
-
- mmu = kmalloc(sizeof(*mmu), GFP_KERNEL);
- if (mmu == NULL)
+ mn = kmalloc(sizeof(*mn), GFP_KERNEL);
+ if (mn == NULL)
return ERR_PTR(-ENOMEM);
- spin_lock_init(&mmu->lock);
- mmu->dev = dev;
- mmu->mn.ops = &i915_gem_userptr_notifier;
- mmu->mm = mm;
- mmu->objects = RB_ROOT;
- mmu->count = 0;
- mmu->serial = 0;
+ spin_lock_init(&mn->lock);
+ mn->mn.ops = &i915_gem_userptr_notifier;
+ mn->objects = RB_ROOT;
+ mn->serial = 1;
+ INIT_LIST_HEAD(&mn->linear);
+ mn->has_linear = false;
- /* Protected by mmap_sem (write-lock) */
- ret = __mmu_notifier_register(&mmu->mn, mm);
+ /* Protected by mmap_sem (write-lock) */
+ ret = __mmu_notifier_register(&mn->mn, mm);
if (ret) {
- kfree(mmu);
+ kfree(mn);
return ERR_PTR(ret);
}
- /* Protected by dev->struct_mutex */
- hash_add(dev_priv->mmu_notifiers, &mmu->node, (unsigned long)mm);
- return mmu;
-}
-
-static void
-__i915_mmu_notifier_destroy_worker(struct work_struct *work)
-{
- struct i915_mmu_notifier *mmu = container_of(work, typeof(*mmu), work);
- mmu_notifier_unregister(&mmu->mn, mmu->mm);
- kfree(mmu);
+ return mn;
}
-static void
-__i915_mmu_notifier_destroy(struct i915_mmu_notifier *mmu)
+static void __i915_mmu_notifier_update_serial(struct i915_mmu_notifier *mn)
{
- lockdep_assert_held(&mmu->dev->struct_mutex);
-
- /* Protected by dev->struct_mutex */
- hash_del(&mmu->node);
-
- /* Our lock ordering is: mmap_sem, mmu_notifier_scru, struct_mutex.
- * We enter the function holding struct_mutex, therefore we need
- * to drop our mutex prior to calling mmu_notifier_unregister in
- * order to prevent lock inversion (and system-wide deadlock)
- * between the mmap_sem and struct-mutex. Hence we defer the
- * unregistration to a workqueue where we hold no locks.
- */
- INIT_WORK(&mmu->work, __i915_mmu_notifier_destroy_worker);
- schedule_work(&mmu->work);
-}
-
-static void __i915_mmu_notifier_update_serial(struct i915_mmu_notifier *mmu)
-{
- if (++mmu->serial == 0)
- mmu->serial = 1;
-}
-
-static void
-i915_mmu_notifier_del(struct i915_mmu_notifier *mmu,
- struct i915_mmu_object *mn)
-{
- lockdep_assert_held(&mmu->dev->struct_mutex);
-
- spin_lock(&mmu->lock);
- interval_tree_remove(&mn->it, &mmu->objects);
- __i915_mmu_notifier_update_serial(mmu);
- spin_unlock(&mmu->lock);
-
- /* Protected against _add() by dev->struct_mutex */
- if (--mmu->count == 0)
- __i915_mmu_notifier_destroy(mmu);
+ if (++mn->serial == 0)
+ mn->serial = 1;
}
static int
-i915_mmu_notifier_add(struct i915_mmu_notifier *mmu,
- struct i915_mmu_object *mn)
+i915_mmu_notifier_add(struct drm_device *dev,
+ struct i915_mmu_notifier *mn,
+ struct i915_mmu_object *mo)
{
struct interval_tree_node *it;
int ret;
- ret = i915_mutex_lock_interruptible(mmu->dev);
+ ret = i915_mutex_lock_interruptible(dev);
if (ret)
return ret;
@@ -228,12 +213,11 @@ i915_mmu_notifier_add(struct i915_mmu_notifier *mmu,
* remove the objects from the interval tree) before we do
* the check for overlapping objects.
*/
- i915_gem_retire_requests(mmu->dev);
+ i915_gem_retire_requests(dev);
- /* Disallow overlapping userptr objects */
- spin_lock(&mmu->lock);
- it = interval_tree_iter_first(&mmu->objects,
- mn->it.start, mn->it.last);
+ spin_lock(&mn->lock);
+ it = interval_tree_iter_first(&mn->objects,
+ mo->it.start, mo->it.last);
if (it) {
struct drm_i915_gem_object *obj;
@@ -243,85 +227,137 @@ i915_mmu_notifier_add(struct i915_mmu_notifier *mmu,
* to flush their object references upon which the object will
* be removed from the interval-tree, or the the range is
* still in use by another client and the overlap is invalid.
+ *
+ * If we do have an overlap, we cannot use the interval tree
+ * for fast range invalidation.
*/
obj = container_of(it, struct i915_mmu_object, it)->obj;
- ret = obj->userptr.workers ? -EAGAIN : -EINVAL;
- } else {
- interval_tree_insert(&mn->it, &mmu->objects);
- __i915_mmu_notifier_update_serial(mmu);
- ret = 0;
+ if (!obj->userptr.workers)
+ mn->has_linear = mo->is_linear = true;
+ else
+ ret = -EAGAIN;
+ } else
+ interval_tree_insert(&mo->it, &mn->objects);
+
+ if (ret == 0) {
+ list_add(&mo->link, &mn->linear);
+ __i915_mmu_notifier_update_serial(mn);
}
- spin_unlock(&mmu->lock);
- mutex_unlock(&mmu->dev->struct_mutex);
+ spin_unlock(&mn->lock);
+ mutex_unlock(&dev->struct_mutex);
return ret;
}
+static bool i915_mmu_notifier_has_linear(struct i915_mmu_notifier *mn)
+{
+ struct i915_mmu_object *mo;
+
+ list_for_each_entry(mo, &mn->linear, link)
+ if (mo->is_linear)
+ return true;
+
+ return false;
+}
+
+static void
+i915_mmu_notifier_del(struct i915_mmu_notifier *mn,
+ struct i915_mmu_object *mo)
+{
+ spin_lock(&mn->lock);
+ list_del(&mo->link);
+ if (mo->is_linear)
+ mn->has_linear = i915_mmu_notifier_has_linear(mn);
+ else
+ interval_tree_remove(&mo->it, &mn->objects);
+ __i915_mmu_notifier_update_serial(mn);
+ spin_unlock(&mn->lock);
+}
+
static void
i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj)
{
- struct i915_mmu_object *mn;
+ struct i915_mmu_object *mo;
- mn = obj->userptr.mn;
- if (mn == NULL)
+ mo = obj->userptr.mmu_object;
+ if (mo == NULL)
return;
- i915_mmu_notifier_del(mn->mmu, mn);
- obj->userptr.mn = NULL;
+ i915_mmu_notifier_del(mo->mn, mo);
+ kfree(mo);
+
+ obj->userptr.mmu_object = NULL;
+}
+
+static struct i915_mmu_notifier *
+i915_mmu_notifier_find(struct i915_mm_struct *mm)
+{
+ struct i915_mmu_notifier *mn = mm->mn;
+
+ mn = mm->mn;
+ if (mn)
+ return mn;
+
+ down_write(&mm->mm->mmap_sem);
+ mutex_lock(&to_i915(mm->dev)->mm_lock);
+ if ((mn = mm->mn) == NULL) {
+ mn = i915_mmu_notifier_create(mm->mm);
+ if (!IS_ERR(mn))
+ mm->mn = mn;
+ }
+ mutex_unlock(&to_i915(mm->dev)->mm_lock);
+ up_write(&mm->mm->mmap_sem);
+
+ return mn;
}
static int
i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj,
unsigned flags)
{
- struct i915_mmu_notifier *mmu;
- struct i915_mmu_object *mn;
+ struct i915_mmu_notifier *mn;
+ struct i915_mmu_object *mo;
int ret;
if (flags & I915_USERPTR_UNSYNCHRONIZED)
return capable(CAP_SYS_ADMIN) ? 0 : -EPERM;
- down_write(&obj->userptr.mm->mmap_sem);
- ret = i915_mutex_lock_interruptible(obj->base.dev);
- if (ret == 0) {
- mmu = i915_mmu_notifier_get(obj->base.dev, obj->userptr.mm);
- if (!IS_ERR(mmu))
- mmu->count++; /* preemptive add to act as a refcount */
- else
- ret = PTR_ERR(mmu);
- mutex_unlock(&obj->base.dev->struct_mutex);
- }
- up_write(&obj->userptr.mm->mmap_sem);
- if (ret)
- return ret;
+ if (WARN_ON(obj->userptr.mm == NULL))
+ return -EINVAL;
- mn = kzalloc(sizeof(*mn), GFP_KERNEL);
- if (mn == NULL) {
- ret = -ENOMEM;
- goto destroy_mmu;
- }
+ mn = i915_mmu_notifier_find(obj->userptr.mm);
+ if (IS_ERR(mn))
+ return PTR_ERR(mn);
- mn->mmu = mmu;
- mn->it.start = obj->userptr.ptr;
- mn->it.last = mn->it.start + obj->base.size - 1;
- mn->obj = obj;
+ mo = kzalloc(sizeof(*mo), GFP_KERNEL);
+ if (mo == NULL)
+ return -ENOMEM;
- ret = i915_mmu_notifier_add(mmu, mn);
- if (ret)
- goto free_mn;
+ mo->mn = mn;
+ mo->it.start = obj->userptr.ptr;
+ mo->it.last = mo->it.start + obj->base.size - 1;
+ mo->obj = obj;
- obj->userptr.mn = mn;
+ ret = i915_mmu_notifier_add(obj->base.dev, mn, mo);
+ if (ret) {
+ kfree(mo);
+ return ret;
+ }
+
+ obj->userptr.mmu_object = mo;
return 0;
+}
-free_mn:
+static void
+i915_mmu_notifier_free(struct i915_mmu_notifier *mn,
+ struct mm_struct *mm)
+{
+ if (mn == NULL)
+ return;
+
+ mmu_notifier_unregister(&mn->mn, mm);
kfree(mn);
-destroy_mmu:
- mutex_lock(&obj->base.dev->struct_mutex);
- if (--mmu->count == 0)
- __i915_mmu_notifier_destroy(mmu);
- mutex_unlock(&obj->base.dev->struct_mutex);
- return ret;
}
#else
@@ -343,15 +379,114 @@ i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj,
return 0;
}
+
+static void
+i915_mmu_notifier_free(struct i915_mmu_notifier *mn,
+ struct mm_struct *mm)
+{
+}
+
#endif
+static struct i915_mm_struct *
+__i915_mm_struct_find(struct drm_i915_private *dev_priv, struct mm_struct *real)
+{
+ struct i915_mm_struct *mm;
+
+ /* Protected by dev_priv->mm_lock */
+ hash_for_each_possible(dev_priv->mm_structs, mm, node, (unsigned long)real)
+ if (mm->mm == real)
+ return mm;
+
+ return NULL;
+}
+
+static int
+i915_gem_userptr_init__mm_struct(struct drm_i915_gem_object *obj)
+{
+ struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
+ struct i915_mm_struct *mm;
+ int ret = 0;
+
+ /* During release of the GEM object we hold the struct_mutex. This
+ * precludes us from calling mmput() at that time as that may be
+ * the last reference and so call exit_mmap(). exit_mmap() will
+ * attempt to reap the vma, and if we were holding a GTT mmap
+ * would then call drm_gem_vm_close() and attempt to reacquire
+ * the struct mutex. So in order to avoid that recursion, we have
+ * to defer releasing the mm reference until after we drop the
+ * struct_mutex, i.e. we need to schedule a worker to do the clean
+ * up.
+ */
+ mutex_lock(&dev_priv->mm_lock);
+ mm = __i915_mm_struct_find(dev_priv, current->mm);
+ if (mm == NULL) {
+ mm = kmalloc(sizeof(*mm), GFP_KERNEL);
+ if (mm == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ kref_init(&mm->kref);
+ mm->dev = obj->base.dev;
+
+ mm->mm = current->mm;
+ atomic_inc(&current->mm->mm_count);
+
+ mm->mn = NULL;
+
+ /* Protected by dev_priv->mm_lock */
+ hash_add(dev_priv->mm_structs,
+ &mm->node, (unsigned long)mm->mm);
+ } else
+ kref_get(&mm->kref);
+
+ obj->userptr.mm = mm;
+out:
+ mutex_unlock(&dev_priv->mm_lock);
+ return ret;
+}
+
+static void
+__i915_mm_struct_free__worker(struct work_struct *work)
+{
+ struct i915_mm_struct *mm = container_of(work, typeof(*mm), work);
+ i915_mmu_notifier_free(mm->mn, mm->mm);
+ mmdrop(mm->mm);
+ kfree(mm);
+}
+
+static void
+__i915_mm_struct_free(struct kref *kref)
+{
+ struct i915_mm_struct *mm = container_of(kref, typeof(*mm), kref);
+
+ /* Protected by dev_priv->mm_lock */
+ hash_del(&mm->node);
+ mutex_unlock(&to_i915(mm->dev)->mm_lock);
+
+ INIT_WORK(&mm->work, __i915_mm_struct_free__worker);
+ schedule_work(&mm->work);
+}
+
+static void
+i915_gem_userptr_release__mm_struct(struct drm_i915_gem_object *obj)
+{
+ if (obj->userptr.mm == NULL)
+ return;
+
+ kref_put_mutex(&obj->userptr.mm->kref,
+ __i915_mm_struct_free,
+ &to_i915(obj->base.dev)->mm_lock);
+ obj->userptr.mm = NULL;
+}
+
struct get_pages_work {
struct work_struct work;
struct drm_i915_gem_object *obj;
struct task_struct *task;
};
-
#if IS_ENABLED(CONFIG_SWIOTLB)
#define swiotlb_active() swiotlb_nr_tbl()
#else
@@ -409,7 +544,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
if (pvec == NULL)
pvec = drm_malloc_ab(num_pages, sizeof(struct page *));
if (pvec != NULL) {
- struct mm_struct *mm = obj->userptr.mm;
+ struct mm_struct *mm = obj->userptr.mm->mm;
down_read(&mm->mmap_sem);
while (pinned < num_pages) {
@@ -475,7 +610,7 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
pvec = NULL;
pinned = 0;
- if (obj->userptr.mm == current->mm) {
+ if (obj->userptr.mm->mm == current->mm) {
pvec = kmalloc(num_pages*sizeof(struct page *),
GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
if (pvec == NULL) {
@@ -554,16 +689,15 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
static void
i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj)
{
- struct scatterlist *sg;
- int i;
+ struct sg_page_iter sg_iter;
BUG_ON(obj->userptr.work != NULL);
if (obj->madv != I915_MADV_WILLNEED)
obj->dirty = 0;
- for_each_sg(obj->pages->sgl, sg, obj->pages->nents, i) {
- struct page *page = sg_page(sg);
+ for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) {
+ struct page *page = sg_page_iter_page(&sg_iter);
if (obj->dirty)
set_page_dirty(page);
@@ -581,17 +715,13 @@ static void
i915_gem_userptr_release(struct drm_i915_gem_object *obj)
{
i915_gem_userptr_release__mmu_notifier(obj);
-
- if (obj->userptr.mm) {
- mmput(obj->userptr.mm);
- obj->userptr.mm = NULL;
- }
+ i915_gem_userptr_release__mm_struct(obj);
}
static int
i915_gem_userptr_dmabuf_export(struct drm_i915_gem_object *obj)
{
- if (obj->userptr.mn)
+ if (obj->userptr.mmu_object)
return 0;
return i915_gem_userptr_init__mmu_notifier(obj, 0);
@@ -611,12 +741,11 @@ static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = {
* We impose several restrictions upon the memory being mapped
* into the GPU.
* 1. It must be page aligned (both start/end addresses, i.e ptr and size).
- * 2. It cannot overlap any other userptr object in the same address space.
- * 3. It must be normal system memory, not a pointer into another map of IO
+ * 2. It must be normal system memory, not a pointer into another map of IO
* space (e.g. it must not be a GTT mmapping of another object).
- * 4. We only allow a bo as large as we could in theory map into the GTT,
+ * 3. We only allow a bo as large as we could in theory map into the GTT,
* that is we limit the size to the total size of the GTT.
- * 5. The bo is marked as being snoopable. The backing pages are left
+ * 4. The bo is marked as being snoopable. The backing pages are left
* accessible directly by the CPU, but reads and writes by the GPU may
* incur the cost of a snoop (unless you have an LLC architecture).
*
@@ -667,7 +796,6 @@ i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file
return -ENODEV;
}
- /* Allocate the new object */
obj = i915_gem_object_alloc(dev);
if (obj == NULL)
return -ENOMEM;
@@ -685,8 +813,8 @@ i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file
* at binding. This means that we need to hook into the mmu_notifier
* in order to detect if the mmu is destroyed.
*/
- ret = -ENOMEM;
- if ((obj->userptr.mm = get_task_mm(current)))
+ ret = i915_gem_userptr_init__mm_struct(obj);
+ if (ret == 0)
ret = i915_gem_userptr_init__mmu_notifier(obj, args->flags);
if (ret == 0)
ret = drm_gem_handle_create(file, &obj->base, &handle);
@@ -703,9 +831,8 @@ i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file
int
i915_gem_init_userptr(struct drm_device *dev)
{
-#if defined(CONFIG_MMU_NOTIFIER)
struct drm_i915_private *dev_priv = to_i915(dev);
- hash_init(dev_priv->mmu_notifiers);
-#endif
+ mutex_init(&dev_priv->mm_lock);
+ hash_init(dev_priv->mm_structs);
return 0;
}
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 66cf41765bf9..2c87a797213f 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -192,10 +192,10 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m,
struct drm_i915_error_buffer *err,
int count)
{
- err_printf(m, "%s [%d]:\n", name, count);
+ err_printf(m, " %s [%d]:\n", name, count);
while (count--) {
- err_printf(m, " %08x %8u %02x %02x %x %x",
+ err_printf(m, " %08x %8u %02x %02x %x %x",
err->gtt_offset,
err->size,
err->read_domains,
@@ -208,7 +208,7 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m,
err_puts(m, err->userptr ? " userptr" : "");
err_puts(m, err->ring != -1 ? " " : "");
err_puts(m, ring_str(err->ring));
- err_puts(m, i915_cache_level_str(err->cache_level));
+ err_puts(m, i915_cache_level_str(m->i915, err->cache_level));
if (err->name)
err_printf(m, " (name: %d)", err->name);
@@ -229,6 +229,8 @@ static const char *hangcheck_action_to_str(enum intel_ring_hangcheck_action a)
return "wait";
case HANGCHECK_ACTIVE:
return "active";
+ case HANGCHECK_ACTIVE_LOOP:
+ return "active (loop)";
case HANGCHECK_KICK:
return "kick";
case HANGCHECK_HUNG:
@@ -327,6 +329,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
struct drm_device *dev = error_priv->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_error_state *error = error_priv->error;
+ struct drm_i915_error_object *obj;
int i, j, offset, elt;
int max_hangcheck_score;
@@ -358,6 +361,12 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
err_printf(m, "PCI ID: 0x%04x\n", dev->pdev->device);
err_printf(m, "EIR: 0x%08x\n", error->eir);
err_printf(m, "IER: 0x%08x\n", error->ier);
+ if (INTEL_INFO(dev)->gen >= 8) {
+ for (i = 0; i < 4; i++)
+ err_printf(m, "GTIER gt %d: 0x%08x\n", i,
+ error->gtier[i]);
+ } else if (HAS_PCH_SPLIT(dev) || IS_VALLEYVIEW(dev))
+ err_printf(m, "GTIER: 0x%08x\n", error->gtier[0]);
err_printf(m, "PGTBL_ER: 0x%08x\n", error->pgtbl_er);
err_printf(m, "FORCEWAKE: 0x%08x\n", error->forcewake);
err_printf(m, "DERRMR: 0x%08x\n", error->derrmr);
@@ -384,19 +393,19 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
i915_ring_error_state(m, dev, &error->ring[i]);
}
- if (error->active_bo)
+ for (i = 0; i < error->vm_count; i++) {
+ err_printf(m, "vm[%d]\n", i);
+
print_error_buffers(m, "Active",
- error->active_bo[0],
- error->active_bo_count[0]);
+ error->active_bo[i],
+ error->active_bo_count[i]);
- if (error->pinned_bo)
print_error_buffers(m, "Pinned",
- error->pinned_bo[0],
- error->pinned_bo_count[0]);
+ error->pinned_bo[i],
+ error->pinned_bo_count[i]);
+ }
for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
- struct drm_i915_error_object *obj;
-
obj = error->ring[i].batchbuffer;
if (obj) {
err_puts(m, dev_priv->ring[i].name);
@@ -459,6 +468,18 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
}
}
+ if ((obj = error->semaphore_obj)) {
+ err_printf(m, "Semaphore page = 0x%08x\n", obj->gtt_offset);
+ for (elt = 0; elt < PAGE_SIZE/16; elt += 4) {
+ err_printf(m, "[%04x] %08x %08x %08x %08x\n",
+ elt * 4,
+ obj->pages[0][elt],
+ obj->pages[0][elt+1],
+ obj->pages[0][elt+2],
+ obj->pages[0][elt+3]);
+ }
+ }
+
if (error->overlay)
intel_overlay_print_error_state(m, error->overlay);
@@ -473,9 +494,11 @@ out:
}
int i915_error_state_buf_init(struct drm_i915_error_state_buf *ebuf,
+ struct drm_i915_private *i915,
size_t count, loff_t pos)
{
memset(ebuf, 0, sizeof(*ebuf));
+ ebuf->i915 = i915;
/* We need to have enough room to store any i915_error_state printf
* so that we can move it to start position.
@@ -529,6 +552,7 @@ static void i915_error_state_free(struct kref *error_ref)
kfree(error->ring[i].requests);
}
+ i915_error_object_free(error->semaphore_obj);
kfree(error->active_bo);
kfree(error->overlay);
kfree(error->display);
@@ -536,24 +560,54 @@ static void i915_error_state_free(struct kref *error_ref)
}
static struct drm_i915_error_object *
-i915_error_object_create_sized(struct drm_i915_private *dev_priv,
- struct drm_i915_gem_object *src,
- struct i915_address_space *vm,
- const int num_pages)
+i915_error_object_create(struct drm_i915_private *dev_priv,
+ struct drm_i915_gem_object *src,
+ struct i915_address_space *vm)
{
struct drm_i915_error_object *dst;
- int i;
+ int num_pages;
+ bool use_ggtt;
+ int i = 0;
u32 reloc_offset;
if (src == NULL || src->pages == NULL)
return NULL;
+ num_pages = src->base.size >> PAGE_SHIFT;
+
dst = kmalloc(sizeof(*dst) + num_pages * sizeof(u32 *), GFP_ATOMIC);
if (dst == NULL)
return NULL;
- reloc_offset = dst->gtt_offset = i915_gem_obj_offset(src, vm);
- for (i = 0; i < num_pages; i++) {
+ if (i915_gem_obj_bound(src, vm))
+ dst->gtt_offset = i915_gem_obj_offset(src, vm);
+ else
+ dst->gtt_offset = -1;
+
+ reloc_offset = dst->gtt_offset;
+ use_ggtt = (src->cache_level == I915_CACHE_NONE &&
+ i915_is_ggtt(vm) &&
+ src->has_global_gtt_mapping &&
+ reloc_offset + num_pages * PAGE_SIZE <= dev_priv->gtt.mappable_end);
+
+ /* Cannot access stolen address directly, try to use the aperture */
+ if (src->stolen) {
+ use_ggtt = true;
+
+ if (!src->has_global_gtt_mapping)
+ goto unwind;
+
+ reloc_offset = i915_gem_obj_ggtt_offset(src);
+ if (reloc_offset + num_pages * PAGE_SIZE > dev_priv->gtt.mappable_end)
+ goto unwind;
+ }
+
+ /* Cannot access snooped pages through the aperture */
+ if (use_ggtt && src->cache_level != I915_CACHE_NONE && !HAS_LLC(dev_priv->dev))
+ goto unwind;
+
+ dst->page_count = num_pages;
+ while (num_pages--) {
unsigned long flags;
void *d;
@@ -562,10 +616,7 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv,
goto unwind;
local_irq_save(flags);
- if (src->cache_level == I915_CACHE_NONE &&
- reloc_offset < dev_priv->gtt.mappable_end &&
- src->has_global_gtt_mapping &&
- i915_is_ggtt(vm)) {
+ if (use_ggtt) {
void __iomem *s;
/* Simply ignore tiling or any overlapping fence.
@@ -577,14 +628,6 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv,
reloc_offset);
memcpy_fromio(d, s, PAGE_SIZE);
io_mapping_unmap_atomic(s);
- } else if (src->stolen) {
- unsigned long offset;
-
- offset = dev_priv->mm.stolen_base;
- offset += src->stolen->start;
- offset += i << PAGE_SHIFT;
-
- memcpy_fromio(d, (void __iomem *) offset, PAGE_SIZE);
} else {
struct page *page;
void *s;
@@ -601,11 +644,9 @@ i915_error_object_create_sized(struct drm_i915_private *dev_priv,
}
local_irq_restore(flags);
- dst->pages[i] = d;
-
+ dst->pages[i++] = d;
reloc_offset += PAGE_SIZE;
}
- dst->page_count = num_pages;
return dst;
@@ -615,22 +656,19 @@ unwind:
kfree(dst);
return NULL;
}
-#define i915_error_object_create(dev_priv, src, vm) \
- i915_error_object_create_sized((dev_priv), (src), (vm), \
- (src)->base.size>>PAGE_SHIFT)
-
#define i915_error_ggtt_object_create(dev_priv, src) \
- i915_error_object_create_sized((dev_priv), (src), &(dev_priv)->gtt.base, \
- (src)->base.size>>PAGE_SHIFT)
+ i915_error_object_create((dev_priv), (src), &(dev_priv)->gtt.base)
static void capture_bo(struct drm_i915_error_buffer *err,
- struct drm_i915_gem_object *obj)
+ struct i915_vma *vma)
{
+ struct drm_i915_gem_object *obj = vma->obj;
+
err->size = obj->base.size;
err->name = obj->base.name;
err->rseqno = obj->last_read_seqno;
err->wseqno = obj->last_write_seqno;
- err->gtt_offset = i915_gem_obj_ggtt_offset(obj);
+ err->gtt_offset = vma->node.start;
err->read_domains = obj->base.read_domains;
err->write_domain = obj->base.write_domain;
err->fence_reg = obj->fence_reg;
@@ -654,7 +692,7 @@ static u32 capture_active_bo(struct drm_i915_error_buffer *err,
int i = 0;
list_for_each_entry(vma, head, mm_list) {
- capture_bo(err++, vma->obj);
+ capture_bo(err++, vma);
if (++i == count)
break;
}
@@ -663,21 +701,27 @@ static u32 capture_active_bo(struct drm_i915_error_buffer *err,
}
static u32 capture_pinned_bo(struct drm_i915_error_buffer *err,
- int count, struct list_head *head)
+ int count, struct list_head *head,
+ struct i915_address_space *vm)
{
struct drm_i915_gem_object *obj;
- int i = 0;
+ struct drm_i915_error_buffer * const first = err;
+ struct drm_i915_error_buffer * const last = err + count;
list_for_each_entry(obj, head, global_list) {
- if (!i915_gem_obj_is_pinned(obj))
- continue;
+ struct i915_vma *vma;
- capture_bo(err++, obj);
- if (++i == count)
+ if (err == last)
break;
+
+ list_for_each_entry(vma, &obj->vma_list, vma_link)
+ if (vma->vm == vm && vma->pin_count > 0) {
+ capture_bo(err++, vma);
+ break;
+ }
}
- return i;
+ return err - first;
}
/* Generate a semi-unique error code. The code is not meant to have meaning, The
@@ -746,7 +790,60 @@ static void i915_gem_record_fences(struct drm_device *dev,
}
}
+
+static void gen8_record_semaphore_state(struct drm_i915_private *dev_priv,
+ struct drm_i915_error_state *error,
+ struct intel_engine_cs *ring,
+ struct drm_i915_error_ring *ering)
+{
+ struct intel_engine_cs *to;
+ int i;
+
+ if (!i915_semaphore_is_enabled(dev_priv->dev))
+ return;
+
+ if (!error->semaphore_obj)
+ error->semaphore_obj =
+ i915_error_object_create(dev_priv,
+ dev_priv->semaphore_obj,
+ &dev_priv->gtt.base);
+
+ for_each_ring(to, dev_priv, i) {
+ int idx;
+ u16 signal_offset;
+ u32 *tmp;
+
+ if (ring == to)
+ continue;
+
+ signal_offset = (GEN8_SIGNAL_OFFSET(ring, i) & (PAGE_SIZE - 1))
+ / 4;
+ tmp = error->semaphore_obj->pages[0];
+ idx = intel_ring_sync_index(ring, to);
+
+ ering->semaphore_mboxes[idx] = tmp[signal_offset];
+ ering->semaphore_seqno[idx] = ring->semaphore.sync_seqno[idx];
+ }
+}
+
+static void gen6_record_semaphore_state(struct drm_i915_private *dev_priv,
+ struct intel_engine_cs *ring,
+ struct drm_i915_error_ring *ering)
+{
+ ering->semaphore_mboxes[0] = I915_READ(RING_SYNC_0(ring->mmio_base));
+ ering->semaphore_mboxes[1] = I915_READ(RING_SYNC_1(ring->mmio_base));
+ ering->semaphore_seqno[0] = ring->semaphore.sync_seqno[0];
+ ering->semaphore_seqno[1] = ring->semaphore.sync_seqno[1];
+
+ if (HAS_VEBOX(dev_priv->dev)) {
+ ering->semaphore_mboxes[2] =
+ I915_READ(RING_SYNC_2(ring->mmio_base));
+ ering->semaphore_seqno[2] = ring->semaphore.sync_seqno[2];
+ }
+}
+
static void i915_record_ring_state(struct drm_device *dev,
+ struct drm_i915_error_state *error,
struct intel_engine_cs *ring,
struct drm_i915_error_ring *ering)
{
@@ -755,18 +852,10 @@ static void i915_record_ring_state(struct drm_device *dev,
if (INTEL_INFO(dev)->gen >= 6) {
ering->rc_psmi = I915_READ(ring->mmio_base + 0x50);
ering->fault_reg = I915_READ(RING_FAULT_REG(ring));
- ering->semaphore_mboxes[0]
- = I915_READ(RING_SYNC_0(ring->mmio_base));
- ering->semaphore_mboxes[1]
- = I915_READ(RING_SYNC_1(ring->mmio_base));
- ering->semaphore_seqno[0] = ring->semaphore.sync_seqno[0];
- ering->semaphore_seqno[1] = ring->semaphore.sync_seqno[1];
- }
-
- if (HAS_VEBOX(dev)) {
- ering->semaphore_mboxes[2] =
- I915_READ(RING_SYNC_2(ring->mmio_base));
- ering->semaphore_seqno[2] = ring->semaphore.sync_seqno[2];
+ if (INTEL_INFO(dev)->gen >= 8)
+ gen8_record_semaphore_state(dev_priv, error, ring, ering);
+ else
+ gen6_record_semaphore_state(dev_priv, ring, ering);
}
if (INTEL_INFO(dev)->gen >= 4) {
@@ -825,9 +914,6 @@ static void i915_record_ring_state(struct drm_device *dev,
ering->hws = I915_READ(mmio);
}
- ering->cpu_ring_head = ring->buffer->head;
- ering->cpu_ring_tail = ring->buffer->tail;
-
ering->hangcheck_score = ring->hangcheck.score;
ering->hangcheck_action = ring->hangcheck.action;
@@ -871,6 +957,9 @@ static void i915_gem_record_active_context(struct intel_engine_cs *ring,
return;
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
+ if (!i915_gem_obj_ggtt_bound(obj))
+ continue;
+
if ((error->ccid & PAGE_MASK) == i915_gem_obj_ggtt_offset(obj)) {
ering->ctx = i915_error_ggtt_object_create(dev_priv, obj);
break;
@@ -887,6 +976,7 @@ static void i915_gem_record_rings(struct drm_device *dev,
for (i = 0; i < I915_NUM_RINGS; i++) {
struct intel_engine_cs *ring = &dev_priv->ring[i];
+ struct intel_ringbuffer *rbuf;
error->ring[i].pid = -1;
@@ -895,10 +985,16 @@ static void i915_gem_record_rings(struct drm_device *dev,
error->ring[i].valid = true;
- i915_record_ring_state(dev, ring, &error->ring[i]);
+ i915_record_ring_state(dev, error, ring, &error->ring[i]);
request = i915_gem_find_active_request(ring);
if (request) {
+ struct i915_address_space *vm;
+
+ vm = request->ctx && request->ctx->ppgtt ?
+ &request->ctx->ppgtt->base :
+ &dev_priv->gtt.base;
+
/* We need to copy these to an anonymous buffer
* as the simplest method to avoid being overwritten
* by userspace.
@@ -906,12 +1002,9 @@ static void i915_gem_record_rings(struct drm_device *dev,
error->ring[i].batchbuffer =
i915_error_object_create(dev_priv,
request->batch_obj,
- request->ctx ?
- request->ctx->vm :
- &dev_priv->gtt.base);
+ vm);
- if (HAS_BROKEN_CS_TLB(dev_priv->dev) &&
- ring->scratch.obj)
+ if (HAS_BROKEN_CS_TLB(dev_priv->dev))
error->ring[i].wa_batchbuffer =
i915_error_ggtt_object_create(dev_priv,
ring->scratch.obj);
@@ -930,12 +1023,27 @@ static void i915_gem_record_rings(struct drm_device *dev,
}
}
+ if (i915.enable_execlists) {
+ /* TODO: This is only a small fix to keep basic error
+ * capture working, but we need to add more information
+ * for it to be useful (e.g. dump the context being
+ * executed).
+ */
+ if (request)
+ rbuf = request->ctx->engine[ring->id].ringbuf;
+ else
+ rbuf = ring->default_context->engine[ring->id].ringbuf;
+ } else
+ rbuf = ring->buffer;
+
+ error->ring[i].cpu_ring_head = rbuf->head;
+ error->ring[i].cpu_ring_tail = rbuf->tail;
+
error->ring[i].ringbuffer =
- i915_error_ggtt_object_create(dev_priv, ring->buffer->obj);
+ i915_error_ggtt_object_create(dev_priv, rbuf->obj);
- if (ring->status_page.obj)
- error->ring[i].hws_page =
- i915_error_ggtt_object_create(dev_priv, ring->status_page.obj);
+ error->ring[i].hws_page =
+ i915_error_ggtt_object_create(dev_priv, ring->status_page.obj);
i915_gem_record_active_context(ring, error, &error->ring[i]);
@@ -981,9 +1089,14 @@ static void i915_gem_capture_vm(struct drm_i915_private *dev_priv,
list_for_each_entry(vma, &vm->active_list, mm_list)
i++;
error->active_bo_count[ndx] = i;
- list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list)
- if (i915_gem_obj_is_pinned(obj))
- i++;
+
+ list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
+ list_for_each_entry(vma, &obj->vma_list, vma_link)
+ if (vma->vm == vm && vma->pin_count > 0) {
+ i++;
+ break;
+ }
+ }
error->pinned_bo_count[ndx] = i - error->active_bo_count[ndx];
if (i) {
@@ -1002,7 +1115,7 @@ static void i915_gem_capture_vm(struct drm_i915_private *dev_priv,
error->pinned_bo_count[ndx] =
capture_pinned_bo(pinned_bo,
error->pinned_bo_count[ndx],
- &dev_priv->mm.bound_list);
+ &dev_priv->mm.bound_list, vm);
error->active_bo[ndx] = active_bo;
error->pinned_bo[ndx] = pinned_bo;
}
@@ -1023,8 +1136,25 @@ static void i915_gem_capture_buffers(struct drm_i915_private *dev_priv,
error->pinned_bo_count = kcalloc(cnt, sizeof(*error->pinned_bo_count),
GFP_ATOMIC);
- list_for_each_entry(vm, &dev_priv->vm_list, global_link)
- i915_gem_capture_vm(dev_priv, error, vm, i++);
+ if (error->active_bo == NULL ||
+ error->pinned_bo == NULL ||
+ error->active_bo_count == NULL ||
+ error->pinned_bo_count == NULL) {
+ kfree(error->active_bo);
+ kfree(error->active_bo_count);
+ kfree(error->pinned_bo);
+ kfree(error->pinned_bo_count);
+
+ error->active_bo = NULL;
+ error->active_bo_count = NULL;
+ error->pinned_bo = NULL;
+ error->pinned_bo_count = NULL;
+ } else {
+ list_for_each_entry(vm, &dev_priv->vm_list, global_link)
+ i915_gem_capture_vm(dev_priv, error, vm, i++);
+
+ error->vm_count = cnt;
+ }
}
/* Capture all registers which don't fit into another category. */
@@ -1032,6 +1162,7 @@ static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
struct drm_i915_error_state *error)
{
struct drm_device *dev = dev_priv->dev;
+ int i;
/* General organization
* 1. Registers specific to a single generation
@@ -1043,7 +1174,8 @@ static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
/* 1: Registers specific to a single generation */
if (IS_VALLEYVIEW(dev)) {
- error->ier = I915_READ(GTIER) | I915_READ(VLV_IER);
+ error->gtier[0] = I915_READ(GTIER);
+ error->ier = I915_READ(VLV_IER);
error->forcewake = I915_READ(FORCEWAKE_VLV);
}
@@ -1076,16 +1208,18 @@ static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
if (HAS_HW_CONTEXTS(dev))
error->ccid = I915_READ(CCID);
- if (HAS_PCH_SPLIT(dev))
- error->ier = I915_READ(DEIER) | I915_READ(GTIER);
- else {
- if (IS_GEN2(dev))
- error->ier = I915_READ16(IER);
- else
- error->ier = I915_READ(IER);
+ if (INTEL_INFO(dev)->gen >= 8) {
+ error->ier = I915_READ(GEN8_DE_MISC_IER);
+ for (i = 0; i < 4; i++)
+ error->gtier[i] = I915_READ(GEN8_GT_IER(i));
+ } else if (HAS_PCH_SPLIT(dev)) {
+ error->ier = I915_READ(DEIER);
+ error->gtier[0] = I915_READ(GTIER);
+ } else if (IS_GEN2(dev)) {
+ error->ier = I915_READ16(IER);
+ } else if (!IS_VALLEYVIEW(dev)) {
+ error->ier = I915_READ(IER);
}
-
- /* 4: Everything else */
error->eir = I915_READ(EIR);
error->pgtbl_er = I915_READ(PGTBL_ER);
@@ -1223,11 +1357,11 @@ void i915_destroy_error_state(struct drm_device *dev)
kref_put(&error->ref, i915_error_state_free);
}
-const char *i915_cache_level_str(int type)
+const char *i915_cache_level_str(struct drm_i915_private *i915, int type)
{
switch (type) {
case I915_CACHE_NONE: return " uncached";
- case I915_CACHE_LLC: return " snooped or LLC";
+ case I915_CACHE_LLC: return HAS_LLC(i915) ? " LLC" : " snooped";
case I915_CACHE_L3_LLC: return " L3+LLC";
case I915_CACHE_WT: return " WT";
default: return "";
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index c05c84f3f091..f66392b6e287 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -136,7 +136,7 @@ ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
{
assert_spin_locked(&dev_priv->irq_lock);
- if (WARN_ON(dev_priv->pm.irqs_disabled))
+ if (WARN_ON(!intel_irqs_enabled(dev_priv)))
return;
if ((dev_priv->irq_mask & mask) != 0) {
@@ -151,7 +151,7 @@ ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
{
assert_spin_locked(&dev_priv->irq_lock);
- if (WARN_ON(dev_priv->pm.irqs_disabled))
+ if (WARN_ON(!intel_irqs_enabled(dev_priv)))
return;
if ((dev_priv->irq_mask & mask) != mask) {
@@ -173,7 +173,7 @@ static void ilk_update_gt_irq(struct drm_i915_private *dev_priv,
{
assert_spin_locked(&dev_priv->irq_lock);
- if (WARN_ON(dev_priv->pm.irqs_disabled))
+ if (WARN_ON(!intel_irqs_enabled(dev_priv)))
return;
dev_priv->gt_irq_mask &= ~interrupt_mask;
@@ -182,12 +182,12 @@ static void ilk_update_gt_irq(struct drm_i915_private *dev_priv,
POSTING_READ(GTIMR);
}
-void ilk_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+void gen5_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask)
{
ilk_update_gt_irq(dev_priv, mask, mask);
}
-void ilk_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+void gen5_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask)
{
ilk_update_gt_irq(dev_priv, mask, 0);
}
@@ -206,7 +206,7 @@ static void snb_update_pm_irq(struct drm_i915_private *dev_priv,
assert_spin_locked(&dev_priv->irq_lock);
- if (WARN_ON(dev_priv->pm.irqs_disabled))
+ if (WARN_ON(!intel_irqs_enabled(dev_priv)))
return;
new_val = dev_priv->pm_irq_mask;
@@ -220,12 +220,12 @@ static void snb_update_pm_irq(struct drm_i915_private *dev_priv,
}
}
-void snb_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+void gen6_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
{
snb_update_pm_irq(dev_priv, mask, mask);
}
-void snb_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+void gen6_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
{
snb_update_pm_irq(dev_priv, mask, 0);
}
@@ -238,7 +238,7 @@ static bool ivb_can_enable_err_int(struct drm_device *dev)
assert_spin_locked(&dev_priv->irq_lock);
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
if (crtc->cpu_fifo_underrun_disabled)
@@ -264,7 +264,7 @@ static void bdw_update_pm_irq(struct drm_i915_private *dev_priv,
assert_spin_locked(&dev_priv->irq_lock);
- if (WARN_ON(dev_priv->pm.irqs_disabled))
+ if (WARN_ON(!intel_irqs_enabled(dev_priv)))
return;
new_val = dev_priv->pm_irq_mask;
@@ -278,12 +278,12 @@ static void bdw_update_pm_irq(struct drm_i915_private *dev_priv,
}
}
-void bdw_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+void gen8_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
{
bdw_update_pm_irq(dev_priv, mask, mask);
}
-void bdw_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
+void gen8_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
{
bdw_update_pm_irq(dev_priv, mask, 0);
}
@@ -296,7 +296,7 @@ static bool cpt_can_enable_serr_int(struct drm_device *dev)
assert_spin_locked(&dev_priv->irq_lock);
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
if (crtc->pch_fifo_underrun_disabled)
@@ -420,7 +420,7 @@ static void ibx_display_interrupt_update(struct drm_i915_private *dev_priv,
assert_spin_locked(&dev_priv->irq_lock);
- if (WARN_ON(dev_priv->pm.irqs_disabled))
+ if (WARN_ON(!intel_irqs_enabled(dev_priv)))
return;
I915_WRITE(SDEIMR, sdeimr);
@@ -497,7 +497,7 @@ static bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
old = !intel_crtc->cpu_fifo_underrun_disabled;
intel_crtc->cpu_fifo_underrun_disabled = !enable;
- if (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev))
+ if (HAS_GMCH_DISPLAY(dev))
i9xx_set_fifo_underrun_reporting(dev, pipe, enable, old);
else if (IS_GEN5(dev) || IS_GEN6(dev))
ironlake_set_fifo_underrun_reporting(dev, pipe, enable);
@@ -1020,7 +1020,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
/* In vblank? */
if (in_vbl)
- ret |= DRM_SCANOUTPOS_INVBL;
+ ret |= DRM_SCANOUTPOS_IN_VBLANK;
return ret;
}
@@ -1090,6 +1090,53 @@ static bool intel_hpd_irq_event(struct drm_device *dev,
return true;
}
+static void i915_digport_work_func(struct work_struct *work)
+{
+ struct drm_i915_private *dev_priv =
+ container_of(work, struct drm_i915_private, dig_port_work);
+ unsigned long irqflags;
+ u32 long_port_mask, short_port_mask;
+ struct intel_digital_port *intel_dig_port;
+ int i, ret;
+ u32 old_bits = 0;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ long_port_mask = dev_priv->long_hpd_port_mask;
+ dev_priv->long_hpd_port_mask = 0;
+ short_port_mask = dev_priv->short_hpd_port_mask;
+ dev_priv->short_hpd_port_mask = 0;
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+ for (i = 0; i < I915_MAX_PORTS; i++) {
+ bool valid = false;
+ bool long_hpd = false;
+ intel_dig_port = dev_priv->hpd_irq_port[i];
+ if (!intel_dig_port || !intel_dig_port->hpd_pulse)
+ continue;
+
+ if (long_port_mask & (1 << i)) {
+ valid = true;
+ long_hpd = true;
+ } else if (short_port_mask & (1 << i))
+ valid = true;
+
+ if (valid) {
+ ret = intel_dig_port->hpd_pulse(intel_dig_port, long_hpd);
+ if (ret == true) {
+ /* if we get true fallback to old school hpd */
+ old_bits |= (1 << intel_dig_port->base.hpd_pin);
+ }
+ }
+ }
+
+ if (old_bits) {
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ dev_priv->hpd_event_bits |= old_bits;
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+ schedule_work(&dev_priv->hotplug_work);
+ }
+}
+
/*
* Handle hotplug events outside the interrupt handler proper.
*/
@@ -1109,10 +1156,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
bool changed = false;
u32 hpd_event_bits;
- /* HPD irq before everything is fully set up. */
- if (!dev_priv->enable_hotplug_processing)
- return;
-
mutex_lock(&mode_config->mutex);
DRM_DEBUG_KMS("running encoder hotplug functions\n");
@@ -1122,6 +1165,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
dev_priv->hpd_event_bits = 0;
list_for_each_entry(connector, &mode_config->connector_list, head) {
intel_connector = to_intel_connector(connector);
+ if (!intel_connector->encoder)
+ continue;
intel_encoder = intel_connector->encoder;
if (intel_encoder->hpd_pin > HPD_NONE &&
dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_MARK_DISABLED &&
@@ -1144,14 +1189,16 @@ static void i915_hotplug_work_func(struct work_struct *work)
* some connectors */
if (hpd_disabled) {
drm_kms_helper_poll_enable(dev);
- mod_timer(&dev_priv->hotplug_reenable_timer,
- jiffies + msecs_to_jiffies(I915_REENABLE_HOTPLUG_DELAY));
+ mod_delayed_work(system_wq, &dev_priv->hotplug_reenable_work,
+ msecs_to_jiffies(I915_REENABLE_HOTPLUG_DELAY));
}
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
list_for_each_entry(connector, &mode_config->connector_list, head) {
intel_connector = to_intel_connector(connector);
+ if (!intel_connector->encoder)
+ continue;
intel_encoder = intel_connector->encoder;
if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
if (intel_encoder->hot_plug)
@@ -1166,11 +1213,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
drm_kms_helper_hotplug_event(dev);
}
-static void intel_hpd_irq_uninstall(struct drm_i915_private *dev_priv)
-{
- del_timer_sync(&dev_priv->hotplug_reenable_timer);
-}
-
static void ironlake_rps_change_irq_handler(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1218,10 +1260,138 @@ static void notify_ring(struct drm_device *dev,
trace_i915_gem_request_complete(ring);
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ intel_notify_mmio_flip(ring);
+
wake_up_all(&ring->irq_queue);
i915_queue_hangcheck(dev);
}
+static u32 vlv_c0_residency(struct drm_i915_private *dev_priv,
+ struct intel_rps_ei *rps_ei)
+{
+ u32 cz_ts, cz_freq_khz;
+ u32 render_count, media_count;
+ u32 elapsed_render, elapsed_media, elapsed_time;
+ u32 residency = 0;
+
+ cz_ts = vlv_punit_read(dev_priv, PUNIT_REG_CZ_TIMESTAMP);
+ cz_freq_khz = DIV_ROUND_CLOSEST(dev_priv->mem_freq * 1000, 4);
+
+ render_count = I915_READ(VLV_RENDER_C0_COUNT_REG);
+ media_count = I915_READ(VLV_MEDIA_C0_COUNT_REG);
+
+ if (rps_ei->cz_clock == 0) {
+ rps_ei->cz_clock = cz_ts;
+ rps_ei->render_c0 = render_count;
+ rps_ei->media_c0 = media_count;
+
+ return dev_priv->rps.cur_freq;
+ }
+
+ elapsed_time = cz_ts - rps_ei->cz_clock;
+ rps_ei->cz_clock = cz_ts;
+
+ elapsed_render = render_count - rps_ei->render_c0;
+ rps_ei->render_c0 = render_count;
+
+ elapsed_media = media_count - rps_ei->media_c0;
+ rps_ei->media_c0 = media_count;
+
+ /* Convert all the counters into common unit of milli sec */
+ elapsed_time /= VLV_CZ_CLOCK_TO_MILLI_SEC;
+ elapsed_render /= cz_freq_khz;
+ elapsed_media /= cz_freq_khz;
+
+ /*
+ * Calculate overall C0 residency percentage
+ * only if elapsed time is non zero
+ */
+ if (elapsed_time) {
+ residency =
+ ((max(elapsed_render, elapsed_media) * 100)
+ / elapsed_time);
+ }
+
+ return residency;
+}
+
+/**
+ * vlv_calc_delay_from_C0_counters - Increase/Decrease freq based on GPU
+ * busy-ness calculated from C0 counters of render & media power wells
+ * @dev_priv: DRM device private
+ *
+ */
+static int vlv_calc_delay_from_C0_counters(struct drm_i915_private *dev_priv)
+{
+ u32 residency_C0_up = 0, residency_C0_down = 0;
+ int new_delay, adj;
+
+ dev_priv->rps.ei_interrupt_count++;
+
+ WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
+
+
+ if (dev_priv->rps.up_ei.cz_clock == 0) {
+ vlv_c0_residency(dev_priv, &dev_priv->rps.up_ei);
+ vlv_c0_residency(dev_priv, &dev_priv->rps.down_ei);
+ return dev_priv->rps.cur_freq;
+ }
+
+
+ /*
+ * To down throttle, C0 residency should be less than down threshold
+ * for continous EI intervals. So calculate down EI counters
+ * once in VLV_INT_COUNT_FOR_DOWN_EI
+ */
+ if (dev_priv->rps.ei_interrupt_count == VLV_INT_COUNT_FOR_DOWN_EI) {
+
+ dev_priv->rps.ei_interrupt_count = 0;
+
+ residency_C0_down = vlv_c0_residency(dev_priv,
+ &dev_priv->rps.down_ei);
+ } else {
+ residency_C0_up = vlv_c0_residency(dev_priv,
+ &dev_priv->rps.up_ei);
+ }
+
+ new_delay = dev_priv->rps.cur_freq;
+
+ adj = dev_priv->rps.last_adj;
+ /* C0 residency is greater than UP threshold. Increase Frequency */
+ if (residency_C0_up >= VLV_RP_UP_EI_THRESHOLD) {
+ if (adj > 0)
+ adj *= 2;
+ else
+ adj = 1;
+
+ if (dev_priv->rps.cur_freq < dev_priv->rps.max_freq_softlimit)
+ new_delay = dev_priv->rps.cur_freq + adj;
+
+ /*
+ * For better performance, jump directly
+ * to RPe if we're below it.
+ */
+ if (new_delay < dev_priv->rps.efficient_freq)
+ new_delay = dev_priv->rps.efficient_freq;
+
+ } else if (!dev_priv->rps.ei_interrupt_count &&
+ (residency_C0_down < VLV_RP_DOWN_EI_THRESHOLD)) {
+ if (adj < 0)
+ adj *= 2;
+ else
+ adj = -1;
+ /*
+ * This means, C0 residency is less than down threshold over
+ * a period of VLV_INT_COUNT_FOR_DOWN_EI. So, reduce the freq
+ */
+ if (dev_priv->rps.cur_freq > dev_priv->rps.min_freq_softlimit)
+ new_delay = dev_priv->rps.cur_freq + adj;
+ }
+
+ return new_delay;
+}
+
static void gen6_pm_rps_work(struct work_struct *work)
{
struct drm_i915_private *dev_priv =
@@ -1232,11 +1402,11 @@ static void gen6_pm_rps_work(struct work_struct *work)
spin_lock_irq(&dev_priv->irq_lock);
pm_iir = dev_priv->rps.pm_iir;
dev_priv->rps.pm_iir = 0;
- if (IS_BROADWELL(dev_priv->dev))
- bdw_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
+ if (INTEL_INFO(dev_priv->dev)->gen >= 8)
+ gen8_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
else {
/* Make sure not to corrupt PMIMR state used by ringbuffer */
- snb_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
+ gen6_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
}
spin_unlock_irq(&dev_priv->irq_lock);
@@ -1252,8 +1422,10 @@ static void gen6_pm_rps_work(struct work_struct *work)
if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
if (adj > 0)
adj *= 2;
- else
- adj = 1;
+ else {
+ /* CHV needs even encode values */
+ adj = IS_CHERRYVIEW(dev_priv->dev) ? 2 : 1;
+ }
new_delay = dev_priv->rps.cur_freq + adj;
/*
@@ -1268,11 +1440,15 @@ static void gen6_pm_rps_work(struct work_struct *work)
else
new_delay = dev_priv->rps.min_freq_softlimit;
adj = 0;
+ } else if (pm_iir & GEN6_PM_RP_UP_EI_EXPIRED) {
+ new_delay = vlv_calc_delay_from_C0_counters(dev_priv);
} else if (pm_iir & GEN6_PM_RP_DOWN_THRESHOLD) {
if (adj < 0)
adj *= 2;
- else
- adj = -1;
+ else {
+ /* CHV needs even encode values */
+ adj = IS_CHERRYVIEW(dev_priv->dev) ? -2 : -1;
+ }
new_delay = dev_priv->rps.cur_freq + adj;
} else { /* unknown event */
new_delay = dev_priv->rps.cur_freq;
@@ -1372,7 +1548,7 @@ static void ivybridge_parity_work(struct work_struct *work)
out:
WARN_ON(dev_priv->l3_parity.which_slice);
spin_lock_irqsave(&dev_priv->irq_lock, flags);
- ilk_enable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv->dev));
+ gen5_enable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv->dev));
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
mutex_unlock(&dev_priv->dev->struct_mutex);
@@ -1386,7 +1562,7 @@ static void ivybridge_parity_error_irq_handler(struct drm_device *dev, u32 iir)
return;
spin_lock(&dev_priv->irq_lock);
- ilk_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev));
+ gen5_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev));
spin_unlock(&dev_priv->irq_lock);
iir &= GT_PARITY_ERROR(dev);
@@ -1441,7 +1617,7 @@ static void gen8_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
spin_lock(&dev_priv->irq_lock);
dev_priv->rps.pm_iir |= pm_iir & dev_priv->pm_rps_events;
- bdw_disable_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events);
+ gen8_disable_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events);
spin_unlock(&dev_priv->irq_lock);
queue_work(dev_priv->wq, &dev_priv->rps.work);
@@ -1451,6 +1627,7 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
struct drm_i915_private *dev_priv,
u32 master_ctl)
{
+ struct intel_engine_cs *ring;
u32 rcs, bcs, vcs;
uint32_t tmp = 0;
irqreturn_t ret = IRQ_NONE;
@@ -1458,14 +1635,22 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) {
tmp = I915_READ(GEN8_GT_IIR(0));
if (tmp) {
+ I915_WRITE(GEN8_GT_IIR(0), tmp);
ret = IRQ_HANDLED;
+
rcs = tmp >> GEN8_RCS_IRQ_SHIFT;
- bcs = tmp >> GEN8_BCS_IRQ_SHIFT;
+ ring = &dev_priv->ring[RCS];
if (rcs & GT_RENDER_USER_INTERRUPT)
- notify_ring(dev, &dev_priv->ring[RCS]);
+ notify_ring(dev, ring);
+ if (rcs & GT_CONTEXT_SWITCH_INTERRUPT)
+ intel_execlists_handle_ctx_events(ring);
+
+ bcs = tmp >> GEN8_BCS_IRQ_SHIFT;
+ ring = &dev_priv->ring[BCS];
if (bcs & GT_RENDER_USER_INTERRUPT)
- notify_ring(dev, &dev_priv->ring[BCS]);
- I915_WRITE(GEN8_GT_IIR(0), tmp);
+ notify_ring(dev, ring);
+ if (bcs & GT_CONTEXT_SWITCH_INTERRUPT)
+ intel_execlists_handle_ctx_events(ring);
} else
DRM_ERROR("The master control interrupt lied (GT0)!\n");
}
@@ -1473,14 +1658,22 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
if (master_ctl & (GEN8_GT_VCS1_IRQ | GEN8_GT_VCS2_IRQ)) {
tmp = I915_READ(GEN8_GT_IIR(1));
if (tmp) {
+ I915_WRITE(GEN8_GT_IIR(1), tmp);
ret = IRQ_HANDLED;
+
vcs = tmp >> GEN8_VCS1_IRQ_SHIFT;
+ ring = &dev_priv->ring[VCS];
if (vcs & GT_RENDER_USER_INTERRUPT)
- notify_ring(dev, &dev_priv->ring[VCS]);
+ notify_ring(dev, ring);
+ if (vcs & GT_CONTEXT_SWITCH_INTERRUPT)
+ intel_execlists_handle_ctx_events(ring);
+
vcs = tmp >> GEN8_VCS2_IRQ_SHIFT;
+ ring = &dev_priv->ring[VCS2];
if (vcs & GT_RENDER_USER_INTERRUPT)
- notify_ring(dev, &dev_priv->ring[VCS2]);
- I915_WRITE(GEN8_GT_IIR(1), tmp);
+ notify_ring(dev, ring);
+ if (vcs & GT_CONTEXT_SWITCH_INTERRUPT)
+ intel_execlists_handle_ctx_events(ring);
} else
DRM_ERROR("The master control interrupt lied (GT1)!\n");
}
@@ -1488,10 +1681,10 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
if (master_ctl & GEN8_GT_PM_IRQ) {
tmp = I915_READ(GEN8_GT_IIR(2));
if (tmp & dev_priv->pm_rps_events) {
- ret = IRQ_HANDLED;
- gen8_rps_irq_handler(dev_priv, tmp);
I915_WRITE(GEN8_GT_IIR(2),
tmp & dev_priv->pm_rps_events);
+ ret = IRQ_HANDLED;
+ gen8_rps_irq_handler(dev_priv, tmp);
} else
DRM_ERROR("The master control interrupt lied (PM)!\n");
}
@@ -1499,11 +1692,15 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
if (master_ctl & GEN8_GT_VECS_IRQ) {
tmp = I915_READ(GEN8_GT_IIR(3));
if (tmp) {
+ I915_WRITE(GEN8_GT_IIR(3), tmp);
ret = IRQ_HANDLED;
+
vcs = tmp >> GEN8_VECS_IRQ_SHIFT;
+ ring = &dev_priv->ring[VECS];
if (vcs & GT_RENDER_USER_INTERRUPT)
- notify_ring(dev, &dev_priv->ring[VECS]);
- I915_WRITE(GEN8_GT_IIR(3), tmp);
+ notify_ring(dev, ring);
+ if (vcs & GT_CONTEXT_SWITCH_INTERRUPT)
+ intel_execlists_handle_ctx_events(ring);
} else
DRM_ERROR("The master control interrupt lied (GT3)!\n");
}
@@ -1514,23 +1711,106 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
#define HPD_STORM_DETECT_PERIOD 1000
#define HPD_STORM_THRESHOLD 5
+static int pch_port_to_hotplug_shift(enum port port)
+{
+ switch (port) {
+ case PORT_A:
+ case PORT_E:
+ default:
+ return -1;
+ case PORT_B:
+ return 0;
+ case PORT_C:
+ return 8;
+ case PORT_D:
+ return 16;
+ }
+}
+
+static int i915_port_to_hotplug_shift(enum port port)
+{
+ switch (port) {
+ case PORT_A:
+ case PORT_E:
+ default:
+ return -1;
+ case PORT_B:
+ return 17;
+ case PORT_C:
+ return 19;
+ case PORT_D:
+ return 21;
+ }
+}
+
+static inline enum port get_port_from_pin(enum hpd_pin pin)
+{
+ switch (pin) {
+ case HPD_PORT_B:
+ return PORT_B;
+ case HPD_PORT_C:
+ return PORT_C;
+ case HPD_PORT_D:
+ return PORT_D;
+ default:
+ return PORT_A; /* no hpd */
+ }
+}
+
static inline void intel_hpd_irq_handler(struct drm_device *dev,
u32 hotplug_trigger,
+ u32 dig_hotplug_reg,
const u32 *hpd)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int i;
+ enum port port;
bool storm_detected = false;
+ bool queue_dig = false, queue_hp = false;
+ u32 dig_shift;
+ u32 dig_port_mask = 0;
if (!hotplug_trigger)
return;
- DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
- hotplug_trigger);
+ DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x, dig 0x%08x\n",
+ hotplug_trigger, dig_hotplug_reg);
spin_lock(&dev_priv->irq_lock);
for (i = 1; i < HPD_NUM_PINS; i++) {
+ if (!(hpd[i] & hotplug_trigger))
+ continue;
+
+ port = get_port_from_pin(i);
+ if (port && dev_priv->hpd_irq_port[port]) {
+ bool long_hpd;
+ if (HAS_PCH_SPLIT(dev)) {
+ dig_shift = pch_port_to_hotplug_shift(port);
+ long_hpd = (dig_hotplug_reg >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT;
+ } else {
+ dig_shift = i915_port_to_hotplug_shift(port);
+ long_hpd = (hotplug_trigger >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT;
+ }
+
+ DRM_DEBUG_DRIVER("digital hpd port %c - %s\n",
+ port_name(port),
+ long_hpd ? "long" : "short");
+ /* for long HPD pulses we want to have the digital queue happen,
+ but we still want HPD storm detection to function. */
+ if (long_hpd) {
+ dev_priv->long_hpd_port_mask |= (1 << port);
+ dig_port_mask |= hpd[i];
+ } else {
+ /* for short HPD just trigger the digital queue */
+ dev_priv->short_hpd_port_mask |= (1 << port);
+ hotplug_trigger &= ~hpd[i];
+ }
+ queue_dig = true;
+ }
+ }
+
+ for (i = 1; i < HPD_NUM_PINS; i++) {
if (hpd[i] & hotplug_trigger &&
dev_priv->hpd_stats[i].hpd_mark == HPD_DISABLED) {
/*
@@ -1550,7 +1830,11 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
dev_priv->hpd_stats[i].hpd_mark != HPD_ENABLED)
continue;
- dev_priv->hpd_event_bits |= (1 << i);
+ if (!(dig_port_mask & hpd[i])) {
+ dev_priv->hpd_event_bits |= (1 << i);
+ queue_hp = true;
+ }
+
if (!time_in_range(jiffies, dev_priv->hpd_stats[i].hpd_last_jiffies,
dev_priv->hpd_stats[i].hpd_last_jiffies
+ msecs_to_jiffies(HPD_STORM_DETECT_PERIOD))) {
@@ -1579,7 +1863,10 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
* queue for otherwise the flush_work in the pageflip code will
* deadlock.
*/
- schedule_work(&dev_priv->hotplug_work);
+ if (queue_dig)
+ queue_work(dev_priv->dp_wq, &dev_priv->dig_port_work);
+ if (queue_hp)
+ schedule_work(&dev_priv->hotplug_work);
}
static void gmbus_irq_handler(struct drm_device *dev)
@@ -1700,7 +1987,7 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
if (pm_iir & dev_priv->pm_rps_events) {
spin_lock(&dev_priv->irq_lock);
dev_priv->rps.pm_iir |= pm_iir & dev_priv->pm_rps_events;
- snb_disable_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events);
+ gen6_disable_pm_irq(dev_priv, pm_iir & dev_priv->pm_rps_events);
spin_unlock(&dev_priv->irq_lock);
queue_work(dev_priv->wq, &dev_priv->rps.work);
@@ -1720,14 +2007,9 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
static bool intel_pipe_handle_vblank(struct drm_device *dev, enum pipe pipe)
{
- struct intel_crtc *crtc;
-
if (!drm_handle_vblank(dev, pipe))
return false;
- crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe));
- wake_up(&crtc->vbl_wait);
-
return true;
}
@@ -1738,7 +2020,7 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
int pipe;
spin_lock(&dev_priv->irq_lock);
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
int reg;
u32 mask, iir_bit = 0;
@@ -1783,9 +2065,10 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
}
spin_unlock(&dev_priv->irq_lock);
- for_each_pipe(pipe) {
- if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
- intel_pipe_handle_vblank(dev, pipe);
+ for_each_pipe(dev_priv, pipe) {
+ if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS &&
+ intel_pipe_handle_vblank(dev, pipe))
+ intel_check_page_flip(dev, pipe);
if (pipe_stats[pipe] & PLANE_FLIP_DONE_INT_STATUS_VLV) {
intel_prepare_page_flip(dev, pipe);
@@ -1809,26 +2092,28 @@ static void i9xx_hpd_irq_handler(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
- if (IS_G4X(dev)) {
- u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X;
+ if (hotplug_status) {
+ I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
+ /*
+ * Make sure hotplug status is cleared before we clear IIR, or else we
+ * may miss hotplug events.
+ */
+ POSTING_READ(PORT_HOTPLUG_STAT);
- intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_g4x);
- } else {
- u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
+ if (IS_G4X(dev)) {
+ u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X;
- intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915);
- }
+ intel_hpd_irq_handler(dev, hotplug_trigger, 0, hpd_status_g4x);
+ } else {
+ u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
- if ((IS_G4X(dev) || IS_VALLEYVIEW(dev)) &&
- hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X)
- dp_aux_irq_handler(dev);
+ intel_hpd_irq_handler(dev, hotplug_trigger, 0, hpd_status_i915);
+ }
- I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
- /*
- * Make sure hotplug status is cleared before we clear IIR, or else we
- * may miss hotplug events.
- */
- POSTING_READ(PORT_HOTPLUG_STAT);
+ if ((IS_G4X(dev) || IS_VALLEYVIEW(dev)) &&
+ hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X)
+ dp_aux_irq_handler(dev);
+ }
}
static irqreturn_t valleyview_irq_handler(int irq, void *arg)
@@ -1839,29 +2124,36 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
irqreturn_t ret = IRQ_NONE;
while (true) {
- iir = I915_READ(VLV_IIR);
+ /* Find, clear, then process each source of interrupt */
+
gt_iir = I915_READ(GTIIR);
+ if (gt_iir)
+ I915_WRITE(GTIIR, gt_iir);
+
pm_iir = I915_READ(GEN6_PMIIR);
+ if (pm_iir)
+ I915_WRITE(GEN6_PMIIR, pm_iir);
+
+ iir = I915_READ(VLV_IIR);
+ if (iir) {
+ /* Consume port before clearing IIR or we'll miss events */
+ if (iir & I915_DISPLAY_PORT_INTERRUPT)
+ i9xx_hpd_irq_handler(dev);
+ I915_WRITE(VLV_IIR, iir);
+ }
if (gt_iir == 0 && pm_iir == 0 && iir == 0)
goto out;
ret = IRQ_HANDLED;
- snb_gt_irq_handler(dev, dev_priv, gt_iir);
-
- valleyview_pipestat_irq_handler(dev, iir);
-
- /* Consume port. Then clear IIR or we'll miss events */
- if (iir & I915_DISPLAY_PORT_INTERRUPT)
- i9xx_hpd_irq_handler(dev);
-
+ if (gt_iir)
+ snb_gt_irq_handler(dev, dev_priv, gt_iir);
if (pm_iir)
gen6_rps_irq_handler(dev_priv, pm_iir);
-
- I915_WRITE(GTIIR, gt_iir);
- I915_WRITE(GEN6_PMIIR, pm_iir);
- I915_WRITE(VLV_IIR, iir);
+ /* Call regardless, as some status bits might not be
+ * signalled in iir */
+ valleyview_pipestat_irq_handler(dev, iir);
}
out:
@@ -1882,21 +2174,27 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg)
if (master_ctl == 0 && iir == 0)
break;
+ ret = IRQ_HANDLED;
+
I915_WRITE(GEN8_MASTER_IRQ, 0);
- gen8_gt_irq_handler(dev, dev_priv, master_ctl);
+ /* Find, clear, then process each source of interrupt */
- valleyview_pipestat_irq_handler(dev, iir);
+ if (iir) {
+ /* Consume port before clearing IIR or we'll miss events */
+ if (iir & I915_DISPLAY_PORT_INTERRUPT)
+ i9xx_hpd_irq_handler(dev);
+ I915_WRITE(VLV_IIR, iir);
+ }
- /* Consume port. Then clear IIR or we'll miss events */
- i9xx_hpd_irq_handler(dev);
+ gen8_gt_irq_handler(dev, dev_priv, master_ctl);
- I915_WRITE(VLV_IIR, iir);
+ /* Call regardless, as some status bits might not be
+ * signalled in iir */
+ valleyview_pipestat_irq_handler(dev, iir);
I915_WRITE(GEN8_MASTER_IRQ, DE_MASTER_IRQ_CONTROL);
POSTING_READ(GEN8_MASTER_IRQ);
-
- ret = IRQ_HANDLED;
}
return ret;
@@ -1907,8 +2205,12 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK;
+ u32 dig_hotplug_reg;
+
+ dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
+ I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
- intel_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx);
+ intel_hpd_irq_handler(dev, hotplug_trigger, dig_hotplug_reg, hpd_ibx);
if (pch_iir & SDE_AUDIO_POWER_MASK) {
int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >>
@@ -1933,7 +2235,7 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
DRM_ERROR("PCH poison interrupt\n");
if (pch_iir & SDE_FDI_MASK)
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n",
pipe_name(pipe),
I915_READ(FDI_RX_IIR(pipe)));
@@ -1964,7 +2266,7 @@ static void ivb_err_int_handler(struct drm_device *dev)
if (err_int & ERR_INT_POISON)
DRM_ERROR("Poison interrupt\n");
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
if (err_int & ERR_INT_FIFO_UNDERRUN(pipe)) {
if (intel_set_cpu_fifo_underrun_reporting(dev, pipe,
false))
@@ -2014,8 +2316,12 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT;
+ u32 dig_hotplug_reg;
+
+ dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
+ I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
- intel_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt);
+ intel_hpd_irq_handler(dev, hotplug_trigger, dig_hotplug_reg, hpd_cpt);
if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) {
int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >>
@@ -2037,7 +2343,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
DRM_DEBUG_DRIVER("Audio CP change interrupt\n");
if (pch_iir & SDE_FDI_MASK_CPT)
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n",
pipe_name(pipe),
I915_READ(FDI_RX_IIR(pipe)));
@@ -2060,9 +2366,10 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
if (de_iir & DE_POISON)
DRM_ERROR("Poison interrupt\n");
- for_each_pipe(pipe) {
- if (de_iir & DE_PIPE_VBLANK(pipe))
- intel_pipe_handle_vblank(dev, pipe);
+ for_each_pipe(dev_priv, pipe) {
+ if (de_iir & DE_PIPE_VBLANK(pipe) &&
+ intel_pipe_handle_vblank(dev, pipe))
+ intel_check_page_flip(dev, pipe);
if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
@@ -2110,9 +2417,10 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
if (de_iir & DE_GSE_IVB)
intel_opregion_asle_intr(dev);
- for_each_pipe(pipe) {
- if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)))
- intel_pipe_handle_vblank(dev, pipe);
+ for_each_pipe(dev_priv, pipe) {
+ if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)) &&
+ intel_pipe_handle_vblank(dev, pipe))
+ intel_check_page_flip(dev, pipe);
/* plane/pipes map 1:1 on ilk+ */
if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe)) {
@@ -2132,6 +2440,14 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
}
}
+/*
+ * To handle irqs with the minimum potential races with fresh interrupts, we:
+ * 1 - Disable Master Interrupt Control.
+ * 2 - Find the source(s) of the interrupt.
+ * 3 - Clear the Interrupt Identity bits (IIR).
+ * 4 - Process the interrupt(s) that had bits set in the IIRs.
+ * 5 - Re-enable Master Interrupt Control.
+ */
static irqreturn_t ironlake_irq_handler(int irq, void *arg)
{
struct drm_device *dev = arg;
@@ -2159,32 +2475,34 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
POSTING_READ(SDEIER);
}
+ /* Find, clear, then process each source of interrupt */
+
gt_iir = I915_READ(GTIIR);
if (gt_iir) {
+ I915_WRITE(GTIIR, gt_iir);
+ ret = IRQ_HANDLED;
if (INTEL_INFO(dev)->gen >= 6)
snb_gt_irq_handler(dev, dev_priv, gt_iir);
else
ilk_gt_irq_handler(dev, dev_priv, gt_iir);
- I915_WRITE(GTIIR, gt_iir);
- ret = IRQ_HANDLED;
}
de_iir = I915_READ(DEIIR);
if (de_iir) {
+ I915_WRITE(DEIIR, de_iir);
+ ret = IRQ_HANDLED;
if (INTEL_INFO(dev)->gen >= 7)
ivb_display_irq_handler(dev, de_iir);
else
ilk_display_irq_handler(dev, de_iir);
- I915_WRITE(DEIIR, de_iir);
- ret = IRQ_HANDLED;
}
if (INTEL_INFO(dev)->gen >= 6) {
u32 pm_iir = I915_READ(GEN6_PMIIR);
if (pm_iir) {
- gen6_rps_irq_handler(dev_priv, pm_iir);
I915_WRITE(GEN6_PMIIR, pm_iir);
ret = IRQ_HANDLED;
+ gen6_rps_irq_handler(dev_priv, pm_iir);
}
}
@@ -2215,72 +2533,72 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
I915_WRITE(GEN8_MASTER_IRQ, 0);
POSTING_READ(GEN8_MASTER_IRQ);
+ /* Find, clear, then process each source of interrupt */
+
ret = gen8_gt_irq_handler(dev, dev_priv, master_ctl);
if (master_ctl & GEN8_DE_MISC_IRQ) {
tmp = I915_READ(GEN8_DE_MISC_IIR);
- if (tmp & GEN8_DE_MISC_GSE)
- intel_opregion_asle_intr(dev);
- else if (tmp)
- DRM_ERROR("Unexpected DE Misc interrupt\n");
- else
- DRM_ERROR("The master control interrupt lied (DE MISC)!\n");
-
if (tmp) {
I915_WRITE(GEN8_DE_MISC_IIR, tmp);
ret = IRQ_HANDLED;
+ if (tmp & GEN8_DE_MISC_GSE)
+ intel_opregion_asle_intr(dev);
+ else
+ DRM_ERROR("Unexpected DE Misc interrupt\n");
}
+ else
+ DRM_ERROR("The master control interrupt lied (DE MISC)!\n");
}
if (master_ctl & GEN8_DE_PORT_IRQ) {
tmp = I915_READ(GEN8_DE_PORT_IIR);
- if (tmp & GEN8_AUX_CHANNEL_A)
- dp_aux_irq_handler(dev);
- else if (tmp)
- DRM_ERROR("Unexpected DE Port interrupt\n");
- else
- DRM_ERROR("The master control interrupt lied (DE PORT)!\n");
-
if (tmp) {
I915_WRITE(GEN8_DE_PORT_IIR, tmp);
ret = IRQ_HANDLED;
+ if (tmp & GEN8_AUX_CHANNEL_A)
+ dp_aux_irq_handler(dev);
+ else
+ DRM_ERROR("Unexpected DE Port interrupt\n");
}
+ else
+ DRM_ERROR("The master control interrupt lied (DE PORT)!\n");
}
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
uint32_t pipe_iir;
if (!(master_ctl & GEN8_DE_PIPE_IRQ(pipe)))
continue;
pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
- if (pipe_iir & GEN8_PIPE_VBLANK)
- intel_pipe_handle_vblank(dev, pipe);
-
- if (pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE) {
- intel_prepare_page_flip(dev, pipe);
- intel_finish_page_flip_plane(dev, pipe);
- }
+ if (pipe_iir) {
+ ret = IRQ_HANDLED;
+ I915_WRITE(GEN8_DE_PIPE_IIR(pipe), pipe_iir);
+ if (pipe_iir & GEN8_PIPE_VBLANK &&
+ intel_pipe_handle_vblank(dev, pipe))
+ intel_check_page_flip(dev, pipe);
- if (pipe_iir & GEN8_PIPE_CDCLK_CRC_DONE)
- hsw_pipe_crc_irq_handler(dev, pipe);
+ if (pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE) {
+ intel_prepare_page_flip(dev, pipe);
+ intel_finish_page_flip_plane(dev, pipe);
+ }
- if (pipe_iir & GEN8_PIPE_FIFO_UNDERRUN) {
- if (intel_set_cpu_fifo_underrun_reporting(dev, pipe,
- false))
- DRM_ERROR("Pipe %c FIFO underrun\n",
- pipe_name(pipe));
- }
+ if (pipe_iir & GEN8_PIPE_CDCLK_CRC_DONE)
+ hsw_pipe_crc_irq_handler(dev, pipe);
- if (pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS) {
- DRM_ERROR("Fault errors on pipe %c\n: 0x%08x",
- pipe_name(pipe),
- pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS);
- }
+ if (pipe_iir & GEN8_PIPE_FIFO_UNDERRUN) {
+ if (intel_set_cpu_fifo_underrun_reporting(dev, pipe,
+ false))
+ DRM_ERROR("Pipe %c FIFO underrun\n",
+ pipe_name(pipe));
+ }
- if (pipe_iir) {
- ret = IRQ_HANDLED;
- I915_WRITE(GEN8_DE_PIPE_IIR(pipe), pipe_iir);
+ if (pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS) {
+ DRM_ERROR("Fault errors on pipe %c\n: 0x%08x",
+ pipe_name(pipe),
+ pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS);
+ }
} else
DRM_ERROR("The master control interrupt lied (DE PIPE)!\n");
}
@@ -2292,13 +2610,13 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
* on older pch-split platforms. But this needs testing.
*/
u32 pch_iir = I915_READ(SDEIIR);
-
- cpt_irq_handler(dev, pch_iir);
-
if (pch_iir) {
I915_WRITE(SDEIIR, pch_iir);
ret = IRQ_HANDLED;
- }
+ cpt_irq_handler(dev, pch_iir);
+ } else
+ DRM_ERROR("The master control interrupt lied (SDE)!\n");
+
}
I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
@@ -2467,7 +2785,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev)
if (eir & I915_ERROR_MEMORY_REFRESH) {
pr_err("memory refresh error:\n");
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
pr_err("pipe %c stat: 0x%08x\n",
pipe_name(pipe), I915_READ(PIPESTAT(pipe)));
/* pipestat has already been acked */
@@ -2564,52 +2882,6 @@ void i915_handle_error(struct drm_device *dev, bool wedged,
schedule_work(&dev_priv->gpu_error.work);
}
-static void __always_unused i915_pageflip_stall_check(struct drm_device *dev, int pipe)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- struct drm_i915_gem_object *obj;
- struct intel_unpin_work *work;
- unsigned long flags;
- bool stall_detected;
-
- /* Ignore early vblank irqs */
- if (intel_crtc == NULL)
- return;
-
- spin_lock_irqsave(&dev->event_lock, flags);
- work = intel_crtc->unpin_work;
-
- if (work == NULL ||
- atomic_read(&work->pending) >= INTEL_FLIP_COMPLETE ||
- !work->enable_stall_check) {
- /* Either the pending flip IRQ arrived, or we're too early. Don't check */
- spin_unlock_irqrestore(&dev->event_lock, flags);
- return;
- }
-
- /* Potential stall - if we see that the flip has happened, assume a missed interrupt */
- obj = work->pending_flip_obj;
- if (INTEL_INFO(dev)->gen >= 4) {
- int dspsurf = DSPSURF(intel_crtc->plane);
- stall_detected = I915_HI_DISPBASE(I915_READ(dspsurf)) ==
- i915_gem_obj_ggtt_offset(obj);
- } else {
- int dspaddr = DSPADDR(intel_crtc->plane);
- stall_detected = I915_READ(dspaddr) == (i915_gem_obj_ggtt_offset(obj) +
- crtc->y * crtc->primary->fb->pitches[0] +
- crtc->x * crtc->primary->fb->bits_per_pixel/8);
- }
-
- spin_unlock_irqrestore(&dev->event_lock, flags);
-
- if (stall_detected) {
- DRM_DEBUG_DRIVER("Pageflip stall detected\n");
- intel_prepare_page_flip(dev, intel_crtc->plane);
- }
-}
-
/* Called from drm generic code, passed 'crtc' which
* we use as a pipe index
*/
@@ -2753,12 +3025,7 @@ static bool
ipehr_is_semaphore_wait(struct drm_device *dev, u32 ipehr)
{
if (INTEL_INFO(dev)->gen >= 8) {
- /*
- * FIXME: gen8 semaphore support - currently we don't emit
- * semaphores on bdw anyway, but this needs to be addressed when
- * we merge that code.
- */
- return false;
+ return (ipehr >> 23) == 0x1c;
} else {
ipehr &= ~MI_SEMAPHORE_SYNC_MASK;
return ipehr == (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE |
@@ -2767,19 +3034,20 @@ ipehr_is_semaphore_wait(struct drm_device *dev, u32 ipehr)
}
static struct intel_engine_cs *
-semaphore_wait_to_signaller_ring(struct intel_engine_cs *ring, u32 ipehr)
+semaphore_wait_to_signaller_ring(struct intel_engine_cs *ring, u32 ipehr, u64 offset)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
struct intel_engine_cs *signaller;
int i;
if (INTEL_INFO(dev_priv->dev)->gen >= 8) {
- /*
- * FIXME: gen8 semaphore support - currently we don't emit
- * semaphores on bdw anyway, but this needs to be addressed when
- * we merge that code.
- */
- return NULL;
+ for_each_ring(signaller, dev_priv, i) {
+ if (ring == signaller)
+ continue;
+
+ if (offset == signaller->semaphore.signal_ggtt[ring->id])
+ return signaller;
+ }
} else {
u32 sync_bits = ipehr & MI_SEMAPHORE_SYNC_MASK;
@@ -2792,8 +3060,8 @@ semaphore_wait_to_signaller_ring(struct intel_engine_cs *ring, u32 ipehr)
}
}
- DRM_ERROR("No signaller ring found for ring %i, ipehr 0x%08x\n",
- ring->id, ipehr);
+ DRM_ERROR("No signaller ring found for ring %i, ipehr 0x%08x, offset 0x%016llx\n",
+ ring->id, ipehr, offset);
return NULL;
}
@@ -2803,7 +3071,8 @@ semaphore_waits_for(struct intel_engine_cs *ring, u32 *seqno)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
u32 cmd, ipehr, head;
- int i;
+ u64 offset = 0;
+ int i, backwards;
ipehr = I915_READ(RING_IPEHR(ring->mmio_base));
if (!ipehr_is_semaphore_wait(ring->dev, ipehr))
@@ -2812,13 +3081,15 @@ semaphore_waits_for(struct intel_engine_cs *ring, u32 *seqno)
/*
* HEAD is likely pointing to the dword after the actual command,
* so scan backwards until we find the MBOX. But limit it to just 3
- * dwords. Note that we don't care about ACTHD here since that might
+ * or 4 dwords depending on the semaphore wait command size.
+ * Note that we don't care about ACTHD here since that might
* point at at batch, and semaphores are always emitted into the
* ringbuffer itself.
*/
head = I915_READ_HEAD(ring) & HEAD_ADDR;
+ backwards = (INTEL_INFO(ring->dev)->gen >= 8) ? 5 : 4;
- for (i = 4; i; --i) {
+ for (i = backwards; i; --i) {
/*
* Be paranoid and presume the hw has gone off into the wild -
* our ring is smaller than what the hardware (and hence
@@ -2838,7 +3109,12 @@ semaphore_waits_for(struct intel_engine_cs *ring, u32 *seqno)
return NULL;
*seqno = ioread32(ring->buffer->virtual_start + head + 4) + 1;
- return semaphore_wait_to_signaller_ring(ring, ipehr);
+ if (INTEL_INFO(ring->dev)->gen >= 8) {
+ offset = ioread32(ring->buffer->virtual_start + head + 12);
+ offset <<= 32;
+ offset = ioread32(ring->buffer->virtual_start + head + 8);
+ }
+ return semaphore_wait_to_signaller_ring(ring, ipehr, offset);
}
static int semaphore_passed(struct intel_engine_cs *ring)
@@ -2884,8 +3160,14 @@ ring_stuck(struct intel_engine_cs *ring, u64 acthd)
struct drm_i915_private *dev_priv = dev->dev_private;
u32 tmp;
- if (ring->hangcheck.acthd != acthd)
- return HANGCHECK_ACTIVE;
+ if (acthd != ring->hangcheck.acthd) {
+ if (acthd > ring->hangcheck.max_acthd) {
+ ring->hangcheck.max_acthd = acthd;
+ return HANGCHECK_ACTIVE;
+ }
+
+ return HANGCHECK_ACTIVE_LOOP;
+ }
if (IS_GEN2(dev))
return HANGCHECK_HUNG;
@@ -2996,8 +3278,9 @@ static void i915_hangcheck_elapsed(unsigned long data)
switch (ring->hangcheck.action) {
case HANGCHECK_IDLE:
case HANGCHECK_WAIT:
- break;
case HANGCHECK_ACTIVE:
+ break;
+ case HANGCHECK_ACTIVE_LOOP:
ring->hangcheck.score += BUSY;
break;
case HANGCHECK_KICK:
@@ -3017,6 +3300,8 @@ static void i915_hangcheck_elapsed(unsigned long data)
*/
if (ring->hangcheck.score > 0)
ring->hangcheck.score--;
+
+ ring->hangcheck.acthd = ring->hangcheck.max_acthd = 0;
}
ring->hangcheck.seqno = seqno;
@@ -3132,7 +3417,7 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
I915_WRITE(PIPESTAT(pipe), 0xffff);
I915_WRITE(VLV_IIR, 0xffffffff);
I915_WRITE(VLV_IMR, 0xffffffff);
@@ -3158,8 +3443,10 @@ static void gen8_irq_reset(struct drm_device *dev)
gen8_gt_irq_reset(dev_priv);
- for_each_pipe(pipe)
- GEN8_IRQ_RESET_NDX(DE_PIPE, pipe);
+ for_each_pipe(dev_priv, pipe)
+ if (intel_display_power_enabled(dev_priv,
+ POWER_DOMAIN_PIPE(pipe)))
+ GEN8_IRQ_RESET_NDX(DE_PIPE, pipe);
GEN5_IRQ_RESET(GEN8_DE_PORT_);
GEN5_IRQ_RESET(GEN8_DE_MISC_);
@@ -3168,6 +3455,19 @@ static void gen8_irq_reset(struct drm_device *dev)
ibx_irq_reset(dev);
}
+void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv)
+{
+ unsigned long irqflags;
+ uint32_t extra_ier = GEN8_PIPE_VBLANK | GEN8_PIPE_FIFO_UNDERRUN;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_B, dev_priv->de_irq_mask[PIPE_B],
+ ~dev_priv->de_irq_mask[PIPE_B] | extra_ier);
+ GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_C, dev_priv->de_irq_mask[PIPE_C],
+ ~dev_priv->de_irq_mask[PIPE_C] | extra_ier);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+}
+
static void cherryview_irq_preinstall(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3187,7 +3487,7 @@ static void cherryview_irq_preinstall(struct drm_device *dev)
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
I915_WRITE(PIPESTAT(pipe), 0xffff);
I915_WRITE(VLV_IMR, 0xffffffff);
@@ -3199,18 +3499,17 @@ static void cherryview_irq_preinstall(struct drm_device *dev)
static void ibx_hpd_irq_setup(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *intel_encoder;
u32 hotplug_irqs, hotplug, enabled_irqs = 0;
if (HAS_PCH_IBX(dev)) {
hotplug_irqs = SDE_HOTPLUG_MASK;
- list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head)
+ for_each_intel_encoder(dev, intel_encoder)
if (dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_ENABLED)
enabled_irqs |= hpd_ibx[intel_encoder->hpd_pin];
} else {
hotplug_irqs = SDE_HOTPLUG_MASK_CPT;
- list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head)
+ for_each_intel_encoder(dev, intel_encoder)
if (dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_ENABLED)
enabled_irqs |= hpd_cpt[intel_encoder->hpd_pin];
}
@@ -3459,28 +3758,31 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv)
{
- int i;
-
/* These are interrupts we'll toggle with the ring mask register */
uint32_t gt_interrupts[] = {
GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT |
+ GT_CONTEXT_SWITCH_INTERRUPT << GEN8_RCS_IRQ_SHIFT |
GT_RENDER_L3_PARITY_ERROR_INTERRUPT |
- GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT,
+ GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT |
+ GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT,
GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT |
- GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT,
+ GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS1_IRQ_SHIFT |
+ GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT |
+ GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS2_IRQ_SHIFT,
0,
- GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT
+ GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT |
+ GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VECS_IRQ_SHIFT
};
- for (i = 0; i < ARRAY_SIZE(gt_interrupts); i++)
- GEN8_IRQ_INIT_NDX(GT, i, ~gt_interrupts[i], gt_interrupts[i]);
-
dev_priv->pm_irq_mask = 0xffffffff;
+ GEN8_IRQ_INIT_NDX(GT, 0, ~gt_interrupts[0], gt_interrupts[0]);
+ GEN8_IRQ_INIT_NDX(GT, 1, ~gt_interrupts[1], gt_interrupts[1]);
+ GEN8_IRQ_INIT_NDX(GT, 2, dev_priv->pm_irq_mask, dev_priv->pm_rps_events);
+ GEN8_IRQ_INIT_NDX(GT, 3, ~gt_interrupts[3], gt_interrupts[3]);
}
static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
{
- struct drm_device *dev = dev_priv->dev;
uint32_t de_pipe_masked = GEN8_PIPE_PRIMARY_FLIP_DONE |
GEN8_PIPE_CDCLK_CRC_DONE |
GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
@@ -3491,9 +3793,12 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
dev_priv->de_irq_mask[PIPE_B] = ~de_pipe_masked;
dev_priv->de_irq_mask[PIPE_C] = ~de_pipe_masked;
- for_each_pipe(pipe)
- GEN8_IRQ_INIT_NDX(DE_PIPE, pipe, dev_priv->de_irq_mask[pipe],
- de_pipe_enables);
+ for_each_pipe(dev_priv, pipe)
+ if (intel_display_power_enabled(dev_priv,
+ POWER_DOMAIN_PIPE(pipe)))
+ GEN8_IRQ_INIT_NDX(DE_PIPE, pipe,
+ dev_priv->de_irq_mask[pipe],
+ de_pipe_enables);
GEN5_IRQ_INIT(GEN8_DE_PORT_, ~GEN8_AUX_CHANNEL_A, GEN8_AUX_CHANNEL_A);
}
@@ -3533,12 +3838,12 @@ static int cherryview_irq_postinstall(struct drm_device *dev)
*/
dev_priv->irq_mask = ~enable_mask;
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
I915_WRITE(PIPESTAT(pipe), 0xffff);
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
i915_enable_pipestat(dev_priv, pipe, pipestat_enable);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
@@ -3561,8 +3866,6 @@ static void gen8_irq_uninstall(struct drm_device *dev)
if (!dev_priv)
return;
- intel_hpd_irq_uninstall(dev_priv);
-
gen8_irq_reset(dev);
}
@@ -3577,9 +3880,7 @@ static void valleyview_irq_uninstall(struct drm_device *dev)
I915_WRITE(VLV_MASTER_IER, 0);
- intel_hpd_irq_uninstall(dev_priv);
-
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
I915_WRITE(PIPESTAT(pipe), 0xffff);
I915_WRITE(HWSTAM, 0xffffffff);
@@ -3641,7 +3942,7 @@ do { \
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
I915_WRITE(PIPESTAT(pipe), 0xffff);
I915_WRITE(VLV_IMR, 0xffffffff);
@@ -3657,8 +3958,6 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
if (!dev_priv)
return;
- intel_hpd_irq_uninstall(dev_priv);
-
ironlake_irq_reset(dev);
}
@@ -3667,7 +3966,7 @@ static void i8xx_irq_preinstall(struct drm_device * dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
I915_WRITE(PIPESTAT(pipe), 0);
I915_WRITE16(IMR, 0xffff);
I915_WRITE16(IER, 0x0);
@@ -3721,7 +4020,7 @@ static bool i8xx_handle_vblank(struct drm_device *dev,
return false;
if ((iir & flip_pending) == 0)
- return false;
+ goto check_page_flip;
intel_prepare_page_flip(dev, plane);
@@ -3732,11 +4031,14 @@ static bool i8xx_handle_vblank(struct drm_device *dev,
* an interrupt per se, we watch for the change at vblank.
*/
if (I915_READ16(ISR) & flip_pending)
- return false;
+ goto check_page_flip;
intel_finish_page_flip(dev, pipe);
-
return true;
+
+check_page_flip:
+ intel_check_page_flip(dev, pipe);
+ return false;
}
static irqreturn_t i8xx_irq_handler(int irq, void *arg)
@@ -3767,7 +4069,7 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
"Command parser error, iir 0x%08x",
iir);
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
int reg = PIPESTAT(pipe);
pipe_stats[pipe] = I915_READ(reg);
@@ -3787,7 +4089,7 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
if (iir & I915_USER_INTERRUPT)
notify_ring(dev, &dev_priv->ring[RCS]);
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
int plane = pipe;
if (HAS_FBC(dev))
plane = !plane;
@@ -3815,7 +4117,7 @@ static void i8xx_irq_uninstall(struct drm_device * dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
/* Clear enable bits; then clear status bits */
I915_WRITE(PIPESTAT(pipe), 0);
I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe)));
@@ -3836,7 +4138,7 @@ static void i915_irq_preinstall(struct drm_device * dev)
}
I915_WRITE16(HWSTAM, 0xeffe);
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
I915_WRITE(PIPESTAT(pipe), 0);
I915_WRITE(IMR, 0xffffffff);
I915_WRITE(IER, 0x0);
@@ -3906,7 +4208,7 @@ static bool i915_handle_vblank(struct drm_device *dev,
return false;
if ((iir & flip_pending) == 0)
- return false;
+ goto check_page_flip;
intel_prepare_page_flip(dev, plane);
@@ -3917,11 +4219,14 @@ static bool i915_handle_vblank(struct drm_device *dev,
* an interrupt per se, we watch for the change at vblank.
*/
if (I915_READ(ISR) & flip_pending)
- return false;
+ goto check_page_flip;
intel_finish_page_flip(dev, pipe);
-
return true;
+
+check_page_flip:
+ intel_check_page_flip(dev, pipe);
+ return false;
}
static irqreturn_t i915_irq_handler(int irq, void *arg)
@@ -3951,7 +4256,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
"Command parser error, iir 0x%08x",
iir);
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
int reg = PIPESTAT(pipe);
pipe_stats[pipe] = I915_READ(reg);
@@ -3977,7 +4282,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
if (iir & I915_USER_INTERRUPT)
notify_ring(dev, &dev_priv->ring[RCS]);
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
int plane = pipe;
if (HAS_FBC(dev))
plane = !plane;
@@ -4029,15 +4334,13 @@ static void i915_irq_uninstall(struct drm_device * dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
- intel_hpd_irq_uninstall(dev_priv);
-
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
}
I915_WRITE16(HWSTAM, 0xffff);
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
/* Clear enable bits; then clear status bits */
I915_WRITE(PIPESTAT(pipe), 0);
I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe)));
@@ -4057,7 +4360,7 @@ static void i965_irq_preinstall(struct drm_device * dev)
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
I915_WRITE(HWSTAM, 0xeffe);
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
I915_WRITE(PIPESTAT(pipe), 0);
I915_WRITE(IMR, 0xffffffff);
I915_WRITE(IER, 0x0);
@@ -4126,7 +4429,6 @@ static int i965_irq_postinstall(struct drm_device *dev)
static void i915_hpd_irq_setup(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *intel_encoder;
u32 hotplug_en;
@@ -4137,7 +4439,7 @@ static void i915_hpd_irq_setup(struct drm_device *dev)
hotplug_en &= ~HOTPLUG_INT_EN_MASK;
/* Note HDMI and DP share hotplug bits */
/* enable bits are the same for all generations */
- list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head)
+ for_each_intel_encoder(dev, intel_encoder)
if (dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_ENABLED)
hotplug_en |= hpd_mask_i915[intel_encoder->hpd_pin];
/* Programming the CRT detection parameters tends
@@ -4183,7 +4485,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
"Command parser error, iir 0x%08x",
iir);
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
int reg = PIPESTAT(pipe);
pipe_stats[pipe] = I915_READ(reg);
@@ -4214,7 +4516,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
if (iir & I915_BSD_USER_INTERRUPT)
notify_ring(dev, &dev_priv->ring[VCS]);
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS &&
i915_handle_vblank(dev, pipe, pipe, iir))
flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(pipe);
@@ -4267,31 +4569,33 @@ static void i965_irq_uninstall(struct drm_device * dev)
if (!dev_priv)
return;
- intel_hpd_irq_uninstall(dev_priv);
-
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
I915_WRITE(HWSTAM, 0xffffffff);
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
I915_WRITE(PIPESTAT(pipe), 0);
I915_WRITE(IMR, 0xffffffff);
I915_WRITE(IER, 0x0);
- for_each_pipe(pipe)
+ for_each_pipe(dev_priv, pipe)
I915_WRITE(PIPESTAT(pipe),
I915_READ(PIPESTAT(pipe)) & 0x8000ffff);
I915_WRITE(IIR, I915_READ(IIR));
}
-static void intel_hpd_irq_reenable(unsigned long data)
+static void intel_hpd_irq_reenable(struct work_struct *work)
{
- struct drm_i915_private *dev_priv = (struct drm_i915_private *)data;
+ struct drm_i915_private *dev_priv =
+ container_of(work, typeof(*dev_priv),
+ hotplug_reenable_work.work);
struct drm_device *dev = dev_priv->dev;
struct drm_mode_config *mode_config = &dev->mode_config;
unsigned long irqflags;
int i;
+ intel_runtime_pm_get(dev_priv);
+
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
for (i = (HPD_NONE + 1); i < HPD_NUM_PINS; i++) {
struct drm_connector *connector;
@@ -4317,6 +4621,8 @@ static void intel_hpd_irq_reenable(unsigned long data)
if (dev_priv->display.hpd_irq_setup)
dev_priv->display.hpd_irq_setup(dev);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+ intel_runtime_pm_put(dev_priv);
}
void intel_irq_init(struct drm_device *dev)
@@ -4324,21 +4630,29 @@ void intel_irq_init(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
+ INIT_WORK(&dev_priv->dig_port_work, i915_digport_work_func);
INIT_WORK(&dev_priv->gpu_error.work, i915_error_work_func);
INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work);
/* Let's track the enabled rps events */
- dev_priv->pm_rps_events = GEN6_PM_RPS_EVENTS;
+ if (IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev))
+ /* WaGsvRC0ResidencyMethod:vlv */
+ dev_priv->pm_rps_events = GEN6_PM_RP_UP_EI_EXPIRED;
+ else
+ dev_priv->pm_rps_events = GEN6_PM_RPS_EVENTS;
setup_timer(&dev_priv->gpu_error.hangcheck_timer,
i915_hangcheck_elapsed,
(unsigned long) dev);
- setup_timer(&dev_priv->hotplug_reenable_timer, intel_hpd_irq_reenable,
- (unsigned long) dev_priv);
+ INIT_DELAYED_WORK(&dev_priv->hotplug_reenable_work,
+ intel_hpd_irq_reenable);
pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+ /* Haven't installed the IRQ handler yet */
+ dev_priv->pm._irqs_disabled = true;
+
if (IS_GEN2(dev)) {
dev->max_vblank_count = 0;
dev->driver->get_vblank_counter = i8xx_get_vblank_counter;
@@ -4350,6 +4664,14 @@ void intel_irq_init(struct drm_device *dev)
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
}
+ /*
+ * Opt out of the vblank disable timer on everything except gen2.
+ * Gen2 doesn't have a hardware frame counter and so depends on
+ * vblank interrupts to produce sane vblank seuquence numbers.
+ */
+ if (!IS_GEN2(dev))
+ dev->vblank_disable_immediate = true;
+
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
@@ -4426,7 +4748,9 @@ void intel_hpd_init(struct drm_device *dev)
list_for_each_entry(connector, &mode_config->connector_list, head) {
struct intel_connector *intel_connector = to_intel_connector(connector);
connector->polled = intel_connector->polled;
- if (!connector->polled && I915_HAS_HOTPLUG(dev) && intel_connector->encoder->hpd_pin > HPD_NONE)
+ if (connector->encoder && !connector->polled && I915_HAS_HOTPLUG(dev) && intel_connector->encoder->hpd_pin > HPD_NONE)
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ if (intel_connector->mst_port)
connector->polled = DRM_CONNECTOR_POLL_HPD;
}
@@ -4444,7 +4768,7 @@ void intel_runtime_pm_disable_interrupts(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
dev->driver->irq_uninstall(dev);
- dev_priv->pm.irqs_disabled = true;
+ dev_priv->pm._irqs_disabled = true;
}
/* Restore interrupts so we can recover from runtime PM. */
@@ -4452,7 +4776,7 @@ void intel_runtime_pm_restore_interrupts(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- dev_priv->pm.irqs_disabled = false;
+ dev_priv->pm._irqs_disabled = false;
dev->driver->irq_preinstall(dev);
dev->driver->irq_postinstall(dev);
}
diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c
index d05a2afa17dc..c91cb2033cc5 100644
--- a/drivers/gpu/drm/i915/i915_params.c
+++ b/drivers/gpu/drm/i915/i915_params.c
@@ -35,6 +35,7 @@ struct i915_params i915 __read_mostly = {
.vbt_sdvo_panel_type = -1,
.enable_rc6 = -1,
.enable_fbc = -1,
+ .enable_execlists = 0,
.enable_hangcheck = true,
.enable_ppgtt = -1,
.enable_psr = 0,
@@ -48,6 +49,8 @@ struct i915_params i915 __read_mostly = {
.disable_display = 0,
.enable_cmd_parser = 1,
.disable_vtd_wa = 0,
+ .use_mmio_flip = 0,
+ .mmio_debug = 0,
};
module_param_named(modeset, i915.modeset, int, 0400);
@@ -64,12 +67,12 @@ module_param_named(powersave, i915.powersave, int, 0600);
MODULE_PARM_DESC(powersave,
"Enable powersavings, fbc, downclocking, etc. (default: true)");
-module_param_named(semaphores, i915.semaphores, int, 0400);
+module_param_named_unsafe(semaphores, i915.semaphores, int, 0400);
MODULE_PARM_DESC(semaphores,
"Use semaphores for inter-ring sync "
"(default: -1 (use per-chip defaults))");
-module_param_named(enable_rc6, i915.enable_rc6, int, 0400);
+module_param_named_unsafe(enable_rc6, i915.enable_rc6, int, 0400);
MODULE_PARM_DESC(enable_rc6,
"Enable power-saving render C-state 6. "
"Different stages can be selected via bitmask values "
@@ -77,7 +80,7 @@ MODULE_PARM_DESC(enable_rc6,
"For example, 3 would enable rc6 and deep rc6, and 7 would enable everything. "
"default: -1 (use per-chip default)");
-module_param_named(enable_fbc, i915.enable_fbc, int, 0600);
+module_param_named_unsafe(enable_fbc, i915.enable_fbc, int, 0600);
MODULE_PARM_DESC(enable_fbc,
"Enable frame buffer compression for power savings "
"(default: -1 (use per-chip default))");
@@ -111,11 +114,16 @@ MODULE_PARM_DESC(enable_hangcheck,
"WARNING: Disabling this can cause system wide hangs. "
"(default: true)");
-module_param_named(enable_ppgtt, i915.enable_ppgtt, int, 0400);
+module_param_named_unsafe(enable_ppgtt, i915.enable_ppgtt, int, 0400);
MODULE_PARM_DESC(enable_ppgtt,
"Override PPGTT usage. "
"(-1=auto [default], 0=disabled, 1=aliasing, 2=full)");
+module_param_named(enable_execlists, i915.enable_execlists, int, 0400);
+MODULE_PARM_DESC(enable_execlists,
+ "Override execlists usage. "
+ "(-1=auto, 0=disabled [default], 1=enabled)");
+
module_param_named(enable_psr, i915.enable_psr, int, 0600);
MODULE_PARM_DESC(enable_psr, "Enable PSR (default: false)");
@@ -156,3 +164,12 @@ MODULE_PARM_DESC(disable_vtd_wa, "Disable all VT-d workarounds (default: false)"
module_param_named(enable_cmd_parser, i915.enable_cmd_parser, int, 0600);
MODULE_PARM_DESC(enable_cmd_parser,
"Enable command parsing (1=enabled [default], 0=disabled)");
+
+module_param_named(use_mmio_flip, i915.use_mmio_flip, int, 0600);
+MODULE_PARM_DESC(use_mmio_flip,
+ "use MMIO flips (-1=never, 0=driver discretion [default], 1=always)");
+
+module_param_named(mmio_debug, i915.mmio_debug, bool, 0600);
+MODULE_PARM_DESC(mmio_debug,
+ "Enable the MMIO debug code (default: false). This may negatively "
+ "affect performance.");
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index a5bab61bfc00..c01e5f31430e 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -29,8 +29,8 @@
#define _TRANSCODER(tran, a, b) ((a) + (tran)*((b)-(a)))
#define _PORT(port, a, b) ((a) + (port)*((b)-(a)))
-#define _PIPE3(pipe, a, b, c) (pipe < 2 ? _PIPE(pipe, a, b) : c)
-#define _PORT3(port, a, b, c) (port < 2 ? _PORT(port, a, b) : c)
+#define _PIPE3(pipe, a, b, c) ((pipe) == PIPE_A ? (a) : \
+ (pipe) == PIPE_B ? (b) : (c))
#define _MASKED_BIT_ENABLE(a) (((a) << 16) | (a))
#define _MASKED_BIT_DISABLE(a) ((a) << 16)
@@ -143,6 +143,14 @@
#define GAB_CTL 0x24000
#define GAB_CTL_CONT_AFTER_PAGEFAULT (1<<8)
+#define GEN7_BIOS_RESERVED 0x1082C0
+#define GEN7_BIOS_RESERVED_1M (0 << 5)
+#define GEN7_BIOS_RESERVED_256K (1 << 5)
+#define GEN8_BIOS_RESERVED_SHIFT 7
+#define GEN7_BIOS_RESERVED_MASK 0x1
+#define GEN8_BIOS_RESERVED_MASK 0x3
+
+
/* VGA stuff */
#define VGA_ST01_MDA 0x3ba
@@ -240,7 +248,7 @@
#define MI_DISPLAY_FLIP_IVB_SPRITE_B (3 << 19)
#define MI_DISPLAY_FLIP_IVB_PLANE_C (4 << 19)
#define MI_DISPLAY_FLIP_IVB_SPRITE_C (5 << 19)
-#define MI_SEMAPHORE_MBOX MI_INSTR(0x16, 1) /* gen6+ */
+#define MI_SEMAPHORE_MBOX MI_INSTR(0x16, 1) /* gen6, gen7 */
#define MI_SEMAPHORE_GLOBAL_GTT (1<<22)
#define MI_SEMAPHORE_UPDATE (1<<21)
#define MI_SEMAPHORE_COMPARE (1<<20)
@@ -266,7 +274,13 @@
#define MI_RESTORE_EXT_STATE_EN (1<<2)
#define MI_FORCE_RESTORE (1<<1)
#define MI_RESTORE_INHIBIT (1<<0)
+#define MI_SEMAPHORE_SIGNAL MI_INSTR(0x1b, 0) /* GEN8+ */
+#define MI_SEMAPHORE_TARGET(engine) ((engine)<<15)
+#define MI_SEMAPHORE_WAIT MI_INSTR(0x1c, 2) /* GEN8+ */
+#define MI_SEMAPHORE_POLL (1<<15)
+#define MI_SEMAPHORE_SAD_GTE_SDD (1<<12)
#define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1)
+#define MI_STORE_DWORD_IMM_GEN8 MI_INSTR(0x20, 2)
#define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */
#define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1)
#define MI_STORE_DWORD_INDEX_SHIFT 2
@@ -277,6 +291,7 @@
* address/value pairs. Don't overdue it, though, x <= 2^4 must hold!
*/
#define MI_LOAD_REGISTER_IMM(x) MI_INSTR(0x22, 2*(x)-1)
+#define MI_LRI_FORCE_POSTED (1<<12)
#define MI_STORE_REGISTER_MEM(x) MI_INSTR(0x24, 2*(x)-1)
#define MI_STORE_REGISTER_MEM_GEN8(x) MI_INSTR(0x24, 3*(x)-1)
#define MI_SRM_LRM_GLOBAL_GTT (1<<22)
@@ -329,16 +344,20 @@
#define GFX_OP_DESTBUFFER_INFO ((0x3<<29)|(0x1d<<24)|(0x8e<<16)|1)
#define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3))
#define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2)
-#define SRC_COPY_BLT_CMD ((2<<29)|(0x43<<22)|4)
+
+#define COLOR_BLT_CMD (2<<29 | 0x40<<22 | (5-2))
+#define SRC_COPY_BLT_CMD ((2<<29)|(0x43<<22)|4)
#define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)|6)
#define XY_MONO_SRC_COPY_IMM_BLT ((2<<29)|(0x71<<22)|5)
-#define XY_SRC_COPY_BLT_WRITE_ALPHA (1<<21)
-#define XY_SRC_COPY_BLT_WRITE_RGB (1<<20)
+#define BLT_WRITE_A (2<<20)
+#define BLT_WRITE_RGB (1<<20)
+#define BLT_WRITE_RGBA (BLT_WRITE_RGB | BLT_WRITE_A)
#define BLT_DEPTH_8 (0<<24)
#define BLT_DEPTH_16_565 (1<<24)
#define BLT_DEPTH_16_1555 (2<<24)
#define BLT_DEPTH_32 (3<<24)
-#define BLT_ROP_GXCOPY (0xcc<<16)
+#define BLT_ROP_SRC_COPY (0xcc<<16)
+#define BLT_ROP_COLOR_COPY (0xf0<<16)
#define XY_SRC_COPY_BLT_SRC_TILED (1<<15) /* 965+ only */
#define XY_SRC_COPY_BLT_DST_TILED (1<<11) /* 965+ only */
#define CMD_OP_DISPLAYBUFFER_INFO ((0x0<<29)|(0x14<<23)|2)
@@ -360,6 +379,7 @@
#define PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE (1<<10) /* GM45+ only */
#define PIPE_CONTROL_INDIRECT_STATE_DISABLE (1<<9)
#define PIPE_CONTROL_NOTIFY (1<<8)
+#define PIPE_CONTROL_FLUSH_ENABLE (1<<7) /* gen7+ */
#define PIPE_CONTROL_VF_CACHE_INVALIDATE (1<<4)
#define PIPE_CONTROL_CONST_CACHE_INVALIDATE (1<<3)
#define PIPE_CONTROL_STATE_CACHE_INVALIDATE (1<<2)
@@ -491,10 +511,26 @@
#define BUNIT_REG_BISOC 0x11
#define PUNIT_REG_DSPFREQ 0x36
+#define DSPFREQSTAT_SHIFT_CHV 24
+#define DSPFREQSTAT_MASK_CHV (0x1f << DSPFREQSTAT_SHIFT_CHV)
+#define DSPFREQGUAR_SHIFT_CHV 8
+#define DSPFREQGUAR_MASK_CHV (0x1f << DSPFREQGUAR_SHIFT_CHV)
#define DSPFREQSTAT_SHIFT 30
#define DSPFREQSTAT_MASK (0x3 << DSPFREQSTAT_SHIFT)
#define DSPFREQGUAR_SHIFT 14
#define DSPFREQGUAR_MASK (0x3 << DSPFREQGUAR_SHIFT)
+#define _DP_SSC(val, pipe) ((val) << (2 * (pipe)))
+#define DP_SSC_MASK(pipe) _DP_SSC(0x3, (pipe))
+#define DP_SSC_PWR_ON(pipe) _DP_SSC(0x0, (pipe))
+#define DP_SSC_CLK_GATE(pipe) _DP_SSC(0x1, (pipe))
+#define DP_SSC_RESET(pipe) _DP_SSC(0x2, (pipe))
+#define DP_SSC_PWR_GATE(pipe) _DP_SSC(0x3, (pipe))
+#define _DP_SSS(val, pipe) ((val) << (2 * (pipe) + 16))
+#define DP_SSS_MASK(pipe) _DP_SSS(0x3, (pipe))
+#define DP_SSS_PWR_ON(pipe) _DP_SSS(0x0, (pipe))
+#define DP_SSS_CLK_GATE(pipe) _DP_SSS(0x1, (pipe))
+#define DP_SSS_RESET(pipe) _DP_SSS(0x2, (pipe))
+#define DP_SSS_PWR_GATE(pipe) _DP_SSS(0x3, (pipe))
/* See the PUNIT HAS v0.8 for the below bits */
enum punit_power_well {
@@ -508,6 +544,11 @@ enum punit_power_well {
PUNIT_POWER_WELL_DPIO_TX_C_LANES_23 = 9,
PUNIT_POWER_WELL_DPIO_RX0 = 10,
PUNIT_POWER_WELL_DPIO_RX1 = 11,
+ PUNIT_POWER_WELL_DPIO_CMN_D = 12,
+ /* FIXME: guesswork below */
+ PUNIT_POWER_WELL_DPIO_TX_D_LANES_01 = 13,
+ PUNIT_POWER_WELL_DPIO_TX_D_LANES_23 = 14,
+ PUNIT_POWER_WELL_DPIO_RX2 = 15,
PUNIT_POWER_WELL_NUM,
};
@@ -525,10 +566,21 @@ enum punit_power_well {
#define PUNIT_REG_GPU_FREQ_STS 0xd8
#define GENFREQSTATUS (1<<0)
#define PUNIT_REG_MEDIA_TURBO_FREQ_REQ 0xdc
+#define PUNIT_REG_CZ_TIMESTAMP 0xce
#define PUNIT_FUSE_BUS2 0xf6 /* bits 47:40 */
#define PUNIT_FUSE_BUS1 0xf5 /* bits 55:48 */
+#define PUNIT_GPU_STATUS_REG 0xdb
+#define PUNIT_GPU_STATUS_MAX_FREQ_SHIFT 16
+#define PUNIT_GPU_STATUS_MAX_FREQ_MASK 0xff
+#define PUNIT_GPU_STATIS_GFX_MIN_FREQ_SHIFT 8
+#define PUNIT_GPU_STATUS_GFX_MIN_FREQ_MASK 0xff
+
+#define PUNIT_GPU_DUTYCYCLE_REG 0xdf
+#define PUNIT_GPU_DUTYCYCLE_RPE_FREQ_SHIFT 8
+#define PUNIT_GPU_DUTYCYCLE_RPE_FREQ_MASK 0xff
+
#define IOSF_NC_FB_GFX_FREQ_FUSE 0x1c
#define FB_GFX_MAX_FREQ_FUSE_SHIFT 3
#define FB_GFX_MAX_FREQ_FUSE_MASK 0x000007f8
@@ -540,6 +592,11 @@ enum punit_power_well {
#define FB_FMAX_VMIN_FREQ_LO_SHIFT 27
#define FB_FMAX_VMIN_FREQ_LO_MASK 0xf8000000
+#define VLV_CZ_CLOCK_TO_MILLI_SEC 100000
+#define VLV_RP_UP_EI_THRESHOLD 90
+#define VLV_RP_DOWN_EI_THRESHOLD 70
+#define VLV_INT_COUNT_FOR_DOWN_EI 5
+
/* vlv2 north clock has */
#define CCK_FUSE_REG 0x8
#define CCK_FUSE_HPLL_FREQ_MASK 0x3
@@ -574,6 +631,11 @@ enum punit_power_well {
#define DSI_PLL_M1_DIV_SHIFT 0
#define DSI_PLL_M1_DIV_MASK (0x1ff << 0)
#define CCK_DISPLAY_CLOCK_CONTROL 0x6b
+#define DISPLAY_TRUNK_FORCE_ON (1 << 17)
+#define DISPLAY_TRUNK_FORCE_OFF (1 << 16)
+#define DISPLAY_FREQUENCY_STATUS (0x1f << 8)
+#define DISPLAY_FREQUENCY_STATUS_SHIFT 8
+#define DISPLAY_FREQUENCY_VALUES (0x1f << 0)
/**
* DOC: DPIO
@@ -761,6 +823,8 @@ enum punit_power_well {
#define _VLV_PCS_DW8_CH0 0x8220
#define _VLV_PCS_DW8_CH1 0x8420
+#define CHV_PCS_USEDCLKCHANNEL_OVRRIDE (1 << 20)
+#define CHV_PCS_USEDCLKCHANNEL (1 << 21)
#define VLV_PCS_DW8(ch) _PORT(ch, _VLV_PCS_DW8_CH0, _VLV_PCS_DW8_CH1)
#define _VLV_PCS01_DW8_CH0 0x0220
@@ -805,8 +869,8 @@ enum punit_power_well {
#define _VLV_TX_DW2_CH0 0x8288
#define _VLV_TX_DW2_CH1 0x8488
-#define DPIO_SWING_MARGIN_SHIFT 16
-#define DPIO_SWING_MARGIN_MASK (0xff << DPIO_SWING_MARGIN_SHIFT)
+#define DPIO_SWING_MARGIN000_SHIFT 16
+#define DPIO_SWING_MARGIN000_MASK (0xff << DPIO_SWING_MARGIN000_SHIFT)
#define DPIO_UNIQ_TRANS_SCALE_SHIFT 8
#define VLV_TX_DW2(ch) _PORT(ch, _VLV_TX_DW2_CH0, _VLV_TX_DW2_CH1)
@@ -814,12 +878,16 @@ enum punit_power_well {
#define _VLV_TX_DW3_CH1 0x848c
/* The following bit for CHV phy */
#define DPIO_TX_UNIQ_TRANS_SCALE_EN (1<<27)
+#define DPIO_SWING_MARGIN101_SHIFT 16
+#define DPIO_SWING_MARGIN101_MASK (0xff << DPIO_SWING_MARGIN101_SHIFT)
#define VLV_TX_DW3(ch) _PORT(ch, _VLV_TX_DW3_CH0, _VLV_TX_DW3_CH1)
#define _VLV_TX_DW4_CH0 0x8290
#define _VLV_TX_DW4_CH1 0x8490
#define DPIO_SWING_DEEMPH9P5_SHIFT 24
#define DPIO_SWING_DEEMPH9P5_MASK (0xff << DPIO_SWING_DEEMPH9P5_SHIFT)
+#define DPIO_SWING_DEEMPH6P0_SHIFT 16
+#define DPIO_SWING_DEEMPH6P0_MASK (0xff << DPIO_SWING_DEEMPH6P0_SHIFT)
#define VLV_TX_DW4(ch) _PORT(ch, _VLV_TX_DW4_CH0, _VLV_TX_DW4_CH1)
#define _VLV_TX3_DW4_CH0 0x690
@@ -869,6 +937,16 @@ enum punit_power_well {
#define DPIO_CHV_PROP_COEFF_SHIFT 0
#define CHV_PLL_DW6(ch) _PIPE(ch, _CHV_PLL_DW6_CH0, _CHV_PLL_DW6_CH1)
+#define _CHV_CMN_DW5_CH0 0x8114
+#define CHV_BUFRIGHTENA1_DISABLE (0 << 20)
+#define CHV_BUFRIGHTENA1_NORMAL (1 << 20)
+#define CHV_BUFRIGHTENA1_FORCE (3 << 20)
+#define CHV_BUFRIGHTENA1_MASK (3 << 20)
+#define CHV_BUFLEFTENA1_DISABLE (0 << 22)
+#define CHV_BUFLEFTENA1_NORMAL (1 << 22)
+#define CHV_BUFLEFTENA1_FORCE (3 << 22)
+#define CHV_BUFLEFTENA1_MASK (3 << 22)
+
#define _CHV_CMN_DW13_CH0 0x8134
#define _CHV_CMN_DW0_CH1 0x8080
#define DPIO_CHV_S1_DIV_SHIFT 21
@@ -883,8 +961,21 @@ enum punit_power_well {
#define _CHV_CMN_DW1_CH1 0x8084
#define DPIO_AFC_RECAL (1 << 14)
#define DPIO_DCLKP_EN (1 << 13)
+#define CHV_BUFLEFTENA2_DISABLE (0 << 17) /* CL2 DW1 only */
+#define CHV_BUFLEFTENA2_NORMAL (1 << 17) /* CL2 DW1 only */
+#define CHV_BUFLEFTENA2_FORCE (3 << 17) /* CL2 DW1 only */
+#define CHV_BUFLEFTENA2_MASK (3 << 17) /* CL2 DW1 only */
+#define CHV_BUFRIGHTENA2_DISABLE (0 << 19) /* CL2 DW1 only */
+#define CHV_BUFRIGHTENA2_NORMAL (1 << 19) /* CL2 DW1 only */
+#define CHV_BUFRIGHTENA2_FORCE (3 << 19) /* CL2 DW1 only */
+#define CHV_BUFRIGHTENA2_MASK (3 << 19) /* CL2 DW1 only */
#define CHV_CMN_DW14(ch) _PIPE(ch, _CHV_CMN_DW14_CH0, _CHV_CMN_DW1_CH1)
+#define _CHV_CMN_DW19_CH0 0x814c
+#define _CHV_CMN_DW6_CH1 0x8098
+#define CHV_CMN_USEDCLKCHANNEL (1 << 13)
+#define CHV_CMN_DW19(ch) _PIPE(ch, _CHV_CMN_DW19_CH0, _CHV_CMN_DW6_CH1)
+
#define CHV_CMN_DW30 0x8178
#define DPIO_LRC_BYPASS (1 << 3)
@@ -933,6 +1024,7 @@ enum punit_power_well {
#define SANDYBRIDGE_FENCE_PITCH_SHIFT 32
#define GEN7_FENCE_MAX_PITCH_VAL 0x0800
+
/* control register for cpu gtt access */
#define TILECTL 0x101000
#define TILECTL_SWZCTL (1 << 0)
@@ -946,6 +1038,13 @@ enum punit_power_well {
#define PGTBL_ADDRESS_LO_MASK 0xfffff000 /* bits [31:12] */
#define PGTBL_ADDRESS_HI_MASK 0x000000f0 /* bits [35:32] (gen4) */
#define PGTBL_ER 0x02024
+#define PRB0_BASE (0x2030-0x30)
+#define PRB1_BASE (0x2040-0x30) /* 830,gen3 */
+#define PRB2_BASE (0x2050-0x30) /* gen3 */
+#define SRB0_BASE (0x2100-0x30) /* gen2 */
+#define SRB1_BASE (0x2110-0x30) /* gen2 */
+#define SRB2_BASE (0x2120-0x30) /* 830 */
+#define SRB3_BASE (0x2130-0x30) /* 830 */
#define RENDER_RING_BASE 0x02000
#define BSD_RING_BASE 0x04000
#define GEN6_BSD_RING_BASE 0x12000
@@ -1007,6 +1106,7 @@ enum punit_power_well {
#define RING_ACTHD_UDW(base) ((base)+0x5c)
#define RING_NOPID(base) ((base)+0x94)
#define RING_IMR(base) ((base)+0xa8)
+#define RING_HWSTAM(base) ((base)+0x98)
#define RING_TIMESTAMP(base) ((base)+0x358)
#define TAIL_ADDR 0x001FFFF8
#define HEAD_WRAP_COUNT 0xFFE00000
@@ -1170,6 +1270,8 @@ enum punit_power_well {
#define VLV_IMR (VLV_DISPLAY_BASE + 0x20a8)
#define VLV_ISR (VLV_DISPLAY_BASE + 0x20ac)
#define VLV_PCBR (VLV_DISPLAY_BASE + 0x2120)
+#define VLV_PCBR_ADDR_SHIFT 12
+
#define DISPLAY_PLANE_FLIP_PENDING(plane) (1<<(11-(plane))) /* A and B only */
#define EIR 0x020b0
#define EMR 0x020b4
@@ -1189,6 +1291,10 @@ enum punit_power_well {
#define INSTPM_TLB_INVALIDATE (1<<9)
#define INSTPM_SYNC_FLUSH (1<<5)
#define ACTHD 0x020c8
+#define MEM_MODE 0x020cc
+#define MEM_DISPLAY_B_TRICKLE_FEED_DISABLE (1<<3) /* 830 only */
+#define MEM_DISPLAY_A_TRICKLE_FEED_DISABLE (1<<2) /* 830/845 only */
+#define MEM_DISPLAY_TRICKLE_FEED_DISABLE (1<<2) /* 85x only */
#define FW_BLC 0x020d8
#define FW_BLC2 0x020dc
#define FW_BLC_SELF 0x020e0 /* 915+ only */
@@ -1321,6 +1427,7 @@ enum punit_power_well {
#define GT_BSD_CS_ERROR_INTERRUPT (1 << 15)
#define GT_BSD_USER_INTERRUPT (1 << 12)
#define GT_RENDER_L3_PARITY_ERROR_INTERRUPT_S1 (1 << 11) /* hsw+; rsvd on snb, ivb, vlv */
+#define GT_CONTEXT_SWITCH_INTERRUPT (1 << 8)
#define GT_RENDER_L3_PARITY_ERROR_INTERRUPT (1 << 5) /* !snb */
#define GT_RENDER_PIPECTL_NOTIFY_INTERRUPT (1 << 4)
#define GT_RENDER_CS_MASTER_ERROR_INTERRUPT (1 << 3)
@@ -1460,6 +1567,7 @@ enum punit_power_well {
/* Framebuffer compression for Ironlake */
#define ILK_DPFC_CB_BASE 0x43200
#define ILK_DPFC_CONTROL 0x43208
+#define FBC_CTL_FALSE_COLOR (1<<10)
/* The bit 28-8 is reserved */
#define DPFC_RESERVED (0x1FFFFF00)
#define ILK_DPFC_RECOMP_CTL 0x4320c
@@ -1570,11 +1678,10 @@ enum punit_power_well {
/*
* Clock control & power management
*/
-#define DPLL_A_OFFSET 0x6014
-#define DPLL_B_OFFSET 0x6018
-#define CHV_DPLL_C_OFFSET 0x6030
-#define DPLL(pipe) (dev_priv->info.dpll_offsets[pipe] + \
- dev_priv->info.display_mmio_offset)
+#define _DPLL_A (dev_priv->info.display_mmio_offset + 0x6014)
+#define _DPLL_B (dev_priv->info.display_mmio_offset + 0x6018)
+#define _CHV_DPLL_C (dev_priv->info.display_mmio_offset + 0x6030)
+#define DPLL(pipe) _PIPE3((pipe), _DPLL_A, _DPLL_B, _CHV_DPLL_C)
#define VGA0 0x6000
#define VGA1 0x6004
@@ -1617,12 +1724,9 @@ enum punit_power_well {
#define DPIO_PHY_STATUS (VLV_DISPLAY_BASE + 0x6240)
#define DPLL_PORTD_READY_MASK (0xf)
#define DISPLAY_PHY_CONTROL (VLV_DISPLAY_BASE + 0x60100)
-#define PHY_COM_LANE_RESET_DEASSERT(phy, val) \
- ((phy == DPIO_PHY0) ? (val | 1) : (val | 2))
-#define PHY_COM_LANE_RESET_ASSERT(phy, val) \
- ((phy == DPIO_PHY0) ? (val & ~1) : (val & ~2))
+#define PHY_COM_LANE_RESET_DEASSERT(phy) (1 << (phy))
#define DISPLAY_PHY_STATUS (VLV_DISPLAY_BASE + 0x60104)
-#define PHY_POWERGOOD(phy) ((phy == DPIO_PHY0) ? (1<<31) : (1<<30))
+#define PHY_POWERGOOD(phy) (((phy) == DPIO_PHY0) ? (1<<31) : (1<<30))
/*
* The i830 generation, in LVDS mode, defines P1 as the bit number set within
@@ -1662,11 +1766,10 @@ enum punit_power_well {
#define SDVO_MULTIPLIER_SHIFT_HIRES 4
#define SDVO_MULTIPLIER_SHIFT_VGA 0
-#define DPLL_A_MD_OFFSET 0x601c /* 965+ only */
-#define DPLL_B_MD_OFFSET 0x6020 /* 965+ only */
-#define CHV_DPLL_C_MD_OFFSET 0x603c
-#define DPLL_MD(pipe) (dev_priv->info.dpll_md_offsets[pipe] + \
- dev_priv->info.display_mmio_offset)
+#define _DPLL_A_MD (dev_priv->info.display_mmio_offset + 0x601c)
+#define _DPLL_B_MD (dev_priv->info.display_mmio_offset + 0x6020)
+#define _CHV_DPLL_C_MD (dev_priv->info.display_mmio_offset + 0x603c)
+#define DPLL_MD(pipe) _PIPE3((pipe), _DPLL_A_MD, _DPLL_B_MD, _CHV_DPLL_C_MD)
/*
* UDI pixel divider, controlling how many pixels are stuffed into a packet.
@@ -2231,7 +2334,7 @@ enum punit_power_well {
/* Same as Haswell, but 72064 bytes now. */
#define GEN8_CXT_TOTAL_SIZE (18 * PAGE_SIZE)
-
+#define CHV_CLK_CTL1 0x101100
#define VLV_CLK_CTL2 0x101104
#define CLK_CTL2_CZCOUNT_30NS_SHIFT 28
@@ -2340,6 +2443,7 @@ enum punit_power_well {
#define _PIPEASRC 0x6001c
#define _BCLRPAT_A 0x60020
#define _VSYNCSHIFT_A 0x60028
+#define _PIPE_MULT_A 0x6002c
/* Pipe B timing regs */
#define _HTOTAL_B 0x61000
@@ -2351,6 +2455,7 @@ enum punit_power_well {
#define _PIPEBSRC 0x6101c
#define _BCLRPAT_B 0x61020
#define _VSYNCSHIFT_B 0x61028
+#define _PIPE_MULT_B 0x6102c
#define TRANSCODER_A_OFFSET 0x60000
#define TRANSCODER_B_OFFSET 0x61000
@@ -2371,11 +2476,13 @@ enum punit_power_well {
#define BCLRPAT(trans) _TRANSCODER2(trans, _BCLRPAT_A)
#define VSYNCSHIFT(trans) _TRANSCODER2(trans, _VSYNCSHIFT_A)
#define PIPESRC(trans) _TRANSCODER2(trans, _PIPEASRC)
+#define PIPE_MULT(trans) _TRANSCODER2(trans, _PIPE_MULT_A)
/* HSW+ eDP PSR registers */
#define EDP_PSR_BASE(dev) (IS_HASWELL(dev) ? 0x64800 : 0x6f800)
#define EDP_PSR_CTL(dev) (EDP_PSR_BASE(dev) + 0)
#define EDP_PSR_ENABLE (1<<31)
+#define BDW_PSR_SINGLE_FRAME (1<<30)
#define EDP_PSR_LINK_DISABLE (0<<27)
#define EDP_PSR_LINK_STANDBY (1<<27)
#define EDP_PSR_MIN_LINK_ENTRY_TIME_MASK (3<<25)
@@ -2533,8 +2640,14 @@ enum punit_power_well {
#define PORTC_HOTPLUG_LIVE_STATUS_VLV (1 << 28)
#define PORTB_HOTPLUG_LIVE_STATUS_VLV (1 << 29)
#define PORTD_HOTPLUG_INT_STATUS (3 << 21)
+#define PORTD_HOTPLUG_INT_LONG_PULSE (2 << 21)
+#define PORTD_HOTPLUG_INT_SHORT_PULSE (1 << 21)
#define PORTC_HOTPLUG_INT_STATUS (3 << 19)
+#define PORTC_HOTPLUG_INT_LONG_PULSE (2 << 19)
+#define PORTC_HOTPLUG_INT_SHORT_PULSE (1 << 19)
#define PORTB_HOTPLUG_INT_STATUS (3 << 17)
+#define PORTB_HOTPLUG_INT_LONG_PULSE (2 << 17)
+#define PORTB_HOTPLUG_INT_SHORT_PLUSE (1 << 17)
/* CRT/TV common between gen3+ */
#define CRT_HOTPLUG_INT_STATUS (1 << 11)
#define TV_HOTPLUG_INT_STATUS (1 << 10)
@@ -2588,7 +2701,7 @@ enum punit_power_well {
#define PORT_DFT_I9XX 0x61150
#define DC_BALANCE_RESET (1 << 25)
-#define PORT_DFT2_G4X 0x61154
+#define PORT_DFT2_G4X (dev_priv->info.display_mmio_offset + 0x61154)
#define DC_BALANCE_RESET_VLV (1 << 31)
#define PIPE_SCRAMBLE_RESET_MASK (0x3 << 0)
#define PIPE_B_SCRAMBLE_RESET (1 << 1)
@@ -3412,6 +3525,8 @@ enum punit_power_well {
#define DP_LINK_TRAIN_OFF (3 << 28)
#define DP_LINK_TRAIN_MASK (3 << 28)
#define DP_LINK_TRAIN_SHIFT 28
+#define DP_LINK_TRAIN_PAT_3_CHV (1 << 14)
+#define DP_LINK_TRAIN_MASK_CHV ((3 << 28)|(1<<14))
/* CPT Link training mode */
#define DP_LINK_TRAIN_PAT_1_CPT (0 << 8)
@@ -3668,7 +3783,6 @@ enum punit_power_well {
#define PIPE_VSYNC_INTERRUPT_STATUS (1UL<<9)
#define PIPE_DISPLAY_LINE_COMPARE_STATUS (1UL<<8)
#define PIPE_DPST_EVENT_STATUS (1UL<<7)
-#define PIPE_LEGACY_BLC_EVENT_STATUS (1UL<<6)
#define PIPE_A_PSR_STATUS_VLV (1UL<<6)
#define PIPE_LEGACY_BLC_EVENT_STATUS (1UL<<6)
#define PIPE_ODD_FIELD_INTERRUPT_STATUS (1UL<<5)
@@ -3778,73 +3892,151 @@ enum punit_power_well {
#define DSPARB_BEND_SHIFT 9 /* on 855 */
#define DSPARB_AEND_SHIFT 0
+/* pnv/gen4/g4x/vlv/chv */
#define DSPFW1 (dev_priv->info.display_mmio_offset + 0x70034)
-#define DSPFW_SR_SHIFT 23
-#define DSPFW_SR_MASK (0x1ff<<23)
-#define DSPFW_CURSORB_SHIFT 16
-#define DSPFW_CURSORB_MASK (0x3f<<16)
-#define DSPFW_PLANEB_SHIFT 8
-#define DSPFW_PLANEB_MASK (0x7f<<8)
-#define DSPFW_PLANEA_MASK (0x7f)
+#define DSPFW_SR_SHIFT 23
+#define DSPFW_SR_MASK (0x1ff<<23)
+#define DSPFW_CURSORB_SHIFT 16
+#define DSPFW_CURSORB_MASK (0x3f<<16)
+#define DSPFW_PLANEB_SHIFT 8
+#define DSPFW_PLANEB_MASK (0x7f<<8)
+#define DSPFW_PLANEB_MASK_VLV (0xff<<8) /* vlv/chv */
+#define DSPFW_PLANEA_SHIFT 0
+#define DSPFW_PLANEA_MASK (0x7f<<0)
+#define DSPFW_PLANEA_MASK_VLV (0xff<<0) /* vlv/chv */
#define DSPFW2 (dev_priv->info.display_mmio_offset + 0x70038)
-#define DSPFW_CURSORA_MASK 0x00003f00
-#define DSPFW_CURSORA_SHIFT 8
-#define DSPFW_PLANEC_MASK (0x7f)
+#define DSPFW_FBC_SR_EN (1<<31) /* g4x */
+#define DSPFW_FBC_SR_SHIFT 28
+#define DSPFW_FBC_SR_MASK (0x7<<28) /* g4x */
+#define DSPFW_FBC_HPLL_SR_SHIFT 24
+#define DSPFW_FBC_HPLL_SR_MASK (0xf<<24) /* g4x */
+#define DSPFW_SPRITEB_SHIFT (16)
+#define DSPFW_SPRITEB_MASK (0x7f<<16) /* g4x */
+#define DSPFW_SPRITEB_MASK_VLV (0xff<<16) /* vlv/chv */
+#define DSPFW_CURSORA_SHIFT 8
+#define DSPFW_CURSORA_MASK (0x3f<<8)
+#define DSPFW_PLANEC_SHIFT_OLD 0
+#define DSPFW_PLANEC_MASK_OLD (0x7f<<0) /* pre-gen4 sprite C */
+#define DSPFW_SPRITEA_SHIFT 0
+#define DSPFW_SPRITEA_MASK (0x7f<<0) /* g4x */
+#define DSPFW_SPRITEA_MASK_VLV (0xff<<0) /* vlv/chv */
#define DSPFW3 (dev_priv->info.display_mmio_offset + 0x7003c)
-#define DSPFW_HPLL_SR_EN (1<<31)
-#define DSPFW_CURSOR_SR_SHIFT 24
+#define DSPFW_HPLL_SR_EN (1<<31)
#define PINEVIEW_SELF_REFRESH_EN (1<<30)
+#define DSPFW_CURSOR_SR_SHIFT 24
#define DSPFW_CURSOR_SR_MASK (0x3f<<24)
#define DSPFW_HPLL_CURSOR_SHIFT 16
#define DSPFW_HPLL_CURSOR_MASK (0x3f<<16)
-#define DSPFW_HPLL_SR_MASK (0x1ff)
-#define DSPFW4 (dev_priv->info.display_mmio_offset + 0x70070)
-#define DSPFW7 (dev_priv->info.display_mmio_offset + 0x7007c)
+#define DSPFW_HPLL_SR_SHIFT 0
+#define DSPFW_HPLL_SR_MASK (0x1ff<<0)
+
+/* vlv/chv */
+#define DSPFW4 (VLV_DISPLAY_BASE + 0x70070)
+#define DSPFW_SPRITEB_WM1_SHIFT 16
+#define DSPFW_SPRITEB_WM1_MASK (0xff<<16)
+#define DSPFW_CURSORA_WM1_SHIFT 8
+#define DSPFW_CURSORA_WM1_MASK (0x3f<<8)
+#define DSPFW_SPRITEA_WM1_SHIFT 0
+#define DSPFW_SPRITEA_WM1_MASK (0xff<<0)
+#define DSPFW5 (VLV_DISPLAY_BASE + 0x70074)
+#define DSPFW_PLANEB_WM1_SHIFT 24
+#define DSPFW_PLANEB_WM1_MASK (0xff<<24)
+#define DSPFW_PLANEA_WM1_SHIFT 16
+#define DSPFW_PLANEA_WM1_MASK (0xff<<16)
+#define DSPFW_CURSORB_WM1_SHIFT 8
+#define DSPFW_CURSORB_WM1_MASK (0x3f<<8)
+#define DSPFW_CURSOR_SR_WM1_SHIFT 0
+#define DSPFW_CURSOR_SR_WM1_MASK (0x3f<<0)
+#define DSPFW6 (VLV_DISPLAY_BASE + 0x70078)
+#define DSPFW_SR_WM1_SHIFT 0
+#define DSPFW_SR_WM1_MASK (0x1ff<<0)
+#define DSPFW7 (VLV_DISPLAY_BASE + 0x7007c)
+#define DSPFW7_CHV (VLV_DISPLAY_BASE + 0x700b4) /* wtf #1? */
+#define DSPFW_SPRITED_WM1_SHIFT 24
+#define DSPFW_SPRITED_WM1_MASK (0xff<<24)
+#define DSPFW_SPRITED_SHIFT 16
+#define DSPFW_SPRITED_MASK (0xff<<16)
+#define DSPFW_SPRITEC_WM1_SHIFT 8
+#define DSPFW_SPRITEC_WM1_MASK (0xff<<8)
+#define DSPFW_SPRITEC_SHIFT 0
+#define DSPFW_SPRITEC_MASK (0xff<<0)
+#define DSPFW8_CHV (VLV_DISPLAY_BASE + 0x700b8)
+#define DSPFW_SPRITEF_WM1_SHIFT 24
+#define DSPFW_SPRITEF_WM1_MASK (0xff<<24)
+#define DSPFW_SPRITEF_SHIFT 16
+#define DSPFW_SPRITEF_MASK (0xff<<16)
+#define DSPFW_SPRITEE_WM1_SHIFT 8
+#define DSPFW_SPRITEE_WM1_MASK (0xff<<8)
+#define DSPFW_SPRITEE_SHIFT 0
+#define DSPFW_SPRITEE_MASK (0xff<<0)
+#define DSPFW9_CHV (VLV_DISPLAY_BASE + 0x7007c) /* wtf #2? */
+#define DSPFW_PLANEC_WM1_SHIFT 24
+#define DSPFW_PLANEC_WM1_MASK (0xff<<24)
+#define DSPFW_PLANEC_SHIFT 16
+#define DSPFW_PLANEC_MASK (0xff<<16)
+#define DSPFW_CURSORC_WM1_SHIFT 8
+#define DSPFW_CURSORC_WM1_MASK (0x3f<<16)
+#define DSPFW_CURSORC_SHIFT 0
+#define DSPFW_CURSORC_MASK (0x3f<<0)
+
+/* vlv/chv high order bits */
+#define DSPHOWM (VLV_DISPLAY_BASE + 0x70064)
+#define DSPFW_SR_HI_SHIFT 24
+#define DSPFW_SR_HI_MASK (1<<24)
+#define DSPFW_SPRITEF_HI_SHIFT 23
+#define DSPFW_SPRITEF_HI_MASK (1<<23)
+#define DSPFW_SPRITEE_HI_SHIFT 22
+#define DSPFW_SPRITEE_HI_MASK (1<<22)
+#define DSPFW_PLANEC_HI_SHIFT 21
+#define DSPFW_PLANEC_HI_MASK (1<<21)
+#define DSPFW_SPRITED_HI_SHIFT 20
+#define DSPFW_SPRITED_HI_MASK (1<<20)
+#define DSPFW_SPRITEC_HI_SHIFT 16
+#define DSPFW_SPRITEC_HI_MASK (1<<16)
+#define DSPFW_PLANEB_HI_SHIFT 12
+#define DSPFW_PLANEB_HI_MASK (1<<12)
+#define DSPFW_SPRITEB_HI_SHIFT 8
+#define DSPFW_SPRITEB_HI_MASK (1<<8)
+#define DSPFW_SPRITEA_HI_SHIFT 4
+#define DSPFW_SPRITEA_HI_MASK (1<<4)
+#define DSPFW_PLANEA_HI_SHIFT 0
+#define DSPFW_PLANEA_HI_MASK (1<<0)
+#define DSPHOWM1 (VLV_DISPLAY_BASE + 0x70068)
+#define DSPFW_SR_WM1_HI_SHIFT 24
+#define DSPFW_SR_WM1_HI_MASK (1<<24)
+#define DSPFW_SPRITEF_WM1_HI_SHIFT 23
+#define DSPFW_SPRITEF_WM1_HI_MASK (1<<23)
+#define DSPFW_SPRITEE_WM1_HI_SHIFT 22
+#define DSPFW_SPRITEE_WM1_HI_MASK (1<<22)
+#define DSPFW_PLANEC_WM1_HI_SHIFT 21
+#define DSPFW_PLANEC_WM1_HI_MASK (1<<21)
+#define DSPFW_SPRITED_WM1_HI_SHIFT 20
+#define DSPFW_SPRITED_WM1_HI_MASK (1<<20)
+#define DSPFW_SPRITEC_WM1_HI_SHIFT 16
+#define DSPFW_SPRITEC_WM1_HI_MASK (1<<16)
+#define DSPFW_PLANEB_WM1_HI_SHIFT 12
+#define DSPFW_PLANEB_WM1_HI_MASK (1<<12)
+#define DSPFW_SPRITEB_WM1_HI_SHIFT 8
+#define DSPFW_SPRITEB_WM1_HI_MASK (1<<8)
+#define DSPFW_SPRITEA_WM1_HI_SHIFT 4
+#define DSPFW_SPRITEA_WM1_HI_MASK (1<<4)
+#define DSPFW_PLANEA_WM1_HI_SHIFT 0
+#define DSPFW_PLANEA_WM1_HI_MASK (1<<0)
/* drain latency register values*/
#define DRAIN_LATENCY_PRECISION_32 32
-#define DRAIN_LATENCY_PRECISION_16 16
-#define VLV_DDL1 (VLV_DISPLAY_BASE + 0x70050)
-#define DDL_CURSORA_PRECISION_32 (1<<31)
-#define DDL_CURSORA_PRECISION_16 (0<<31)
-#define DDL_CURSORA_SHIFT 24
-#define DDL_SPRITEB_PRECISION_32 (1<<23)
-#define DDL_SPRITEB_PRECISION_16 (0<<23)
-#define DDL_SPRITEB_SHIFT 16
-#define DDL_SPRITEA_PRECISION_32 (1<<15)
-#define DDL_SPRITEA_PRECISION_16 (0<<15)
-#define DDL_SPRITEA_SHIFT 8
-#define DDL_PLANEA_PRECISION_32 (1<<7)
-#define DDL_PLANEA_PRECISION_16 (0<<7)
-#define DDL_PLANEA_SHIFT 0
-
-#define VLV_DDL2 (VLV_DISPLAY_BASE + 0x70054)
-#define DDL_CURSORB_PRECISION_32 (1<<31)
-#define DDL_CURSORB_PRECISION_16 (0<<31)
-#define DDL_CURSORB_SHIFT 24
-#define DDL_SPRITED_PRECISION_32 (1<<23)
-#define DDL_SPRITED_PRECISION_16 (0<<23)
-#define DDL_SPRITED_SHIFT 16
-#define DDL_SPRITEC_PRECISION_32 (1<<15)
-#define DDL_SPRITEC_PRECISION_16 (0<<15)
-#define DDL_SPRITEC_SHIFT 8
-#define DDL_PLANEB_PRECISION_32 (1<<7)
-#define DDL_PLANEB_PRECISION_16 (0<<7)
-#define DDL_PLANEB_SHIFT 0
-
-#define VLV_DDL3 (VLV_DISPLAY_BASE + 0x70058)
-#define DDL_CURSORC_PRECISION_32 (1<<31)
-#define DDL_CURSORC_PRECISION_16 (0<<31)
-#define DDL_CURSORC_SHIFT 24
-#define DDL_SPRITEF_PRECISION_32 (1<<23)
-#define DDL_SPRITEF_PRECISION_16 (0<<23)
-#define DDL_SPRITEF_SHIFT 16
-#define DDL_SPRITEE_PRECISION_32 (1<<15)
-#define DDL_SPRITEE_PRECISION_16 (0<<15)
-#define DDL_SPRITEE_SHIFT 8
-#define DDL_PLANEC_PRECISION_32 (1<<7)
-#define DDL_PLANEC_PRECISION_16 (0<<7)
-#define DDL_PLANEC_SHIFT 0
+#define DRAIN_LATENCY_PRECISION_64 64
+#define VLV_DDL(pipe) (VLV_DISPLAY_BASE + 0x70050 + 4 * (pipe))
+#define DDL_CURSOR_PRECISION_64 (1<<31)
+#define DDL_CURSOR_PRECISION_32 (0<<31)
+#define DDL_CURSOR_SHIFT 24
+#define DDL_SPRITE_PRECISION_64(sprite) (1<<(15+8*(sprite)))
+#define DDL_SPRITE_PRECISION_32(sprite) (0<<(15+8*(sprite)))
+#define DDL_SPRITE_SHIFT(sprite) (8+8*(sprite))
+#define DDL_PLANE_PRECISION_64 (1<<7)
+#define DDL_PLANE_PRECISION_32 (0<<7)
+#define DDL_PLANE_SHIFT 0
+#define DRAIN_LATENCY_MASK 0x7f
/* FIFO watermark sizes etc */
#define G4X_FIFO_LINE_SIZE 64
@@ -3962,7 +4154,8 @@ enum punit_power_well {
/* Old style CUR*CNTR flags (desktop 8xx) */
#define CURSOR_ENABLE 0x80000000
#define CURSOR_GAMMA_ENABLE 0x40000000
-#define CURSOR_STRIDE_MASK 0x30000000
+#define CURSOR_STRIDE_SHIFT 28
+#define CURSOR_STRIDE(x) ((ffs(x)-9) << CURSOR_STRIDE_SHIFT) /* 256,512,1k,2k */
#define CURSOR_PIPE_CSC_ENABLE (1<<24)
#define CURSOR_FORMAT_SHIFT 24
#define CURSOR_FORMAT_MASK (0x07 << CURSOR_FORMAT_SHIFT)
@@ -4047,6 +4240,7 @@ enum punit_power_well {
#define DISPPLANE_NO_LINE_DOUBLE 0
#define DISPPLANE_STEREO_POLARITY_FIRST 0
#define DISPPLANE_STEREO_POLARITY_SECOND (1<<18)
+#define DISPPLANE_ROTATE_180 (1<<15)
#define DISPPLANE_TRICKLE_FEED_DISABLE (1<<14) /* Ironlake */
#define DISPPLANE_TILED (1<<10)
#define _DSPAADDR 0x70184
@@ -4131,6 +4325,7 @@ enum punit_power_well {
#define DVS_YUV_ORDER_UYVY (1<<16)
#define DVS_YUV_ORDER_YVYU (2<<16)
#define DVS_YUV_ORDER_VYUY (3<<16)
+#define DVS_ROTATE_180 (1<<15)
#define DVS_DEST_KEY (1<<2)
#define DVS_TRICKLE_FEED_DISABLE (1<<14)
#define DVS_TILED (1<<10)
@@ -4201,6 +4396,7 @@ enum punit_power_well {
#define SPRITE_YUV_ORDER_UYVY (1<<16)
#define SPRITE_YUV_ORDER_YVYU (2<<16)
#define SPRITE_YUV_ORDER_VYUY (3<<16)
+#define SPRITE_ROTATE_180 (1<<15)
#define SPRITE_TRICKLE_FEED_DISABLE (1<<14)
#define SPRITE_INT_GAMMA_ENABLE (1<<13)
#define SPRITE_TILED (1<<10)
@@ -4274,6 +4470,7 @@ enum punit_power_well {
#define SP_YUV_ORDER_UYVY (1<<16)
#define SP_YUV_ORDER_YVYU (2<<16)
#define SP_YUV_ORDER_VYUY (3<<16)
+#define SP_ROTATE_180 (1<<15)
#define SP_TILED (1<<10)
#define _SPALINOFF (VLV_DISPLAY_BASE + 0x72184)
#define _SPASTRIDE (VLV_DISPLAY_BASE + 0x72188)
@@ -4630,6 +4827,8 @@ enum punit_power_well {
#define GEN7_L3CNTLREG1 0xB01C
#define GEN7_WA_FOR_GEN7_L3_CONTROL 0x3C47FF8C
#define GEN7_L3AGDIS (1<<19)
+#define GEN7_L3CNTLREG2 0xB020
+#define GEN7_L3CNTLREG3 0xB024
#define GEN7_L3_CHICKEN_MODE_REGISTER 0xB030
#define GEN7_WA_L3_CHICKEN_MODE 0x20000000
@@ -4876,8 +5075,7 @@ enum punit_power_well {
#define _PCH_TRANSA_LINK_M2 0xe0048
#define _PCH_TRANSA_LINK_N2 0xe004c
-/* Per-transcoder DIP controls */
-
+/* Per-transcoder DIP controls (PCH) */
#define _VIDEO_DIP_CTL_A 0xe0200
#define _VIDEO_DIP_DATA_A 0xe0208
#define _VIDEO_DIP_GCP_A 0xe0210
@@ -4890,6 +5088,7 @@ enum punit_power_well {
#define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B)
#define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B)
+/* Per-transcoder DIP controls (VLV) */
#define VLV_VIDEO_DIP_CTL_A (VLV_DISPLAY_BASE + 0x60200)
#define VLV_VIDEO_DIP_DATA_A (VLV_DISPLAY_BASE + 0x60208)
#define VLV_VIDEO_DIP_GDCP_PAYLOAD_A (VLV_DISPLAY_BASE + 0x60210)
@@ -4898,12 +5097,19 @@ enum punit_power_well {
#define VLV_VIDEO_DIP_DATA_B (VLV_DISPLAY_BASE + 0x61174)
#define VLV_VIDEO_DIP_GDCP_PAYLOAD_B (VLV_DISPLAY_BASE + 0x61178)
+#define CHV_VIDEO_DIP_CTL_C (VLV_DISPLAY_BASE + 0x611f0)
+#define CHV_VIDEO_DIP_DATA_C (VLV_DISPLAY_BASE + 0x611f4)
+#define CHV_VIDEO_DIP_GDCP_PAYLOAD_C (VLV_DISPLAY_BASE + 0x611f8)
+
#define VLV_TVIDEO_DIP_CTL(pipe) \
- _PIPE(pipe, VLV_VIDEO_DIP_CTL_A, VLV_VIDEO_DIP_CTL_B)
+ _PIPE3((pipe), VLV_VIDEO_DIP_CTL_A, \
+ VLV_VIDEO_DIP_CTL_B, CHV_VIDEO_DIP_CTL_C)
#define VLV_TVIDEO_DIP_DATA(pipe) \
- _PIPE(pipe, VLV_VIDEO_DIP_DATA_A, VLV_VIDEO_DIP_DATA_B)
+ _PIPE3((pipe), VLV_VIDEO_DIP_DATA_A, \
+ VLV_VIDEO_DIP_DATA_B, CHV_VIDEO_DIP_DATA_C)
#define VLV_TVIDEO_DIP_GCP(pipe) \
- _PIPE(pipe, VLV_VIDEO_DIP_GDCP_PAYLOAD_A, VLV_VIDEO_DIP_GDCP_PAYLOAD_B)
+ _PIPE3((pipe), VLV_VIDEO_DIP_GDCP_PAYLOAD_A, \
+ VLV_VIDEO_DIP_GDCP_PAYLOAD_B, CHV_VIDEO_DIP_GDCP_PAYLOAD_C)
/* Haswell DIP controls */
#define HSW_VIDEO_DIP_CTL_A 0x60200
@@ -5173,8 +5379,7 @@ enum punit_power_well {
#define PIPEA_PP_STATUS (VLV_DISPLAY_BASE + 0x61200)
#define PIPEA_PP_CONTROL (VLV_DISPLAY_BASE + 0x61204)
#define PIPEA_PP_ON_DELAYS (VLV_DISPLAY_BASE + 0x61208)
-#define PANEL_PORT_SELECT_DPB_VLV (1 << 30)
-#define PANEL_PORT_SELECT_DPC_VLV (2 << 30)
+#define PANEL_PORT_SELECT_VLV(port) ((port) << 30)
#define PIPEA_PP_OFF_DELAYS (VLV_DISPLAY_BASE + 0x6120c)
#define PIPEA_PP_DIVISOR (VLV_DISPLAY_BASE + 0x61210)
@@ -5481,6 +5686,8 @@ enum punit_power_well {
#define GEN6_GT_GFX_RC6_LOCKED 0x138104
#define VLV_COUNTER_CONTROL 0x138104
#define VLV_COUNT_RANGE_HIGH (1<<15)
+#define VLV_MEDIA_RC0_COUNT_EN (1<<5)
+#define VLV_RENDER_RC0_COUNT_EN (1<<4)
#define VLV_MEDIA_RC6_COUNT_EN (1<<1)
#define VLV_RENDER_RC6_COUNT_EN (1<<0)
#define GEN6_GT_GFX_RC6 0x138108
@@ -5489,6 +5696,8 @@ enum punit_power_well {
#define GEN6_GT_GFX_RC6p 0x13810C
#define GEN6_GT_GFX_RC6pp 0x138110
+#define VLV_RENDER_C0_COUNT_REG 0x138118
+#define VLV_MEDIA_C0_COUNT_REG 0x13811C
#define GEN6_PCODE_MAILBOX 0x138124
#define GEN6_PCODE_READY (1<<31)
@@ -5723,6 +5932,7 @@ enum punit_power_well {
#define TRANS_DDI_FUNC_ENABLE (1<<31)
/* Those bits are ignored by pipe EDP since it can only connect to DDI A */
#define TRANS_DDI_PORT_MASK (7<<28)
+#define TRANS_DDI_PORT_SHIFT 28
#define TRANS_DDI_SELECT_PORT(x) ((x)<<28)
#define TRANS_DDI_PORT_NONE (0<<28)
#define TRANS_DDI_MODE_SELECT_MASK (7<<24)
@@ -5743,6 +5953,7 @@ enum punit_power_well {
#define TRANS_DDI_EDP_INPUT_A_ONOFF (4<<12)
#define TRANS_DDI_EDP_INPUT_B_ONOFF (5<<12)
#define TRANS_DDI_EDP_INPUT_C_ONOFF (6<<12)
+#define TRANS_DDI_DP_VC_PAYLOAD_ALLOC (1<<8)
#define TRANS_DDI_BFI_ENABLE (1<<4)
/* DisplayPort Transport Control */
@@ -5752,6 +5963,7 @@ enum punit_power_well {
#define DP_TP_CTL_ENABLE (1<<31)
#define DP_TP_CTL_MODE_SST (0<<27)
#define DP_TP_CTL_MODE_MST (1<<27)
+#define DP_TP_CTL_FORCE_ACT (1<<25)
#define DP_TP_CTL_ENHANCED_FRAME_ENABLE (1<<18)
#define DP_TP_CTL_FDI_AUTOTRAIN (1<<15)
#define DP_TP_CTL_LINK_TRAIN_MASK (7<<8)
@@ -5766,34 +5978,20 @@ enum punit_power_well {
#define DP_TP_STATUS_A 0x64044
#define DP_TP_STATUS_B 0x64144
#define DP_TP_STATUS(port) _PORT(port, DP_TP_STATUS_A, DP_TP_STATUS_B)
-#define DP_TP_STATUS_IDLE_DONE (1<<25)
-#define DP_TP_STATUS_AUTOTRAIN_DONE (1<<12)
+#define DP_TP_STATUS_IDLE_DONE (1<<25)
+#define DP_TP_STATUS_ACT_SENT (1<<24)
+#define DP_TP_STATUS_MODE_STATUS_MST (1<<23)
+#define DP_TP_STATUS_AUTOTRAIN_DONE (1<<12)
+#define DP_TP_STATUS_PAYLOAD_MAPPING_VC2 (3 << 8)
+#define DP_TP_STATUS_PAYLOAD_MAPPING_VC1 (3 << 4)
+#define DP_TP_STATUS_PAYLOAD_MAPPING_VC0 (3 << 0)
/* DDI Buffer Control */
#define DDI_BUF_CTL_A 0x64000
#define DDI_BUF_CTL_B 0x64100
#define DDI_BUF_CTL(port) _PORT(port, DDI_BUF_CTL_A, DDI_BUF_CTL_B)
#define DDI_BUF_CTL_ENABLE (1<<31)
-/* Haswell */
-#define DDI_BUF_EMP_400MV_0DB_HSW (0<<24) /* Sel0 */
-#define DDI_BUF_EMP_400MV_3_5DB_HSW (1<<24) /* Sel1 */
-#define DDI_BUF_EMP_400MV_6DB_HSW (2<<24) /* Sel2 */
-#define DDI_BUF_EMP_400MV_9_5DB_HSW (3<<24) /* Sel3 */
-#define DDI_BUF_EMP_600MV_0DB_HSW (4<<24) /* Sel4 */
-#define DDI_BUF_EMP_600MV_3_5DB_HSW (5<<24) /* Sel5 */
-#define DDI_BUF_EMP_600MV_6DB_HSW (6<<24) /* Sel6 */
-#define DDI_BUF_EMP_800MV_0DB_HSW (7<<24) /* Sel7 */
-#define DDI_BUF_EMP_800MV_3_5DB_HSW (8<<24) /* Sel8 */
-/* Broadwell */
-#define DDI_BUF_EMP_400MV_0DB_BDW (0<<24) /* Sel0 */
-#define DDI_BUF_EMP_400MV_3_5DB_BDW (1<<24) /* Sel1 */
-#define DDI_BUF_EMP_400MV_6DB_BDW (2<<24) /* Sel2 */
-#define DDI_BUF_EMP_600MV_0DB_BDW (3<<24) /* Sel3 */
-#define DDI_BUF_EMP_600MV_3_5DB_BDW (4<<24) /* Sel4 */
-#define DDI_BUF_EMP_600MV_6DB_BDW (5<<24) /* Sel5 */
-#define DDI_BUF_EMP_800MV_0DB_BDW (6<<24) /* Sel6 */
-#define DDI_BUF_EMP_800MV_3_5DB_BDW (7<<24) /* Sel7 */
-#define DDI_BUF_EMP_1200MV_0DB_BDW (8<<24) /* Sel8 */
+#define DDI_BUF_TRANS_SELECT(n) ((n) << 24)
#define DDI_BUF_EMP_MASK (0xf<<24)
#define DDI_BUF_PORT_REVERSAL (1<<16)
#define DDI_BUF_IS_IDLE (1<<7)
@@ -5861,10 +6059,12 @@ enum punit_power_well {
/* WRPLL */
#define WRPLL_CTL1 0x46040
#define WRPLL_CTL2 0x46060
+#define WRPLL_CTL(pll) (pll == 0 ? WRPLL_CTL1 : WRPLL_CTL2)
#define WRPLL_PLL_ENABLE (1<<31)
-#define WRPLL_PLL_SELECT_SSC (0x01<<28)
-#define WRPLL_PLL_SELECT_NON_SSC (0x02<<28)
-#define WRPLL_PLL_SELECT_LCPLL_2700 (0x03<<28)
+#define WRPLL_PLL_SSC (1<<28)
+#define WRPLL_PLL_NON_SSC (2<<28)
+#define WRPLL_PLL_LCPLL (3<<28)
+#define WRPLL_PLL_REF_MASK (3<<28)
/* WRPLL divider programming */
#define WRPLL_DIVIDER_REFERENCE(x) ((x)<<0)
#define WRPLL_DIVIDER_REF_MASK (0xff)
@@ -5883,6 +6083,7 @@ enum punit_power_well {
#define PORT_CLK_SEL_LCPLL_1350 (1<<29)
#define PORT_CLK_SEL_LCPLL_810 (2<<29)
#define PORT_CLK_SEL_SPLL (3<<29)
+#define PORT_CLK_SEL_WRPLL(pll) (((pll)+4)<<29)
#define PORT_CLK_SEL_WRPLL1 (4<<29)
#define PORT_CLK_SEL_WRPLL2 (5<<29)
#define PORT_CLK_SEL_NONE (7<<29)
@@ -5924,7 +6125,10 @@ enum punit_power_well {
#define LCPLL_CD_SOURCE_FCLK (1<<21)
#define LCPLL_CD_SOURCE_FCLK_DONE (1<<19)
-#define D_COMP (MCHBAR_MIRROR_BASE_SNB + 0x5F0C)
+/* Please see hsw_read_dcomp() and hsw_write_dcomp() before using this register,
+ * since on HSW we can't write to it using I915_WRITE. */
+#define D_COMP_HSW (MCHBAR_MIRROR_BASE_SNB + 0x5F0C)
+#define D_COMP_BDW 0x138144
#define D_COMP_RCOMP_IN_PROGRESS (1<<9)
#define D_COMP_COMP_FORCE (1<<8)
#define D_COMP_COMP_DISABLE (1<<0)
@@ -6005,7 +6209,8 @@ enum punit_power_well {
#define _MIPIA_PORT_CTRL (VLV_DISPLAY_BASE + 0x61190)
#define _MIPIB_PORT_CTRL (VLV_DISPLAY_BASE + 0x61700)
-#define MIPI_PORT_CTRL(pipe) _PIPE(pipe, _MIPIA_PORT_CTRL, _MIPIB_PORT_CTRL)
+#define MIPI_PORT_CTRL(tc) _TRANSCODER(tc, _MIPIA_PORT_CTRL, \
+ _MIPIB_PORT_CTRL)
#define DPI_ENABLE (1 << 31) /* A + B */
#define MIPIA_MIPI4DPHY_DELAY_COUNT_SHIFT 27
#define MIPIA_MIPI4DPHY_DELAY_COUNT_MASK (0xf << 27)
@@ -6047,18 +6252,20 @@ enum punit_power_well {
#define _MIPIA_TEARING_CTRL (VLV_DISPLAY_BASE + 0x61194)
#define _MIPIB_TEARING_CTRL (VLV_DISPLAY_BASE + 0x61704)
-#define MIPI_TEARING_CTRL(pipe) _PIPE(pipe, _MIPIA_TEARING_CTRL, _MIPIB_TEARING_CTRL)
+#define MIPI_TEARING_CTRL(tc) _TRANSCODER(tc, \
+ _MIPIA_TEARING_CTRL, _MIPIB_TEARING_CTRL)
#define TEARING_EFFECT_DELAY_SHIFT 0
#define TEARING_EFFECT_DELAY_MASK (0xffff << 0)
/* XXX: all bits reserved */
-#define _MIPIA_AUTOPWG (VLV_DISPLAY_BASE + 0x611a0)
+#define _MIPIA_AUTOPWG (VLV_DISPLAY_BASE + 0x611a0)
/* MIPI DSI Controller and D-PHY registers */
-#define _MIPIA_DEVICE_READY (VLV_DISPLAY_BASE + 0xb000)
-#define _MIPIB_DEVICE_READY (VLV_DISPLAY_BASE + 0xb800)
-#define MIPI_DEVICE_READY(pipe) _PIPE(pipe, _MIPIA_DEVICE_READY, _MIPIB_DEVICE_READY)
+#define _MIPIA_DEVICE_READY (dev_priv->mipi_mmio_base + 0xb000)
+#define _MIPIB_DEVICE_READY (dev_priv->mipi_mmio_base + 0xb800)
+#define MIPI_DEVICE_READY(tc) _TRANSCODER(tc, _MIPIA_DEVICE_READY, \
+ _MIPIB_DEVICE_READY)
#define BUS_POSSESSION (1 << 3) /* set to give bus to receiver */
#define ULPS_STATE_MASK (3 << 1)
#define ULPS_STATE_ENTER (2 << 1)
@@ -6066,12 +6273,14 @@ enum punit_power_well {
#define ULPS_STATE_NORMAL_OPERATION (0 << 1)
#define DEVICE_READY (1 << 0)
-#define _MIPIA_INTR_STAT (VLV_DISPLAY_BASE + 0xb004)
-#define _MIPIB_INTR_STAT (VLV_DISPLAY_BASE + 0xb804)
-#define MIPI_INTR_STAT(pipe) _PIPE(pipe, _MIPIA_INTR_STAT, _MIPIB_INTR_STAT)
-#define _MIPIA_INTR_EN (VLV_DISPLAY_BASE + 0xb008)
-#define _MIPIB_INTR_EN (VLV_DISPLAY_BASE + 0xb808)
-#define MIPI_INTR_EN(pipe) _PIPE(pipe, _MIPIA_INTR_EN, _MIPIB_INTR_EN)
+#define _MIPIA_INTR_STAT (dev_priv->mipi_mmio_base + 0xb004)
+#define _MIPIB_INTR_STAT (dev_priv->mipi_mmio_base + 0xb804)
+#define MIPI_INTR_STAT(tc) _TRANSCODER(tc, _MIPIA_INTR_STAT, \
+ _MIPIB_INTR_STAT)
+#define _MIPIA_INTR_EN (dev_priv->mipi_mmio_base + 0xb008)
+#define _MIPIB_INTR_EN (dev_priv->mipi_mmio_base + 0xb808)
+#define MIPI_INTR_EN(tc) _TRANSCODER(tc, _MIPIA_INTR_EN, \
+ _MIPIB_INTR_EN)
#define TEARING_EFFECT (1 << 31)
#define SPL_PKT_SENT_INTERRUPT (1 << 30)
#define GEN_READ_DATA_AVAIL (1 << 29)
@@ -6105,9 +6314,10 @@ enum punit_power_well {
#define RXSOT_SYNC_ERROR (1 << 1)
#define RXSOT_ERROR (1 << 0)
-#define _MIPIA_DSI_FUNC_PRG (VLV_DISPLAY_BASE + 0xb00c)
-#define _MIPIB_DSI_FUNC_PRG (VLV_DISPLAY_BASE + 0xb80c)
-#define MIPI_DSI_FUNC_PRG(pipe) _PIPE(pipe, _MIPIA_DSI_FUNC_PRG, _MIPIB_DSI_FUNC_PRG)
+#define _MIPIA_DSI_FUNC_PRG (dev_priv->mipi_mmio_base + 0xb00c)
+#define _MIPIB_DSI_FUNC_PRG (dev_priv->mipi_mmio_base + 0xb80c)
+#define MIPI_DSI_FUNC_PRG(tc) _TRANSCODER(tc, _MIPIA_DSI_FUNC_PRG, \
+ _MIPIB_DSI_FUNC_PRG)
#define CMD_MODE_DATA_WIDTH_MASK (7 << 13)
#define CMD_MODE_NOT_SUPPORTED (0 << 13)
#define CMD_MODE_DATA_WIDTH_16_BIT (1 << 13)
@@ -6128,78 +6338,94 @@ enum punit_power_well {
#define DATA_LANES_PRG_REG_SHIFT 0
#define DATA_LANES_PRG_REG_MASK (7 << 0)
-#define _MIPIA_HS_TX_TIMEOUT (VLV_DISPLAY_BASE + 0xb010)
-#define _MIPIB_HS_TX_TIMEOUT (VLV_DISPLAY_BASE + 0xb810)
-#define MIPI_HS_TX_TIMEOUT(pipe) _PIPE(pipe, _MIPIA_HS_TX_TIMEOUT, _MIPIB_HS_TX_TIMEOUT)
+#define _MIPIA_HS_TX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb010)
+#define _MIPIB_HS_TX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb810)
+#define MIPI_HS_TX_TIMEOUT(tc) _TRANSCODER(tc, _MIPIA_HS_TX_TIMEOUT, \
+ _MIPIB_HS_TX_TIMEOUT)
#define HIGH_SPEED_TX_TIMEOUT_COUNTER_MASK 0xffffff
-#define _MIPIA_LP_RX_TIMEOUT (VLV_DISPLAY_BASE + 0xb014)
-#define _MIPIB_LP_RX_TIMEOUT (VLV_DISPLAY_BASE + 0xb814)
-#define MIPI_LP_RX_TIMEOUT(pipe) _PIPE(pipe, _MIPIA_LP_RX_TIMEOUT, _MIPIB_LP_RX_TIMEOUT)
+#define _MIPIA_LP_RX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb014)
+#define _MIPIB_LP_RX_TIMEOUT (dev_priv->mipi_mmio_base + 0xb814)
+#define MIPI_LP_RX_TIMEOUT(tc) _TRANSCODER(tc, _MIPIA_LP_RX_TIMEOUT, \
+ _MIPIB_LP_RX_TIMEOUT)
#define LOW_POWER_RX_TIMEOUT_COUNTER_MASK 0xffffff
-#define _MIPIA_TURN_AROUND_TIMEOUT (VLV_DISPLAY_BASE + 0xb018)
-#define _MIPIB_TURN_AROUND_TIMEOUT (VLV_DISPLAY_BASE + 0xb818)
-#define MIPI_TURN_AROUND_TIMEOUT(pipe) _PIPE(pipe, _MIPIA_TURN_AROUND_TIMEOUT, _MIPIB_TURN_AROUND_TIMEOUT)
+#define _MIPIA_TURN_AROUND_TIMEOUT (dev_priv->mipi_mmio_base + 0xb018)
+#define _MIPIB_TURN_AROUND_TIMEOUT (dev_priv->mipi_mmio_base + 0xb818)
+#define MIPI_TURN_AROUND_TIMEOUT(tc) _TRANSCODER(tc, \
+ _MIPIA_TURN_AROUND_TIMEOUT, _MIPIB_TURN_AROUND_TIMEOUT)
#define TURN_AROUND_TIMEOUT_MASK 0x3f
-#define _MIPIA_DEVICE_RESET_TIMER (VLV_DISPLAY_BASE + 0xb01c)
-#define _MIPIB_DEVICE_RESET_TIMER (VLV_DISPLAY_BASE + 0xb81c)
-#define MIPI_DEVICE_RESET_TIMER(pipe) _PIPE(pipe, _MIPIA_DEVICE_RESET_TIMER, _MIPIB_DEVICE_RESET_TIMER)
+#define _MIPIA_DEVICE_RESET_TIMER (dev_priv->mipi_mmio_base + 0xb01c)
+#define _MIPIB_DEVICE_RESET_TIMER (dev_priv->mipi_mmio_base + 0xb81c)
+#define MIPI_DEVICE_RESET_TIMER(tc) _TRANSCODER(tc, \
+ _MIPIA_DEVICE_RESET_TIMER, _MIPIB_DEVICE_RESET_TIMER)
#define DEVICE_RESET_TIMER_MASK 0xffff
-#define _MIPIA_DPI_RESOLUTION (VLV_DISPLAY_BASE + 0xb020)
-#define _MIPIB_DPI_RESOLUTION (VLV_DISPLAY_BASE + 0xb820)
-#define MIPI_DPI_RESOLUTION(pipe) _PIPE(pipe, _MIPIA_DPI_RESOLUTION, _MIPIB_DPI_RESOLUTION)
+#define _MIPIA_DPI_RESOLUTION (dev_priv->mipi_mmio_base + 0xb020)
+#define _MIPIB_DPI_RESOLUTION (dev_priv->mipi_mmio_base + 0xb820)
+#define MIPI_DPI_RESOLUTION(tc) _TRANSCODER(tc, _MIPIA_DPI_RESOLUTION, \
+ _MIPIB_DPI_RESOLUTION)
#define VERTICAL_ADDRESS_SHIFT 16
#define VERTICAL_ADDRESS_MASK (0xffff << 16)
#define HORIZONTAL_ADDRESS_SHIFT 0
#define HORIZONTAL_ADDRESS_MASK 0xffff
-#define _MIPIA_DBI_FIFO_THROTTLE (VLV_DISPLAY_BASE + 0xb024)
-#define _MIPIB_DBI_FIFO_THROTTLE (VLV_DISPLAY_BASE + 0xb824)
-#define MIPI_DBI_FIFO_THROTTLE(pipe) _PIPE(pipe, _MIPIA_DBI_FIFO_THROTTLE, _MIPIB_DBI_FIFO_THROTTLE)
+#define _MIPIA_DBI_FIFO_THROTTLE (dev_priv->mipi_mmio_base + 0xb024)
+#define _MIPIB_DBI_FIFO_THROTTLE (dev_priv->mipi_mmio_base + 0xb824)
+#define MIPI_DBI_FIFO_THROTTLE(tc) _TRANSCODER(tc, \
+ _MIPIA_DBI_FIFO_THROTTLE, _MIPIB_DBI_FIFO_THROTTLE)
#define DBI_FIFO_EMPTY_HALF (0 << 0)
#define DBI_FIFO_EMPTY_QUARTER (1 << 0)
#define DBI_FIFO_EMPTY_7_LOCATIONS (2 << 0)
/* regs below are bits 15:0 */
-#define _MIPIA_HSYNC_PADDING_COUNT (VLV_DISPLAY_BASE + 0xb028)
-#define _MIPIB_HSYNC_PADDING_COUNT (VLV_DISPLAY_BASE + 0xb828)
-#define MIPI_HSYNC_PADDING_COUNT(pipe) _PIPE(pipe, _MIPIA_HSYNC_PADDING_COUNT, _MIPIB_HSYNC_PADDING_COUNT)
-
-#define _MIPIA_HBP_COUNT (VLV_DISPLAY_BASE + 0xb02c)
-#define _MIPIB_HBP_COUNT (VLV_DISPLAY_BASE + 0xb82c)
-#define MIPI_HBP_COUNT(pipe) _PIPE(pipe, _MIPIA_HBP_COUNT, _MIPIB_HBP_COUNT)
-
-#define _MIPIA_HFP_COUNT (VLV_DISPLAY_BASE + 0xb030)
-#define _MIPIB_HFP_COUNT (VLV_DISPLAY_BASE + 0xb830)
-#define MIPI_HFP_COUNT(pipe) _PIPE(pipe, _MIPIA_HFP_COUNT, _MIPIB_HFP_COUNT)
-
-#define _MIPIA_HACTIVE_AREA_COUNT (VLV_DISPLAY_BASE + 0xb034)
-#define _MIPIB_HACTIVE_AREA_COUNT (VLV_DISPLAY_BASE + 0xb834)
-#define MIPI_HACTIVE_AREA_COUNT(pipe) _PIPE(pipe, _MIPIA_HACTIVE_AREA_COUNT, _MIPIB_HACTIVE_AREA_COUNT)
-
-#define _MIPIA_VSYNC_PADDING_COUNT (VLV_DISPLAY_BASE + 0xb038)
-#define _MIPIB_VSYNC_PADDING_COUNT (VLV_DISPLAY_BASE + 0xb838)
-#define MIPI_VSYNC_PADDING_COUNT(pipe) _PIPE(pipe, _MIPIA_VSYNC_PADDING_COUNT, _MIPIB_VSYNC_PADDING_COUNT)
+#define _MIPIA_HSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb028)
+#define _MIPIB_HSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb828)
+#define MIPI_HSYNC_PADDING_COUNT(tc) _TRANSCODER(tc, \
+ _MIPIA_HSYNC_PADDING_COUNT, _MIPIB_HSYNC_PADDING_COUNT)
+
+#define _MIPIA_HBP_COUNT (dev_priv->mipi_mmio_base + 0xb02c)
+#define _MIPIB_HBP_COUNT (dev_priv->mipi_mmio_base + 0xb82c)
+#define MIPI_HBP_COUNT(tc) _TRANSCODER(tc, _MIPIA_HBP_COUNT, \
+ _MIPIB_HBP_COUNT)
+
+#define _MIPIA_HFP_COUNT (dev_priv->mipi_mmio_base + 0xb030)
+#define _MIPIB_HFP_COUNT (dev_priv->mipi_mmio_base + 0xb830)
+#define MIPI_HFP_COUNT(tc) _TRANSCODER(tc, _MIPIA_HFP_COUNT, \
+ _MIPIB_HFP_COUNT)
+
+#define _MIPIA_HACTIVE_AREA_COUNT (dev_priv->mipi_mmio_base + 0xb034)
+#define _MIPIB_HACTIVE_AREA_COUNT (dev_priv->mipi_mmio_base + 0xb834)
+#define MIPI_HACTIVE_AREA_COUNT(tc) _TRANSCODER(tc, \
+ _MIPIA_HACTIVE_AREA_COUNT, _MIPIB_HACTIVE_AREA_COUNT)
+
+#define _MIPIA_VSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb038)
+#define _MIPIB_VSYNC_PADDING_COUNT (dev_priv->mipi_mmio_base + 0xb838)
+#define MIPI_VSYNC_PADDING_COUNT(tc) _TRANSCODER(tc, \
+ _MIPIA_VSYNC_PADDING_COUNT, _MIPIB_VSYNC_PADDING_COUNT)
+
+#define _MIPIA_VBP_COUNT (dev_priv->mipi_mmio_base + 0xb03c)
+#define _MIPIB_VBP_COUNT (dev_priv->mipi_mmio_base + 0xb83c)
+#define MIPI_VBP_COUNT(tc) _TRANSCODER(tc, _MIPIA_VBP_COUNT, \
+ _MIPIB_VBP_COUNT)
+
+#define _MIPIA_VFP_COUNT (dev_priv->mipi_mmio_base + 0xb040)
+#define _MIPIB_VFP_COUNT (dev_priv->mipi_mmio_base + 0xb840)
+#define MIPI_VFP_COUNT(tc) _TRANSCODER(tc, _MIPIA_VFP_COUNT, \
+ _MIPIB_VFP_COUNT)
+
+#define _MIPIA_HIGH_LOW_SWITCH_COUNT (dev_priv->mipi_mmio_base + 0xb044)
+#define _MIPIB_HIGH_LOW_SWITCH_COUNT (dev_priv->mipi_mmio_base + 0xb844)
+#define MIPI_HIGH_LOW_SWITCH_COUNT(tc) _TRANSCODER(tc, \
+ _MIPIA_HIGH_LOW_SWITCH_COUNT, _MIPIB_HIGH_LOW_SWITCH_COUNT)
-#define _MIPIA_VBP_COUNT (VLV_DISPLAY_BASE + 0xb03c)
-#define _MIPIB_VBP_COUNT (VLV_DISPLAY_BASE + 0xb83c)
-#define MIPI_VBP_COUNT(pipe) _PIPE(pipe, _MIPIA_VBP_COUNT, _MIPIB_VBP_COUNT)
-
-#define _MIPIA_VFP_COUNT (VLV_DISPLAY_BASE + 0xb040)
-#define _MIPIB_VFP_COUNT (VLV_DISPLAY_BASE + 0xb840)
-#define MIPI_VFP_COUNT(pipe) _PIPE(pipe, _MIPIA_VFP_COUNT, _MIPIB_VFP_COUNT)
-
-#define _MIPIA_HIGH_LOW_SWITCH_COUNT (VLV_DISPLAY_BASE + 0xb044)
-#define _MIPIB_HIGH_LOW_SWITCH_COUNT (VLV_DISPLAY_BASE + 0xb844)
-#define MIPI_HIGH_LOW_SWITCH_COUNT(pipe) _PIPE(pipe, _MIPIA_HIGH_LOW_SWITCH_COUNT, _MIPIB_HIGH_LOW_SWITCH_COUNT)
/* regs above are bits 15:0 */
-#define _MIPIA_DPI_CONTROL (VLV_DISPLAY_BASE + 0xb048)
-#define _MIPIB_DPI_CONTROL (VLV_DISPLAY_BASE + 0xb848)
-#define MIPI_DPI_CONTROL(pipe) _PIPE(pipe, _MIPIA_DPI_CONTROL, _MIPIB_DPI_CONTROL)
+#define _MIPIA_DPI_CONTROL (dev_priv->mipi_mmio_base + 0xb048)
+#define _MIPIB_DPI_CONTROL (dev_priv->mipi_mmio_base + 0xb848)
+#define MIPI_DPI_CONTROL(tc) _TRANSCODER(tc, _MIPIA_DPI_CONTROL, \
+ _MIPIB_DPI_CONTROL)
#define DPI_LP_MODE (1 << 6)
#define BACKLIGHT_OFF (1 << 5)
#define BACKLIGHT_ON (1 << 4)
@@ -6208,27 +6434,31 @@ enum punit_power_well {
#define TURN_ON (1 << 1)
#define SHUTDOWN (1 << 0)
-#define _MIPIA_DPI_DATA (VLV_DISPLAY_BASE + 0xb04c)
-#define _MIPIB_DPI_DATA (VLV_DISPLAY_BASE + 0xb84c)
-#define MIPI_DPI_DATA(pipe) _PIPE(pipe, _MIPIA_DPI_DATA, _MIPIB_DPI_DATA)
+#define _MIPIA_DPI_DATA (dev_priv->mipi_mmio_base + 0xb04c)
+#define _MIPIB_DPI_DATA (dev_priv->mipi_mmio_base + 0xb84c)
+#define MIPI_DPI_DATA(tc) _TRANSCODER(tc, _MIPIA_DPI_DATA, \
+ _MIPIB_DPI_DATA)
#define COMMAND_BYTE_SHIFT 0
#define COMMAND_BYTE_MASK (0x3f << 0)
-#define _MIPIA_INIT_COUNT (VLV_DISPLAY_BASE + 0xb050)
-#define _MIPIB_INIT_COUNT (VLV_DISPLAY_BASE + 0xb850)
-#define MIPI_INIT_COUNT(pipe) _PIPE(pipe, _MIPIA_INIT_COUNT, _MIPIB_INIT_COUNT)
+#define _MIPIA_INIT_COUNT (dev_priv->mipi_mmio_base + 0xb050)
+#define _MIPIB_INIT_COUNT (dev_priv->mipi_mmio_base + 0xb850)
+#define MIPI_INIT_COUNT(tc) _TRANSCODER(tc, _MIPIA_INIT_COUNT, \
+ _MIPIB_INIT_COUNT)
#define MASTER_INIT_TIMER_SHIFT 0
#define MASTER_INIT_TIMER_MASK (0xffff << 0)
-#define _MIPIA_MAX_RETURN_PKT_SIZE (VLV_DISPLAY_BASE + 0xb054)
-#define _MIPIB_MAX_RETURN_PKT_SIZE (VLV_DISPLAY_BASE + 0xb854)
-#define MIPI_MAX_RETURN_PKT_SIZE(pipe) _PIPE(pipe, _MIPIA_MAX_RETURN_PKT_SIZE, _MIPIB_MAX_RETURN_PKT_SIZE)
+#define _MIPIA_MAX_RETURN_PKT_SIZE (dev_priv->mipi_mmio_base + 0xb054)
+#define _MIPIB_MAX_RETURN_PKT_SIZE (dev_priv->mipi_mmio_base + 0xb854)
+#define MIPI_MAX_RETURN_PKT_SIZE(tc) _TRANSCODER(tc, \
+ _MIPIA_MAX_RETURN_PKT_SIZE, _MIPIB_MAX_RETURN_PKT_SIZE)
#define MAX_RETURN_PKT_SIZE_SHIFT 0
#define MAX_RETURN_PKT_SIZE_MASK (0x3ff << 0)
-#define _MIPIA_VIDEO_MODE_FORMAT (VLV_DISPLAY_BASE + 0xb058)
-#define _MIPIB_VIDEO_MODE_FORMAT (VLV_DISPLAY_BASE + 0xb858)
-#define MIPI_VIDEO_MODE_FORMAT(pipe) _PIPE(pipe, _MIPIA_VIDEO_MODE_FORMAT, _MIPIB_VIDEO_MODE_FORMAT)
+#define _MIPIA_VIDEO_MODE_FORMAT (dev_priv->mipi_mmio_base + 0xb058)
+#define _MIPIB_VIDEO_MODE_FORMAT (dev_priv->mipi_mmio_base + 0xb858)
+#define MIPI_VIDEO_MODE_FORMAT(tc) _TRANSCODER(tc, \
+ _MIPIA_VIDEO_MODE_FORMAT, _MIPIB_VIDEO_MODE_FORMAT)
#define RANDOM_DPI_DISPLAY_RESOLUTION (1 << 4)
#define DISABLE_VIDEO_BTA (1 << 3)
#define IP_TG_CONFIG (1 << 2)
@@ -6236,9 +6466,10 @@ enum punit_power_well {
#define VIDEO_MODE_NON_BURST_WITH_SYNC_EVENTS (2 << 0)
#define VIDEO_MODE_BURST (3 << 0)
-#define _MIPIA_EOT_DISABLE (VLV_DISPLAY_BASE + 0xb05c)
-#define _MIPIB_EOT_DISABLE (VLV_DISPLAY_BASE + 0xb85c)
-#define MIPI_EOT_DISABLE(pipe) _PIPE(pipe, _MIPIA_EOT_DISABLE, _MIPIB_EOT_DISABLE)
+#define _MIPIA_EOT_DISABLE (dev_priv->mipi_mmio_base + 0xb05c)
+#define _MIPIB_EOT_DISABLE (dev_priv->mipi_mmio_base + 0xb85c)
+#define MIPI_EOT_DISABLE(tc) _TRANSCODER(tc, _MIPIA_EOT_DISABLE, \
+ _MIPIB_EOT_DISABLE)
#define LP_RX_TIMEOUT_ERROR_RECOVERY_DISABLE (1 << 7)
#define HS_RX_TIMEOUT_ERROR_RECOVERY_DISABLE (1 << 6)
#define LOW_CONTENTION_RECOVERY_DISABLE (1 << 5)
@@ -6248,28 +6479,33 @@ enum punit_power_well {
#define CLOCKSTOP (1 << 1)
#define EOT_DISABLE (1 << 0)
-#define _MIPIA_LP_BYTECLK (VLV_DISPLAY_BASE + 0xb060)
-#define _MIPIB_LP_BYTECLK (VLV_DISPLAY_BASE + 0xb860)
-#define MIPI_LP_BYTECLK(pipe) _PIPE(pipe, _MIPIA_LP_BYTECLK, _MIPIB_LP_BYTECLK)
+#define _MIPIA_LP_BYTECLK (dev_priv->mipi_mmio_base + 0xb060)
+#define _MIPIB_LP_BYTECLK (dev_priv->mipi_mmio_base + 0xb860)
+#define MIPI_LP_BYTECLK(tc) _TRANSCODER(tc, _MIPIA_LP_BYTECLK, \
+ _MIPIB_LP_BYTECLK)
#define LP_BYTECLK_SHIFT 0
#define LP_BYTECLK_MASK (0xffff << 0)
/* bits 31:0 */
-#define _MIPIA_LP_GEN_DATA (VLV_DISPLAY_BASE + 0xb064)
-#define _MIPIB_LP_GEN_DATA (VLV_DISPLAY_BASE + 0xb864)
-#define MIPI_LP_GEN_DATA(pipe) _PIPE(pipe, _MIPIA_LP_GEN_DATA, _MIPIB_LP_GEN_DATA)
+#define _MIPIA_LP_GEN_DATA (dev_priv->mipi_mmio_base + 0xb064)
+#define _MIPIB_LP_GEN_DATA (dev_priv->mipi_mmio_base + 0xb864)
+#define MIPI_LP_GEN_DATA(tc) _TRANSCODER(tc, _MIPIA_LP_GEN_DATA, \
+ _MIPIB_LP_GEN_DATA)
/* bits 31:0 */
-#define _MIPIA_HS_GEN_DATA (VLV_DISPLAY_BASE + 0xb068)
-#define _MIPIB_HS_GEN_DATA (VLV_DISPLAY_BASE + 0xb868)
-#define MIPI_HS_GEN_DATA(pipe) _PIPE(pipe, _MIPIA_HS_GEN_DATA, _MIPIB_HS_GEN_DATA)
-
-#define _MIPIA_LP_GEN_CTRL (VLV_DISPLAY_BASE + 0xb06c)
-#define _MIPIB_LP_GEN_CTRL (VLV_DISPLAY_BASE + 0xb86c)
-#define MIPI_LP_GEN_CTRL(pipe) _PIPE(pipe, _MIPIA_LP_GEN_CTRL, _MIPIB_LP_GEN_CTRL)
-#define _MIPIA_HS_GEN_CTRL (VLV_DISPLAY_BASE + 0xb070)
-#define _MIPIB_HS_GEN_CTRL (VLV_DISPLAY_BASE + 0xb870)
-#define MIPI_HS_GEN_CTRL(pipe) _PIPE(pipe, _MIPIA_HS_GEN_CTRL, _MIPIB_HS_GEN_CTRL)
+#define _MIPIA_HS_GEN_DATA (dev_priv->mipi_mmio_base + 0xb068)
+#define _MIPIB_HS_GEN_DATA (dev_priv->mipi_mmio_base + 0xb868)
+#define MIPI_HS_GEN_DATA(tc) _TRANSCODER(tc, _MIPIA_HS_GEN_DATA, \
+ _MIPIB_HS_GEN_DATA)
+
+#define _MIPIA_LP_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb06c)
+#define _MIPIB_LP_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb86c)
+#define MIPI_LP_GEN_CTRL(tc) _TRANSCODER(tc, _MIPIA_LP_GEN_CTRL, \
+ _MIPIB_LP_GEN_CTRL)
+#define _MIPIA_HS_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb070)
+#define _MIPIB_HS_GEN_CTRL (dev_priv->mipi_mmio_base + 0xb870)
+#define MIPI_HS_GEN_CTRL(tc) _TRANSCODER(tc, _MIPIA_HS_GEN_CTRL, \
+ _MIPIB_HS_GEN_CTRL)
#define LONG_PACKET_WORD_COUNT_SHIFT 8
#define LONG_PACKET_WORD_COUNT_MASK (0xffff << 8)
#define SHORT_PACKET_PARAM_SHIFT 8
@@ -6280,9 +6516,10 @@ enum punit_power_well {
#define DATA_TYPE_MASK (3f << 0)
/* data type values, see include/video/mipi_display.h */
-#define _MIPIA_GEN_FIFO_STAT (VLV_DISPLAY_BASE + 0xb074)
-#define _MIPIB_GEN_FIFO_STAT (VLV_DISPLAY_BASE + 0xb874)
-#define MIPI_GEN_FIFO_STAT(pipe) _PIPE(pipe, _MIPIA_GEN_FIFO_STAT, _MIPIB_GEN_FIFO_STAT)
+#define _MIPIA_GEN_FIFO_STAT (dev_priv->mipi_mmio_base + 0xb074)
+#define _MIPIB_GEN_FIFO_STAT (dev_priv->mipi_mmio_base + 0xb874)
+#define MIPI_GEN_FIFO_STAT(tc) _TRANSCODER(tc, _MIPIA_GEN_FIFO_STAT, \
+ _MIPIB_GEN_FIFO_STAT)
#define DPI_FIFO_EMPTY (1 << 28)
#define DBI_FIFO_EMPTY (1 << 27)
#define LP_CTRL_FIFO_EMPTY (1 << 26)
@@ -6298,16 +6535,18 @@ enum punit_power_well {
#define HS_DATA_FIFO_HALF_EMPTY (1 << 1)
#define HS_DATA_FIFO_FULL (1 << 0)
-#define _MIPIA_HS_LS_DBI_ENABLE (VLV_DISPLAY_BASE + 0xb078)
-#define _MIPIB_HS_LS_DBI_ENABLE (VLV_DISPLAY_BASE + 0xb878)
-#define MIPI_HS_LP_DBI_ENABLE(pipe) _PIPE(pipe, _MIPIA_HS_LS_DBI_ENABLE, _MIPIB_HS_LS_DBI_ENABLE)
+#define _MIPIA_HS_LS_DBI_ENABLE (dev_priv->mipi_mmio_base + 0xb078)
+#define _MIPIB_HS_LS_DBI_ENABLE (dev_priv->mipi_mmio_base + 0xb878)
+#define MIPI_HS_LP_DBI_ENABLE(tc) _TRANSCODER(tc, \
+ _MIPIA_HS_LS_DBI_ENABLE, _MIPIB_HS_LS_DBI_ENABLE)
#define DBI_HS_LP_MODE_MASK (1 << 0)
#define DBI_LP_MODE (1 << 0)
#define DBI_HS_MODE (0 << 0)
-#define _MIPIA_DPHY_PARAM (VLV_DISPLAY_BASE + 0xb080)
-#define _MIPIB_DPHY_PARAM (VLV_DISPLAY_BASE + 0xb880)
-#define MIPI_DPHY_PARAM(pipe) _PIPE(pipe, _MIPIA_DPHY_PARAM, _MIPIB_DPHY_PARAM)
+#define _MIPIA_DPHY_PARAM (dev_priv->mipi_mmio_base + 0xb080)
+#define _MIPIB_DPHY_PARAM (dev_priv->mipi_mmio_base + 0xb880)
+#define MIPI_DPHY_PARAM(tc) _TRANSCODER(tc, _MIPIA_DPHY_PARAM, \
+ _MIPIB_DPHY_PARAM)
#define EXIT_ZERO_COUNT_SHIFT 24
#define EXIT_ZERO_COUNT_MASK (0x3f << 24)
#define TRAIL_COUNT_SHIFT 16
@@ -6318,34 +6557,41 @@ enum punit_power_well {
#define PREPARE_COUNT_MASK (0x3f << 0)
/* bits 31:0 */
-#define _MIPIA_DBI_BW_CTRL (VLV_DISPLAY_BASE + 0xb084)
-#define _MIPIB_DBI_BW_CTRL (VLV_DISPLAY_BASE + 0xb884)
-#define MIPI_DBI_BW_CTRL(pipe) _PIPE(pipe, _MIPIA_DBI_BW_CTRL, _MIPIB_DBI_BW_CTRL)
-
-#define _MIPIA_CLK_LANE_SWITCH_TIME_CNT (VLV_DISPLAY_BASE + 0xb088)
-#define _MIPIB_CLK_LANE_SWITCH_TIME_CNT (VLV_DISPLAY_BASE + 0xb888)
-#define MIPI_CLK_LANE_SWITCH_TIME_CNT(pipe) _PIPE(pipe, _MIPIA_CLK_LANE_SWITCH_TIME_CNT, _MIPIB_CLK_LANE_SWITCH_TIME_CNT)
+#define _MIPIA_DBI_BW_CTRL (dev_priv->mipi_mmio_base + 0xb084)
+#define _MIPIB_DBI_BW_CTRL (dev_priv->mipi_mmio_base + 0xb884)
+#define MIPI_DBI_BW_CTRL(tc) _TRANSCODER(tc, _MIPIA_DBI_BW_CTRL, \
+ _MIPIB_DBI_BW_CTRL)
+
+#define _MIPIA_CLK_LANE_SWITCH_TIME_CNT (dev_priv->mipi_mmio_base \
+ + 0xb088)
+#define _MIPIB_CLK_LANE_SWITCH_TIME_CNT (dev_priv->mipi_mmio_base \
+ + 0xb888)
+#define MIPI_CLK_LANE_SWITCH_TIME_CNT(tc) _TRANSCODER(tc, \
+ _MIPIA_CLK_LANE_SWITCH_TIME_CNT, _MIPIB_CLK_LANE_SWITCH_TIME_CNT)
#define LP_HS_SSW_CNT_SHIFT 16
#define LP_HS_SSW_CNT_MASK (0xffff << 16)
#define HS_LP_PWR_SW_CNT_SHIFT 0
#define HS_LP_PWR_SW_CNT_MASK (0xffff << 0)
-#define _MIPIA_STOP_STATE_STALL (VLV_DISPLAY_BASE + 0xb08c)
-#define _MIPIB_STOP_STATE_STALL (VLV_DISPLAY_BASE + 0xb88c)
-#define MIPI_STOP_STATE_STALL(pipe) _PIPE(pipe, _MIPIA_STOP_STATE_STALL, _MIPIB_STOP_STATE_STALL)
+#define _MIPIA_STOP_STATE_STALL (dev_priv->mipi_mmio_base + 0xb08c)
+#define _MIPIB_STOP_STATE_STALL (dev_priv->mipi_mmio_base + 0xb88c)
+#define MIPI_STOP_STATE_STALL(tc) _TRANSCODER(tc, \
+ _MIPIA_STOP_STATE_STALL, _MIPIB_STOP_STATE_STALL)
#define STOP_STATE_STALL_COUNTER_SHIFT 0
#define STOP_STATE_STALL_COUNTER_MASK (0xff << 0)
-#define _MIPIA_INTR_STAT_REG_1 (VLV_DISPLAY_BASE + 0xb090)
-#define _MIPIB_INTR_STAT_REG_1 (VLV_DISPLAY_BASE + 0xb890)
-#define MIPI_INTR_STAT_REG_1(pipe) _PIPE(pipe, _MIPIA_INTR_STAT_REG_1, _MIPIB_INTR_STAT_REG_1)
-#define _MIPIA_INTR_EN_REG_1 (VLV_DISPLAY_BASE + 0xb094)
-#define _MIPIB_INTR_EN_REG_1 (VLV_DISPLAY_BASE + 0xb894)
-#define MIPI_INTR_EN_REG_1(pipe) _PIPE(pipe, _MIPIA_INTR_EN_REG_1, _MIPIB_INTR_EN_REG_1)
+#define _MIPIA_INTR_STAT_REG_1 (dev_priv->mipi_mmio_base + 0xb090)
+#define _MIPIB_INTR_STAT_REG_1 (dev_priv->mipi_mmio_base + 0xb890)
+#define MIPI_INTR_STAT_REG_1(tc) _TRANSCODER(tc, \
+ _MIPIA_INTR_STAT_REG_1, _MIPIB_INTR_STAT_REG_1)
+#define _MIPIA_INTR_EN_REG_1 (dev_priv->mipi_mmio_base + 0xb094)
+#define _MIPIB_INTR_EN_REG_1 (dev_priv->mipi_mmio_base + 0xb894)
+#define MIPI_INTR_EN_REG_1(tc) _TRANSCODER(tc, _MIPIA_INTR_EN_REG_1, \
+ _MIPIB_INTR_EN_REG_1)
#define RX_CONTENTION_DETECTED (1 << 0)
/* XXX: only pipe A ?!? */
-#define MIPIA_DBI_TYPEC_CTRL (VLV_DISPLAY_BASE + 0xb100)
+#define MIPIA_DBI_TYPEC_CTRL (dev_priv->mipi_mmio_base + 0xb100)
#define DBI_TYPEC_ENABLE (1 << 31)
#define DBI_TYPEC_WIP (1 << 30)
#define DBI_TYPEC_OPTION_SHIFT 28
@@ -6359,9 +6605,10 @@ enum punit_power_well {
/* MIPI adapter registers */
-#define _MIPIA_CTRL (VLV_DISPLAY_BASE + 0xb104)
-#define _MIPIB_CTRL (VLV_DISPLAY_BASE + 0xb904)
-#define MIPI_CTRL(pipe) _PIPE(pipe, _MIPIA_CTRL, _MIPIB_CTRL)
+#define _MIPIA_CTRL (dev_priv->mipi_mmio_base + 0xb104)
+#define _MIPIB_CTRL (dev_priv->mipi_mmio_base + 0xb904)
+#define MIPI_CTRL(tc) _TRANSCODER(tc, _MIPIA_CTRL, \
+ _MIPIB_CTRL)
#define ESCAPE_CLOCK_DIVIDER_SHIFT 5 /* A only */
#define ESCAPE_CLOCK_DIVIDER_MASK (3 << 5)
#define ESCAPE_CLOCK_DIVIDER_1 (0 << 5)
@@ -6373,50 +6620,52 @@ enum punit_power_well {
#define READ_REQUEST_PRIORITY_HIGH (3 << 3)
#define RGB_FLIP_TO_BGR (1 << 2)
-#define _MIPIA_DATA_ADDRESS (VLV_DISPLAY_BASE + 0xb108)
-#define _MIPIB_DATA_ADDRESS (VLV_DISPLAY_BASE + 0xb908)
-#define MIPI_DATA_ADDRESS(pipe) _PIPE(pipe, _MIPIA_DATA_ADDRESS, _MIPIB_DATA_ADDRESS)
+#define _MIPIA_DATA_ADDRESS (dev_priv->mipi_mmio_base + 0xb108)
+#define _MIPIB_DATA_ADDRESS (dev_priv->mipi_mmio_base + 0xb908)
+#define MIPI_DATA_ADDRESS(tc) _TRANSCODER(tc, _MIPIA_DATA_ADDRESS, \
+ _MIPIB_DATA_ADDRESS)
#define DATA_MEM_ADDRESS_SHIFT 5
#define DATA_MEM_ADDRESS_MASK (0x7ffffff << 5)
#define DATA_VALID (1 << 0)
-#define _MIPIA_DATA_LENGTH (VLV_DISPLAY_BASE + 0xb10c)
-#define _MIPIB_DATA_LENGTH (VLV_DISPLAY_BASE + 0xb90c)
-#define MIPI_DATA_LENGTH(pipe) _PIPE(pipe, _MIPIA_DATA_LENGTH, _MIPIB_DATA_LENGTH)
+#define _MIPIA_DATA_LENGTH (dev_priv->mipi_mmio_base + 0xb10c)
+#define _MIPIB_DATA_LENGTH (dev_priv->mipi_mmio_base + 0xb90c)
+#define MIPI_DATA_LENGTH(tc) _TRANSCODER(tc, _MIPIA_DATA_LENGTH, \
+ _MIPIB_DATA_LENGTH)
#define DATA_LENGTH_SHIFT 0
#define DATA_LENGTH_MASK (0xfffff << 0)
-#define _MIPIA_COMMAND_ADDRESS (VLV_DISPLAY_BASE + 0xb110)
-#define _MIPIB_COMMAND_ADDRESS (VLV_DISPLAY_BASE + 0xb910)
-#define MIPI_COMMAND_ADDRESS(pipe) _PIPE(pipe, _MIPIA_COMMAND_ADDRESS, _MIPIB_COMMAND_ADDRESS)
+#define _MIPIA_COMMAND_ADDRESS (dev_priv->mipi_mmio_base + 0xb110)
+#define _MIPIB_COMMAND_ADDRESS (dev_priv->mipi_mmio_base + 0xb910)
+#define MIPI_COMMAND_ADDRESS(tc) _TRANSCODER(tc, \
+ _MIPIA_COMMAND_ADDRESS, _MIPIB_COMMAND_ADDRESS)
#define COMMAND_MEM_ADDRESS_SHIFT 5
#define COMMAND_MEM_ADDRESS_MASK (0x7ffffff << 5)
#define AUTO_PWG_ENABLE (1 << 2)
#define MEMORY_WRITE_DATA_FROM_PIPE_RENDERING (1 << 1)
#define COMMAND_VALID (1 << 0)
-#define _MIPIA_COMMAND_LENGTH (VLV_DISPLAY_BASE + 0xb114)
-#define _MIPIB_COMMAND_LENGTH (VLV_DISPLAY_BASE + 0xb914)
-#define MIPI_COMMAND_LENGTH(pipe) _PIPE(pipe, _MIPIA_COMMAND_LENGTH, _MIPIB_COMMAND_LENGTH)
+#define _MIPIA_COMMAND_LENGTH (dev_priv->mipi_mmio_base + 0xb114)
+#define _MIPIB_COMMAND_LENGTH (dev_priv->mipi_mmio_base + 0xb914)
+#define MIPI_COMMAND_LENGTH(tc) _TRANSCODER(tc, _MIPIA_COMMAND_LENGTH, \
+ _MIPIB_COMMAND_LENGTH)
#define COMMAND_LENGTH_SHIFT(n) (8 * (n)) /* n: 0...3 */
#define COMMAND_LENGTH_MASK(n) (0xff << (8 * (n)))
-#define _MIPIA_READ_DATA_RETURN0 (VLV_DISPLAY_BASE + 0xb118)
-#define _MIPIB_READ_DATA_RETURN0 (VLV_DISPLAY_BASE + 0xb918)
-#define MIPI_READ_DATA_RETURN(pipe, n) \
- (_PIPE(pipe, _MIPIA_READ_DATA_RETURN0, _MIPIB_READ_DATA_RETURN0) + 4 * (n)) /* n: 0...7 */
+#define _MIPIA_READ_DATA_RETURN0 (dev_priv->mipi_mmio_base + 0xb118)
+#define _MIPIB_READ_DATA_RETURN0 (dev_priv->mipi_mmio_base + 0xb918)
+#define MIPI_READ_DATA_RETURN(tc, n) \
+ (_TRANSCODER(tc, _MIPIA_READ_DATA_RETURN0, _MIPIB_READ_DATA_RETURN0) \
+ + 4 * (n)) /* n: 0...7 */
-#define _MIPIA_READ_DATA_VALID (VLV_DISPLAY_BASE + 0xb138)
-#define _MIPIB_READ_DATA_VALID (VLV_DISPLAY_BASE + 0xb938)
-#define MIPI_READ_DATA_VALID(pipe) _PIPE(pipe, _MIPIA_READ_DATA_VALID, _MIPIB_READ_DATA_VALID)
+#define _MIPIA_READ_DATA_VALID (dev_priv->mipi_mmio_base + 0xb138)
+#define _MIPIB_READ_DATA_VALID (dev_priv->mipi_mmio_base + 0xb938)
+#define MIPI_READ_DATA_VALID(tc) _TRANSCODER(tc, \
+ _MIPIA_READ_DATA_VALID, _MIPIB_READ_DATA_VALID)
#define READ_DATA_VALID(n) (1 << (n))
/* For UMS only (deprecated): */
#define _PALETTE_A (dev_priv->info.display_mmio_offset + 0xa000)
#define _PALETTE_B (dev_priv->info.display_mmio_offset + 0xa800)
-#define _DPLL_A (dev_priv->info.display_mmio_offset + 0x6014)
-#define _DPLL_B (dev_priv->info.display_mmio_offset + 0x6018)
-#define _DPLL_A_MD (dev_priv->info.display_mmio_offset + 0x601c)
-#define _DPLL_B_MD (dev_priv->info.display_mmio_offset + 0x6020)
#endif /* _I915_REG_H_ */
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
index 86ce39aad0ff..503847f18fdd 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -47,22 +47,45 @@ static u32 calc_residency(struct drm_device *dev, const u32 reg)
intel_runtime_pm_get(dev_priv);
- /* On VLV, residency time is in CZ units rather than 1.28us */
+ /* On VLV and CHV, residency time is in CZ units rather than 1.28us */
if (IS_VALLEYVIEW(dev)) {
- u32 clkctl2;
+ u32 reg, czcount_30ns;
- clkctl2 = I915_READ(VLV_CLK_CTL2) >>
- CLK_CTL2_CZCOUNT_30NS_SHIFT;
- if (!clkctl2) {
- WARN(!clkctl2, "bogus CZ count value");
+ if (IS_CHERRYVIEW(dev))
+ reg = CHV_CLK_CTL1;
+ else
+ reg = VLV_CLK_CTL2;
+
+ czcount_30ns = I915_READ(reg) >> CLK_CTL2_CZCOUNT_30NS_SHIFT;
+
+ if (!czcount_30ns) {
+ WARN(!czcount_30ns, "bogus CZ count value");
ret = 0;
goto out;
}
- units = DIV_ROUND_UP_ULL(30ULL * bias, (u64)clkctl2);
+
+ units = 0;
+ div = 1000000ULL;
+
+ if (IS_CHERRYVIEW(dev)) {
+ /* Special case for 320Mhz */
+ if (czcount_30ns == 1) {
+ div = 10000000ULL;
+ units = 3125ULL;
+ } else {
+ /* chv counts are one less */
+ czcount_30ns += 1;
+ }
+ }
+
+ if (units == 0)
+ units = DIV_ROUND_UP_ULL(30ULL * bias,
+ (u64)czcount_30ns);
+
if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH)
units <<= 8;
- div = 1000000ULL * bias;
+ div = div * bias;
}
raw_time = I915_READ(reg) * units;
@@ -461,11 +484,20 @@ static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr
mutex_unlock(&dev->struct_mutex);
if (attr == &dev_attr_gt_RP0_freq_mhz) {
- val = ((rp_state_cap & 0x0000ff) >> 0) * GT_FREQUENCY_MULTIPLIER;
+ if (IS_VALLEYVIEW(dev))
+ val = vlv_gpu_freq(dev_priv, dev_priv->rps.rp0_freq);
+ else
+ val = ((rp_state_cap & 0x0000ff) >> 0) * GT_FREQUENCY_MULTIPLIER;
} else if (attr == &dev_attr_gt_RP1_freq_mhz) {
- val = ((rp_state_cap & 0x00ff00) >> 8) * GT_FREQUENCY_MULTIPLIER;
+ if (IS_VALLEYVIEW(dev))
+ val = vlv_gpu_freq(dev_priv, dev_priv->rps.rp1_freq);
+ else
+ val = ((rp_state_cap & 0x00ff00) >> 8) * GT_FREQUENCY_MULTIPLIER;
} else if (attr == &dev_attr_gt_RPn_freq_mhz) {
- val = ((rp_state_cap & 0xff0000) >> 16) * GT_FREQUENCY_MULTIPLIER;
+ if (IS_VALLEYVIEW(dev))
+ val = vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq);
+ else
+ val = ((rp_state_cap & 0xff0000) >> 16) * GT_FREQUENCY_MULTIPLIER;
} else {
BUG();
}
@@ -486,6 +518,9 @@ static const struct attribute *vlv_attrs[] = {
&dev_attr_gt_cur_freq_mhz.attr,
&dev_attr_gt_max_freq_mhz.attr,
&dev_attr_gt_min_freq_mhz.attr,
+ &dev_attr_gt_RP0_freq_mhz.attr,
+ &dev_attr_gt_RP1_freq_mhz.attr,
+ &dev_attr_gt_RPn_freq_mhz.attr,
&dev_attr_vlv_rpe_freq_mhz.attr,
NULL,
};
@@ -505,7 +540,7 @@ static ssize_t error_state_read(struct file *filp, struct kobject *kobj,
memset(&error_priv, 0, sizeof(error_priv));
- ret = i915_error_state_buf_init(&error_str, count, off);
+ ret = i915_error_state_buf_init(&error_str, to_i915(dev), count, off);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 21b347efcf1e..a4bd90f36a03 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -336,11 +336,12 @@ parse_lfp_backlight(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
dev_priv->vbt.backlight.pwm_freq_hz = entry->pwm_freq_hz;
dev_priv->vbt.backlight.active_low_pwm = entry->active_low_pwm;
+ dev_priv->vbt.backlight.min_brightness = entry->min_brightness;
DRM_DEBUG_KMS("VBT backlight PWM modulation frequency %u Hz, "
"active %s, min brightness %u, level %u\n",
dev_priv->vbt.backlight.pwm_freq_hz,
dev_priv->vbt.backlight.active_low_pwm ? "low" : "high",
- entry->min_brightness,
+ dev_priv->vbt.backlight.min_brightness,
backlight_data->level[panel_type]);
}
@@ -626,16 +627,16 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
switch (edp_link_params->preemphasis) {
case EDP_PREEMPHASIS_NONE:
- dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_0;
+ dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_0;
break;
case EDP_PREEMPHASIS_3_5dB:
- dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5;
+ dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_1;
break;
case EDP_PREEMPHASIS_6dB:
- dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_6;
+ dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_2;
break;
case EDP_PREEMPHASIS_9_5dB:
- dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5;
+ dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_3;
break;
default:
DRM_DEBUG_KMS("VBT has unknown eDP pre-emphasis value %u\n",
@@ -645,16 +646,16 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
switch (edp_link_params->vswing) {
case EDP_VSWING_0_4V:
- dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_400;
+ dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
break;
case EDP_VSWING_0_6V:
- dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_600;
+ dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_1;
break;
case EDP_VSWING_0_8V:
- dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_800;
+ dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
break;
case EDP_VSWING_1_2V:
- dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_1200;
+ dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
break;
default:
DRM_DEBUG_KMS("VBT has unknown eDP voltage swing value %u\n",
@@ -877,7 +878,7 @@ err:
/* error during parsing so set all pointers to null
* because of partial parsing */
- memset(dev_priv->vbt.dsi.sequence, 0, MIPI_SEQ_MAX);
+ memset(dev_priv->vbt.dsi.sequence, 0, sizeof(dev_priv->vbt.dsi.sequence));
}
static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
@@ -975,12 +976,10 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
if (bdb->version >= 158) {
/* The VBT HDMI level shift values match the table we have. */
hdmi_level_shift = child->raw[7] & 0xF;
- if (hdmi_level_shift < 0xC) {
- DRM_DEBUG_KMS("VBT HDMI level shift for port %c: %d\n",
- port_name(port),
- hdmi_level_shift);
- info->hdmi_level_shift = hdmi_level_shift;
- }
+ DRM_DEBUG_KMS("VBT HDMI level shift for port %c: %d\n",
+ port_name(port),
+ hdmi_level_shift);
+ info->hdmi_level_shift = hdmi_level_shift;
}
}
@@ -1113,8 +1112,7 @@ init_vbt_defaults(struct drm_i915_private *dev_priv)
struct ddi_vbt_port_info *info =
&dev_priv->vbt.ddi_port_info[port];
- /* Recommended BSpec default: 800mV 0dB. */
- info->hdmi_level_shift = 6;
+ info->hdmi_level_shift = HDMI_LEVEL_SHIFT_UNKNOWN;
info->supports_dvi = (port != PORT_A && port != PORT_E);
info->supports_hdmi = info->supports_dvi;
@@ -1122,7 +1120,7 @@ init_vbt_defaults(struct drm_i915_private *dev_priv)
}
}
-static int __init intel_no_opregion_vbt_callback(const struct dmi_system_id *id)
+static int intel_no_opregion_vbt_callback(const struct dmi_system_id *id)
{
DRM_DEBUG_KMS("Falling back to manually reading VBT from "
"VBIOS ROM for %s\n",
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
index b98667796337..905999bee2ac 100644
--- a/drivers/gpu/drm/i915/intel_bios.h
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -802,7 +802,8 @@ struct mipi_config {
u16 rsvd4;
- u8 rsvd5[5];
+ u8 rsvd5;
+ u32 target_burst_mode_freq;
u32 dsi_ddr_clk;
u32 bridge_ref_clk;
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 5a045d3bd77e..9212e6504e0f 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -137,6 +137,18 @@ static void hsw_crt_get_config(struct intel_encoder *encoder,
pipe_config->adjusted_mode.flags |= intel_crt_get_flags(encoder);
}
+static void hsw_crt_pre_enable(struct intel_encoder *encoder)
+{
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ WARN(I915_READ(SPLL_CTL) & SPLL_PLL_ENABLE, "SPLL already enabled\n");
+ I915_WRITE(SPLL_CTL,
+ SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz | SPLL_PLL_SSC);
+ POSTING_READ(SPLL_CTL);
+ udelay(20);
+}
+
/* Note: The caller is required to filter out dpms modes not supported by the
* platform. */
static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode)
@@ -194,6 +206,20 @@ static void intel_disable_crt(struct intel_encoder *encoder)
intel_crt_set_dpms(encoder, DRM_MODE_DPMS_OFF);
}
+
+static void hsw_crt_post_disable(struct intel_encoder *encoder)
+{
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t val;
+
+ DRM_DEBUG_KMS("Disabling SPLL\n");
+ val = I915_READ(SPLL_CTL);
+ WARN_ON(!(val & SPLL_PLL_ENABLE));
+ I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE);
+ POSTING_READ(SPLL_CTL);
+}
+
static void intel_enable_crt(struct intel_encoder *encoder)
{
struct intel_crt *crt = intel_encoder_to_crt(encoder);
@@ -289,8 +315,10 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder,
pipe_config->pipe_bpp = 24;
/* FDI must always be 2.7 GHz */
- if (HAS_DDI(dev))
+ if (HAS_DDI(dev)) {
+ pipe_config->ddi_pll_sel = PORT_CLK_SEL_SPLL;
pipe_config->port_clock = 135000 * 2;
+ }
return true;
}
@@ -632,8 +660,6 @@ intel_crt_detect(struct drm_connector *connector, bool force)
struct intel_load_detect_pipe tmp;
struct drm_modeset_acquire_ctx ctx;
- intel_runtime_pm_get(dev_priv);
-
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n",
connector->base.id, connector->name,
force);
@@ -673,20 +699,23 @@ intel_crt_detect(struct drm_connector *connector, bool force)
goto out;
}
+ drm_modeset_acquire_init(&ctx, 0);
+
/* for pre-945g platforms use load detect */
if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) {
if (intel_crt_detect_ddc(connector))
status = connector_status_connected;
else
status = intel_crt_load_detect(crt);
- intel_release_load_detect_pipe(connector, &tmp, &ctx);
+ intel_release_load_detect_pipe(connector, &tmp);
} else
status = connector_status_unknown;
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+
out:
intel_display_power_put(dev_priv, power_domain);
- intel_runtime_pm_put(dev_priv);
-
return status;
}
@@ -775,7 +804,7 @@ static const struct drm_encoder_funcs intel_crt_enc_funcs = {
.destroy = intel_encoder_destroy,
};
-static int __init intel_no_crt_dmi_callback(const struct dmi_system_id *id)
+static int intel_no_crt_dmi_callback(const struct dmi_system_id *id)
{
DRM_INFO("Skipping CRT initialization for %s\n", id->ident);
return 1;
@@ -860,6 +889,8 @@ void intel_crt_init(struct drm_device *dev)
if (HAS_DDI(dev)) {
crt->base.get_config = hsw_crt_get_config;
crt->base.get_hw_state = intel_ddi_get_hw_state;
+ crt->base.pre_enable = hsw_crt_pre_enable;
+ crt->base.post_disable = hsw_crt_post_disable;
} else {
crt->base.get_config = intel_crt_get_config;
crt->base.get_hw_state = intel_crt_get_hw_state;
@@ -869,7 +900,7 @@ void intel_crt_init(struct drm_device *dev)
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
if (!I915_HAS_HOTPLUG(dev))
intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT;
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index b17b9c7c769f..b63d4fa204a3 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -28,87 +28,103 @@
#include "i915_drv.h"
#include "intel_drv.h"
+struct ddi_buf_trans {
+ u32 trans1; /* balance leg enable, de-emph level */
+ u32 trans2; /* vref sel, vswing */
+};
+
/* HDMI/DVI modes ignore everything but the last 2 items. So we share
* them for both DP and FDI transports, allowing those ports to
* automatically adapt to HDMI connections as well
*/
-static const u32 hsw_ddi_translations_dp[] = {
- 0x00FFFFFF, 0x0006000E, /* DP parameters */
- 0x00D75FFF, 0x0005000A,
- 0x00C30FFF, 0x00040006,
- 0x80AAAFFF, 0x000B0000,
- 0x00FFFFFF, 0x0005000A,
- 0x00D75FFF, 0x000C0004,
- 0x80C30FFF, 0x000B0000,
- 0x00FFFFFF, 0x00040006,
- 0x80D75FFF, 0x000B0000,
+static const struct ddi_buf_trans hsw_ddi_translations_dp[] = {
+ { 0x00FFFFFF, 0x0006000E },
+ { 0x00D75FFF, 0x0005000A },
+ { 0x00C30FFF, 0x00040006 },
+ { 0x80AAAFFF, 0x000B0000 },
+ { 0x00FFFFFF, 0x0005000A },
+ { 0x00D75FFF, 0x000C0004 },
+ { 0x80C30FFF, 0x000B0000 },
+ { 0x00FFFFFF, 0x00040006 },
+ { 0x80D75FFF, 0x000B0000 },
+};
+
+static const struct ddi_buf_trans hsw_ddi_translations_fdi[] = {
+ { 0x00FFFFFF, 0x0007000E },
+ { 0x00D75FFF, 0x000F000A },
+ { 0x00C30FFF, 0x00060006 },
+ { 0x00AAAFFF, 0x001E0000 },
+ { 0x00FFFFFF, 0x000F000A },
+ { 0x00D75FFF, 0x00160004 },
+ { 0x00C30FFF, 0x001E0000 },
+ { 0x00FFFFFF, 0x00060006 },
+ { 0x00D75FFF, 0x001E0000 },
};
-static const u32 hsw_ddi_translations_fdi[] = {
- 0x00FFFFFF, 0x0007000E, /* FDI parameters */
- 0x00D75FFF, 0x000F000A,
- 0x00C30FFF, 0x00060006,
- 0x00AAAFFF, 0x001E0000,
- 0x00FFFFFF, 0x000F000A,
- 0x00D75FFF, 0x00160004,
- 0x00C30FFF, 0x001E0000,
- 0x00FFFFFF, 0x00060006,
- 0x00D75FFF, 0x001E0000,
+static const struct ddi_buf_trans hsw_ddi_translations_hdmi[] = {
+ /* Idx NT mV d T mV d db */
+ { 0x00FFFFFF, 0x0006000E }, /* 0: 400 400 0 */
+ { 0x00E79FFF, 0x000E000C }, /* 1: 400 500 2 */
+ { 0x00D75FFF, 0x0005000A }, /* 2: 400 600 3.5 */
+ { 0x00FFFFFF, 0x0005000A }, /* 3: 600 600 0 */
+ { 0x00E79FFF, 0x001D0007 }, /* 4: 600 750 2 */
+ { 0x00D75FFF, 0x000C0004 }, /* 5: 600 900 3.5 */
+ { 0x00FFFFFF, 0x00040006 }, /* 6: 800 800 0 */
+ { 0x80E79FFF, 0x00030002 }, /* 7: 800 1000 2 */
+ { 0x00FFFFFF, 0x00140005 }, /* 8: 850 850 0 */
+ { 0x00FFFFFF, 0x000C0004 }, /* 9: 900 900 0 */
+ { 0x00FFFFFF, 0x001C0003 }, /* 10: 950 950 0 */
+ { 0x80FFFFFF, 0x00030002 }, /* 11: 1000 1000 0 */
};
-static const u32 hsw_ddi_translations_hdmi[] = {
- /* Idx NT mV diff T mV diff db */
- 0x00FFFFFF, 0x0006000E, /* 0: 400 400 0 */
- 0x00E79FFF, 0x000E000C, /* 1: 400 500 2 */
- 0x00D75FFF, 0x0005000A, /* 2: 400 600 3.5 */
- 0x00FFFFFF, 0x0005000A, /* 3: 600 600 0 */
- 0x00E79FFF, 0x001D0007, /* 4: 600 750 2 */
- 0x00D75FFF, 0x000C0004, /* 5: 600 900 3.5 */
- 0x00FFFFFF, 0x00040006, /* 6: 800 800 0 */
- 0x80E79FFF, 0x00030002, /* 7: 800 1000 2 */
- 0x00FFFFFF, 0x00140005, /* 8: 850 850 0 */
- 0x00FFFFFF, 0x000C0004, /* 9: 900 900 0 */
- 0x00FFFFFF, 0x001C0003, /* 10: 950 950 0 */
- 0x80FFFFFF, 0x00030002, /* 11: 1000 1000 0 */
+static const struct ddi_buf_trans bdw_ddi_translations_edp[] = {
+ { 0x00FFFFFF, 0x00000012 },
+ { 0x00EBAFFF, 0x00020011 },
+ { 0x00C71FFF, 0x0006000F },
+ { 0x00AAAFFF, 0x000E000A },
+ { 0x00FFFFFF, 0x00020011 },
+ { 0x00DB6FFF, 0x0005000F },
+ { 0x00BEEFFF, 0x000A000C },
+ { 0x00FFFFFF, 0x0005000F },
+ { 0x00DB6FFF, 0x000A000C },
};
-static const u32 bdw_ddi_translations_edp[] = {
- 0x00FFFFFF, 0x00000012, /* eDP parameters */
- 0x00EBAFFF, 0x00020011,
- 0x00C71FFF, 0x0006000F,
- 0x00FFFFFF, 0x00020011,
- 0x00DB6FFF, 0x0005000F,
- 0x00BEEFFF, 0x000A000C,
- 0x00FFFFFF, 0x0005000F,
- 0x00DB6FFF, 0x000A000C,
- 0x00FFFFFF, 0x000A000C,
- 0x00FFFFFF, 0x00140006 /* HDMI parameters 800mV 0dB*/
+static const struct ddi_buf_trans bdw_ddi_translations_dp[] = {
+ { 0x00FFFFFF, 0x0007000E },
+ { 0x00D75FFF, 0x000E000A },
+ { 0x00BEFFFF, 0x00140006 },
+ { 0x80B2CFFF, 0x001B0002 },
+ { 0x00FFFFFF, 0x000E000A },
+ { 0x00D75FFF, 0x00180004 },
+ { 0x80CB2FFF, 0x001B0002 },
+ { 0x00F7DFFF, 0x00180004 },
+ { 0x80D75FFF, 0x001B0002 },
};
-static const u32 bdw_ddi_translations_dp[] = {
- 0x00FFFFFF, 0x0007000E, /* DP parameters */
- 0x00D75FFF, 0x000E000A,
- 0x00BEFFFF, 0x00140006,
- 0x00FFFFFF, 0x000E000A,
- 0x00D75FFF, 0x00180004,
- 0x80CB2FFF, 0x001B0002,
- 0x00F7DFFF, 0x00180004,
- 0x80D75FFF, 0x001B0002,
- 0x80FFFFFF, 0x001B0002,
- 0x00FFFFFF, 0x00140006 /* HDMI parameters 800mV 0dB*/
+static const struct ddi_buf_trans bdw_ddi_translations_fdi[] = {
+ { 0x00FFFFFF, 0x0001000E },
+ { 0x00D75FFF, 0x0004000A },
+ { 0x00C30FFF, 0x00070006 },
+ { 0x00AAAFFF, 0x000C0000 },
+ { 0x00FFFFFF, 0x0004000A },
+ { 0x00D75FFF, 0x00090004 },
+ { 0x00C30FFF, 0x000C0000 },
+ { 0x00FFFFFF, 0x00070006 },
+ { 0x00D75FFF, 0x000C0000 },
};
-static const u32 bdw_ddi_translations_fdi[] = {
- 0x00FFFFFF, 0x0001000E, /* FDI parameters */
- 0x00D75FFF, 0x0004000A,
- 0x00C30FFF, 0x00070006,
- 0x00AAAFFF, 0x000C0000,
- 0x00FFFFFF, 0x0004000A,
- 0x00D75FFF, 0x00090004,
- 0x00C30FFF, 0x000C0000,
- 0x00FFFFFF, 0x00070006,
- 0x00D75FFF, 0x000C0000,
- 0x00FFFFFF, 0x00140006 /* HDMI parameters 800mV 0dB*/
+static const struct ddi_buf_trans bdw_ddi_translations_hdmi[] = {
+ /* Idx NT mV d T mV df db */
+ { 0x00FFFFFF, 0x0007000E }, /* 0: 400 400 0 */
+ { 0x00D75FFF, 0x000E000A }, /* 1: 400 600 3.5 */
+ { 0x00BEFFFF, 0x00140006 }, /* 2: 400 800 6 */
+ { 0x00FFFFFF, 0x0009000D }, /* 3: 450 450 0 */
+ { 0x00FFFFFF, 0x000E000A }, /* 4: 600 600 0 */
+ { 0x00D7FFFF, 0x00140006 }, /* 5: 600 800 2.5 */
+ { 0x80CB2FFF, 0x001B0002 }, /* 6: 600 1000 4.5 */
+ { 0x00FFFFFF, 0x00140006 }, /* 7: 800 800 0 */
+ { 0x80E79FFF, 0x001B0002 }, /* 8: 800 1000 2 */
+ { 0x80FFFFFF, 0x001B0002 }, /* 9: 1000 1000 0 */
};
enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
@@ -116,7 +132,10 @@ enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
struct drm_encoder *encoder = &intel_encoder->base;
int type = intel_encoder->type;
- if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP ||
+ if (type == INTEL_OUTPUT_DP_MST) {
+ struct intel_digital_port *intel_dig_port = enc_to_mst(encoder)->primary;
+ return intel_dig_port->port;
+ } else if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP ||
type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_UNKNOWN) {
struct intel_digital_port *intel_dig_port =
enc_to_dig_port(encoder);
@@ -142,26 +161,36 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 reg;
- int i;
+ int i, n_hdmi_entries, hdmi_800mV_0dB;
int hdmi_level = dev_priv->vbt.ddi_port_info[port].hdmi_level_shift;
- const u32 *ddi_translations_fdi;
- const u32 *ddi_translations_dp;
- const u32 *ddi_translations_edp;
- const u32 *ddi_translations;
+ const struct ddi_buf_trans *ddi_translations_fdi;
+ const struct ddi_buf_trans *ddi_translations_dp;
+ const struct ddi_buf_trans *ddi_translations_edp;
+ const struct ddi_buf_trans *ddi_translations_hdmi;
+ const struct ddi_buf_trans *ddi_translations;
if (IS_BROADWELL(dev)) {
ddi_translations_fdi = bdw_ddi_translations_fdi;
ddi_translations_dp = bdw_ddi_translations_dp;
ddi_translations_edp = bdw_ddi_translations_edp;
+ ddi_translations_hdmi = bdw_ddi_translations_hdmi;
+ n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi);
+ hdmi_800mV_0dB = 7;
} else if (IS_HASWELL(dev)) {
ddi_translations_fdi = hsw_ddi_translations_fdi;
ddi_translations_dp = hsw_ddi_translations_dp;
ddi_translations_edp = hsw_ddi_translations_dp;
+ ddi_translations_hdmi = hsw_ddi_translations_hdmi;
+ n_hdmi_entries = ARRAY_SIZE(hsw_ddi_translations_hdmi);
+ hdmi_800mV_0dB = 6;
} else {
WARN(1, "ddi translation table missing\n");
ddi_translations_edp = bdw_ddi_translations_dp;
ddi_translations_fdi = bdw_ddi_translations_fdi;
ddi_translations_dp = bdw_ddi_translations_dp;
+ ddi_translations_hdmi = bdw_ddi_translations_hdmi;
+ n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi);
+ hdmi_800mV_0dB = 7;
}
switch (port) {
@@ -187,14 +216,22 @@ static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port)
for (i = 0, reg = DDI_BUF_TRANS(port);
i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) {
- I915_WRITE(reg, ddi_translations[i]);
+ I915_WRITE(reg, ddi_translations[i].trans1);
reg += 4;
- }
- /* Entry 9 is for HDMI: */
- for (i = 0; i < 2; i++) {
- I915_WRITE(reg, hsw_ddi_translations_hdmi[hdmi_level * 2 + i]);
+ I915_WRITE(reg, ddi_translations[i].trans2);
reg += 4;
}
+
+ /* Choose a good default if VBT is badly populated */
+ if (hdmi_level == HDMI_LEVEL_SHIFT_UNKNOWN ||
+ hdmi_level >= n_hdmi_entries)
+ hdmi_level = hdmi_800mV_0dB;
+
+ /* Entry 9 is for HDMI: */
+ I915_WRITE(reg, ddi_translations_hdmi[hdmi_level].trans1);
+ reg += 4;
+ I915_WRITE(reg, ddi_translations_hdmi[hdmi_level].trans2);
+ reg += 4;
}
/* Program DDI buffers translations for DP. By default, program ports A-D in DP
@@ -211,18 +248,6 @@ void intel_prepare_ddi(struct drm_device *dev)
intel_prepare_ddi_buffers(dev, port);
}
-static const long hsw_ddi_buf_ctl_values[] = {
- DDI_BUF_EMP_400MV_0DB_HSW,
- DDI_BUF_EMP_400MV_3_5DB_HSW,
- DDI_BUF_EMP_400MV_6DB_HSW,
- DDI_BUF_EMP_400MV_9_5DB_HSW,
- DDI_BUF_EMP_600MV_0DB_HSW,
- DDI_BUF_EMP_600MV_3_5DB_HSW,
- DDI_BUF_EMP_600MV_6DB_HSW,
- DDI_BUF_EMP_800MV_0DB_HSW,
- DDI_BUF_EMP_800MV_3_5DB_HSW
-};
-
static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv,
enum port port)
{
@@ -277,11 +302,12 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
I915_WRITE(_FDI_RXA_CTL, rx_ctl_val);
/* Configure Port Clock Select */
- I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->ddi_pll_sel);
+ I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->config.ddi_pll_sel);
+ WARN_ON(intel_crtc->config.ddi_pll_sel != PORT_CLK_SEL_SPLL);
/* Start the training iterating through available voltages and emphasis,
* testing each value twice. */
- for (i = 0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values) * 2; i++) {
+ for (i = 0; i < ARRAY_SIZE(hsw_ddi_translations_fdi) * 2; i++) {
/* Configure DP_TP_CTL with auto-training */
I915_WRITE(DP_TP_CTL(PORT_E),
DP_TP_CTL_FDI_AUTOTRAIN |
@@ -296,7 +322,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
I915_WRITE(DDI_BUF_CTL(PORT_E),
DDI_BUF_CTL_ENABLE |
((intel_crtc->config.fdi_lanes - 1) << 1) |
- hsw_ddi_buf_ctl_values[i / 2]);
+ DDI_BUF_TRANS_SELECT(i / 2));
POSTING_READ(DDI_BUF_CTL(PORT_E));
udelay(600);
@@ -364,6 +390,18 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
DRM_ERROR("FDI link training failed!\n");
}
+void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder)
+{
+ struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+ struct intel_digital_port *intel_dig_port =
+ enc_to_dig_port(&encoder->base);
+
+ intel_dp->DP = intel_dig_port->saved_port_bits |
+ DDI_BUF_CTL_ENABLE | DDI_BUF_TRANS_SELECT(0);
+ intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
+
+}
+
static struct intel_encoder *
intel_ddi_get_crtc_encoder(struct drm_crtc *crtc)
{
@@ -385,55 +423,8 @@ intel_ddi_get_crtc_encoder(struct drm_crtc *crtc)
return ret;
}
-void intel_ddi_put_crtc_pll(struct drm_crtc *crtc)
-{
- struct drm_i915_private *dev_priv = crtc->dev->dev_private;
- struct intel_ddi_plls *plls = &dev_priv->ddi_plls;
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- uint32_t val;
-
- switch (intel_crtc->ddi_pll_sel) {
- case PORT_CLK_SEL_SPLL:
- plls->spll_refcount--;
- if (plls->spll_refcount == 0) {
- DRM_DEBUG_KMS("Disabling SPLL\n");
- val = I915_READ(SPLL_CTL);
- WARN_ON(!(val & SPLL_PLL_ENABLE));
- I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE);
- POSTING_READ(SPLL_CTL);
- }
- break;
- case PORT_CLK_SEL_WRPLL1:
- plls->wrpll1_refcount--;
- if (plls->wrpll1_refcount == 0) {
- DRM_DEBUG_KMS("Disabling WRPLL 1\n");
- val = I915_READ(WRPLL_CTL1);
- WARN_ON(!(val & WRPLL_PLL_ENABLE));
- I915_WRITE(WRPLL_CTL1, val & ~WRPLL_PLL_ENABLE);
- POSTING_READ(WRPLL_CTL1);
- }
- break;
- case PORT_CLK_SEL_WRPLL2:
- plls->wrpll2_refcount--;
- if (plls->wrpll2_refcount == 0) {
- DRM_DEBUG_KMS("Disabling WRPLL 2\n");
- val = I915_READ(WRPLL_CTL2);
- WARN_ON(!(val & WRPLL_PLL_ENABLE));
- I915_WRITE(WRPLL_CTL2, val & ~WRPLL_PLL_ENABLE);
- POSTING_READ(WRPLL_CTL2);
- }
- break;
- }
-
- WARN(plls->spll_refcount < 0, "Invalid SPLL refcount\n");
- WARN(plls->wrpll1_refcount < 0, "Invalid WRPLL1 refcount\n");
- WARN(plls->wrpll2_refcount < 0, "Invalid WRPLL2 refcount\n");
-
- intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE;
-}
-
#define LC_FREQ 2700
-#define LC_FREQ_2K (LC_FREQ * 2000)
+#define LC_FREQ_2K U64_C(LC_FREQ * 2000)
#define P_MIN 2
#define P_MAX 64
@@ -445,7 +436,11 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc)
#define VCO_MIN 2400
#define VCO_MAX 4800
-#define ABS_DIFF(a, b) ((a > b) ? (a - b) : (b - a))
+#define abs_diff(a, b) ({ \
+ typeof(a) __a = (a); \
+ typeof(b) __b = (b); \
+ (void) (&__a == &__b); \
+ __a > __b ? (__a - __b) : (__b - __a); })
struct wrpll_rnp {
unsigned p, n2, r2;
@@ -555,9 +550,9 @@ static void wrpll_update_rnp(uint64_t freq2k, unsigned budget,
*/
a = freq2k * budget * p * r2;
b = freq2k * budget * best->p * best->r2;
- diff = ABS_DIFF((freq2k * p * r2), (LC_FREQ_2K * n2));
- diff_best = ABS_DIFF((freq2k * best->p * best->r2),
- (LC_FREQ_2K * best->n2));
+ diff = abs_diff(freq2k * p * r2, LC_FREQ_2K * n2);
+ diff_best = abs_diff(freq2k * best->p * best->r2,
+ LC_FREQ_2K * best->n2);
c = 1000000 * diff;
d = 1000000 * diff_best;
@@ -592,9 +587,9 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv,
u32 wrpll;
wrpll = I915_READ(reg);
- switch (wrpll & SPLL_PLL_REF_MASK) {
- case SPLL_PLL_SSC:
- case SPLL_PLL_NON_SSC:
+ switch (wrpll & WRPLL_PLL_REF_MASK) {
+ case WRPLL_PLL_SSC:
+ case WRPLL_PLL_NON_SSC:
/*
* We could calculate spread here, but our checking
* code only cares about 5% accuracy, and spread is a max of
@@ -602,7 +597,7 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv,
*/
refclk = 135;
break;
- case SPLL_PLL_LCPLL:
+ case WRPLL_PLL_LCPLL:
refclk = LC_FREQ;
break;
default:
@@ -618,15 +613,14 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv,
return (refclk * n * 100) / (p * r);
}
-static void intel_ddi_clock_get(struct intel_encoder *encoder,
- struct intel_crtc_config *pipe_config)
+static void hsw_ddi_clock_get(struct intel_encoder *encoder,
+ struct intel_crtc_config *pipe_config)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
- enum port port = intel_ddi_get_encoder_port(encoder);
int link_clock = 0;
u32 val, pll;
- val = I915_READ(PORT_CLK_SEL(port));
+ val = pipe_config->ddi_pll_sel;
switch (val & PORT_CLK_SEL_MASK) {
case PORT_CLK_SEL_LCPLL_810:
link_clock = 81000;
@@ -675,9 +669,15 @@ static void intel_ddi_clock_get(struct intel_encoder *encoder,
pipe_config->adjusted_mode.crtc_clock = pipe_config->port_clock;
}
+void intel_ddi_clock_get(struct intel_encoder *encoder,
+ struct intel_crtc_config *pipe_config)
+{
+ hsw_ddi_clock_get(encoder, pipe_config);
+}
+
static void
-intel_ddi_calculate_wrpll(int clock /* in Hz */,
- unsigned *r2_out, unsigned *n2_out, unsigned *p_out)
+hsw_ddi_calculate_wrpll(int clock /* in Hz */,
+ unsigned *r2_out, unsigned *n2_out, unsigned *p_out)
{
uint64_t freq2k;
unsigned p, n2, r2;
@@ -740,181 +740,54 @@ intel_ddi_calculate_wrpll(int clock /* in Hz */,
*r2_out = best.r2;
}
-/*
- * Tries to find a PLL for the CRTC. If it finds, it increases the refcount and
- * stores it in intel_crtc->ddi_pll_sel, so other mode sets won't be able to
- * steal the selected PLL. You need to call intel_ddi_pll_enable to actually
- * enable the PLL.
- */
-bool intel_ddi_pll_select(struct intel_crtc *intel_crtc)
+static bool
+hsw_ddi_pll_select(struct intel_crtc *intel_crtc,
+ struct intel_encoder *intel_encoder,
+ int clock)
{
- struct drm_crtc *crtc = &intel_crtc->base;
- struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
- struct drm_encoder *encoder = &intel_encoder->base;
- struct drm_i915_private *dev_priv = crtc->dev->dev_private;
- struct intel_ddi_plls *plls = &dev_priv->ddi_plls;
- int type = intel_encoder->type;
- enum pipe pipe = intel_crtc->pipe;
- int clock = intel_crtc->config.port_clock;
-
- intel_ddi_put_crtc_pll(crtc);
-
- if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
- struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
-
- switch (intel_dp->link_bw) {
- case DP_LINK_BW_1_62:
- intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_810;
- break;
- case DP_LINK_BW_2_7:
- intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350;
- break;
- case DP_LINK_BW_5_4:
- intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700;
- break;
- default:
- DRM_ERROR("Link bandwidth %d unsupported\n",
- intel_dp->link_bw);
- return false;
- }
-
- } else if (type == INTEL_OUTPUT_HDMI) {
- uint32_t reg, val;
+ if (intel_encoder->type == INTEL_OUTPUT_HDMI) {
+ struct intel_shared_dpll *pll;
+ uint32_t val;
unsigned p, n2, r2;
- intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p);
+ hsw_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p);
- val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 |
+ val = WRPLL_PLL_ENABLE | WRPLL_PLL_LCPLL |
WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) |
WRPLL_DIVIDER_POST(p);
- if (val == I915_READ(WRPLL_CTL1)) {
- DRM_DEBUG_KMS("Reusing WRPLL 1 on pipe %c\n",
- pipe_name(pipe));
- reg = WRPLL_CTL1;
- } else if (val == I915_READ(WRPLL_CTL2)) {
- DRM_DEBUG_KMS("Reusing WRPLL 2 on pipe %c\n",
- pipe_name(pipe));
- reg = WRPLL_CTL2;
- } else if (plls->wrpll1_refcount == 0) {
- DRM_DEBUG_KMS("Using WRPLL 1 on pipe %c\n",
- pipe_name(pipe));
- reg = WRPLL_CTL1;
- } else if (plls->wrpll2_refcount == 0) {
- DRM_DEBUG_KMS("Using WRPLL 2 on pipe %c\n",
- pipe_name(pipe));
- reg = WRPLL_CTL2;
- } else {
- DRM_ERROR("No WRPLLs available!\n");
- return false;
- }
-
- DRM_DEBUG_KMS("WRPLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n",
- clock, p, n2, r2);
+ intel_crtc->config.dpll_hw_state.wrpll = val;
- if (reg == WRPLL_CTL1) {
- plls->wrpll1_refcount++;
- intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL1;
- } else {
- plls->wrpll2_refcount++;
- intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL2;
- }
-
- } else if (type == INTEL_OUTPUT_ANALOG) {
- if (plls->spll_refcount == 0) {
- DRM_DEBUG_KMS("Using SPLL on pipe %c\n",
- pipe_name(pipe));
- plls->spll_refcount++;
- intel_crtc->ddi_pll_sel = PORT_CLK_SEL_SPLL;
- } else {
- DRM_ERROR("SPLL already in use\n");
+ pll = intel_get_shared_dpll(intel_crtc);
+ if (pll == NULL) {
+ DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n",
+ pipe_name(intel_crtc->pipe));
return false;
}
- } else {
- WARN(1, "Invalid DDI encoder type %d\n", type);
- return false;
+ intel_crtc->config.ddi_pll_sel = PORT_CLK_SEL_WRPLL(pll->id);
}
return true;
}
+
/*
- * To be called after intel_ddi_pll_select(). That one selects the PLL to be
- * used, this one actually enables the PLL.
+ * Tries to find a *shared* PLL for the CRTC and store it in
+ * intel_crtc->ddi_pll_sel.
+ *
+ * For private DPLLs, compute_config() should do the selection for us. This
+ * function should be folded into compute_config() eventually.
*/
-void intel_ddi_pll_enable(struct intel_crtc *crtc)
+bool intel_ddi_pll_select(struct intel_crtc *intel_crtc)
{
- struct drm_device *dev = crtc->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ddi_plls *plls = &dev_priv->ddi_plls;
- int clock = crtc->config.port_clock;
- uint32_t reg, cur_val, new_val;
- int refcount;
- const char *pll_name;
- uint32_t enable_bit = (1 << 31);
- unsigned int p, n2, r2;
-
- BUILD_BUG_ON(enable_bit != SPLL_PLL_ENABLE);
- BUILD_BUG_ON(enable_bit != WRPLL_PLL_ENABLE);
-
- switch (crtc->ddi_pll_sel) {
- case PORT_CLK_SEL_LCPLL_2700:
- case PORT_CLK_SEL_LCPLL_1350:
- case PORT_CLK_SEL_LCPLL_810:
- /*
- * LCPLL should always be enabled at this point of the mode set
- * sequence, so nothing to do.
- */
- return;
-
- case PORT_CLK_SEL_SPLL:
- pll_name = "SPLL";
- reg = SPLL_CTL;
- refcount = plls->spll_refcount;
- new_val = SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz |
- SPLL_PLL_SSC;
- break;
-
- case PORT_CLK_SEL_WRPLL1:
- case PORT_CLK_SEL_WRPLL2:
- if (crtc->ddi_pll_sel == PORT_CLK_SEL_WRPLL1) {
- pll_name = "WRPLL1";
- reg = WRPLL_CTL1;
- refcount = plls->wrpll1_refcount;
- } else {
- pll_name = "WRPLL2";
- reg = WRPLL_CTL2;
- refcount = plls->wrpll2_refcount;
- }
-
- intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p);
-
- new_val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 |
- WRPLL_DIVIDER_REFERENCE(r2) |
- WRPLL_DIVIDER_FEEDBACK(n2) | WRPLL_DIVIDER_POST(p);
-
- break;
-
- case PORT_CLK_SEL_NONE:
- WARN(1, "Bad selected pll: PORT_CLK_SEL_NONE\n");
- return;
- default:
- WARN(1, "Bad selected pll: 0x%08x\n", crtc->ddi_pll_sel);
- return;
- }
+ struct drm_crtc *crtc = &intel_crtc->base;
+ struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
+ int clock = intel_crtc->config.port_clock;
- cur_val = I915_READ(reg);
+ intel_put_shared_dpll(intel_crtc);
- WARN(refcount < 1, "Bad %s refcount: %d\n", pll_name, refcount);
- if (refcount == 1) {
- WARN(cur_val & enable_bit, "%s already enabled\n", pll_name);
- I915_WRITE(reg, new_val);
- POSTING_READ(reg);
- udelay(20);
- } else {
- WARN((cur_val & enable_bit) == 0, "%s disabled\n", pll_name);
- }
+ return hsw_ddi_pll_select(intel_crtc, intel_encoder, clock);
}
void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
@@ -926,8 +799,7 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
int type = intel_encoder->type;
uint32_t temp;
- if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
-
+ if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP || type == INTEL_OUTPUT_DP_MST) {
temp = TRANS_MSA_SYNC_CLK;
switch (intel_crtc->config.pipe_bpp) {
case 18:
@@ -949,6 +821,21 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
}
}
+void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
+ uint32_t temp;
+ temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
+ if (state == true)
+ temp |= TRANS_DDI_DP_VC_PAYLOAD_ALLOC;
+ else
+ temp &= ~TRANS_DDI_DP_VC_PAYLOAD_ALLOC;
+ I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp);
+}
+
void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
{
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -995,7 +882,9 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
* eDP when not using the panel fitter, and when not
* using motion blur mitigation (which we don't
* support). */
- if (IS_HASWELL(dev) && intel_crtc->config.pch_pfit.enabled)
+ if (IS_HASWELL(dev) &&
+ (intel_crtc->config.pch_pfit.enabled ||
+ intel_crtc->config.pch_pfit.force_thru))
temp |= TRANS_DDI_EDP_INPUT_A_ONOFF;
else
temp |= TRANS_DDI_EDP_INPUT_A_ON;
@@ -1026,7 +915,19 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
type == INTEL_OUTPUT_EDP) {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
- temp |= TRANS_DDI_MODE_SELECT_DP_SST;
+ if (intel_dp->is_mst) {
+ temp |= TRANS_DDI_MODE_SELECT_DP_MST;
+ } else
+ temp |= TRANS_DDI_MODE_SELECT_DP_SST;
+
+ temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
+ } else if (type == INTEL_OUTPUT_DP_MST) {
+ struct intel_dp *intel_dp = &enc_to_mst(encoder)->primary->dp;
+
+ if (intel_dp->is_mst) {
+ temp |= TRANS_DDI_MODE_SELECT_DP_MST;
+ } else
+ temp |= TRANS_DDI_MODE_SELECT_DP_SST;
temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
} else {
@@ -1043,7 +944,7 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder);
uint32_t val = I915_READ(reg);
- val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK);
+ val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK | TRANS_DDI_DP_VC_PAYLOAD_ALLOC);
val |= TRANS_DDI_PORT_NONE;
I915_WRITE(reg, val);
}
@@ -1082,8 +983,11 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
case TRANS_DDI_MODE_SELECT_DP_SST:
if (type == DRM_MODE_CONNECTOR_eDP)
return true;
- case TRANS_DDI_MODE_SELECT_DP_MST:
return (type == DRM_MODE_CONNECTOR_DisplayPort);
+ case TRANS_DDI_MODE_SELECT_DP_MST:
+ /* if the transcoder is in MST state then
+ * connector isn't connected */
+ return false;
case TRANS_DDI_MODE_SELECT_FDI:
return (type == DRM_MODE_CONNECTOR_VGA);
@@ -1135,6 +1039,9 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
if ((tmp & TRANS_DDI_PORT_MASK)
== TRANS_DDI_SELECT_PORT(port)) {
+ if ((tmp & TRANS_DDI_MODE_SELECT_MASK) == TRANS_DDI_MODE_SELECT_DP_MST)
+ return false;
+
*pipe = i;
return true;
}
@@ -1146,76 +1053,6 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
return false;
}
-static uint32_t intel_ddi_get_crtc_pll(struct drm_i915_private *dev_priv,
- enum pipe pipe)
-{
- uint32_t temp, ret;
- enum port port = I915_MAX_PORTS;
- enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
- pipe);
- int i;
-
- if (cpu_transcoder == TRANSCODER_EDP) {
- port = PORT_A;
- } else {
- temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
- temp &= TRANS_DDI_PORT_MASK;
-
- for (i = PORT_B; i <= PORT_E; i++)
- if (temp == TRANS_DDI_SELECT_PORT(i))
- port = i;
- }
-
- if (port == I915_MAX_PORTS) {
- WARN(1, "Pipe %c enabled on an unknown port\n",
- pipe_name(pipe));
- ret = PORT_CLK_SEL_NONE;
- } else {
- ret = I915_READ(PORT_CLK_SEL(port));
- DRM_DEBUG_KMS("Pipe %c connected to port %c using clock "
- "0x%08x\n", pipe_name(pipe), port_name(port),
- ret);
- }
-
- return ret;
-}
-
-void intel_ddi_setup_hw_pll_state(struct drm_device *dev)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
- enum pipe pipe;
- struct intel_crtc *intel_crtc;
-
- dev_priv->ddi_plls.spll_refcount = 0;
- dev_priv->ddi_plls.wrpll1_refcount = 0;
- dev_priv->ddi_plls.wrpll2_refcount = 0;
-
- for_each_pipe(pipe) {
- intel_crtc =
- to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
-
- if (!intel_crtc->active) {
- intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE;
- continue;
- }
-
- intel_crtc->ddi_pll_sel = intel_ddi_get_crtc_pll(dev_priv,
- pipe);
-
- switch (intel_crtc->ddi_pll_sel) {
- case PORT_CLK_SEL_SPLL:
- dev_priv->ddi_plls.spll_refcount++;
- break;
- case PORT_CLK_SEL_WRPLL1:
- dev_priv->ddi_plls.wrpll1_refcount++;
- break;
- case PORT_CLK_SEL_WRPLL2:
- dev_priv->ddi_plls.wrpll2_refcount++;
- break;
- }
- }
-}
-
void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc)
{
struct drm_crtc *crtc = &intel_crtc->base;
@@ -1261,17 +1098,13 @@ static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder)
intel_edp_panel_on(intel_dp);
}
- WARN_ON(crtc->ddi_pll_sel == PORT_CLK_SEL_NONE);
- I915_WRITE(PORT_CLK_SEL(port), crtc->ddi_pll_sel);
+ WARN_ON(crtc->config.ddi_pll_sel == PORT_CLK_SEL_NONE);
+ I915_WRITE(PORT_CLK_SEL(port), crtc->config.ddi_pll_sel);
if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
- struct intel_digital_port *intel_dig_port =
- enc_to_dig_port(encoder);
- intel_dp->DP = intel_dig_port->saved_port_bits |
- DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
- intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
+ intel_ddi_init_dp_buf_reg(intel_encoder);
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
intel_dp_start_link_train(intel_dp);
@@ -1391,30 +1224,105 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder)
}
}
-int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv)
+static int bdw_get_cdclk_freq(struct drm_i915_private *dev_priv)
+{
+ uint32_t lcpll = I915_READ(LCPLL_CTL);
+ uint32_t freq = lcpll & LCPLL_CLK_FREQ_MASK;
+
+ if (lcpll & LCPLL_CD_SOURCE_FCLK)
+ return 800000;
+ else if (I915_READ(FUSE_STRAP) & HSW_CDCLK_LIMIT)
+ return 450000;
+ else if (freq == LCPLL_CLK_FREQ_450)
+ return 450000;
+ else if (freq == LCPLL_CLK_FREQ_54O_BDW)
+ return 540000;
+ else if (freq == LCPLL_CLK_FREQ_337_5_BDW)
+ return 337500;
+ else
+ return 675000;
+}
+
+static int hsw_get_cdclk_freq(struct drm_i915_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
uint32_t lcpll = I915_READ(LCPLL_CTL);
uint32_t freq = lcpll & LCPLL_CLK_FREQ_MASK;
- if (lcpll & LCPLL_CD_SOURCE_FCLK) {
+ if (lcpll & LCPLL_CD_SOURCE_FCLK)
return 800000;
- } else if (I915_READ(FUSE_STRAP) & HSW_CDCLK_LIMIT) {
+ else if (I915_READ(FUSE_STRAP) & HSW_CDCLK_LIMIT)
return 450000;
- } else if (freq == LCPLL_CLK_FREQ_450) {
+ else if (freq == LCPLL_CLK_FREQ_450)
return 450000;
- } else if (IS_HASWELL(dev)) {
- if (IS_ULT(dev))
- return 337500;
- else
- return 540000;
- } else {
- if (freq == LCPLL_CLK_FREQ_54O_BDW)
- return 540000;
- else if (freq == LCPLL_CLK_FREQ_337_5_BDW)
- return 337500;
- else
- return 675000;
+ else if (IS_ULT(dev))
+ return 337500;
+ else
+ return 540000;
+}
+
+int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = dev_priv->dev;
+
+ if (IS_BROADWELL(dev))
+ return bdw_get_cdclk_freq(dev_priv);
+
+ /* Haswell */
+ return hsw_get_cdclk_freq(dev_priv);
+}
+
+static void hsw_ddi_pll_enable(struct drm_i915_private *dev_priv,
+ struct intel_shared_dpll *pll)
+{
+ I915_WRITE(WRPLL_CTL(pll->id), pll->hw_state.wrpll);
+ POSTING_READ(WRPLL_CTL(pll->id));
+ udelay(20);
+}
+
+static void hsw_ddi_pll_disable(struct drm_i915_private *dev_priv,
+ struct intel_shared_dpll *pll)
+{
+ uint32_t val;
+
+ val = I915_READ(WRPLL_CTL(pll->id));
+ I915_WRITE(WRPLL_CTL(pll->id), val & ~WRPLL_PLL_ENABLE);
+ POSTING_READ(WRPLL_CTL(pll->id));
+}
+
+static bool hsw_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
+ struct intel_shared_dpll *pll,
+ struct intel_dpll_hw_state *hw_state)
+{
+ uint32_t val;
+
+ if (!intel_display_power_enabled(dev_priv, POWER_DOMAIN_PLLS))
+ return false;
+
+ val = I915_READ(WRPLL_CTL(pll->id));
+ hw_state->wrpll = val;
+
+ return val & WRPLL_PLL_ENABLE;
+}
+
+static const char * const hsw_ddi_pll_names[] = {
+ "WRPLL 1",
+ "WRPLL 2",
+};
+
+static void hsw_shared_dplls_init(struct drm_i915_private *dev_priv)
+{
+ int i;
+
+ dev_priv->num_shared_dpll = 2;
+
+ for (i = 0; i < dev_priv->num_shared_dpll; i++) {
+ dev_priv->shared_dplls[i].id = i;
+ dev_priv->shared_dplls[i].name = hsw_ddi_pll_names[i];
+ dev_priv->shared_dplls[i].disable = hsw_ddi_pll_disable;
+ dev_priv->shared_dplls[i].enable = hsw_ddi_pll_enable;
+ dev_priv->shared_dplls[i].get_hw_state =
+ hsw_ddi_pll_get_hw_state;
}
}
@@ -1423,6 +1331,8 @@ void intel_ddi_pll_init(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t val = I915_READ(LCPLL_CTL);
+ hsw_shared_dplls_init(dev_priv);
+
/* The LCPLL register should be turned on by the BIOS. For now let's
* just check its state and print errors in case something is wrong.
* Don't even try to turn it on.
@@ -1465,10 +1375,15 @@ void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
intel_wait_ddi_buf_idle(dev_priv, port);
}
- val = DP_TP_CTL_ENABLE | DP_TP_CTL_MODE_SST |
+ val = DP_TP_CTL_ENABLE |
DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE;
- if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
- val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
+ if (intel_dp->is_mst)
+ val |= DP_TP_CTL_MODE_MST;
+ else {
+ val |= DP_TP_CTL_MODE_SST;
+ if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
+ val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
+ }
I915_WRITE(DP_TP_CTL(port), val);
POSTING_READ(DP_TP_CTL(port));
@@ -1507,11 +1422,16 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc)
static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder)
{
- struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
- int type = intel_encoder->type;
+ struct intel_digital_port *intel_dig_port = enc_to_dig_port(&intel_encoder->base);
+ int type = intel_dig_port->base.type;
- if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP)
- intel_dp_check_link_status(intel_dp);
+ if (type != INTEL_OUTPUT_DISPLAYPORT &&
+ type != INTEL_OUTPUT_EDP &&
+ type != INTEL_OUTPUT_UNKNOWN) {
+ return;
+ }
+
+ intel_dp_hot_plug(intel_encoder);
}
void intel_ddi_get_config(struct intel_encoder *encoder,
@@ -1592,7 +1512,7 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
dev_priv->vbt.edp_bpp = pipe_config->pipe_bpp;
}
- intel_ddi_clock_get(encoder, pipe_config);
+ hsw_ddi_clock_get(encoder, pipe_config);
}
static void intel_ddi_destroy(struct drm_encoder *encoder)
@@ -1663,15 +1583,13 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
struct intel_digital_port *intel_dig_port;
struct intel_encoder *intel_encoder;
struct drm_encoder *encoder;
- struct intel_connector *hdmi_connector = NULL;
- struct intel_connector *dp_connector = NULL;
bool init_hdmi, init_dp;
init_hdmi = (dev_priv->vbt.ddi_port_info[port].supports_dvi ||
dev_priv->vbt.ddi_port_info[port].supports_hdmi);
init_dp = dev_priv->vbt.ddi_port_info[port].supports_dp;
if (!init_dp && !init_hdmi) {
- DRM_DEBUG_KMS("VBT says port %c is not DVI/HDMI/DP compatible\n",
+ DRM_DEBUG_KMS("VBT says port %c is not DVI/HDMI/DP compatible, assuming it is\n",
port_name(port));
init_hdmi = true;
init_dp = true;
@@ -1701,20 +1619,28 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
DDI_A_4_LANES);
intel_encoder->type = INTEL_OUTPUT_UNKNOWN;
- intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
+ intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
intel_encoder->cloneable = 0;
intel_encoder->hot_plug = intel_ddi_hot_plug;
- if (init_dp)
- dp_connector = intel_ddi_init_dp_connector(intel_dig_port);
+ if (init_dp) {
+ if (!intel_ddi_init_dp_connector(intel_dig_port))
+ goto err;
+
+ intel_dig_port->hpd_pulse = intel_dp_hpd_pulse;
+ dev_priv->hpd_irq_port[port] = intel_dig_port;
+ }
/* In theory we don't need the encoder->type check, but leave it just in
* case we have some really bad VBTs... */
- if (intel_encoder->type != INTEL_OUTPUT_EDP && init_hdmi)
- hdmi_connector = intel_ddi_init_hdmi_connector(intel_dig_port);
-
- if (!dp_connector && !hdmi_connector) {
- drm_encoder_cleanup(encoder);
- kfree(intel_dig_port);
+ if (intel_encoder->type != INTEL_OUTPUT_EDP && init_hdmi) {
+ if (!intel_ddi_init_hdmi_connector(intel_dig_port))
+ goto err;
}
+
+ return;
+
+err:
+ drm_encoder_cleanup(encoder);
+ kfree(intel_dig_port);
}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index f0be855ddf45..f0a1a56406eb 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -39,12 +39,42 @@
#include "i915_trace.h"
#include <drm/drm_dp_helper.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_rect.h>
#include <linux/dma_remapping.h>
-#define DIV_ROUND_CLOSEST_ULL(ll, d) \
- ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; })
+/* Primary plane formats supported by all gen */
+#define COMMON_PRIMARY_FORMATS \
+ DRM_FORMAT_C8, \
+ DRM_FORMAT_RGB565, \
+ DRM_FORMAT_XRGB8888, \
+ DRM_FORMAT_ARGB8888
+
+/* Primary plane formats for gen <= 3 */
+static const uint32_t intel_primary_formats_gen2[] = {
+ COMMON_PRIMARY_FORMATS,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_ARGB1555,
+};
+
+/* Primary plane formats for gen >= 4 */
+static const uint32_t intel_primary_formats_gen4[] = {
+ COMMON_PRIMARY_FORMATS, \
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_XRGB2101010,
+ DRM_FORMAT_ARGB2101010,
+ DRM_FORMAT_XBGR2101010,
+ DRM_FORMAT_ABGR2101010,
+};
+
+/* Cursor formats */
+static const uint32_t intel_cursor_formats[] = {
+ DRM_FORMAT_ARGB8888,
+};
-static void intel_increase_pllclock(struct drm_crtc *crtc);
+static void intel_increase_pllclock(struct drm_device *dev,
+ enum pipe pipe);
static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on);
static void i9xx_crtc_clock_get(struct intel_crtc *crtc,
@@ -58,15 +88,24 @@ static int intel_framebuffer_init(struct drm_device *dev,
struct intel_framebuffer *ifb,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_i915_gem_object *obj);
-static void intel_dp_set_m_n(struct intel_crtc *crtc);
static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc);
static void intel_set_pipe_timings(struct intel_crtc *intel_crtc);
static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc,
- struct intel_link_m_n *m_n);
+ struct intel_link_m_n *m_n,
+ struct intel_link_m_n *m2_n2);
static void ironlake_set_pipeconf(struct drm_crtc *crtc);
static void haswell_set_pipeconf(struct drm_crtc *crtc);
static void intel_set_pipe_csc(struct drm_crtc *crtc);
static void vlv_prepare_pll(struct intel_crtc *crtc);
+static void chv_prepare_pll(struct intel_crtc *crtc);
+
+static struct intel_encoder *intel_find_encoder(struct intel_connector *connector, int pipe)
+{
+ if (!connector->mst_port)
+ return connector->encoder;
+ else
+ return &connector->mst_port->mst_encoders[pipe]->base;
+}
typedef struct {
int min, max;
@@ -858,7 +897,8 @@ static void g4x_wait_for_vblank(struct drm_device *dev, int pipe)
frame = I915_READ(frame_reg);
if (wait_for(I915_READ_NOTRACE(frame_reg) != frame, 50))
- WARN(1, "vblank wait timed out\n");
+ WARN(1, "vblank wait on pipe %c timed out\n",
+ pipe_name(pipe));
}
/**
@@ -899,7 +939,8 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe)
if (wait_for(I915_READ(pipestat_reg) &
PIPE_VBLANK_INTERRUPT_STATUS,
50))
- DRM_DEBUG_KMS("vblank wait timed out\n");
+ DRM_DEBUG_KMS("vblank wait on pipe %c timed out\n",
+ pipe_name(pipe));
}
static bool pipe_dsl_stopped(struct drm_device *dev, enum pipe pipe)
@@ -923,8 +964,7 @@ static bool pipe_dsl_stopped(struct drm_device *dev, enum pipe pipe)
/*
* intel_wait_for_pipe_off - wait for pipe to turn off
- * @dev: drm device
- * @pipe: pipe to wait for
+ * @crtc: crtc whose pipe to wait for
*
* After disabling a pipe, we can't wait for vblank in the usual way,
* spinning on the vblank interrupt status bit, since we won't actually
@@ -938,11 +978,12 @@ static bool pipe_dsl_stopped(struct drm_device *dev, enum pipe pipe)
* ends up stopping at the start of the next frame).
*
*/
-void intel_wait_for_pipe_off(struct drm_device *dev, int pipe)
+static void intel_wait_for_pipe_off(struct intel_crtc *crtc)
{
+ struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
- pipe);
+ enum transcoder cpu_transcoder = crtc->config.cpu_transcoder;
+ enum pipe pipe = crtc->pipe;
if (INTEL_INFO(dev)->gen >= 4) {
int reg = PIPECONF(cpu_transcoder);
@@ -1061,11 +1102,6 @@ void assert_shared_dpll(struct drm_i915_private *dev_priv,
bool cur_state;
struct intel_dpll_hw_state hw_state;
- if (HAS_PCH_LPT(dev_priv->dev)) {
- DRM_DEBUG_DRIVER("LPT detected: skipping PCH PLL test\n");
- return;
- }
-
if (WARN (!pll,
"asserting DPLL %s with no DPLL\n", state_string(state)))
return;
@@ -1156,27 +1192,40 @@ void assert_fdi_rx_pll(struct drm_i915_private *dev_priv,
static void assert_panel_unlocked(struct drm_i915_private *dev_priv,
enum pipe pipe)
{
- int pp_reg, lvds_reg;
+ struct drm_device *dev = dev_priv->dev;
+ int pp_reg;
u32 val;
enum pipe panel_pipe = PIPE_A;
bool locked = true;
- if (HAS_PCH_SPLIT(dev_priv->dev)) {
+ if (WARN_ON(HAS_DDI(dev)))
+ return;
+
+ if (HAS_PCH_SPLIT(dev)) {
+ u32 port_sel;
+
pp_reg = PCH_PP_CONTROL;
- lvds_reg = PCH_LVDS;
+ port_sel = I915_READ(PCH_PP_ON_DELAYS) & PANEL_PORT_SELECT_MASK;
+
+ if (port_sel == PANEL_PORT_SELECT_LVDS &&
+ I915_READ(PCH_LVDS) & LVDS_PIPEB_SELECT)
+ panel_pipe = PIPE_B;
+ /* XXX: else fix for eDP */
+ } else if (IS_VALLEYVIEW(dev)) {
+ /* presumably write lock depends on pipe, not port select */
+ pp_reg = VLV_PIPE_PP_CONTROL(pipe);
+ panel_pipe = pipe;
} else {
pp_reg = PP_CONTROL;
- lvds_reg = LVDS;
+ if (I915_READ(LVDS) & LVDS_PIPEB_SELECT)
+ panel_pipe = PIPE_B;
}
val = I915_READ(pp_reg);
if (!(val & PANEL_POWER_ON) ||
- ((val & PANEL_UNLOCK_REGS) == PANEL_UNLOCK_REGS))
+ ((val & PANEL_UNLOCK_MASK) == PANEL_UNLOCK_REGS))
locked = false;
- if (I915_READ(lvds_reg) & LVDS_PIPEB_SELECT)
- panel_pipe = PIPE_B;
-
WARN(panel_pipe == pipe && locked,
"panel assertion failure, pipe %c regs locked\n",
pipe_name(pipe));
@@ -1209,8 +1258,9 @@ void assert_pipe(struct drm_i915_private *dev_priv,
enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
pipe);
- /* if we need the pipe A quirk it must be always on */
- if (pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE)
+ /* if we need the pipe quirk it must be always on */
+ if ((pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) ||
+ (pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE))
state = true;
if (!intel_display_power_enabled(dev_priv,
@@ -1264,7 +1314,7 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv,
}
/* Need to check both planes against the pipe */
- for_each_pipe(i) {
+ for_each_pipe(dev_priv, i) {
reg = DSPCNTR(i);
val = I915_READ(reg);
cur_pipe = (val & DISPPLANE_SEL_PIPE_MASK) >>
@@ -1305,6 +1355,12 @@ static void assert_sprites_disabled(struct drm_i915_private *dev_priv,
}
}
+static void assert_vblank_disabled(struct drm_crtc *crtc)
+{
+ if (WARN_ON(drm_crtc_vblank_get(crtc) == 0))
+ drm_crtc_vblank_put(crtc);
+}
+
static void ibx_assert_pch_refclk_enabled(struct drm_i915_private *dev_priv)
{
u32 val;
@@ -1477,57 +1533,6 @@ static void intel_init_dpio(struct drm_device *dev)
}
}
-static void intel_reset_dpio(struct drm_device *dev)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
- if (!IS_VALLEYVIEW(dev))
- return;
-
- if (IS_CHERRYVIEW(dev)) {
- enum dpio_phy phy;
- u32 val;
-
- for (phy = DPIO_PHY0; phy < I915_NUM_PHYS_VLV; phy++) {
- /* Poll for phypwrgood signal */
- if (wait_for(I915_READ(DISPLAY_PHY_STATUS) &
- PHY_POWERGOOD(phy), 1))
- DRM_ERROR("Display PHY %d is not power up\n", phy);
-
- /*
- * Deassert common lane reset for PHY.
- *
- * This should only be done on init and resume from S3
- * with both PLLs disabled, or we risk losing DPIO and
- * PLL synchronization.
- */
- val = I915_READ(DISPLAY_PHY_CONTROL);
- I915_WRITE(DISPLAY_PHY_CONTROL,
- PHY_COM_LANE_RESET_DEASSERT(phy, val));
- }
-
- } else {
- /*
- * If DPIO has already been reset, e.g. by BIOS, just skip all
- * this.
- */
- if (I915_READ(DPIO_CTL) & DPIO_CMNRST)
- return;
-
- /*
- * From VLV2A0_DP_eDP_HDMI_DPIO_driver_vbios_notes_11.docx:
- * Need to assert and de-assert PHY SB reset by gating the
- * common lane power, then un-gating it.
- * Simply ungating isn't enough to reset the PHY enough to get
- * ports and lanes running.
- */
- __vlv_set_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC,
- false);
- __vlv_set_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC,
- true);
- }
-}
-
static void vlv_enable_pll(struct intel_crtc *crtc)
{
struct drm_device *dev = crtc->base.dev;
@@ -1541,7 +1546,7 @@ static void vlv_enable_pll(struct intel_crtc *crtc)
BUG_ON(!IS_VALLEYVIEW(dev_priv->dev));
/* PLL is protected by panel, make sure we can write it */
- if (IS_MOBILE(dev_priv->dev) && !IS_I830(dev_priv->dev))
+ if (IS_MOBILE(dev_priv->dev))
assert_panel_unlocked(dev_priv, crtc->pipe);
I915_WRITE(reg, dpll);
@@ -1604,6 +1609,18 @@ static void chv_enable_pll(struct intel_crtc *crtc)
mutex_unlock(&dev_priv->dpio_lock);
}
+static int intel_num_dvo_pipes(struct drm_device *dev)
+{
+ struct intel_crtc *crtc;
+ int count = 0;
+
+ for_each_intel_crtc(dev, crtc)
+ count += crtc->active &&
+ intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DVO);
+
+ return count;
+}
+
static void i9xx_enable_pll(struct intel_crtc *crtc)
{
struct drm_device *dev = crtc->base.dev;
@@ -1620,7 +1637,18 @@ static void i9xx_enable_pll(struct intel_crtc *crtc)
if (IS_MOBILE(dev) && !IS_I830(dev))
assert_panel_unlocked(dev_priv, crtc->pipe);
- I915_WRITE(reg, dpll);
+ /* Enable DVO 2x clock on both PLLs if necessary */
+ if (IS_I830(dev) && intel_num_dvo_pipes(dev) > 0) {
+ /*
+ * It appears to be important that we don't enable this
+ * for the current pipe before otherwise configuring the
+ * PLL. No idea how this should be handled if multiple
+ * DVO outputs are enabled simultaneosly.
+ */
+ dpll |= DPLL_DVO_2X_MODE;
+ I915_WRITE(DPLL(!crtc->pipe),
+ I915_READ(DPLL(!crtc->pipe)) | DPLL_DVO_2X_MODE);
+ }
/* Wait for the clocks to stabilize. */
POSTING_READ(reg);
@@ -1659,10 +1687,25 @@ static void i9xx_enable_pll(struct intel_crtc *crtc)
*
* Note! This is for pre-ILK only.
*/
-static void i9xx_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
+static void i9xx_disable_pll(struct intel_crtc *crtc)
{
- /* Don't disable pipe A or pipe A PLLs if needed */
- if (pipe == PIPE_A && (dev_priv->quirks & QUIRK_PIPEA_FORCE))
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum pipe pipe = crtc->pipe;
+
+ /* Disable DVO 2x clock on both PLLs if necessary */
+ if (IS_I830(dev) &&
+ intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DVO) &&
+ intel_num_dvo_pipes(dev) == 1) {
+ I915_WRITE(DPLL(PIPE_B),
+ I915_READ(DPLL(PIPE_B)) & ~DPLL_DVO_2X_MODE);
+ I915_WRITE(DPLL(PIPE_A),
+ I915_READ(DPLL(PIPE_A)) & ~DPLL_DVO_2X_MODE);
+ }
+
+ /* Don't disable pipe or pipe PLLs if needed */
+ if ((pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) ||
+ (pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE))
return;
/* Make sure the pipe isn't still relying on us */
@@ -1699,7 +1742,7 @@ static void chv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
assert_pipe_disabled(dev_priv, pipe);
/* Set PLL en = 0 */
- val = DPLL_SSC_REF_CLOCK_CHV;
+ val = DPLL_SSC_REF_CLOCK_CHV | DPLL_REFA_CLK_ENABLE_VLV;
if (pipe != PIPE_A)
val |= DPLL_INTEGRATED_CRI_CLK_VLV;
I915_WRITE(DPLL(pipe), val);
@@ -1712,6 +1755,17 @@ static void chv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
val &= ~DPIO_DCLKP_EN;
vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), val);
+ /* disable left/right clock distribution */
+ if (pipe != PIPE_B) {
+ val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0);
+ val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK);
+ vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val);
+ } else {
+ val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1);
+ val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK);
+ vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val);
+ }
+
mutex_unlock(&dev_priv->dpio_lock);
}
@@ -1749,6 +1803,9 @@ static void intel_prepare_shared_dpll(struct intel_crtc *crtc)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
+ if (WARN_ON(pll == NULL))
+ return;
+
WARN_ON(!pll->refcount);
if (pll->active == 0) {
DRM_DEBUG_DRIVER("setting up %s\n", pll->name);
@@ -1779,7 +1836,7 @@ static void intel_enable_shared_dpll(struct intel_crtc *crtc)
if (WARN_ON(pll->refcount == 0))
return;
- DRM_DEBUG_KMS("enable %s (active %d, on? %d)for crtc %d\n",
+ DRM_DEBUG_KMS("enable %s (active %d, on? %d) for crtc %d\n",
pll->name, pll->active, pll->on,
crtc->base.base.id);
@@ -1790,6 +1847,8 @@ static void intel_enable_shared_dpll(struct intel_crtc *crtc)
}
WARN_ON(pll->on);
+ intel_display_power_get(dev_priv, POWER_DOMAIN_PLLS);
+
DRM_DEBUG_KMS("enabling %s\n", pll->name);
pll->enable(dev_priv, pll);
pll->on = true;
@@ -1826,6 +1885,8 @@ static void intel_disable_shared_dpll(struct intel_crtc *crtc)
DRM_DEBUG_KMS("disabling %s\n", pll->name);
pll->disable(dev_priv, pll);
pll->on = false;
+
+ intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
}
static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv,
@@ -1837,7 +1898,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv,
uint32_t reg, val, pipeconf_val;
/* PCH only available on ILK+ */
- BUG_ON(INTEL_INFO(dev)->gen < 5);
+ BUG_ON(!HAS_PCH_SPLIT(dev));
/* Make sure PCH DPLL is enabled */
assert_shared_dpll_enabled(dev_priv,
@@ -1890,7 +1951,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv,
u32 val, pipeconf_val;
/* PCH only available on ILK+ */
- BUG_ON(INTEL_INFO(dev_priv->dev)->gen < 5);
+ BUG_ON(!HAS_PCH_SPLIT(dev_priv->dev));
/* FDI must be feeding us bits for PCH ports */
assert_fdi_tx_enabled(dev_priv, (enum pipe) cpu_transcoder);
@@ -2012,8 +2073,8 @@ static void intel_enable_pipe(struct intel_crtc *crtc)
reg = PIPECONF(cpu_transcoder);
val = I915_READ(reg);
if (val & PIPECONF_ENABLE) {
- WARN_ON(!(pipe == PIPE_A &&
- dev_priv->quirks & QUIRK_PIPEA_FORCE));
+ WARN_ON(!((pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) ||
+ (pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)));
return;
}
@@ -2023,21 +2084,19 @@ static void intel_enable_pipe(struct intel_crtc *crtc)
/**
* intel_disable_pipe - disable a pipe, asserting requirements
- * @dev_priv: i915 private structure
- * @pipe: pipe to disable
- *
- * Disable @pipe, making sure that various hardware specific requirements
- * are met, if applicable, e.g. plane disabled, panel fitter off, etc.
+ * @crtc: crtc whose pipes is to be disabled
*
- * @pipe should be %PIPE_A or %PIPE_B.
+ * Disable the pipe of @crtc, making sure that various hardware
+ * specific requirements are met, if applicable, e.g. plane
+ * disabled, panel fitter off, etc.
*
* Will wait until the pipe has shut down before returning.
*/
-static void intel_disable_pipe(struct drm_i915_private *dev_priv,
- enum pipe pipe)
+static void intel_disable_pipe(struct intel_crtc *crtc)
{
- enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
- pipe);
+ struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+ enum transcoder cpu_transcoder = crtc->config.cpu_transcoder;
+ enum pipe pipe = crtc->pipe;
int reg;
u32 val;
@@ -2049,17 +2108,26 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv,
assert_cursor_disabled(dev_priv, pipe);
assert_sprites_disabled(dev_priv, pipe);
- /* Don't disable pipe A or pipe A PLLs if needed */
- if (pipe == PIPE_A && (dev_priv->quirks & QUIRK_PIPEA_FORCE))
- return;
-
reg = PIPECONF(cpu_transcoder);
val = I915_READ(reg);
if ((val & PIPECONF_ENABLE) == 0)
return;
- I915_WRITE(reg, val & ~PIPECONF_ENABLE);
- intel_wait_for_pipe_off(dev_priv->dev, pipe);
+ /*
+ * Double wide has implications for planes
+ * so best keep it disabled when not needed.
+ */
+ if (crtc->config.double_wide)
+ val &= ~PIPECONF_DOUBLE_WIDE;
+
+ /* Don't disable pipe or pipe PLLs if needed */
+ if (!(pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) &&
+ !(pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE))
+ val &= ~PIPECONF_ENABLE;
+
+ I915_WRITE(reg, val);
+ if ((val & PIPECONF_ENABLE) == 0)
+ intel_wait_for_pipe_off(crtc);
}
/*
@@ -2078,35 +2146,28 @@ void intel_flush_primary_plane(struct drm_i915_private *dev_priv,
/**
* intel_enable_primary_hw_plane - enable the primary plane on a given pipe
- * @dev_priv: i915 private structure
- * @plane: plane to enable
- * @pipe: pipe being fed
+ * @plane: plane to be enabled
+ * @crtc: crtc for the plane
*
- * Enable @plane on @pipe, making sure that @pipe is running first.
+ * Enable @plane on @crtc, making sure that the pipe is running first.
*/
-static void intel_enable_primary_hw_plane(struct drm_i915_private *dev_priv,
- enum plane plane, enum pipe pipe)
+static void intel_enable_primary_hw_plane(struct drm_plane *plane,
+ struct drm_crtc *crtc)
{
- struct drm_device *dev = dev_priv->dev;
- struct intel_crtc *intel_crtc =
- to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
- int reg;
- u32 val;
+ struct drm_device *dev = plane->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
/* If the pipe isn't enabled, we can't pump pixels and may hang */
- assert_pipe_enabled(dev_priv, pipe);
+ assert_pipe_enabled(dev_priv, intel_crtc->pipe);
if (intel_crtc->primary_enabled)
return;
intel_crtc->primary_enabled = true;
- reg = DSPCNTR(plane);
- val = I915_READ(reg);
- WARN_ON(val & DISPLAY_PLANE_ENABLE);
-
- I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE);
- intel_flush_primary_plane(dev_priv, plane);
+ dev_priv->display.update_primary_plane(crtc, plane->fb,
+ crtc->x, crtc->y);
/*
* BDW signals flip done immediately if the plane
@@ -2119,31 +2180,27 @@ static void intel_enable_primary_hw_plane(struct drm_i915_private *dev_priv,
/**
* intel_disable_primary_hw_plane - disable the primary hardware plane
- * @dev_priv: i915 private structure
- * @plane: plane to disable
- * @pipe: pipe consuming the data
+ * @plane: plane to be disabled
+ * @crtc: crtc for the plane
*
- * Disable @plane; should be an independent operation.
+ * Disable @plane on @crtc, making sure that the pipe is running first.
*/
-static void intel_disable_primary_hw_plane(struct drm_i915_private *dev_priv,
- enum plane plane, enum pipe pipe)
+static void intel_disable_primary_hw_plane(struct drm_plane *plane,
+ struct drm_crtc *crtc)
{
- struct intel_crtc *intel_crtc =
- to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
- int reg;
- u32 val;
+ struct drm_device *dev = plane->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ assert_pipe_enabled(dev_priv, intel_crtc->pipe);
if (!intel_crtc->primary_enabled)
return;
intel_crtc->primary_enabled = false;
- reg = DSPCNTR(plane);
- val = I915_READ(reg);
- WARN_ON((val & DISPLAY_PLANE_ENABLE) == 0);
-
- I915_WRITE(reg, val & ~DISPLAY_PLANE_ENABLE);
- intel_flush_primary_plane(dev_priv, plane);
+ dev_priv->display.update_primary_plane(crtc, plane->fb,
+ crtc->x, crtc->y);
}
static bool need_vtd_wa(struct drm_device *dev)
@@ -2172,6 +2229,8 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev,
u32 alignment;
int ret;
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
switch (obj->tiling_mode) {
case I915_TILING_NONE:
if (IS_BROADWATER(dev) || IS_CRESTLINE(dev))
@@ -2200,6 +2259,15 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev,
if (need_vtd_wa(dev) && alignment < 256 * 1024)
alignment = 256 * 1024;
+ /*
+ * Global gtt pte registers are special registers which actually forward
+ * writes to a chunk of system memory. Which means that there is no risk
+ * that the register values disappear as soon as we call
+ * intel_runtime_pm_put(), so it is correct to wrap only the
+ * pin/unpin/fence and not more.
+ */
+ intel_runtime_pm_get(dev_priv);
+
dev_priv->mm.interruptible = false;
ret = i915_gem_object_pin_to_display_plane(obj, alignment, pipelined);
if (ret)
@@ -2217,17 +2285,21 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev,
i915_gem_object_pin_fence(obj);
dev_priv->mm.interruptible = true;
+ intel_runtime_pm_put(dev_priv);
return 0;
err_unpin:
i915_gem_object_unpin_from_display_plane(obj);
err_interruptible:
dev_priv->mm.interruptible = true;
+ intel_runtime_pm_put(dev_priv);
return ret;
}
void intel_unpin_fb_obj(struct drm_i915_gem_object *obj)
{
+ WARN_ON(!mutex_is_locked(&obj->base.dev->struct_mutex));
+
i915_gem_object_unpin_fence(obj);
i915_gem_object_unpin_from_display_plane(obj);
}
@@ -2314,6 +2386,7 @@ static bool intel_alloc_plane_obj(struct intel_crtc *crtc,
goto out_unref_obj;
}
+ obj->frontbuffer_bits = INTEL_FRONTBUFFER_PRIMARY(crtc->pipe);
mutex_unlock(&dev->struct_mutex);
DRM_DEBUG_KMS("plane fb obj %p\n", obj);
@@ -2331,7 +2404,7 @@ static void intel_find_plane_obj(struct intel_crtc *intel_crtc,
struct drm_device *dev = intel_crtc->base.dev;
struct drm_crtc *c;
struct intel_crtc *i;
- struct intel_framebuffer *fb;
+ struct drm_i915_gem_object *obj;
if (!intel_crtc->base.primary->fb)
return;
@@ -2352,13 +2425,17 @@ static void intel_find_plane_obj(struct intel_crtc *intel_crtc,
if (c == &intel_crtc->base)
continue;
- if (!i->active || !c->primary->fb)
+ if (!i->active)
+ continue;
+
+ obj = intel_fb_obj(c->primary->fb);
+ if (obj == NULL)
continue;
- fb = to_intel_framebuffer(c->primary->fb);
- if (i915_gem_obj_ggtt_offset(fb->obj) == plane_config->base) {
+ if (i915_gem_obj_ggtt_offset(obj) == plane_config->base) {
drm_framebuffer_reference(c->primary->fb);
intel_crtc->base.primary->fb = c->primary->fb;
+ obj->frontbuffer_bits |= INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe);
break;
}
}
@@ -2371,20 +2448,46 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- struct intel_framebuffer *intel_fb;
struct drm_i915_gem_object *obj;
int plane = intel_crtc->plane;
unsigned long linear_offset;
u32 dspcntr;
- u32 reg;
+ u32 reg = DSPCNTR(plane);
+ int pixel_size;
- intel_fb = to_intel_framebuffer(fb);
- obj = intel_fb->obj;
+ if (!intel_crtc->primary_enabled) {
+ I915_WRITE(reg, 0);
+ if (INTEL_INFO(dev)->gen >= 4)
+ I915_WRITE(DSPSURF(plane), 0);
+ else
+ I915_WRITE(DSPADDR(plane), 0);
+ POSTING_READ(reg);
+ return;
+ }
+
+ obj = intel_fb_obj(fb);
+ if (WARN_ON(obj == NULL))
+ return;
+
+ pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
+
+ dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+ dspcntr |= DISPLAY_PLANE_ENABLE;
+
+ if (INTEL_INFO(dev)->gen < 4) {
+ if (intel_crtc->pipe == PIPE_B)
+ dspcntr |= DISPPLANE_SEL_PIPE_B;
+
+ /* pipesrc and dspsize control the size that is scaled from,
+ * which should always be the user's requested size.
+ */
+ I915_WRITE(DSPSIZE(plane),
+ ((intel_crtc->config.pipe_src_h - 1) << 16) |
+ (intel_crtc->config.pipe_src_w - 1));
+ I915_WRITE(DSPPOS(plane), 0);
+ }
- reg = DSPCNTR(plane);
- dspcntr = I915_READ(reg);
- /* Mask out pixel format bits in case we change it */
- dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
switch (fb->pixel_format) {
case DRM_FORMAT_C8:
dspcntr |= DISPPLANE_8BPP;
@@ -2416,30 +2519,40 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc,
BUG();
}
- if (INTEL_INFO(dev)->gen >= 4) {
- if (obj->tiling_mode != I915_TILING_NONE)
- dspcntr |= DISPPLANE_TILED;
- else
- dspcntr &= ~DISPPLANE_TILED;
- }
+ if (INTEL_INFO(dev)->gen >= 4 &&
+ obj->tiling_mode != I915_TILING_NONE)
+ dspcntr |= DISPPLANE_TILED;
if (IS_G4X(dev))
dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
- I915_WRITE(reg, dspcntr);
-
- linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8);
+ linear_offset = y * fb->pitches[0] + x * pixel_size;
if (INTEL_INFO(dev)->gen >= 4) {
intel_crtc->dspaddr_offset =
intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode,
- fb->bits_per_pixel / 8,
+ pixel_size,
fb->pitches[0]);
linear_offset -= intel_crtc->dspaddr_offset;
} else {
intel_crtc->dspaddr_offset = linear_offset;
}
+ if (to_intel_plane(crtc->primary)->rotation == BIT(DRM_ROTATE_180)) {
+ dspcntr |= DISPPLANE_ROTATE_180;
+
+ x += (intel_crtc->config.pipe_src_w - 1);
+ y += (intel_crtc->config.pipe_src_h - 1);
+
+ /* Finding the last pixel of the last line of the display
+ data and adding to linear_offset*/
+ linear_offset +=
+ (intel_crtc->config.pipe_src_h - 1) * fb->pitches[0] +
+ (intel_crtc->config.pipe_src_w - 1) * pixel_size;
+ }
+
+ I915_WRITE(reg, dspcntr);
+
DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n",
i915_gem_obj_ggtt_offset(obj), linear_offset, x, y,
fb->pitches[0]);
@@ -2461,20 +2574,33 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- struct intel_framebuffer *intel_fb;
struct drm_i915_gem_object *obj;
int plane = intel_crtc->plane;
unsigned long linear_offset;
u32 dspcntr;
- u32 reg;
+ u32 reg = DSPCNTR(plane);
+ int pixel_size;
- intel_fb = to_intel_framebuffer(fb);
- obj = intel_fb->obj;
+ if (!intel_crtc->primary_enabled) {
+ I915_WRITE(reg, 0);
+ I915_WRITE(DSPSURF(plane), 0);
+ POSTING_READ(reg);
+ return;
+ }
+
+ obj = intel_fb_obj(fb);
+ if (WARN_ON(obj == NULL))
+ return;
+
+ pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
+
+ dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+ dspcntr |= DISPLAY_PLANE_ENABLE;
+
+ if (IS_HASWELL(dev) || IS_BROADWELL(dev))
+ dspcntr |= DISPPLANE_PIPE_CSC_ENABLE;
- reg = DSPCNTR(plane);
- dspcntr = I915_READ(reg);
- /* Mask out pixel format bits in case we change it */
- dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
switch (fb->pixel_format) {
case DRM_FORMAT_C8:
dspcntr |= DISPPLANE_8BPP;
@@ -2504,22 +2630,32 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc,
if (obj->tiling_mode != I915_TILING_NONE)
dspcntr |= DISPPLANE_TILED;
- else
- dspcntr &= ~DISPPLANE_TILED;
- if (IS_HASWELL(dev) || IS_BROADWELL(dev))
- dspcntr &= ~DISPPLANE_TRICKLE_FEED_DISABLE;
- else
+ if (!IS_HASWELL(dev) && !IS_BROADWELL(dev))
dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
- I915_WRITE(reg, dspcntr);
-
- linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8);
+ linear_offset = y * fb->pitches[0] + x * pixel_size;
intel_crtc->dspaddr_offset =
intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode,
- fb->bits_per_pixel / 8,
+ pixel_size,
fb->pitches[0]);
linear_offset -= intel_crtc->dspaddr_offset;
+ if (to_intel_plane(crtc->primary)->rotation == BIT(DRM_ROTATE_180)) {
+ dspcntr |= DISPPLANE_ROTATE_180;
+
+ if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
+ x += (intel_crtc->config.pipe_src_w - 1);
+ y += (intel_crtc->config.pipe_src_h - 1);
+
+ /* Finding the last pixel of the last line of the display
+ data and adding to linear_offset*/
+ linear_offset +=
+ (intel_crtc->config.pipe_src_h - 1) * fb->pitches[0] +
+ (intel_crtc->config.pipe_src_w - 1) * pixel_size;
+ }
+ }
+
+ I915_WRITE(reg, dspcntr);
DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n",
i915_gem_obj_ggtt_offset(obj), linear_offset, x, y,
@@ -2546,7 +2682,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
if (dev_priv->display.disable_fbc)
dev_priv->display.disable_fbc(dev);
- intel_increase_pllclock(crtc);
+ intel_increase_pllclock(dev, to_intel_crtc(crtc)->pipe);
dev_priv->display.update_primary_plane(crtc, fb, x, y);
@@ -2601,7 +2737,7 @@ void intel_display_handle_reset(struct drm_device *dev)
static int
intel_finish_fb(struct drm_framebuffer *old_fb)
{
- struct drm_i915_gem_object *obj = to_intel_framebuffer(old_fb)->obj;
+ struct drm_i915_gem_object *obj = intel_fb_obj(old_fb);
struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
bool was_interruptible = dev_priv->mm.interruptible;
int ret;
@@ -2647,7 +2783,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- struct drm_framebuffer *old_fb;
+ enum pipe pipe = intel_crtc->pipe;
+ struct drm_framebuffer *old_fb = crtc->primary->fb;
+ struct drm_i915_gem_object *obj = intel_fb_obj(fb);
+ struct drm_i915_gem_object *old_obj = intel_fb_obj(old_fb);
int ret;
if (intel_crtc_has_pending_flip(crtc)) {
@@ -2669,9 +2808,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
}
mutex_lock(&dev->struct_mutex);
- ret = intel_pin_and_fence_fb_obj(dev,
- to_intel_framebuffer(fb)->obj,
- NULL);
+ ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
+ if (ret == 0)
+ i915_gem_track_fb(old_obj, obj,
+ INTEL_FRONTBUFFER_PRIMARY(pipe));
mutex_unlock(&dev->struct_mutex);
if (ret != 0) {
DRM_ERROR("pin & fence failed\n");
@@ -2711,7 +2851,9 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
dev_priv->display.update_primary_plane(crtc, fb, x, y);
- old_fb = crtc->primary->fb;
+ if (intel_crtc->active)
+ intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_PRIMARY(pipe));
+
crtc->primary->fb = fb;
crtc->x = x;
crtc->y = y;
@@ -2720,13 +2862,12 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
if (intel_crtc->active && old_fb != fb)
intel_wait_for_vblank(dev, intel_crtc->pipe);
mutex_lock(&dev->struct_mutex);
- intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj);
+ intel_unpin_fb_obj(old_obj);
mutex_unlock(&dev->struct_mutex);
}
mutex_lock(&dev->struct_mutex);
intel_update_fbc(dev);
- intel_edp_psr_update(dev);
mutex_unlock(&dev->struct_mutex);
return 0;
@@ -3298,23 +3439,54 @@ bool intel_has_pending_fb_unpin(struct drm_device *dev)
return false;
}
+static void page_flip_completed(struct intel_crtc *intel_crtc)
+{
+ struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev);
+ struct intel_unpin_work *work = intel_crtc->unpin_work;
+
+ /* ensure that the unpin work is consistent wrt ->pending. */
+ smp_rmb();
+ intel_crtc->unpin_work = NULL;
+
+ if (work->event)
+ drm_send_vblank_event(intel_crtc->base.dev,
+ intel_crtc->pipe,
+ work->event);
+
+ drm_crtc_vblank_put(&intel_crtc->base);
+
+ wake_up_all(&dev_priv->pending_flip_queue);
+ queue_work(dev_priv->wq, &work->work);
+
+ trace_i915_flip_complete(intel_crtc->plane,
+ work->pending_flip_obj);
+}
+
void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- if (crtc->primary->fb == NULL)
- return;
-
WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue));
+ if (WARN_ON(wait_event_timeout(dev_priv->pending_flip_queue,
+ !intel_crtc_has_pending_flip(crtc),
+ 60*HZ) == 0)) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ unsigned long flags;
- WARN_ON(wait_event_timeout(dev_priv->pending_flip_queue,
- !intel_crtc_has_pending_flip(crtc),
- 60*HZ) == 0);
+ spin_lock_irqsave(&dev->event_lock, flags);
+ if (intel_crtc->unpin_work) {
+ WARN_ONCE(1, "Removing stuck page flip\n");
+ page_flip_completed(intel_crtc);
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ }
- mutex_lock(&dev->struct_mutex);
- intel_finish_fb(crtc->primary->fb);
- mutex_unlock(&dev->struct_mutex);
+ if (crtc->primary->fb) {
+ mutex_lock(&dev->struct_mutex);
+ intel_finish_fb(crtc->primary->fb);
+ mutex_unlock(&dev->struct_mutex);
+ }
}
/* Program iCLKIP clock to the desired frequency */
@@ -3587,7 +3759,7 @@ static void lpt_pch_enable(struct drm_crtc *crtc)
lpt_enable_pch_transcoder(dev_priv, cpu_transcoder);
}
-static void intel_put_shared_dpll(struct intel_crtc *crtc)
+void intel_put_shared_dpll(struct intel_crtc *crtc)
{
struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
@@ -3607,7 +3779,7 @@ static void intel_put_shared_dpll(struct intel_crtc *crtc)
crtc->config.shared_dpll = DPLL_ID_PRIVATE;
}
-static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc)
+struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc)
{
struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
@@ -3818,7 +3990,7 @@ static void intel_crtc_load_lut(struct drm_crtc *crtc)
}
/* use legacy palette for Ironlake */
- if (HAS_PCH_SPLIT(dev))
+ if (!HAS_GMCH_DISPLAY(dev))
palreg = LGC_PALETTE(pipe);
/* Workaround : Do not read or write the pipe palette/gamma data while
@@ -3860,43 +4032,18 @@ static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable)
*/
}
-/**
- * i9xx_fixup_plane - ugly workaround for G45 to fire up the hardware
- * cursor plane briefly if not already running after enabling the display
- * plane.
- * This workaround avoids occasional blank screens when self refresh is
- * enabled.
- */
-static void
-g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe)
-{
- u32 cntl = I915_READ(CURCNTR(pipe));
-
- if ((cntl & CURSOR_MODE) == 0) {
- u32 fw_bcl_self = I915_READ(FW_BLC_SELF);
-
- I915_WRITE(FW_BLC_SELF, fw_bcl_self & ~FW_BLC_SELF_EN);
- I915_WRITE(CURCNTR(pipe), CURSOR_MODE_64_ARGB_AX);
- intel_wait_for_vblank(dev_priv->dev, pipe);
- I915_WRITE(CURCNTR(pipe), cntl);
- I915_WRITE(CURBASE(pipe), I915_READ(CURBASE(pipe)));
- I915_WRITE(FW_BLC_SELF, fw_bcl_self);
- }
-}
-
static void intel_crtc_enable_planes(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
- int plane = intel_crtc->plane;
- intel_enable_primary_hw_plane(dev_priv, plane, pipe);
+ assert_vblank_disabled(crtc);
+
+ drm_vblank_on(dev, pipe);
+
+ intel_enable_primary_hw_plane(crtc->primary, crtc);
intel_enable_planes(crtc);
- /* The fixup needs to happen before cursor is enabled */
- if (IS_G4X(dev))
- g4x_fixup_plane(dev_priv, pipe);
intel_crtc_update_cursor(crtc, true);
intel_crtc_dpms_overlay(intel_crtc, true);
@@ -3904,8 +4051,14 @@ static void intel_crtc_enable_planes(struct drm_crtc *crtc)
mutex_lock(&dev->struct_mutex);
intel_update_fbc(dev);
- intel_edp_psr_update(dev);
mutex_unlock(&dev->struct_mutex);
+
+ /*
+ * FIXME: Once we grow proper nuclear flip support out of this we need
+ * to compute the mask of flip planes precisely. For the time being
+ * consider this a flip from a NULL plane.
+ */
+ intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_ALL_MASK(pipe));
}
static void intel_crtc_disable_planes(struct drm_crtc *crtc)
@@ -3917,7 +4070,6 @@ static void intel_crtc_disable_planes(struct drm_crtc *crtc)
int plane = intel_crtc->plane;
intel_crtc_wait_for_pending_flips(crtc);
- drm_crtc_vblank_off(crtc);
if (dev_priv->fbc.plane == plane)
intel_disable_fbc(dev);
@@ -3927,7 +4079,18 @@ static void intel_crtc_disable_planes(struct drm_crtc *crtc)
intel_crtc_dpms_overlay(intel_crtc, false);
intel_crtc_update_cursor(crtc, false);
intel_disable_planes(crtc);
- intel_disable_primary_hw_plane(dev_priv, plane, pipe);
+ intel_disable_primary_hw_plane(crtc->primary, crtc);
+
+ /*
+ * FIXME: Once we grow proper nuclear flip support out of this we need
+ * to compute the mask of flip planes precisely. For the time being
+ * consider this a flip to a NULL plane.
+ */
+ intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_ALL_MASK(pipe));
+
+ drm_vblank_off(dev, pipe);
+
+ assert_vblank_disabled(crtc);
}
static void ironlake_crtc_enable(struct drm_crtc *crtc)
@@ -3937,7 +4100,6 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe;
- enum plane plane = intel_crtc->plane;
WARN_ON(!crtc->enabled);
@@ -3954,18 +4116,11 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
if (intel_crtc->config.has_pch_encoder) {
intel_cpu_transcoder_set_m_n(intel_crtc,
- &intel_crtc->config.fdi_m_n);
+ &intel_crtc->config.fdi_m_n, NULL);
}
ironlake_set_pipeconf(crtc);
- /* Set up the display plane register */
- I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE);
- POSTING_READ(DSPCNTR(plane));
-
- dev_priv->display.update_primary_plane(crtc, crtc->primary->fb,
- crtc->x, crtc->y);
-
intel_crtc->active = true;
intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
@@ -4006,8 +4161,6 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
cpt_verify_modeset(dev, intel_crtc->pipe);
intel_crtc_enable_planes(crtc);
-
- drm_crtc_vblank_on(crtc);
}
/* IPS only exists on ULT machines and is tied to pipe A. */
@@ -4052,47 +4205,46 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe;
- enum plane plane = intel_crtc->plane;
WARN_ON(!crtc->enabled);
if (intel_crtc->active)
return;
+ if (intel_crtc_to_shared_dpll(intel_crtc))
+ intel_enable_shared_dpll(intel_crtc);
+
if (intel_crtc->config.has_dp_encoder)
intel_dp_set_m_n(intel_crtc);
intel_set_pipe_timings(intel_crtc);
+ if (intel_crtc->config.cpu_transcoder != TRANSCODER_EDP) {
+ I915_WRITE(PIPE_MULT(intel_crtc->config.cpu_transcoder),
+ intel_crtc->config.pixel_multiplier - 1);
+ }
+
if (intel_crtc->config.has_pch_encoder) {
intel_cpu_transcoder_set_m_n(intel_crtc,
- &intel_crtc->config.fdi_m_n);
+ &intel_crtc->config.fdi_m_n, NULL);
}
haswell_set_pipeconf(crtc);
intel_set_pipe_csc(crtc);
- /* Set up the display plane register */
- I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE | DISPPLANE_PIPE_CSC_ENABLE);
- POSTING_READ(DSPCNTR(plane));
-
- dev_priv->display.update_primary_plane(crtc, crtc->primary->fb,
- crtc->x, crtc->y);
-
intel_crtc->active = true;
intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
- if (intel_crtc->config.has_pch_encoder)
- intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true);
-
- if (intel_crtc->config.has_pch_encoder)
- dev_priv->display.fdi_link_train(crtc);
-
for_each_encoder_on_crtc(dev, crtc, encoder)
if (encoder->pre_enable)
encoder->pre_enable(encoder);
+ if (intel_crtc->config.has_pch_encoder) {
+ intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true);
+ dev_priv->display.fdi_link_train(crtc);
+ }
+
intel_ddi_enable_pipe_clock(intel_crtc);
ironlake_pfit_enable(intel_crtc);
@@ -4112,6 +4264,9 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
if (intel_crtc->config.has_pch_encoder)
lpt_pch_enable(crtc);
+ if (intel_crtc->config.dp_encoder_is_mst)
+ intel_ddi_set_vc_payload_alloc(crtc, true);
+
for_each_encoder_on_crtc(dev, crtc, encoder) {
encoder->enable(encoder);
intel_opregion_notify_encoder(encoder, true);
@@ -4121,8 +4276,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
* to change the workaround. */
haswell_mode_set_planes_workaround(intel_crtc);
intel_crtc_enable_planes(crtc);
-
- drm_crtc_vblank_on(crtc);
}
static void ironlake_pfit_disable(struct intel_crtc *crtc)
@@ -4160,7 +4313,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
if (intel_crtc->config.has_pch_encoder)
intel_set_pch_fifo_underrun_reporting(dev, pipe, false);
- intel_disable_pipe(dev_priv, pipe);
+ intel_disable_pipe(intel_crtc);
ironlake_pfit_disable(intel_crtc);
@@ -4200,7 +4353,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
mutex_lock(&dev->struct_mutex);
intel_update_fbc(dev);
- intel_edp_psr_update(dev);
mutex_unlock(&dev->struct_mutex);
}
@@ -4210,7 +4362,6 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_encoder *encoder;
- int pipe = intel_crtc->pipe;
enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
if (!intel_crtc->active)
@@ -4225,7 +4376,10 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
if (intel_crtc->config.has_pch_encoder)
intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, false);
- intel_disable_pipe(dev_priv, pipe);
+ intel_disable_pipe(intel_crtc);
+
+ if (intel_crtc->config.dp_encoder_is_mst)
+ intel_ddi_set_vc_payload_alloc(crtc, false);
intel_ddi_disable_transcoder_func(dev_priv, cpu_transcoder);
@@ -4233,23 +4387,25 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
intel_ddi_disable_pipe_clock(intel_crtc);
- for_each_encoder_on_crtc(dev, crtc, encoder)
- if (encoder->post_disable)
- encoder->post_disable(encoder);
-
if (intel_crtc->config.has_pch_encoder) {
lpt_disable_pch_transcoder(dev_priv);
intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true);
intel_ddi_fdi_disable(crtc);
}
+ for_each_encoder_on_crtc(dev, crtc, encoder)
+ if (encoder->post_disable)
+ encoder->post_disable(encoder);
+
intel_crtc->active = false;
intel_update_watermarks(crtc);
mutex_lock(&dev->struct_mutex);
intel_update_fbc(dev);
- intel_edp_psr_update(dev);
mutex_unlock(&dev->struct_mutex);
+
+ if (intel_crtc_to_shared_dpll(intel_crtc))
+ intel_disable_shared_dpll(intel_crtc);
}
static void ironlake_crtc_off(struct drm_crtc *crtc)
@@ -4258,10 +4414,6 @@ static void ironlake_crtc_off(struct drm_crtc *crtc)
intel_put_shared_dpll(intel_crtc);
}
-static void haswell_crtc_off(struct drm_crtc *crtc)
-{
- intel_ddi_put_crtc_pll(crtc);
-}
static void i9xx_pfit_enable(struct intel_crtc *crtc)
{
@@ -4287,6 +4439,23 @@ static void i9xx_pfit_enable(struct intel_crtc *crtc)
I915_WRITE(BCLRPAT(crtc->pipe), 0);
}
+static enum intel_display_power_domain port_to_power_domain(enum port port)
+{
+ switch (port) {
+ case PORT_A:
+ return POWER_DOMAIN_PORT_DDI_A_4_LANES;
+ case PORT_B:
+ return POWER_DOMAIN_PORT_DDI_B_4_LANES;
+ case PORT_C:
+ return POWER_DOMAIN_PORT_DDI_C_4_LANES;
+ case PORT_D:
+ return POWER_DOMAIN_PORT_DDI_D_4_LANES;
+ default:
+ WARN_ON_ONCE(1);
+ return POWER_DOMAIN_PORT_OTHER;
+ }
+}
+
#define for_each_power_domain(domain, mask) \
for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++) \
if ((1 << (domain)) & (mask))
@@ -4305,19 +4474,10 @@ intel_display_port_power_domain(struct intel_encoder *intel_encoder)
case INTEL_OUTPUT_HDMI:
case INTEL_OUTPUT_EDP:
intel_dig_port = enc_to_dig_port(&intel_encoder->base);
- switch (intel_dig_port->port) {
- case PORT_A:
- return POWER_DOMAIN_PORT_DDI_A_4_LANES;
- case PORT_B:
- return POWER_DOMAIN_PORT_DDI_B_4_LANES;
- case PORT_C:
- return POWER_DOMAIN_PORT_DDI_C_4_LANES;
- case PORT_D:
- return POWER_DOMAIN_PORT_DDI_D_4_LANES;
- default:
- WARN_ON_ONCE(1);
- return POWER_DOMAIN_PORT_OTHER;
- }
+ return port_to_power_domain(intel_dig_port->port);
+ case INTEL_OUTPUT_DP_MST:
+ intel_dig_port = enc_to_mst(&intel_encoder->base)->primary;
+ return port_to_power_domain(intel_dig_port->port);
case INTEL_OUTPUT_ANALOG:
return POWER_DOMAIN_PORT_CRT;
case INTEL_OUTPUT_DSI:
@@ -4333,7 +4493,6 @@ static unsigned long get_crtc_power_domains(struct drm_crtc *crtc)
struct intel_encoder *intel_encoder;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
enum pipe pipe = intel_crtc->pipe;
- bool pfit_enabled = intel_crtc->config.pch_pfit.enabled;
unsigned long mask;
enum transcoder transcoder;
@@ -4341,7 +4500,8 @@ static unsigned long get_crtc_power_domains(struct drm_crtc *crtc)
mask = BIT(POWER_DOMAIN_PIPE(pipe));
mask |= BIT(POWER_DOMAIN_TRANSCODER(transcoder));
- if (pfit_enabled)
+ if (intel_crtc->config.pch_pfit.enabled ||
+ intel_crtc->config.pch_pfit.force_thru)
mask |= BIT(POWER_DOMAIN_PIPE_PANEL_FITTER(pipe));
for_each_encoder_on_crtc(dev, crtc, intel_encoder)
@@ -4398,7 +4558,8 @@ static void modeset_update_crtc_power_domains(struct drm_device *dev)
intel_display_set_init_power(dev_priv, false);
}
-int valleyview_get_vco(struct drm_i915_private *dev_priv)
+/* returns HPLL frequency in kHz */
+static int valleyview_get_vco(struct drm_i915_private *dev_priv)
{
int hpll_freq, vco_freq[] = { 800, 1600, 2000, 2400 };
@@ -4408,7 +4569,23 @@ int valleyview_get_vco(struct drm_i915_private *dev_priv)
CCK_FUSE_HPLL_FREQ_MASK;
mutex_unlock(&dev_priv->dpio_lock);
- return vco_freq[hpll_freq];
+ return vco_freq[hpll_freq] * 1000;
+}
+
+static void vlv_update_cdclk(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->vlv_cdclk_freq = dev_priv->display.get_display_clock_speed(dev);
+ DRM_DEBUG_DRIVER("Current CD clock rate: %d kHz",
+ dev_priv->vlv_cdclk_freq);
+
+ /*
+ * Program the gmbus_freq based on the cdclk frequency.
+ * BSpec erroneously claims we should aim for 4MHz, but
+ * in fact 1MHz is the correct frequency.
+ */
+ I915_WRITE(GMBUSFREQ_VLV, DIV_ROUND_UP(dev_priv->vlv_cdclk_freq, 1000));
}
/* Adjust CDclk dividers to allow high res or save power if possible */
@@ -4417,12 +4594,11 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk)
struct drm_i915_private *dev_priv = dev->dev_private;
u32 val, cmd;
- WARN_ON(valleyview_cur_cdclk(dev_priv) != dev_priv->vlv_cdclk_freq);
- dev_priv->vlv_cdclk_freq = cdclk;
+ WARN_ON(dev_priv->display.get_display_clock_speed(dev) != dev_priv->vlv_cdclk_freq);
- if (cdclk >= 320) /* jump to highest voltage for 400MHz too */
+ if (cdclk >= 320000) /* jump to highest voltage for 400MHz too */
cmd = 2;
- else if (cdclk == 266)
+ else if (cdclk == 266667)
cmd = 1;
else
cmd = 0;
@@ -4439,18 +4615,23 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk)
}
mutex_unlock(&dev_priv->rps.hw_lock);
- if (cdclk == 400) {
+ if (cdclk == 400000) {
u32 divider, vco;
vco = valleyview_get_vco(dev_priv);
- divider = ((vco << 1) / cdclk) - 1;
+ divider = DIV_ROUND_CLOSEST(vco << 1, cdclk) - 1;
mutex_lock(&dev_priv->dpio_lock);
/* adjust cdclk divider */
val = vlv_cck_read(dev_priv, CCK_DISPLAY_CLOCK_CONTROL);
- val &= ~0xf;
+ val &= ~DISPLAY_FREQUENCY_VALUES;
val |= divider;
vlv_cck_write(dev_priv, CCK_DISPLAY_CLOCK_CONTROL, val);
+
+ if (wait_for((vlv_cck_read(dev_priv, CCK_DISPLAY_CLOCK_CONTROL) &
+ DISPLAY_FREQUENCY_STATUS) == (divider << DISPLAY_FREQUENCY_STATUS_SHIFT),
+ 50))
+ DRM_ERROR("timed out waiting for CDclk change\n");
mutex_unlock(&dev_priv->dpio_lock);
}
@@ -4463,54 +4644,88 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk)
* For high bandwidth configs, we set a higher latency in the bunit
* so that the core display fetch happens in time to avoid underruns.
*/
- if (cdclk == 400)
+ if (cdclk == 400000)
val |= 4500 / 250; /* 4.5 usec */
else
val |= 3000 / 250; /* 3.0 usec */
vlv_bunit_write(dev_priv, BUNIT_REG_BISOC, val);
mutex_unlock(&dev_priv->dpio_lock);
- /* Since we changed the CDclk, we need to update the GMBUSFREQ too */
- intel_i2c_reset(dev);
+ vlv_update_cdclk(dev);
}
-int valleyview_cur_cdclk(struct drm_i915_private *dev_priv)
+static void cherryview_set_cdclk(struct drm_device *dev, int cdclk)
{
- int cur_cdclk, vco;
- int divider;
-
- vco = valleyview_get_vco(dev_priv);
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 val, cmd;
- mutex_lock(&dev_priv->dpio_lock);
- divider = vlv_cck_read(dev_priv, CCK_DISPLAY_CLOCK_CONTROL);
- mutex_unlock(&dev_priv->dpio_lock);
+ WARN_ON(dev_priv->display.get_display_clock_speed(dev) != dev_priv->vlv_cdclk_freq);
- divider &= 0xf;
+ switch (cdclk) {
+ case 400000:
+ cmd = 3;
+ break;
+ case 333333:
+ case 320000:
+ cmd = 2;
+ break;
+ case 266667:
+ cmd = 1;
+ break;
+ case 200000:
+ cmd = 0;
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
- cur_cdclk = (vco << 1) / (divider + 1);
+ mutex_lock(&dev_priv->rps.hw_lock);
+ val = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ);
+ val &= ~DSPFREQGUAR_MASK_CHV;
+ val |= (cmd << DSPFREQGUAR_SHIFT_CHV);
+ vlv_punit_write(dev_priv, PUNIT_REG_DSPFREQ, val);
+ if (wait_for((vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) &
+ DSPFREQSTAT_MASK_CHV) == (cmd << DSPFREQSTAT_SHIFT_CHV),
+ 50)) {
+ DRM_ERROR("timed out waiting for CDclk change\n");
+ }
+ mutex_unlock(&dev_priv->rps.hw_lock);
- return cur_cdclk;
+ vlv_update_cdclk(dev);
}
static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv,
int max_pixclk)
{
+ int vco = valleyview_get_vco(dev_priv);
+ int freq_320 = (vco << 1) % 320000 != 0 ? 333333 : 320000;
+
+ /* FIXME: Punit isn't quite ready yet */
+ if (IS_CHERRYVIEW(dev_priv->dev))
+ return 400000;
+
/*
* Really only a few cases to deal with, as only 4 CDclks are supported:
* 200MHz
* 267MHz
- * 320MHz
+ * 320/333MHz (depends on HPLL freq)
* 400MHz
* So we check to see whether we're above 90% of the lower bin and
* adjust if needed.
+ *
+ * We seem to get an unstable or solid color picture at 200MHz.
+ * Not sure what's wrong. For now use 200MHz only when all pipes
+ * are off.
*/
- if (max_pixclk > 288000) {
- return 400;
- } else if (max_pixclk > 240000) {
- return 320;
- } else
- return 266;
- /* Looks like the 200MHz CDclk freq doesn't work on some configs */
+ if (max_pixclk > freq_320*9/10)
+ return 400000;
+ else if (max_pixclk > 266667*9/10)
+ return freq_320;
+ else if (max_pixclk > 0)
+ return 266667;
+ else
+ return 200000;
}
/* compute the max pixel clock for new configuration */
@@ -4552,21 +4767,23 @@ static void valleyview_modeset_global_resources(struct drm_device *dev)
int max_pixclk = intel_mode_max_pixclk(dev_priv);
int req_cdclk = valleyview_calc_cdclk(dev_priv, max_pixclk);
- if (req_cdclk != dev_priv->vlv_cdclk_freq)
- valleyview_set_cdclk(dev, req_cdclk);
+ if (req_cdclk != dev_priv->vlv_cdclk_freq) {
+ if (IS_CHERRYVIEW(dev))
+ cherryview_set_cdclk(dev, req_cdclk);
+ else
+ valleyview_set_cdclk(dev, req_cdclk);
+ }
+
modeset_update_crtc_power_domains(dev);
}
static void valleyview_crtc_enable(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe;
- int plane = intel_crtc->plane;
bool is_dsi;
- u32 dspcntr;
WARN_ON(!crtc->enabled);
@@ -4575,33 +4792,20 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
is_dsi = intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI);
- if (!is_dsi && !IS_CHERRYVIEW(dev))
- vlv_prepare_pll(intel_crtc);
-
- /* Set up the display plane register */
- dspcntr = DISPPLANE_GAMMA_ENABLE;
+ if (!is_dsi) {
+ if (IS_CHERRYVIEW(dev))
+ chv_prepare_pll(intel_crtc);
+ else
+ vlv_prepare_pll(intel_crtc);
+ }
if (intel_crtc->config.has_dp_encoder)
intel_dp_set_m_n(intel_crtc);
intel_set_pipe_timings(intel_crtc);
- /* pipesrc and dspsize control the size that is scaled from,
- * which should always be the user's requested size.
- */
- I915_WRITE(DSPSIZE(plane),
- ((intel_crtc->config.pipe_src_h - 1) << 16) |
- (intel_crtc->config.pipe_src_w - 1));
- I915_WRITE(DSPPOS(plane), 0);
-
i9xx_set_pipeconf(intel_crtc);
- I915_WRITE(DSPCNTR(plane), dspcntr);
- POSTING_READ(DSPCNTR(plane));
-
- dev_priv->display.update_primary_plane(crtc, crtc->primary->fb,
- crtc->x, crtc->y);
-
intel_crtc->active = true;
intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
@@ -4633,8 +4837,6 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
intel_crtc_enable_planes(crtc);
- drm_crtc_vblank_on(crtc);
-
/* Underruns don't raise interrupts, so check manually. */
i9xx_check_fifo_underruns(dev);
}
@@ -4651,12 +4853,9 @@ static void i9xx_set_pll_dividers(struct intel_crtc *crtc)
static void i9xx_crtc_enable(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe;
- int plane = intel_crtc->plane;
- u32 dspcntr;
WARN_ON(!crtc->enabled);
@@ -4665,35 +4864,13 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
i9xx_set_pll_dividers(intel_crtc);
- /* Set up the display plane register */
- dspcntr = DISPPLANE_GAMMA_ENABLE;
-
- if (pipe == 0)
- dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
- else
- dspcntr |= DISPPLANE_SEL_PIPE_B;
-
if (intel_crtc->config.has_dp_encoder)
intel_dp_set_m_n(intel_crtc);
intel_set_pipe_timings(intel_crtc);
- /* pipesrc and dspsize control the size that is scaled from,
- * which should always be the user's requested size.
- */
- I915_WRITE(DSPSIZE(plane),
- ((intel_crtc->config.pipe_src_h - 1) << 16) |
- (intel_crtc->config.pipe_src_w - 1));
- I915_WRITE(DSPPOS(plane), 0);
-
i9xx_set_pipeconf(intel_crtc);
- I915_WRITE(DSPCNTR(plane), dspcntr);
- POSTING_READ(DSPCNTR(plane));
-
- dev_priv->display.update_primary_plane(crtc, crtc->primary->fb,
- crtc->x, crtc->y);
-
intel_crtc->active = true;
if (!IS_GEN2(dev))
@@ -4727,8 +4904,6 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
if (IS_GEN2(dev))
intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
- drm_crtc_vblank_on(crtc);
-
/* Underruns don't raise interrupts, so check manually. */
i9xx_check_fifo_underruns(dev);
}
@@ -4768,6 +4943,16 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
if (IS_GEN2(dev))
intel_set_cpu_fifo_underrun_reporting(dev, pipe, false);
+ /*
+ * Vblank time updates from the shadow to live plane control register
+ * are blocked if the memory self-refresh mode is active at that
+ * moment. So to make sure the plane gets truly disabled, disable
+ * first the self-refresh mode. The self-refresh enable bit in turn
+ * will be checked/applied by the HW only at the next frame start
+ * event which is after the vblank start event, so we need to have a
+ * wait-for-vblank between disabling the plane and the pipe.
+ */
+ intel_set_memory_cxsr(dev_priv, false);
intel_crtc_disable_planes(crtc);
for_each_encoder_on_crtc(dev, crtc, encoder)
@@ -4776,11 +4961,12 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
/*
* On gen2 planes are double buffered but the pipe isn't, so we must
* wait for planes to fully turn off before disabling the pipe.
+ * We also need to wait on all gmch platforms because of the
+ * self-refresh mode constraint explained above.
*/
- if (IS_GEN2(dev))
- intel_wait_for_vblank(dev, pipe);
+ intel_wait_for_vblank(dev, pipe);
- intel_disable_pipe(dev_priv, pipe);
+ intel_disable_pipe(intel_crtc);
i9xx_pfit_disable(intel_crtc);
@@ -4794,7 +4980,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
else if (IS_VALLEYVIEW(dev))
vlv_disable_pll(dev_priv, pipe);
else
- i9xx_disable_pll(dev_priv, pipe);
+ i9xx_disable_pll(intel_crtc);
}
if (!IS_GEN2(dev))
@@ -4805,7 +4991,6 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
mutex_lock(&dev->struct_mutex);
intel_update_fbc(dev);
- intel_edp_psr_update(dev);
mutex_unlock(&dev->struct_mutex);
}
@@ -4843,23 +5028,49 @@ static void intel_crtc_update_sarea(struct drm_crtc *crtc,
}
}
+/* Master function to enable/disable CRTC and corresponding power wells */
+void intel_crtc_control(struct drm_crtc *crtc, bool enable)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ enum intel_display_power_domain domain;
+ unsigned long domains;
+
+ if (enable) {
+ if (!intel_crtc->active) {
+ domains = get_crtc_power_domains(crtc);
+ for_each_power_domain(domain, domains)
+ intel_display_power_get(dev_priv, domain);
+ intel_crtc->enabled_power_domains = domains;
+
+ dev_priv->display.crtc_enable(crtc);
+ }
+ } else {
+ if (intel_crtc->active) {
+ dev_priv->display.crtc_disable(crtc);
+
+ domains = intel_crtc->enabled_power_domains;
+ for_each_power_domain(domain, domains)
+ intel_display_power_put(dev_priv, domain);
+ intel_crtc->enabled_power_domains = 0;
+ }
+ }
+}
+
/**
* Sets the power management mode of the pipe and plane.
*/
void intel_crtc_update_dpms(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_encoder *intel_encoder;
bool enable = false;
for_each_encoder_on_crtc(dev, crtc, intel_encoder)
enable |= intel_encoder->connectors_active;
- if (enable)
- dev_priv->display.crtc_enable(crtc);
- else
- dev_priv->display.crtc_disable(crtc);
+ intel_crtc_control(crtc, enable);
intel_crtc_update_sarea(crtc, enable);
}
@@ -4869,6 +5080,8 @@ static void intel_crtc_disable(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
struct drm_connector *connector;
struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *old_obj = intel_fb_obj(crtc->primary->fb);
+ enum pipe pipe = to_intel_crtc(crtc)->pipe;
/* crtc should still be enabled when we disable it. */
WARN_ON(!crtc->enabled);
@@ -4877,13 +5090,11 @@ static void intel_crtc_disable(struct drm_crtc *crtc)
intel_crtc_update_sarea(crtc, false);
dev_priv->display.off(crtc);
- assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane);
- assert_cursor_disabled(dev_priv, to_intel_crtc(crtc)->pipe);
- assert_pipe_disabled(dev->dev_private, to_intel_crtc(crtc)->pipe);
-
if (crtc->primary->fb) {
mutex_lock(&dev->struct_mutex);
- intel_unpin_fb_obj(to_intel_framebuffer(crtc->primary->fb)->obj);
+ intel_unpin_fb_obj(old_obj);
+ i915_gem_track_fb(old_obj, NULL,
+ INTEL_FRONTBUFFER_PRIMARY(pipe));
mutex_unlock(&dev->struct_mutex);
crtc->primary->fb = NULL;
}
@@ -4939,24 +5150,31 @@ static void intel_connector_check_state(struct intel_connector *connector)
connector->base.base.id,
connector->base.name);
+ /* there is no real hw state for MST connectors */
+ if (connector->mst_port)
+ return;
+
WARN(connector->base.dpms == DRM_MODE_DPMS_OFF,
"wrong connector dpms state\n");
WARN(connector->base.encoder != &encoder->base,
"active connector not linked to encoder\n");
- WARN(!encoder->connectors_active,
- "encoder->connectors_active not set\n");
- encoder_enabled = encoder->get_hw_state(encoder, &pipe);
- WARN(!encoder_enabled, "encoder not enabled\n");
- if (WARN_ON(!encoder->base.crtc))
- return;
+ if (encoder) {
+ WARN(!encoder->connectors_active,
+ "encoder->connectors_active not set\n");
- crtc = encoder->base.crtc;
+ encoder_enabled = encoder->get_hw_state(encoder, &pipe);
+ WARN(!encoder_enabled, "encoder not enabled\n");
+ if (WARN_ON(!encoder->base.crtc))
+ return;
- WARN(!crtc->enabled, "crtc not enabled\n");
- WARN(!to_intel_crtc(crtc)->active, "crtc not active\n");
- WARN(pipe != to_intel_crtc(crtc)->pipe,
- "encoder active on the wrong pipe\n");
+ crtc = encoder->base.crtc;
+
+ WARN(!crtc->enabled, "crtc not enabled\n");
+ WARN(!to_intel_crtc(crtc)->active, "crtc not active\n");
+ WARN(pipe != to_intel_crtc(crtc)->pipe,
+ "encoder active on the wrong pipe\n");
+ }
}
}
@@ -5161,9 +5379,11 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc,
if (HAS_IPS(dev))
hsw_compute_ips_config(crtc, pipe_config);
- /* XXX: PCH clock sharing is done in ->mode_set, so make sure the old
- * clock survives for now. */
- if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
+ /*
+ * XXX: PCH/WRPLL clock sharing is done in ->mode_set, so make sure the
+ * old clock survives for now.
+ */
+ if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev) || HAS_DDI(dev))
pipe_config->shared_dpll = crtc->config.shared_dpll;
if (pipe_config->has_pch_encoder)
@@ -5174,7 +5394,26 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc,
static int valleyview_get_display_clock_speed(struct drm_device *dev)
{
- return 400000; /* FIXME */
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int vco = valleyview_get_vco(dev_priv);
+ u32 val;
+ int divider;
+
+ /* FIXME: Punit isn't quite ready yet */
+ if (IS_CHERRYVIEW(dev))
+ return 400000;
+
+ mutex_lock(&dev_priv->dpio_lock);
+ val = vlv_cck_read(dev_priv, CCK_DISPLAY_CLOCK_CONTROL);
+ mutex_unlock(&dev_priv->dpio_lock);
+
+ divider = val & DISPLAY_FREQUENCY_VALUES;
+
+ WARN((val & DISPLAY_FREQUENCY_STATUS) !=
+ (divider << DISPLAY_FREQUENCY_STATUS_SHIFT),
+ "cdclk change in progress\n");
+
+ return DIV_ROUND_CLOSEST(vco << 1, divider + 1);
}
static int i945_get_display_clock_speed(struct drm_device *dev)
@@ -5408,7 +5647,8 @@ static void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc,
}
static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc,
- struct intel_link_m_n *m_n)
+ struct intel_link_m_n *m_n,
+ struct intel_link_m_n *m2_n2)
{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -5420,6 +5660,18 @@ static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc,
I915_WRITE(PIPE_DATA_N1(transcoder), m_n->gmch_n);
I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m);
I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n);
+ /* M2_N2 registers to be set only for gen < 8 (M2_N2 available
+ * for gen < 8) and if DRRS is supported (to make sure the
+ * registers are not unnecessarily accessed).
+ */
+ if (m2_n2 && INTEL_INFO(dev)->gen < 8 &&
+ crtc->config.has_drrs) {
+ I915_WRITE(PIPE_DATA_M2(transcoder),
+ TU_SIZE(m2_n2->tu) | m2_n2->gmch_m);
+ I915_WRITE(PIPE_DATA_N2(transcoder), m2_n2->gmch_n);
+ I915_WRITE(PIPE_LINK_M2(transcoder), m2_n2->link_m);
+ I915_WRITE(PIPE_LINK_N2(transcoder), m2_n2->link_n);
+ }
} else {
I915_WRITE(PIPE_DATA_M_G4X(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m);
I915_WRITE(PIPE_DATA_N_G4X(pipe), m_n->gmch_n);
@@ -5428,12 +5680,13 @@ static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc,
}
}
-static void intel_dp_set_m_n(struct intel_crtc *crtc)
+void intel_dp_set_m_n(struct intel_crtc *crtc)
{
if (crtc->config.has_pch_encoder)
intel_pch_transcoder_set_m_n(crtc, &crtc->config.dp_m_n);
else
- intel_cpu_transcoder_set_m_n(crtc, &crtc->config.dp_m_n);
+ intel_cpu_transcoder_set_m_n(crtc, &crtc->config.dp_m_n,
+ &crtc->config.dp_m2_n2);
}
static void vlv_update_pll(struct intel_crtc *crtc)
@@ -5551,6 +5804,18 @@ static void vlv_prepare_pll(struct intel_crtc *crtc)
static void chv_update_pll(struct intel_crtc *crtc)
{
+ crtc->config.dpll_hw_state.dpll = DPLL_SSC_REF_CLOCK_CHV |
+ DPLL_REFA_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS |
+ DPLL_VCO_ENABLE;
+ if (crtc->pipe != PIPE_A)
+ crtc->config.dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
+
+ crtc->config.dpll_hw_state.dpll_md =
+ (crtc->config.pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
+}
+
+static void chv_prepare_pll(struct intel_crtc *crtc)
+{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int pipe = crtc->pipe;
@@ -5560,15 +5825,6 @@ static void chv_update_pll(struct intel_crtc *crtc)
u32 bestn, bestm1, bestm2, bestp1, bestp2, bestm2_frac;
int refclk;
- crtc->config.dpll_hw_state.dpll = DPLL_SSC_REF_CLOCK_CHV |
- DPLL_REFA_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS |
- DPLL_VCO_ENABLE;
- if (pipe != PIPE_A)
- crtc->config.dpll_hw_state.dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
-
- crtc->config.dpll_hw_state.dpll_md =
- (crtc->config.pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
-
bestn = crtc->config.dpll.n;
bestm2_frac = crtc->config.dpll.m2 & 0x3fffff;
bestm1 = crtc->config.dpll.m1;
@@ -5728,7 +5984,7 @@ static void i8xx_update_pll(struct intel_crtc *crtc,
dpll |= PLL_P2_DIVIDE_BY_4;
}
- if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DVO))
+ if (!IS_I830(dev) && intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DVO))
dpll |= DPLL_DVO_2X_MODE;
if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) &&
@@ -5879,9 +6135,9 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc)
pipeconf = 0;
- if (dev_priv->quirks & QUIRK_PIPEA_FORCE &&
- I915_READ(PIPECONF(intel_crtc->pipe)) & PIPECONF_ENABLE)
- pipeconf |= PIPECONF_ENABLE;
+ if ((intel_crtc->pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) ||
+ (intel_crtc->pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE))
+ pipeconf |= I915_READ(PIPECONF(intel_crtc->pipe)) & PIPECONF_ENABLE;
if (intel_crtc->config.double_wide)
pipeconf |= PIPECONF_DOUBLE_WIDE;
@@ -6060,6 +6316,10 @@ static void vlv_crtc_clock_get(struct intel_crtc *crtc,
u32 mdiv;
int refclk = 100000;
+ /* In case of MIPI DPLL will not even be used */
+ if (!(pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE))
+ return;
+
mutex_lock(&dev_priv->dpio_lock);
mdiv = vlv_dpio_read(dev_priv, pipe, VLV_PLL_DW3(pipe));
mutex_unlock(&dev_priv->dpio_lock);
@@ -6120,13 +6380,13 @@ static void i9xx_get_plane_config(struct intel_crtc *crtc,
crtc->base.primary->fb->height = ((val >> 0) & 0xfff) + 1;
val = I915_READ(DSPSTRIDE(pipe));
- crtc->base.primary->fb->pitches[0] = val & 0xffffff80;
+ crtc->base.primary->fb->pitches[0] = val & 0xffffffc0;
aligned_height = intel_align_height(dev, crtc->base.primary->fb->height,
plane_config->tiled);
- plane_config->size = ALIGN(crtc->base.primary->fb->pitches[0] *
- aligned_height, PAGE_SIZE);
+ plane_config->size = PAGE_ALIGN(crtc->base.primary->fb->pitches[0] *
+ aligned_height);
DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
pipe, plane, crtc->base.primary->fb->width,
@@ -6230,6 +6490,14 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
}
pipe_config->dpll_hw_state.dpll = I915_READ(DPLL(crtc->pipe));
if (!IS_VALLEYVIEW(dev)) {
+ /*
+ * DPLL_DVO_2X_MODE must be enabled for both DPLLs
+ * on 830. Filter it out here so that we don't
+ * report errors due to that.
+ */
+ if (IS_I830(dev))
+ pipe_config->dpll_hw_state.dpll &= ~DPLL_DVO_2X_MODE;
+
pipe_config->dpll_hw_state.fp0 = I915_READ(FP0(crtc->pipe));
pipe_config->dpll_hw_state.fp1 = I915_READ(FP1(crtc->pipe));
} else {
@@ -6252,7 +6520,6 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
static void ironlake_init_pch_refclk(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *encoder;
u32 val, final;
bool has_lvds = false;
@@ -6262,8 +6529,7 @@ static void ironlake_init_pch_refclk(struct drm_device *dev)
bool can_ssc = false;
/* We need to take the global config into account */
- list_for_each_entry(encoder, &mode_config->encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
switch (encoder->type) {
case INTEL_OUTPUT_LVDS:
has_panel = true;
@@ -6570,11 +6836,10 @@ static void lpt_disable_clkout_dp(struct drm_device *dev)
static void lpt_init_pch_refclk(struct drm_device *dev)
{
- struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *encoder;
bool has_vga = false;
- list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
+ for_each_intel_encoder(dev, encoder) {
switch (encoder->type) {
case INTEL_OUTPUT_ANALOG:
has_vga = true;
@@ -7030,7 +7295,8 @@ static void intel_pch_transcoder_get_m_n(struct intel_crtc *crtc,
static void intel_cpu_transcoder_get_m_n(struct intel_crtc *crtc,
enum transcoder transcoder,
- struct intel_link_m_n *m_n)
+ struct intel_link_m_n *m_n,
+ struct intel_link_m_n *m2_n2)
{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -7044,6 +7310,20 @@ static void intel_cpu_transcoder_get_m_n(struct intel_crtc *crtc,
m_n->gmch_n = I915_READ(PIPE_DATA_N1(transcoder));
m_n->tu = ((I915_READ(PIPE_DATA_M1(transcoder))
& TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1;
+ /* Read M2_N2 registers only for gen < 8 (M2_N2 available for
+ * gen < 8) and if DRRS is supported (to make sure the
+ * registers are not unnecessarily read).
+ */
+ if (m2_n2 && INTEL_INFO(dev)->gen < 8 &&
+ crtc->config.has_drrs) {
+ m2_n2->link_m = I915_READ(PIPE_LINK_M2(transcoder));
+ m2_n2->link_n = I915_READ(PIPE_LINK_N2(transcoder));
+ m2_n2->gmch_m = I915_READ(PIPE_DATA_M2(transcoder))
+ & ~TU_SIZE_MASK;
+ m2_n2->gmch_n = I915_READ(PIPE_DATA_N2(transcoder));
+ m2_n2->tu = ((I915_READ(PIPE_DATA_M2(transcoder))
+ & TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1;
+ }
} else {
m_n->link_m = I915_READ(PIPE_LINK_M_G4X(pipe));
m_n->link_n = I915_READ(PIPE_LINK_N_G4X(pipe));
@@ -7062,14 +7342,15 @@ void intel_dp_get_m_n(struct intel_crtc *crtc,
intel_pch_transcoder_get_m_n(crtc, &pipe_config->dp_m_n);
else
intel_cpu_transcoder_get_m_n(crtc, pipe_config->cpu_transcoder,
- &pipe_config->dp_m_n);
+ &pipe_config->dp_m_n,
+ &pipe_config->dp_m2_n2);
}
static void ironlake_get_fdi_m_n_config(struct intel_crtc *crtc,
struct intel_crtc_config *pipe_config)
{
intel_cpu_transcoder_get_m_n(crtc, pipe_config->cpu_transcoder,
- &pipe_config->fdi_m_n);
+ &pipe_config->fdi_m_n, NULL);
}
static void ironlake_get_pfit_config(struct intel_crtc *crtc,
@@ -7140,13 +7421,13 @@ static void ironlake_get_plane_config(struct intel_crtc *crtc,
crtc->base.primary->fb->height = ((val >> 0) & 0xfff) + 1;
val = I915_READ(DSPSTRIDE(pipe));
- crtc->base.primary->fb->pitches[0] = val & 0xffffff80;
+ crtc->base.primary->fb->pitches[0] = val & 0xffffffc0;
aligned_height = intel_align_height(dev, crtc->base.primary->fb->height,
plane_config->tiled);
- plane_config->size = ALIGN(crtc->base.primary->fb->pitches[0] *
- aligned_height, PAGE_SIZE);
+ plane_config->size = PAGE_ALIGN(crtc->base.primary->fb->pitches[0] *
+ aligned_height);
DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
pipe, plane, crtc->base.primary->fb->width,
@@ -7163,6 +7444,10 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t tmp;
+ if (!intel_display_power_enabled(dev_priv,
+ POWER_DOMAIN_PIPE(crtc->pipe)))
+ return false;
+
pipe_config->cpu_transcoder = (enum transcoder) crtc->pipe;
pipe_config->shared_dpll = DPLL_ID_PRIVATE;
@@ -7237,7 +7522,6 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
- struct intel_ddi_plls *plls = &dev_priv->ddi_plls;
struct intel_crtc *crtc;
for_each_intel_crtc(dev, crtc)
@@ -7245,14 +7529,15 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
pipe_name(crtc->pipe));
WARN(I915_READ(HSW_PWR_WELL_DRIVER), "Power well on\n");
- WARN(plls->spll_refcount, "SPLL enabled\n");
- WARN(plls->wrpll1_refcount, "WRPLL1 enabled\n");
- WARN(plls->wrpll2_refcount, "WRPLL2 enabled\n");
+ WARN(I915_READ(SPLL_CTL) & SPLL_PLL_ENABLE, "SPLL enabled\n");
+ WARN(I915_READ(WRPLL_CTL1) & WRPLL_PLL_ENABLE, "WRPLL1 enabled\n");
+ WARN(I915_READ(WRPLL_CTL2) & WRPLL_PLL_ENABLE, "WRPLL2 enabled\n");
WARN(I915_READ(PCH_PP_STATUS) & PP_ON, "Panel power on\n");
WARN(I915_READ(BLC_PWM_CPU_CTL2) & BLM_PWM_ENABLE,
"CPU PWM1 enabled\n");
- WARN(I915_READ(HSW_BLC_PWM2_CTL) & BLM_PWM_ENABLE,
- "CPU PWM2 enabled\n");
+ if (IS_HASWELL(dev))
+ WARN(I915_READ(HSW_BLC_PWM2_CTL) & BLM_PWM_ENABLE,
+ "CPU PWM2 enabled\n");
WARN(I915_READ(BLC_PWM_PCH_CTL1) & BLM_PCH_PWM_ENABLE,
"PCH PWM1 enabled\n");
WARN(I915_READ(UTIL_PIN_CTL) & UTIL_PIN_ENABLE,
@@ -7265,7 +7550,17 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
* gen-specific and since we only disable LCPLL after we fully disable
* the interrupts, the check below should be enough.
*/
- WARN(!dev_priv->pm.irqs_disabled, "IRQs enabled\n");
+ WARN(intel_irqs_enabled(dev_priv), "IRQs enabled\n");
+}
+
+static uint32_t hsw_read_dcomp(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = dev_priv->dev;
+
+ if (IS_HASWELL(dev))
+ return I915_READ(D_COMP_HSW);
+ else
+ return I915_READ(D_COMP_BDW);
}
static void hsw_write_dcomp(struct drm_i915_private *dev_priv, uint32_t val)
@@ -7276,12 +7571,12 @@ static void hsw_write_dcomp(struct drm_i915_private *dev_priv, uint32_t val)
mutex_lock(&dev_priv->rps.hw_lock);
if (sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_D_COMP,
val))
- DRM_ERROR("Failed to disable D_COMP\n");
+ DRM_ERROR("Failed to write to D_COMP\n");
mutex_unlock(&dev_priv->rps.hw_lock);
} else {
- I915_WRITE(D_COMP, val);
+ I915_WRITE(D_COMP_BDW, val);
+ POSTING_READ(D_COMP_BDW);
}
- POSTING_READ(D_COMP);
}
/*
@@ -7319,12 +7614,13 @@ static void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
if (wait_for((I915_READ(LCPLL_CTL) & LCPLL_PLL_LOCK) == 0, 1))
DRM_ERROR("LCPLL still locked\n");
- val = I915_READ(D_COMP);
+ val = hsw_read_dcomp(dev_priv);
val |= D_COMP_COMP_DISABLE;
hsw_write_dcomp(dev_priv, val);
ndelay(100);
- if (wait_for((I915_READ(D_COMP) & D_COMP_RCOMP_IN_PROGRESS) == 0, 1))
+ if (wait_for((hsw_read_dcomp(dev_priv) & D_COMP_RCOMP_IN_PROGRESS) == 0,
+ 1))
DRM_ERROR("D_COMP RCOMP still in progress\n");
if (allow_power_down) {
@@ -7373,7 +7669,7 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
POSTING_READ(LCPLL_CTL);
}
- val = I915_READ(D_COMP);
+ val = hsw_read_dcomp(dev_priv);
val |= D_COMP_COMP_FORCE;
val &= ~D_COMP_COMP_DISABLE;
hsw_write_dcomp(dev_priv, val);
@@ -7479,13 +7775,66 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc,
if (!intel_ddi_pll_select(intel_crtc))
return -EINVAL;
- intel_ddi_pll_enable(intel_crtc);
intel_crtc->lowfreq_avail = false;
return 0;
}
+static void haswell_get_ddi_pll(struct drm_i915_private *dev_priv,
+ enum port port,
+ struct intel_crtc_config *pipe_config)
+{
+ pipe_config->ddi_pll_sel = I915_READ(PORT_CLK_SEL(port));
+
+ switch (pipe_config->ddi_pll_sel) {
+ case PORT_CLK_SEL_WRPLL1:
+ pipe_config->shared_dpll = DPLL_ID_WRPLL1;
+ break;
+ case PORT_CLK_SEL_WRPLL2:
+ pipe_config->shared_dpll = DPLL_ID_WRPLL2;
+ break;
+ }
+}
+
+static void haswell_get_ddi_port_state(struct intel_crtc *crtc,
+ struct intel_crtc_config *pipe_config)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_shared_dpll *pll;
+ enum port port;
+ uint32_t tmp;
+
+ tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe_config->cpu_transcoder));
+
+ port = (tmp & TRANS_DDI_PORT_MASK) >> TRANS_DDI_PORT_SHIFT;
+
+ haswell_get_ddi_pll(dev_priv, port, pipe_config);
+
+ if (pipe_config->shared_dpll >= 0) {
+ pll = &dev_priv->shared_dplls[pipe_config->shared_dpll];
+
+ WARN_ON(!pll->get_hw_state(dev_priv, pll,
+ &pipe_config->dpll_hw_state));
+ }
+
+ /*
+ * Haswell has only FDI/PCH transcoder A. It is which is connected to
+ * DDI E. So just check whether this pipe is wired to DDI E and whether
+ * the PCH transcoder is on.
+ */
+ if ((port == PORT_E) && I915_READ(LPT_TRANSCONF) & TRANS_ENABLE) {
+ pipe_config->has_pch_encoder = true;
+
+ tmp = I915_READ(FDI_RX_CTL(PIPE_A));
+ pipe_config->fdi_lanes = ((FDI_DP_PORT_WIDTH_MASK & tmp) >>
+ FDI_DP_PORT_WIDTH_SHIFT) + 1;
+
+ ironlake_get_fdi_m_n_config(crtc, pipe_config);
+ }
+}
+
static bool haswell_get_pipe_config(struct intel_crtc *crtc,
struct intel_crtc_config *pipe_config)
{
@@ -7531,22 +7880,7 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
if (!(tmp & PIPECONF_ENABLE))
return false;
- /*
- * Haswell has only FDI/PCH transcoder A. It is which is connected to
- * DDI E. So just check whether this pipe is wired to DDI E and whether
- * the PCH transcoder is on.
- */
- tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe_config->cpu_transcoder));
- if ((tmp & TRANS_DDI_PORT_MASK) == TRANS_DDI_SELECT_PORT(PORT_E) &&
- I915_READ(LPT_TRANSCONF) & TRANS_ENABLE) {
- pipe_config->has_pch_encoder = true;
-
- tmp = I915_READ(FDI_RX_CTL(PIPE_A));
- pipe_config->fdi_lanes = ((FDI_DP_PORT_WIDTH_MASK & tmp) >>
- FDI_DP_PORT_WIDTH_SHIFT) + 1;
-
- ironlake_get_fdi_m_n_config(crtc, pipe_config);
- }
+ haswell_get_ddi_port_state(crtc, pipe_config);
intel_get_pipe_timings(crtc, pipe_config);
@@ -7558,7 +7892,12 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
pipe_config->ips_enabled = hsw_crtc_supports_ips(crtc) &&
(I915_READ(IPS_CTL) & IPS_ENABLE);
- pipe_config->pixel_multiplier = 1;
+ if (pipe_config->cpu_transcoder != TRANSCODER_EDP) {
+ pipe_config->pixel_multiplier =
+ I915_READ(PIPE_MULT(pipe_config->cpu_transcoder)) + 1;
+ } else {
+ pipe_config->pixel_multiplier = 1;
+ }
return true;
}
@@ -7876,74 +8215,62 @@ static void i845_update_cursor(struct drm_crtc *crtc, u32 base)
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- uint32_t cntl;
+ uint32_t cntl = 0, size = 0;
- if (base != intel_crtc->cursor_base) {
- /* On these chipsets we can only modify the base whilst
- * the cursor is disabled.
- */
- if (intel_crtc->cursor_cntl) {
- I915_WRITE(_CURACNTR, 0);
- POSTING_READ(_CURACNTR);
- intel_crtc->cursor_cntl = 0;
+ if (base) {
+ unsigned int width = intel_crtc->cursor_width;
+ unsigned int height = intel_crtc->cursor_height;
+ unsigned int stride = roundup_pow_of_two(width) * 4;
+
+ switch (stride) {
+ default:
+ WARN_ONCE(1, "Invalid cursor width/stride, width=%u, stride=%u\n",
+ width, stride);
+ stride = 256;
+ /* fallthrough */
+ case 256:
+ case 512:
+ case 1024:
+ case 2048:
+ break;
}
- I915_WRITE(_CURABASE, base);
- POSTING_READ(_CURABASE);
+ cntl |= CURSOR_ENABLE |
+ CURSOR_GAMMA_ENABLE |
+ CURSOR_FORMAT_ARGB |
+ CURSOR_STRIDE(stride);
+
+ size = (height << 12) | width;
}
- /* XXX width must be 64, stride 256 => 0x00 << 28 */
- cntl = 0;
- if (base)
- cntl = (CURSOR_ENABLE |
- CURSOR_GAMMA_ENABLE |
- CURSOR_FORMAT_ARGB);
- if (intel_crtc->cursor_cntl != cntl) {
- I915_WRITE(_CURACNTR, cntl);
+ if (intel_crtc->cursor_cntl != 0 &&
+ (intel_crtc->cursor_base != base ||
+ intel_crtc->cursor_size != size ||
+ intel_crtc->cursor_cntl != cntl)) {
+ /* On these chipsets we can only modify the base/size/stride
+ * whilst the cursor is disabled.
+ */
+ I915_WRITE(_CURACNTR, 0);
POSTING_READ(_CURACNTR);
- intel_crtc->cursor_cntl = cntl;
+ intel_crtc->cursor_cntl = 0;
}
-}
-static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
-{
- struct drm_device *dev = crtc->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- int pipe = intel_crtc->pipe;
- uint32_t cntl;
+ if (intel_crtc->cursor_base != base)
+ I915_WRITE(_CURABASE, base);
- cntl = 0;
- if (base) {
- cntl = MCURSOR_GAMMA_ENABLE;
- switch (intel_crtc->cursor_width) {
- case 64:
- cntl |= CURSOR_MODE_64_ARGB_AX;
- break;
- case 128:
- cntl |= CURSOR_MODE_128_ARGB_AX;
- break;
- case 256:
- cntl |= CURSOR_MODE_256_ARGB_AX;
- break;
- default:
- WARN_ON(1);
- return;
- }
- cntl |= pipe << 28; /* Connect to correct pipe */
+ if (intel_crtc->cursor_size != size) {
+ I915_WRITE(CURSIZE, size);
+ intel_crtc->cursor_size = size;
}
+
if (intel_crtc->cursor_cntl != cntl) {
- I915_WRITE(CURCNTR(pipe), cntl);
- POSTING_READ(CURCNTR(pipe));
+ I915_WRITE(_CURACNTR, cntl);
+ POSTING_READ(_CURACNTR);
intel_crtc->cursor_cntl = cntl;
}
-
- /* and commit changes on next vblank */
- I915_WRITE(CURBASE(pipe), base);
- POSTING_READ(CURBASE(pipe));
}
-static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
+static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -7968,6 +8295,7 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
WARN_ON(1);
return;
}
+ cntl |= pipe << 28; /* Connect to correct pipe */
}
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
cntl |= CURSOR_PIPE_CSC_ENABLE;
@@ -7991,8 +8319,8 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
- int x = intel_crtc->cursor_x;
- int y = intel_crtc->cursor_y;
+ int x = crtc->cursor_x;
+ int y = crtc->cursor_y;
u32 base = 0, pos = 0;
if (on)
@@ -8027,51 +8355,86 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
I915_WRITE(CURPOS(pipe), pos);
- if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev))
- ivb_update_cursor(crtc, base);
- else if (IS_845G(dev) || IS_I865G(dev))
+ if (IS_845G(dev) || IS_I865G(dev))
i845_update_cursor(crtc, base);
else
i9xx_update_cursor(crtc, base);
intel_crtc->cursor_base = base;
}
-static int intel_crtc_cursor_set(struct drm_crtc *crtc,
- struct drm_file *file,
- uint32_t handle,
- uint32_t width, uint32_t height)
+static bool cursor_size_ok(struct drm_device *dev,
+ uint32_t width, uint32_t height)
+{
+ if (width == 0 || height == 0)
+ return false;
+
+ /*
+ * 845g/865g are special in that they are only limited by
+ * the width of their cursors, the height is arbitrary up to
+ * the precision of the register. Everything else requires
+ * square cursors, limited to a few power-of-two sizes.
+ */
+ if (IS_845G(dev) || IS_I865G(dev)) {
+ if ((width & 63) != 0)
+ return false;
+
+ if (width > (IS_845G(dev) ? 64 : 512))
+ return false;
+
+ if (height > 1023)
+ return false;
+ } else {
+ switch (width | height) {
+ case 256:
+ case 128:
+ if (IS_GEN2(dev))
+ return false;
+ case 64:
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * intel_crtc_cursor_set_obj - Set cursor to specified GEM object
+ *
+ * Note that the object's reference will be consumed if the update fails. If
+ * the update succeeds, the reference of the old object (if any) will be
+ * consumed.
+ */
+static int intel_crtc_cursor_set_obj(struct drm_crtc *crtc,
+ struct drm_i915_gem_object *obj,
+ uint32_t width, uint32_t height)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- struct drm_i915_gem_object *obj;
- unsigned old_width;
+ enum pipe pipe = intel_crtc->pipe;
+ unsigned old_width, stride;
uint32_t addr;
int ret;
/* if we want to turn off the cursor ignore width and height */
- if (!handle) {
+ if (!obj) {
DRM_DEBUG_KMS("cursor off\n");
addr = 0;
- obj = NULL;
mutex_lock(&dev->struct_mutex);
goto finish;
}
/* Check for which cursor types we support */
- if (!((width == 64 && height == 64) ||
- (width == 128 && height == 128 && !IS_GEN2(dev)) ||
- (width == 256 && height == 256 && !IS_GEN2(dev)))) {
+ if (!cursor_size_ok(dev, width, height)) {
DRM_DEBUG("Cursor dimension not supported\n");
return -EINVAL;
}
- obj = to_intel_bo(drm_gem_object_lookup(dev, file, handle));
- if (&obj->base == NULL)
- return -ENOENT;
-
- if (obj->base.size < width * height * 4) {
- DRM_DEBUG_KMS("buffer is to small\n");
+ stride = roundup_pow_of_two(width) * 4;
+ if (obj->base.size < stride * height) {
+ DRM_DEBUG_KMS("buffer is too small\n");
ret = -ENOMEM;
goto fail;
}
@@ -8087,6 +8450,15 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
goto fail_locked;
}
+ /*
+ * Global gtt pte registers are special registers which actually
+ * forward writes to a chunk of system memory. Which means that
+ * there is no risk that the register values disappear as soon
+ * as we call intel_runtime_pm_put(), so it is correct to wrap
+ * only the pin/unpin/fence and not more.
+ */
+ intel_runtime_pm_get(dev_priv);
+
/* Note that the w/a also requires 2 PTE of padding following
* the bo. We currently fill all unused PTE with the shadow
* page and so we should always have valid PTE following the
@@ -8099,16 +8471,20 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
ret = i915_gem_object_pin_to_display_plane(obj, alignment, NULL);
if (ret) {
DRM_DEBUG_KMS("failed to move cursor bo into the GTT\n");
+ intel_runtime_pm_put(dev_priv);
goto fail_locked;
}
ret = i915_gem_object_put_fence(obj);
if (ret) {
DRM_DEBUG_KMS("failed to release fence for cursor");
+ intel_runtime_pm_put(dev_priv);
goto fail_unpin;
}
addr = i915_gem_obj_ggtt_offset(obj);
+
+ intel_runtime_pm_put(dev_priv);
} else {
int align = IS_I830(dev) ? 16 * 1024 : 256;
ret = i915_gem_object_attach_phys(obj, align);
@@ -8119,16 +8495,14 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
addr = obj->phys_handle->busaddr;
}
- if (IS_GEN2(dev))
- I915_WRITE(CURSIZE, (height << 12) | width);
-
finish:
if (intel_crtc->cursor_bo) {
if (!INTEL_INFO(dev)->cursor_needs_physical)
i915_gem_object_unpin_from_display_plane(intel_crtc->cursor_bo);
- drm_gem_object_unreference(&intel_crtc->cursor_bo->base);
}
+ i915_gem_track_fb(intel_crtc->cursor_bo, obj,
+ INTEL_FRONTBUFFER_CURSOR(pipe));
mutex_unlock(&dev->struct_mutex);
old_width = intel_crtc->cursor_width;
@@ -8144,6 +8518,8 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL);
}
+ intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_CURSOR(pipe));
+
return 0;
fail_unpin:
i915_gem_object_unpin_from_display_plane(obj);
@@ -8154,19 +8530,6 @@ fail:
return ret;
}
-static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
-{
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-
- intel_crtc->cursor_x = clamp_t(int, x, SHRT_MIN, SHRT_MAX);
- intel_crtc->cursor_y = clamp_t(int, y, SHRT_MIN, SHRT_MAX);
-
- if (intel_crtc->active)
- intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL);
-
- return 0;
-}
-
static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, uint32_t start, uint32_t size)
{
@@ -8242,7 +8605,7 @@ static u32
intel_framebuffer_size_for_mode(struct drm_display_mode *mode, int bpp)
{
u32 pitch = intel_framebuffer_pitch_for_width(mode->hdisplay, bpp);
- return ALIGN(pitch * mode->vdisplay, PAGE_SIZE);
+ return PAGE_ALIGN(pitch * mode->vdisplay);
}
static struct drm_framebuffer *
@@ -8319,8 +8682,6 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
connector->base.id, connector->name,
encoder->base.id, encoder->name);
- drm_modeset_acquire_init(ctx, 0);
-
retry:
ret = drm_modeset_lock(&config->connection_mutex, ctx);
if (ret)
@@ -8359,10 +8720,14 @@ retry:
i++;
if (!(encoder->possible_crtcs & (1 << i)))
continue;
- if (!possible_crtc->enabled) {
- crtc = possible_crtc;
- break;
- }
+ if (possible_crtc->enabled)
+ continue;
+ /* This can occur when applying the pipe A quirk on resume. */
+ if (to_intel_crtc(possible_crtc)->new_enabled)
+ continue;
+
+ crtc = possible_crtc;
+ break;
}
/*
@@ -8431,15 +8796,11 @@ fail_unlock:
goto retry;
}
- drm_modeset_drop_locks(ctx);
- drm_modeset_acquire_fini(ctx);
-
return false;
}
void intel_release_load_detect_pipe(struct drm_connector *connector,
- struct intel_load_detect_pipe *old,
- struct drm_modeset_acquire_ctx *ctx)
+ struct intel_load_detect_pipe *old)
{
struct intel_encoder *intel_encoder =
intel_attached_encoder(connector);
@@ -8463,17 +8824,12 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
drm_framebuffer_unreference(old->release_fb);
}
- goto unlock;
return;
}
/* Switch crtc and encoder back off if necessary */
if (old->dpms_mode != DRM_MODE_DPMS_ON)
connector->funcs->dpms(connector, old->dpms_mode);
-
-unlock:
- drm_modeset_drop_locks(ctx);
- drm_modeset_acquire_fini(ctx);
}
static int i9xx_pll_refclk(struct drm_device *dev,
@@ -8667,16 +9023,14 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
return mode;
}
-static void intel_increase_pllclock(struct drm_crtc *crtc)
+static void intel_increase_pllclock(struct drm_device *dev,
+ enum pipe pipe)
{
- struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- int pipe = intel_crtc->pipe;
int dpll_reg = DPLL(pipe);
int dpll;
- if (HAS_PCH_SPLIT(dev))
+ if (!HAS_GMCH_DISPLAY(dev))
return;
if (!dev_priv->lvds_downclock_avail)
@@ -8704,7 +9058,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- if (HAS_PCH_SPLIT(dev))
+ if (!HAS_GMCH_DISPLAY(dev))
return;
if (!dev_priv->lvds_downclock_avail)
@@ -8773,28 +9127,188 @@ out:
intel_runtime_pm_put(dev_priv);
}
-void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
- struct intel_engine_cs *ring)
+
+/**
+ * intel_mark_fb_busy - mark given planes as busy
+ * @dev: DRM device
+ * @frontbuffer_bits: bits for the affected planes
+ * @ring: optional ring for asynchronous commands
+ *
+ * This function gets called every time the screen contents change. It can be
+ * used to keep e.g. the update rate at the nominal refresh rate with DRRS.
+ */
+static void intel_mark_fb_busy(struct drm_device *dev,
+ unsigned frontbuffer_bits,
+ struct intel_engine_cs *ring)
{
- struct drm_device *dev = obj->base.dev;
- struct drm_crtc *crtc;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum pipe pipe;
if (!i915.powersave)
return;
- for_each_crtc(dev, crtc) {
- if (!crtc->primary->fb)
- continue;
-
- if (to_intel_framebuffer(crtc->primary->fb)->obj != obj)
+ for_each_pipe(dev_priv, pipe) {
+ if (!(frontbuffer_bits & INTEL_FRONTBUFFER_ALL_MASK(pipe)))
continue;
- intel_increase_pllclock(crtc);
+ intel_increase_pllclock(dev, pipe);
if (ring && intel_fbc_enabled(dev))
ring->fbc_dirty = true;
}
}
+/**
+ * intel_fb_obj_invalidate - invalidate frontbuffer object
+ * @obj: GEM object to invalidate
+ * @ring: set for asynchronous rendering
+ *
+ * This function gets called every time rendering on the given object starts and
+ * frontbuffer caching (fbc, low refresh rate for DRRS, panel self refresh) must
+ * be invalidated. If @ring is non-NULL any subsequent invalidation will be delayed
+ * until the rendering completes or a flip on this frontbuffer plane is
+ * scheduled.
+ */
+void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
+ struct intel_engine_cs *ring)
+{
+ struct drm_device *dev = obj->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+ if (!obj->frontbuffer_bits)
+ return;
+
+ if (ring) {
+ mutex_lock(&dev_priv->fb_tracking.lock);
+ dev_priv->fb_tracking.busy_bits
+ |= obj->frontbuffer_bits;
+ dev_priv->fb_tracking.flip_bits
+ &= ~obj->frontbuffer_bits;
+ mutex_unlock(&dev_priv->fb_tracking.lock);
+ }
+
+ intel_mark_fb_busy(dev, obj->frontbuffer_bits, ring);
+
+ intel_edp_psr_invalidate(dev, obj->frontbuffer_bits);
+}
+
+/**
+ * intel_frontbuffer_flush - flush frontbuffer
+ * @dev: DRM device
+ * @frontbuffer_bits: frontbuffer plane tracking bits
+ *
+ * This function gets called every time rendering on the given planes has
+ * completed and frontbuffer caching can be started again. Flushes will get
+ * delayed if they're blocked by some oustanding asynchronous rendering.
+ *
+ * Can be called without any locks held.
+ */
+void intel_frontbuffer_flush(struct drm_device *dev,
+ unsigned frontbuffer_bits)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /* Delay flushing when rings are still busy.*/
+ mutex_lock(&dev_priv->fb_tracking.lock);
+ frontbuffer_bits &= ~dev_priv->fb_tracking.busy_bits;
+ mutex_unlock(&dev_priv->fb_tracking.lock);
+
+ intel_mark_fb_busy(dev, frontbuffer_bits, NULL);
+
+ intel_edp_psr_flush(dev, frontbuffer_bits);
+
+ /*
+ * FIXME: Unconditional fbc flushing here is a rather gross hack and
+ * needs to be reworked into a proper frontbuffer tracking scheme like
+ * psr employs.
+ */
+ if (IS_BROADWELL(dev))
+ gen8_fbc_sw_flush(dev, FBC_REND_CACHE_CLEAN);
+}
+
+/**
+ * intel_fb_obj_flush - flush frontbuffer object
+ * @obj: GEM object to flush
+ * @retire: set when retiring asynchronous rendering
+ *
+ * This function gets called every time rendering on the given object has
+ * completed and frontbuffer caching can be started again. If @retire is true
+ * then any delayed flushes will be unblocked.
+ */
+void intel_fb_obj_flush(struct drm_i915_gem_object *obj,
+ bool retire)
+{
+ struct drm_device *dev = obj->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned frontbuffer_bits;
+
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+ if (!obj->frontbuffer_bits)
+ return;
+
+ frontbuffer_bits = obj->frontbuffer_bits;
+
+ if (retire) {
+ mutex_lock(&dev_priv->fb_tracking.lock);
+ /* Filter out new bits since rendering started. */
+ frontbuffer_bits &= dev_priv->fb_tracking.busy_bits;
+
+ dev_priv->fb_tracking.busy_bits &= ~frontbuffer_bits;
+ mutex_unlock(&dev_priv->fb_tracking.lock);
+ }
+
+ intel_frontbuffer_flush(dev, frontbuffer_bits);
+}
+
+/**
+ * intel_frontbuffer_flip_prepare - prepare asnychronous frontbuffer flip
+ * @dev: DRM device
+ * @frontbuffer_bits: frontbuffer plane tracking bits
+ *
+ * This function gets called after scheduling a flip on @obj. The actual
+ * frontbuffer flushing will be delayed until completion is signalled with
+ * intel_frontbuffer_flip_complete. If an invalidate happens in between this
+ * flush will be cancelled.
+ *
+ * Can be called without any locks held.
+ */
+void intel_frontbuffer_flip_prepare(struct drm_device *dev,
+ unsigned frontbuffer_bits)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ mutex_lock(&dev_priv->fb_tracking.lock);
+ dev_priv->fb_tracking.flip_bits
+ |= frontbuffer_bits;
+ mutex_unlock(&dev_priv->fb_tracking.lock);
+}
+
+/**
+ * intel_frontbuffer_flip_complete - complete asynchronous frontbuffer flush
+ * @dev: DRM device
+ * @frontbuffer_bits: frontbuffer plane tracking bits
+ *
+ * This function gets called after the flip has been latched and will complete
+ * on the next vblank. It will execute the fush if it hasn't been cancalled yet.
+ *
+ * Can be called without any locks held.
+ */
+void intel_frontbuffer_flip_complete(struct drm_device *dev,
+ unsigned frontbuffer_bits)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ mutex_lock(&dev_priv->fb_tracking.lock);
+ /* Mask any cancelled flips. */
+ frontbuffer_bits &= dev_priv->fb_tracking.flip_bits;
+ dev_priv->fb_tracking.flip_bits &= ~frontbuffer_bits;
+ mutex_unlock(&dev_priv->fb_tracking.lock);
+
+ intel_frontbuffer_flush(dev, frontbuffer_bits);
+}
+
static void intel_crtc_destroy(struct drm_crtc *crtc)
{
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -8812,8 +9326,6 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
kfree(work);
}
- intel_crtc_cursor_set(crtc, NULL, 0, 0, 0);
-
drm_crtc_cleanup(crtc);
kfree(intel_crtc);
@@ -8824,6 +9336,7 @@ static void intel_unpin_work_fn(struct work_struct *__work)
struct intel_unpin_work *work =
container_of(__work, struct intel_unpin_work, work);
struct drm_device *dev = work->crtc->dev;
+ enum pipe pipe = to_intel_crtc(work->crtc)->pipe;
mutex_lock(&dev->struct_mutex);
intel_unpin_fb_obj(work->old_fb_obj);
@@ -8833,6 +9346,8 @@ static void intel_unpin_work_fn(struct work_struct *__work)
intel_update_fbc(dev);
mutex_unlock(&dev->struct_mutex);
+ intel_frontbuffer_flip_complete(dev, INTEL_FRONTBUFFER_PRIMARY(pipe));
+
BUG_ON(atomic_read(&to_intel_crtc(work->crtc)->unpin_work_count) == 0);
atomic_dec(&to_intel_crtc(work->crtc)->unpin_work_count);
@@ -8842,7 +9357,6 @@ static void intel_unpin_work_fn(struct work_struct *__work)
static void do_intel_finish_page_flip(struct drm_device *dev,
struct drm_crtc *crtc)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_unpin_work *work;
unsigned long flags;
@@ -8862,23 +9376,9 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
return;
}
- /* and that the unpin work is consistent wrt ->pending. */
- smp_rmb();
-
- intel_crtc->unpin_work = NULL;
-
- if (work->event)
- drm_send_vblank_event(dev, intel_crtc->pipe, work->event);
-
- drm_crtc_vblank_put(crtc);
+ page_flip_completed(intel_crtc);
spin_unlock_irqrestore(&dev->event_lock, flags);
-
- wake_up_all(&dev_priv->pending_flip_queue);
-
- queue_work(dev_priv->wq, &work->work);
-
- trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj);
}
void intel_finish_page_flip(struct drm_device *dev, int pipe)
@@ -9202,6 +9702,152 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
return 0;
}
+static bool use_mmio_flip(struct intel_engine_cs *ring,
+ struct drm_i915_gem_object *obj)
+{
+ /*
+ * This is not being used for older platforms, because
+ * non-availability of flip done interrupt forces us to use
+ * CS flips. Older platforms derive flip done using some clever
+ * tricks involving the flip_pending status bits and vblank irqs.
+ * So using MMIO flips there would disrupt this mechanism.
+ */
+
+ if (ring == NULL)
+ return true;
+
+ if (INTEL_INFO(ring->dev)->gen < 5)
+ return false;
+
+ if (i915.use_mmio_flip < 0)
+ return false;
+ else if (i915.use_mmio_flip > 0)
+ return true;
+ else if (i915.enable_execlists)
+ return true;
+ else
+ return ring != obj->ring;
+}
+
+static void intel_do_mmio_flip(struct intel_crtc *intel_crtc)
+{
+ struct drm_device *dev = intel_crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_framebuffer *intel_fb =
+ to_intel_framebuffer(intel_crtc->base.primary->fb);
+ struct drm_i915_gem_object *obj = intel_fb->obj;
+ u32 dspcntr;
+ u32 reg;
+
+ intel_mark_page_flip_active(intel_crtc);
+
+ reg = DSPCNTR(intel_crtc->plane);
+ dspcntr = I915_READ(reg);
+
+ if (INTEL_INFO(dev)->gen >= 4) {
+ if (obj->tiling_mode != I915_TILING_NONE)
+ dspcntr |= DISPPLANE_TILED;
+ else
+ dspcntr &= ~DISPPLANE_TILED;
+ }
+ I915_WRITE(reg, dspcntr);
+
+ I915_WRITE(DSPSURF(intel_crtc->plane),
+ intel_crtc->unpin_work->gtt_offset);
+ POSTING_READ(DSPSURF(intel_crtc->plane));
+}
+
+static int intel_postpone_flip(struct drm_i915_gem_object *obj)
+{
+ struct intel_engine_cs *ring;
+ int ret;
+
+ lockdep_assert_held(&obj->base.dev->struct_mutex);
+
+ if (!obj->last_write_seqno)
+ return 0;
+
+ ring = obj->ring;
+
+ if (i915_seqno_passed(ring->get_seqno(ring, true),
+ obj->last_write_seqno))
+ return 0;
+
+ ret = i915_gem_check_olr(ring, obj->last_write_seqno);
+ if (ret)
+ return ret;
+
+ if (WARN_ON(!ring->irq_get(ring)))
+ return 0;
+
+ return 1;
+}
+
+void intel_notify_mmio_flip(struct intel_engine_cs *ring)
+{
+ struct drm_i915_private *dev_priv = to_i915(ring->dev);
+ struct intel_crtc *intel_crtc;
+ unsigned long irq_flags;
+ u32 seqno;
+
+ seqno = ring->get_seqno(ring, false);
+
+ spin_lock_irqsave(&dev_priv->mmio_flip_lock, irq_flags);
+ for_each_intel_crtc(ring->dev, intel_crtc) {
+ struct intel_mmio_flip *mmio_flip;
+
+ mmio_flip = &intel_crtc->mmio_flip;
+ if (mmio_flip->seqno == 0)
+ continue;
+
+ if (ring->id != mmio_flip->ring_id)
+ continue;
+
+ if (i915_seqno_passed(seqno, mmio_flip->seqno)) {
+ intel_do_mmio_flip(intel_crtc);
+ mmio_flip->seqno = 0;
+ ring->irq_put(ring);
+ }
+ }
+ spin_unlock_irqrestore(&dev_priv->mmio_flip_lock, irq_flags);
+}
+
+static int intel_queue_mmio_flip(struct drm_device *dev,
+ struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_i915_gem_object *obj,
+ struct intel_engine_cs *ring,
+ uint32_t flags)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ unsigned long irq_flags;
+ int ret;
+
+ if (WARN_ON(intel_crtc->mmio_flip.seqno))
+ return -EBUSY;
+
+ ret = intel_postpone_flip(obj);
+ if (ret < 0)
+ return ret;
+ if (ret == 0) {
+ intel_do_mmio_flip(intel_crtc);
+ return 0;
+ }
+
+ spin_lock_irqsave(&dev_priv->mmio_flip_lock, irq_flags);
+ intel_crtc->mmio_flip.seqno = obj->last_write_seqno;
+ intel_crtc->mmio_flip.ring_id = obj->ring->id;
+ spin_unlock_irqrestore(&dev_priv->mmio_flip_lock, irq_flags);
+
+ /*
+ * Double check to catch cases where irq fired before
+ * mmio flip data was ready
+ */
+ intel_notify_mmio_flip(obj->ring);
+ return 0;
+}
+
static int intel_default_queue_flip(struct drm_device *dev,
struct drm_crtc *crtc,
struct drm_framebuffer *fb,
@@ -9212,6 +9858,65 @@ static int intel_default_queue_flip(struct drm_device *dev,
return -ENODEV;
}
+static bool __intel_pageflip_stall_check(struct drm_device *dev,
+ struct drm_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_unpin_work *work = intel_crtc->unpin_work;
+ u32 addr;
+
+ if (atomic_read(&work->pending) >= INTEL_FLIP_COMPLETE)
+ return true;
+
+ if (!work->enable_stall_check)
+ return false;
+
+ if (work->flip_ready_vblank == 0) {
+ if (work->flip_queued_ring &&
+ !i915_seqno_passed(work->flip_queued_ring->get_seqno(work->flip_queued_ring, true),
+ work->flip_queued_seqno))
+ return false;
+
+ work->flip_ready_vblank = drm_vblank_count(dev, intel_crtc->pipe);
+ }
+
+ if (drm_vblank_count(dev, intel_crtc->pipe) - work->flip_ready_vblank < 3)
+ return false;
+
+ /* Potential stall - if we see that the flip has happened,
+ * assume a missed interrupt. */
+ if (INTEL_INFO(dev)->gen >= 4)
+ addr = I915_HI_DISPBASE(I915_READ(DSPSURF(intel_crtc->plane)));
+ else
+ addr = I915_READ(DSPADDR(intel_crtc->plane));
+
+ /* There is a potential issue here with a false positive after a flip
+ * to the same address. We could address this by checking for a
+ * non-incrementing frame counter.
+ */
+ return addr == work->gtt_offset;
+}
+
+void intel_check_page_flip(struct drm_device *dev, int pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ unsigned long flags;
+
+ if (crtc == NULL)
+ return;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ if (intel_crtc->unpin_work && __intel_pageflip_stall_check(dev, crtc)) {
+ WARN_ONCE(1, "Kicking stuck page flip: queued at %d, now %d\n",
+ intel_crtc->unpin_work->flip_queued_vblank, drm_vblank_count(dev, pipe));
+ page_flip_completed(intel_crtc);
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
static int intel_crtc_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
@@ -9220,13 +9925,22 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_framebuffer *old_fb = crtc->primary->fb;
- struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj;
+ struct drm_i915_gem_object *obj = intel_fb_obj(fb);
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ enum pipe pipe = intel_crtc->pipe;
struct intel_unpin_work *work;
struct intel_engine_cs *ring;
unsigned long flags;
int ret;
+ /*
+ * drm_mode_page_flip_ioctl() should already catch this, but double
+ * check to be safe. In the future we may enable pageflipping from
+ * a disabled primary plane.
+ */
+ if (WARN_ON(intel_fb_obj(old_fb) == NULL))
+ return -EBUSY;
+
/* Can't change pixel format via MI display flips. */
if (fb->pixel_format != crtc->primary->fb->pixel_format)
return -EINVAL;
@@ -9249,7 +9963,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
work->event = event;
work->crtc = crtc;
- work->old_fb_obj = to_intel_framebuffer(old_fb)->obj;
+ work->old_fb_obj = intel_fb_obj(old_fb);
INIT_WORK(&work->work, intel_unpin_work_fn);
ret = drm_crtc_vblank_get(crtc);
@@ -9259,12 +9973,20 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
/* We borrow the event spin lock for protecting unpin_work */
spin_lock_irqsave(&dev->event_lock, flags);
if (intel_crtc->unpin_work) {
- spin_unlock_irqrestore(&dev->event_lock, flags);
- kfree(work);
- drm_crtc_vblank_put(crtc);
+ /* Before declaring the flip queue wedged, check if
+ * the hardware completed the operation behind our backs.
+ */
+ if (__intel_pageflip_stall_check(dev, crtc)) {
+ DRM_DEBUG_DRIVER("flip queue: previous flip completed, continuing\n");
+ page_flip_completed(intel_crtc);
+ } else {
+ DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
+ spin_unlock_irqrestore(&dev->event_lock, flags);
- DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
- return -EBUSY;
+ drm_crtc_vblank_put(crtc);
+ kfree(work);
+ return -EBUSY;
+ }
}
intel_crtc->unpin_work = work;
spin_unlock_irqrestore(&dev->event_lock, flags);
@@ -9284,16 +10006,19 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
work->pending_flip_obj = obj;
- work->enable_stall_check = true;
-
atomic_inc(&intel_crtc->unpin_work_count);
intel_crtc->reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
if (INTEL_INFO(dev)->gen >= 5 || IS_G4X(dev))
- work->flip_count = I915_READ(PIPE_FLIPCOUNT_GM45(intel_crtc->pipe)) + 1;
+ work->flip_count = I915_READ(PIPE_FLIPCOUNT_GM45(pipe)) + 1;
if (IS_VALLEYVIEW(dev)) {
ring = &dev_priv->ring[BCS];
+ if (obj->tiling_mode != work->old_fb_obj->tiling_mode)
+ /* vlv: DISPLAY_FLIP fails to change tiling */
+ ring = NULL;
+ } else if (IS_IVYBRIDGE(dev)) {
+ ring = &dev_priv->ring[BCS];
} else if (INTEL_INFO(dev)->gen >= 7) {
ring = obj->ring;
if (ring == NULL || ring->id != RCS)
@@ -9309,12 +10034,32 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
work->gtt_offset =
i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset;
- ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, ring, page_flip_flags);
- if (ret)
- goto cleanup_unpin;
+ if (use_mmio_flip(ring, obj)) {
+ ret = intel_queue_mmio_flip(dev, crtc, fb, obj, ring,
+ page_flip_flags);
+ if (ret)
+ goto cleanup_unpin;
+
+ work->flip_queued_seqno = obj->last_write_seqno;
+ work->flip_queued_ring = obj->ring;
+ } else {
+ ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, ring,
+ page_flip_flags);
+ if (ret)
+ goto cleanup_unpin;
+
+ work->flip_queued_seqno = intel_ring_get_seqno(ring);
+ work->flip_queued_ring = ring;
+ }
+
+ work->flip_queued_vblank = drm_vblank_count(dev, intel_crtc->pipe);
+ work->enable_stall_check = true;
+
+ i915_gem_track_fb(work->old_fb_obj, obj,
+ INTEL_FRONTBUFFER_PRIMARY(pipe));
intel_disable_fbc(dev);
- intel_mark_fb_busy(obj, NULL);
+ intel_frontbuffer_flip_prepare(dev, INTEL_FRONTBUFFER_PRIMARY(pipe));
mutex_unlock(&dev->struct_mutex);
trace_i915_flip_request(intel_crtc->plane, obj);
@@ -9343,8 +10088,11 @@ free_work:
out_hang:
intel_crtc_wait_for_pending_flips(crtc);
ret = intel_pipe_set_base(crtc, crtc->x, crtc->y, fb);
- if (ret == 0 && event)
- drm_send_vblank_event(dev, intel_crtc->pipe, event);
+ if (ret == 0 && event) {
+ spin_lock_irqsave(&dev->event_lock, flags);
+ drm_send_vblank_event(dev, pipe, event);
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ }
}
return ret;
}
@@ -9372,8 +10120,7 @@ static void intel_modeset_update_staged_output_state(struct drm_device *dev)
to_intel_encoder(connector->base.encoder);
}
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
encoder->new_crtc =
to_intel_crtc(encoder->base.crtc);
}
@@ -9404,8 +10151,7 @@ static void intel_modeset_commit_output_state(struct drm_device *dev)
connector->base.encoder = &connector->new_encoder->base;
}
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
encoder->base.crtc = &encoder->new_crtc->base;
}
@@ -9532,6 +10278,15 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
pipe_config->dp_m_n.gmch_m, pipe_config->dp_m_n.gmch_n,
pipe_config->dp_m_n.link_m, pipe_config->dp_m_n.link_n,
pipe_config->dp_m_n.tu);
+
+ DRM_DEBUG_KMS("dp: %i, gmch_m2: %u, gmch_n2: %u, link_m2: %u, link_n2: %u, tu2: %u\n",
+ pipe_config->has_dp_encoder,
+ pipe_config->dp_m2_n2.gmch_m,
+ pipe_config->dp_m2_n2.gmch_n,
+ pipe_config->dp_m2_n2.link_m,
+ pipe_config->dp_m2_n2.link_n,
+ pipe_config->dp_m2_n2.tu);
+
DRM_DEBUG_KMS("requested mode:\n");
drm_mode_debug_printmodeline(&pipe_config->requested_mode);
DRM_DEBUG_KMS("adjusted mode:\n");
@@ -9566,8 +10321,7 @@ static bool check_single_encoder_cloning(struct intel_crtc *crtc,
struct drm_device *dev = crtc->base.dev;
struct intel_encoder *source_encoder;
- list_for_each_entry(source_encoder,
- &dev->mode_config.encoder_list, base.head) {
+ for_each_intel_encoder(dev, source_encoder) {
if (source_encoder->new_crtc != crtc)
continue;
@@ -9583,8 +10337,7 @@ static bool check_encoder_cloning(struct intel_crtc *crtc)
struct drm_device *dev = crtc->base.dev;
struct intel_encoder *encoder;
- list_for_each_entry(encoder,
- &dev->mode_config.encoder_list, base.head) {
+ for_each_intel_encoder(dev, encoder) {
if (encoder->new_crtc != crtc)
continue;
@@ -9668,8 +10421,7 @@ encoder_retry:
* adjust it according to limitations or connector properties, and also
* a chance to reject the mode entirely.
*/
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
if (&encoder->new_crtc->base != crtc)
continue;
@@ -9747,8 +10499,7 @@ intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes,
1 << connector->new_encoder->new_crtc->pipe;
}
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
if (encoder->base.crtc == &encoder->new_crtc->base)
continue;
@@ -9822,8 +10573,7 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes)
struct intel_crtc *intel_crtc;
struct drm_connector *connector;
- list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, intel_encoder) {
if (!intel_encoder->base.crtc)
continue;
@@ -9912,6 +10662,22 @@ intel_pipe_config_compare(struct drm_device *dev,
return false; \
}
+/* This is required for BDW+ where there is only one set of registers for
+ * switching between high and low RR.
+ * This macro can be used whenever a comparison has to be made between one
+ * hw state and multiple sw state variables.
+ */
+#define PIPE_CONF_CHECK_I_ALT(name, alt_name) \
+ if ((current_config->name != pipe_config->name) && \
+ (current_config->alt_name != pipe_config->name)) { \
+ DRM_ERROR("mismatch in " #name " " \
+ "(expected %i or %i, found %i)\n", \
+ current_config->name, \
+ current_config->alt_name, \
+ pipe_config->name); \
+ return false; \
+ }
+
#define PIPE_CONF_CHECK_FLAGS(name, mask) \
if ((current_config->name ^ pipe_config->name) & (mask)) { \
DRM_ERROR("mismatch in " #name "(" #mask ") " \
@@ -9944,11 +10710,28 @@ intel_pipe_config_compare(struct drm_device *dev,
PIPE_CONF_CHECK_I(fdi_m_n.tu);
PIPE_CONF_CHECK_I(has_dp_encoder);
- PIPE_CONF_CHECK_I(dp_m_n.gmch_m);
- PIPE_CONF_CHECK_I(dp_m_n.gmch_n);
- PIPE_CONF_CHECK_I(dp_m_n.link_m);
- PIPE_CONF_CHECK_I(dp_m_n.link_n);
- PIPE_CONF_CHECK_I(dp_m_n.tu);
+
+ if (INTEL_INFO(dev)->gen < 8) {
+ PIPE_CONF_CHECK_I(dp_m_n.gmch_m);
+ PIPE_CONF_CHECK_I(dp_m_n.gmch_n);
+ PIPE_CONF_CHECK_I(dp_m_n.link_m);
+ PIPE_CONF_CHECK_I(dp_m_n.link_n);
+ PIPE_CONF_CHECK_I(dp_m_n.tu);
+
+ if (current_config->has_drrs) {
+ PIPE_CONF_CHECK_I(dp_m2_n2.gmch_m);
+ PIPE_CONF_CHECK_I(dp_m2_n2.gmch_n);
+ PIPE_CONF_CHECK_I(dp_m2_n2.link_m);
+ PIPE_CONF_CHECK_I(dp_m2_n2.link_n);
+ PIPE_CONF_CHECK_I(dp_m2_n2.tu);
+ }
+ } else {
+ PIPE_CONF_CHECK_I_ALT(dp_m_n.gmch_m, dp_m2_n2.gmch_m);
+ PIPE_CONF_CHECK_I_ALT(dp_m_n.gmch_n, dp_m2_n2.gmch_n);
+ PIPE_CONF_CHECK_I_ALT(dp_m_n.link_m, dp_m2_n2.link_m);
+ PIPE_CONF_CHECK_I_ALT(dp_m_n.link_n, dp_m2_n2.link_n);
+ PIPE_CONF_CHECK_I_ALT(dp_m_n.tu, dp_m2_n2.tu);
+ }
PIPE_CONF_CHECK_I(adjusted_mode.crtc_hdisplay);
PIPE_CONF_CHECK_I(adjusted_mode.crtc_htotal);
@@ -10017,11 +10800,14 @@ intel_pipe_config_compare(struct drm_device *dev,
PIPE_CONF_CHECK_I(double_wide);
+ PIPE_CONF_CHECK_X(ddi_pll_sel);
+
PIPE_CONF_CHECK_I(shared_dpll);
PIPE_CONF_CHECK_X(dpll_hw_state.dpll);
PIPE_CONF_CHECK_X(dpll_hw_state.dpll_md);
PIPE_CONF_CHECK_X(dpll_hw_state.fp0);
PIPE_CONF_CHECK_X(dpll_hw_state.fp1);
+ PIPE_CONF_CHECK_X(dpll_hw_state.wrpll);
if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5)
PIPE_CONF_CHECK_I(pipe_bpp);
@@ -10031,6 +10817,7 @@ intel_pipe_config_compare(struct drm_device *dev,
#undef PIPE_CONF_CHECK_X
#undef PIPE_CONF_CHECK_I
+#undef PIPE_CONF_CHECK_I_ALT
#undef PIPE_CONF_CHECK_FLAGS
#undef PIPE_CONF_CHECK_CLOCK_FUZZY
#undef PIPE_CONF_QUIRK
@@ -10060,8 +10847,7 @@ check_encoder_state(struct drm_device *dev)
struct intel_encoder *encoder;
struct intel_connector *connector;
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
bool enabled = false;
bool active = false;
enum pipe pipe, tracked_pipe;
@@ -10083,6 +10869,14 @@ check_encoder_state(struct drm_device *dev)
if (connector->base.dpms != DRM_MODE_DPMS_OFF)
active = true;
}
+ /*
+ * for MST connectors if we unplug the connector is gone
+ * away but the encoder is still connected to a crtc
+ * until a modeset happens in response to the hotplug.
+ */
+ if (!enabled && encoder->base.encoder_type == DRM_MODE_ENCODER_DPMST)
+ continue;
+
WARN(!!encoder->base.crtc != enabled,
"encoder's enabled state mismatch "
"(expected %i, found %i)\n",
@@ -10132,8 +10926,7 @@ check_crtc_state(struct drm_device *dev)
WARN(crtc->active && !crtc->base.enabled,
"active crtc, but not enabled in sw tracking\n");
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
if (encoder->base.crtc != &crtc->base)
continue;
enabled = true;
@@ -10151,12 +10944,12 @@ check_crtc_state(struct drm_device *dev)
active = dev_priv->display.get_pipe_config(crtc,
&pipe_config);
- /* hw state is inconsistent with the pipe A quirk */
- if (crtc->pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE)
+ /* hw state is inconsistent with the pipe quirk */
+ if ((crtc->pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) ||
+ (crtc->pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE))
active = crtc->active;
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
enum pipe pipe;
if (encoder->base.crtc != &crtc->base)
continue;
@@ -10378,20 +11171,23 @@ static int __intel_set_mode(struct drm_crtc *crtc,
* on the DPLL.
*/
for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) {
- struct drm_framebuffer *old_fb;
+ struct drm_framebuffer *old_fb = crtc->primary->fb;
+ struct drm_i915_gem_object *old_obj = intel_fb_obj(old_fb);
+ struct drm_i915_gem_object *obj = intel_fb_obj(fb);
mutex_lock(&dev->struct_mutex);
ret = intel_pin_and_fence_fb_obj(dev,
- to_intel_framebuffer(fb)->obj,
+ obj,
NULL);
if (ret != 0) {
DRM_ERROR("pin & fence failed\n");
mutex_unlock(&dev->struct_mutex);
goto done;
}
- old_fb = crtc->primary->fb;
if (old_fb)
- intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj);
+ intel_unpin_fb_obj(old_obj);
+ i915_gem_track_fb(old_obj, obj,
+ INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe));
mutex_unlock(&dev->struct_mutex);
crtc->primary->fb = fb;
@@ -10521,7 +11317,7 @@ static void intel_set_config_restore_state(struct drm_device *dev,
}
count = 0;
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+ for_each_intel_encoder(dev, encoder) {
encoder->new_crtc =
to_intel_crtc(config->save_encoder_crtcs[count++]);
}
@@ -10563,12 +11359,17 @@ intel_set_config_compute_mode_changes(struct drm_mode_set *set,
if (is_crtc_connector_off(set)) {
config->mode_changed = true;
} else if (set->crtc->primary->fb != set->fb) {
- /* If we have no fb then treat it as a full mode set */
+ /*
+ * If we have no fb, we can only flip as long as the crtc is
+ * active, otherwise we need a full mode set. The crtc may
+ * be active if we've only disabled the primary plane, or
+ * in fastboot situations.
+ */
if (set->crtc->primary->fb == NULL) {
struct intel_crtc *intel_crtc =
to_intel_crtc(set->crtc);
- if (intel_crtc->active && i915.fastboot) {
+ if (intel_crtc->active) {
DRM_DEBUG_KMS("crtc has no fb, will flip\n");
config->fb_changed = true;
} else {
@@ -10620,7 +11421,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
* for them. */
for (ro = 0; ro < set->num_connectors; ro++) {
if (set->connectors[ro] == &connector->base) {
- connector->new_encoder = connector->encoder;
+ connector->new_encoder = intel_find_encoder(connector, to_intel_crtc(set->crtc)->pipe);
break;
}
}
@@ -10666,7 +11467,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
new_crtc)) {
return -EINVAL;
}
- connector->encoder->new_crtc = to_intel_crtc(new_crtc);
+ connector->new_encoder->new_crtc = to_intel_crtc(new_crtc);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
connector->base.base.id,
@@ -10675,8 +11476,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
}
/* Check for any encoders that needs to be disabled. */
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
int num_connectors = 0;
list_for_each_entry(connector,
&dev->mode_config.connector_list,
@@ -10700,13 +11500,16 @@ intel_modeset_stage_output_state(struct drm_device *dev,
}
}
/* Now we've also updated encoder->new_crtc for all encoders. */
-
+ list_for_each_entry(connector, &dev->mode_config.connector_list,
+ base.head) {
+ if (connector->new_encoder)
+ if (connector->new_encoder != connector->encoder)
+ connector->encoder = connector->new_encoder;
+ }
for_each_intel_crtc(dev, crtc) {
crtc->new_enabled = false;
- list_for_each_entry(encoder,
- &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
if (encoder->new_crtc == crtc) {
crtc->new_enabled = true;
break;
@@ -10743,7 +11546,7 @@ static void disable_crtc_nofb(struct intel_crtc *crtc)
connector->new_encoder = NULL;
}
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+ for_each_intel_encoder(dev, encoder) {
if (encoder->new_crtc == crtc)
encoder->new_crtc = NULL;
}
@@ -10806,10 +11609,22 @@ static int intel_crtc_set_config(struct drm_mode_set *set)
ret = intel_set_mode(set->crtc, set->mode,
set->x, set->y, set->fb);
} else if (config->fb_changed) {
+ struct intel_crtc *intel_crtc = to_intel_crtc(set->crtc);
+
intel_crtc_wait_for_pending_flips(set->crtc);
ret = intel_pipe_set_base(set->crtc,
set->x, set->y, set->fb);
+
+ /*
+ * We need to make sure the primary plane is re-enabled if it
+ * has previously been turned off.
+ */
+ if (!intel_crtc->primary_enabled && ret == 0) {
+ WARN_ON(!intel_crtc->active);
+ intel_enable_primary_hw_plane(set->crtc->primary, set->crtc);
+ }
+
/*
* In the fastboot case this may be our only check of the
* state after boot. It would be better to only do it on
@@ -10850,26 +11665,21 @@ out_config:
}
static const struct drm_crtc_funcs intel_crtc_funcs = {
- .cursor_set = intel_crtc_cursor_set,
- .cursor_move = intel_crtc_cursor_move,
.gamma_set = intel_crtc_gamma_set,
.set_config = intel_crtc_set_config,
.destroy = intel_crtc_destroy,
.page_flip = intel_crtc_page_flip,
};
-static void intel_cpu_pll_init(struct drm_device *dev)
-{
- if (HAS_DDI(dev))
- intel_ddi_pll_init(dev);
-}
-
static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll,
struct intel_dpll_hw_state *hw_state)
{
uint32_t val;
+ if (!intel_display_power_enabled(dev_priv, POWER_DOMAIN_PLLS))
+ return false;
+
val = I915_READ(PCH_DPLL(pll->id));
hw_state->dpll = val;
hw_state->fp0 = I915_READ(PCH_FP0(pll->id));
@@ -10951,7 +11761,9 @@ static void intel_shared_dpll_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
+ if (HAS_DDI(dev))
+ intel_ddi_pll_init(dev);
+ else if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
ibx_pch_dpll_init(dev);
else
dev_priv->num_shared_dpll = 0;
@@ -10959,17 +11771,383 @@ static void intel_shared_dpll_init(struct drm_device *dev)
BUG_ON(dev_priv->num_shared_dpll > I915_NUM_PLLS);
}
+static int
+intel_primary_plane_disable(struct drm_plane *plane)
+{
+ struct drm_device *dev = plane->dev;
+ struct intel_crtc *intel_crtc;
+
+ if (!plane->fb)
+ return 0;
+
+ BUG_ON(!plane->crtc);
+
+ intel_crtc = to_intel_crtc(plane->crtc);
+
+ /*
+ * Even though we checked plane->fb above, it's still possible that
+ * the primary plane has been implicitly disabled because the crtc
+ * coordinates given weren't visible, or because we detected
+ * that it was 100% covered by a sprite plane. Or, the CRTC may be
+ * off and we've set a fb, but haven't actually turned on the CRTC yet.
+ * In either case, we need to unpin the FB and let the fb pointer get
+ * updated, but otherwise we don't need to touch the hardware.
+ */
+ if (!intel_crtc->primary_enabled)
+ goto disable_unpin;
+
+ intel_crtc_wait_for_pending_flips(plane->crtc);
+ intel_disable_primary_hw_plane(plane, plane->crtc);
+
+disable_unpin:
+ mutex_lock(&dev->struct_mutex);
+ i915_gem_track_fb(intel_fb_obj(plane->fb), NULL,
+ INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe));
+ intel_unpin_fb_obj(intel_fb_obj(plane->fb));
+ mutex_unlock(&dev->struct_mutex);
+ plane->fb = NULL;
+
+ return 0;
+}
+
+static int
+intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct drm_i915_gem_object *obj = intel_fb_obj(fb);
+ struct drm_i915_gem_object *old_obj = intel_fb_obj(plane->fb);
+ struct drm_rect dest = {
+ /* integer pixels */
+ .x1 = crtc_x,
+ .y1 = crtc_y,
+ .x2 = crtc_x + crtc_w,
+ .y2 = crtc_y + crtc_h,
+ };
+ struct drm_rect src = {
+ /* 16.16 fixed point */
+ .x1 = src_x,
+ .y1 = src_y,
+ .x2 = src_x + src_w,
+ .y2 = src_y + src_h,
+ };
+ const struct drm_rect clip = {
+ /* integer pixels */
+ .x2 = intel_crtc->active ? intel_crtc->config.pipe_src_w : 0,
+ .y2 = intel_crtc->active ? intel_crtc->config.pipe_src_h : 0,
+ };
+ const struct {
+ int crtc_x, crtc_y;
+ unsigned int crtc_w, crtc_h;
+ uint32_t src_x, src_y, src_w, src_h;
+ } orig = {
+ .crtc_x = crtc_x,
+ .crtc_y = crtc_y,
+ .crtc_w = crtc_w,
+ .crtc_h = crtc_h,
+ .src_x = src_x,
+ .src_y = src_y,
+ .src_w = src_w,
+ .src_h = src_h,
+ };
+ struct intel_plane *intel_plane = to_intel_plane(plane);
+ bool visible;
+ int ret;
+
+ ret = drm_plane_helper_check_update(plane, crtc, fb,
+ &src, &dest, &clip,
+ DRM_PLANE_HELPER_NO_SCALING,
+ DRM_PLANE_HELPER_NO_SCALING,
+ false, true, &visible);
+
+ if (ret)
+ return ret;
+
+ /*
+ * If the CRTC isn't enabled, we're just pinning the framebuffer,
+ * updating the fb pointer, and returning without touching the
+ * hardware. This allows us to later do a drmModeSetCrtc with fb=-1 to
+ * turn on the display with all planes setup as desired.
+ */
+ if (!crtc->enabled) {
+ mutex_lock(&dev->struct_mutex);
+
+ /*
+ * If we already called setplane while the crtc was disabled,
+ * we may have an fb pinned; unpin it.
+ */
+ if (plane->fb)
+ intel_unpin_fb_obj(old_obj);
+
+ i915_gem_track_fb(old_obj, obj,
+ INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe));
+
+ /* Pin and return without programming hardware */
+ ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+ }
+
+ intel_crtc_wait_for_pending_flips(crtc);
+
+ /*
+ * If clipping results in a non-visible primary plane, we'll disable
+ * the primary plane. Note that this is a bit different than what
+ * happens if userspace explicitly disables the plane by passing fb=0
+ * because plane->fb still gets set and pinned.
+ */
+ if (!visible) {
+ mutex_lock(&dev->struct_mutex);
+
+ /*
+ * Try to pin the new fb first so that we can bail out if we
+ * fail.
+ */
+ if (plane->fb != fb) {
+ ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
+ if (ret) {
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
+ }
+
+ i915_gem_track_fb(old_obj, obj,
+ INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe));
+
+ if (intel_crtc->primary_enabled)
+ intel_disable_primary_hw_plane(plane, crtc);
+
+
+ if (plane->fb != fb)
+ if (plane->fb)
+ intel_unpin_fb_obj(old_obj);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ } else {
+ if (intel_crtc && intel_crtc->active &&
+ intel_crtc->primary_enabled) {
+ /*
+ * FBC does not work on some platforms for rotated
+ * planes, so disable it when rotation is not 0 and
+ * update it when rotation is set back to 0.
+ *
+ * FIXME: This is redundant with the fbc update done in
+ * the primary plane enable function except that that
+ * one is done too late. We eventually need to unify
+ * this.
+ */
+ if (INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev) &&
+ dev_priv->fbc.plane == intel_crtc->plane &&
+ intel_plane->rotation != BIT(DRM_ROTATE_0)) {
+ intel_disable_fbc(dev);
+ }
+ }
+ ret = intel_pipe_set_base(crtc, src.x1, src.y1, fb);
+ if (ret)
+ return ret;
+
+ if (!intel_crtc->primary_enabled)
+ intel_enable_primary_hw_plane(plane, crtc);
+ }
+
+ intel_plane->crtc_x = orig.crtc_x;
+ intel_plane->crtc_y = orig.crtc_y;
+ intel_plane->crtc_w = orig.crtc_w;
+ intel_plane->crtc_h = orig.crtc_h;
+ intel_plane->src_x = orig.src_x;
+ intel_plane->src_y = orig.src_y;
+ intel_plane->src_w = orig.src_w;
+ intel_plane->src_h = orig.src_h;
+ intel_plane->obj = obj;
+
+ return 0;
+}
+
+/* Common destruction function for both primary and cursor planes */
+static void intel_plane_destroy(struct drm_plane *plane)
+{
+ struct intel_plane *intel_plane = to_intel_plane(plane);
+ drm_plane_cleanup(plane);
+ kfree(intel_plane);
+}
+
+static const struct drm_plane_funcs intel_primary_plane_funcs = {
+ .update_plane = intel_primary_plane_setplane,
+ .disable_plane = intel_primary_plane_disable,
+ .destroy = intel_plane_destroy,
+ .set_property = intel_plane_set_property
+};
+
+static struct drm_plane *intel_primary_plane_create(struct drm_device *dev,
+ int pipe)
+{
+ struct intel_plane *primary;
+ const uint32_t *intel_primary_formats;
+ int num_formats;
+
+ primary = kzalloc(sizeof(*primary), GFP_KERNEL);
+ if (primary == NULL)
+ return NULL;
+
+ primary->can_scale = false;
+ primary->max_downscale = 1;
+ primary->pipe = pipe;
+ primary->plane = pipe;
+ primary->rotation = BIT(DRM_ROTATE_0);
+ if (HAS_FBC(dev) && INTEL_INFO(dev)->gen < 4)
+ primary->plane = !pipe;
+
+ if (INTEL_INFO(dev)->gen <= 3) {
+ intel_primary_formats = intel_primary_formats_gen2;
+ num_formats = ARRAY_SIZE(intel_primary_formats_gen2);
+ } else {
+ intel_primary_formats = intel_primary_formats_gen4;
+ num_formats = ARRAY_SIZE(intel_primary_formats_gen4);
+ }
+
+ drm_universal_plane_init(dev, &primary->base, 0,
+ &intel_primary_plane_funcs,
+ intel_primary_formats, num_formats,
+ DRM_PLANE_TYPE_PRIMARY);
+
+ if (INTEL_INFO(dev)->gen >= 4) {
+ if (!dev->mode_config.rotation_property)
+ dev->mode_config.rotation_property =
+ drm_mode_create_rotation_property(dev,
+ BIT(DRM_ROTATE_0) |
+ BIT(DRM_ROTATE_180));
+ if (dev->mode_config.rotation_property)
+ drm_object_attach_property(&primary->base.base,
+ dev->mode_config.rotation_property,
+ primary->rotation);
+ }
+
+ return &primary->base;
+}
+
+static int
+intel_cursor_plane_disable(struct drm_plane *plane)
+{
+ if (!plane->fb)
+ return 0;
+
+ BUG_ON(!plane->crtc);
+
+ return intel_crtc_cursor_set_obj(plane->crtc, NULL, 0, 0);
+}
+
+static int
+intel_cursor_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+ struct drm_i915_gem_object *obj = intel_fb->obj;
+ struct drm_rect dest = {
+ /* integer pixels */
+ .x1 = crtc_x,
+ .y1 = crtc_y,
+ .x2 = crtc_x + crtc_w,
+ .y2 = crtc_y + crtc_h,
+ };
+ struct drm_rect src = {
+ /* 16.16 fixed point */
+ .x1 = src_x,
+ .y1 = src_y,
+ .x2 = src_x + src_w,
+ .y2 = src_y + src_h,
+ };
+ const struct drm_rect clip = {
+ /* integer pixels */
+ .x2 = intel_crtc->active ? intel_crtc->config.pipe_src_w : 0,
+ .y2 = intel_crtc->active ? intel_crtc->config.pipe_src_h : 0,
+ };
+ bool visible;
+ int ret;
+
+ ret = drm_plane_helper_check_update(plane, crtc, fb,
+ &src, &dest, &clip,
+ DRM_PLANE_HELPER_NO_SCALING,
+ DRM_PLANE_HELPER_NO_SCALING,
+ true, true, &visible);
+ if (ret)
+ return ret;
+
+ crtc->cursor_x = crtc_x;
+ crtc->cursor_y = crtc_y;
+ if (fb != crtc->cursor->fb) {
+ return intel_crtc_cursor_set_obj(crtc, obj, crtc_w, crtc_h);
+ } else {
+ intel_crtc_update_cursor(crtc, visible);
+
+ intel_frontbuffer_flip(crtc->dev,
+ INTEL_FRONTBUFFER_CURSOR(intel_crtc->pipe));
+
+ return 0;
+ }
+}
+static const struct drm_plane_funcs intel_cursor_plane_funcs = {
+ .update_plane = intel_cursor_plane_update,
+ .disable_plane = intel_cursor_plane_disable,
+ .destroy = intel_plane_destroy,
+};
+
+static struct drm_plane *intel_cursor_plane_create(struct drm_device *dev,
+ int pipe)
+{
+ struct intel_plane *cursor;
+
+ cursor = kzalloc(sizeof(*cursor), GFP_KERNEL);
+ if (cursor == NULL)
+ return NULL;
+
+ cursor->can_scale = false;
+ cursor->max_downscale = 1;
+ cursor->pipe = pipe;
+ cursor->plane = pipe;
+
+ drm_universal_plane_init(dev, &cursor->base, 0,
+ &intel_cursor_plane_funcs,
+ intel_cursor_formats,
+ ARRAY_SIZE(intel_cursor_formats),
+ DRM_PLANE_TYPE_CURSOR);
+ return &cursor->base;
+}
+
static void intel_crtc_init(struct drm_device *dev, int pipe)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc;
- int i;
+ struct drm_plane *primary = NULL;
+ struct drm_plane *cursor = NULL;
+ int i, ret;
intel_crtc = kzalloc(sizeof(*intel_crtc), GFP_KERNEL);
if (intel_crtc == NULL)
return;
- drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
+ primary = intel_primary_plane_create(dev, pipe);
+ if (!primary)
+ goto fail;
+
+ cursor = intel_cursor_plane_create(dev, pipe);
+ if (!cursor)
+ goto fail;
+
+ ret = drm_crtc_init_with_planes(dev, &intel_crtc->base, primary,
+ cursor, &intel_crtc_funcs);
+ if (ret)
+ goto fail;
drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
for (i = 0; i < 256; i++) {
@@ -10980,7 +12158,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
/*
* On gen2/3 only plane A can do fbc, but the panel fitter and lvds port
- * is hooked to plane B. Hence we want plane A feeding pipe B.
+ * is hooked to pipe B. Hence we want plane A feeding pipe B.
*/
intel_crtc->pipe = pipe;
intel_crtc->plane = pipe;
@@ -10991,8 +12169,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
intel_crtc->cursor_base = ~0;
intel_crtc->cursor_cntl = ~0;
-
- init_waitqueue_head(&intel_crtc->vbl_wait);
+ intel_crtc->cursor_size = ~0;
BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL);
@@ -11002,6 +12179,14 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
WARN_ON(drm_crtc_index(&intel_crtc->base) != intel_crtc->pipe);
+ return;
+
+fail:
+ if (primary)
+ drm_plane_cleanup(primary);
+ if (cursor)
+ drm_plane_cleanup(cursor);
+ kfree(intel_crtc);
}
enum pipe intel_get_pipe_from_connector(struct intel_connector *connector)
@@ -11021,21 +12206,20 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data;
- struct drm_mode_object *drmmode_obj;
+ struct drm_crtc *drmmode_crtc;
struct intel_crtc *crtc;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -ENODEV;
- drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id,
- DRM_MODE_OBJECT_CRTC);
+ drmmode_crtc = drm_crtc_find(dev, pipe_from_crtc_id->crtc_id);
- if (!drmmode_obj) {
+ if (!drmmode_crtc) {
DRM_ERROR("no such CRTC id\n");
return -ENOENT;
}
- crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
+ crtc = to_intel_crtc(drmmode_crtc);
pipe_from_crtc_id->pipe = crtc->pipe;
return 0;
@@ -11048,8 +12232,7 @@ static int intel_encoder_clones(struct intel_encoder *encoder)
int index_mask = 0;
int entry = 0;
- list_for_each_entry(source_encoder,
- &dev->mode_config.encoder_list, base.head) {
+ for_each_intel_encoder(dev, source_encoder) {
if (encoders_cloneable(encoder, source_encoder))
index_mask |= (1 << entry);
@@ -11171,27 +12354,36 @@ static void intel_setup_outputs(struct drm_device *dev)
if (I915_READ(PCH_DP_D) & DP_DETECTED)
intel_dp_init(dev, PCH_DP_D, PORT_D);
} else if (IS_VALLEYVIEW(dev)) {
- if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIB) & SDVO_DETECTED) {
+ /*
+ * The DP_DETECTED bit is the latched state of the DDC
+ * SDA pin at boot. However since eDP doesn't require DDC
+ * (no way to plug in a DP->HDMI dongle) the DDC pins for
+ * eDP ports may have been muxed to an alternate function.
+ * Thus we can't rely on the DP_DETECTED bit alone to detect
+ * eDP ports. Consult the VBT as well as DP_DETECTED to
+ * detect eDP ports.
+ */
+ if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIB) & SDVO_DETECTED)
intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIB,
PORT_B);
- if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED)
- intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B);
- }
+ if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED ||
+ intel_dp_is_edp(dev, PORT_B))
+ intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B);
- if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIC) & SDVO_DETECTED) {
+ if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIC) & SDVO_DETECTED)
intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIC,
PORT_C);
- if (I915_READ(VLV_DISPLAY_BASE + DP_C) & DP_DETECTED)
- intel_dp_init(dev, VLV_DISPLAY_BASE + DP_C, PORT_C);
- }
+ if (I915_READ(VLV_DISPLAY_BASE + DP_C) & DP_DETECTED ||
+ intel_dp_is_edp(dev, PORT_C))
+ intel_dp_init(dev, VLV_DISPLAY_BASE + DP_C, PORT_C);
if (IS_CHERRYVIEW(dev)) {
- if (I915_READ(VLV_DISPLAY_BASE + CHV_HDMID) & SDVO_DETECTED) {
+ if (I915_READ(VLV_DISPLAY_BASE + CHV_HDMID) & SDVO_DETECTED)
intel_hdmi_init(dev, VLV_DISPLAY_BASE + CHV_HDMID,
PORT_D);
- if (I915_READ(VLV_DISPLAY_BASE + DP_D) & DP_DETECTED)
- intel_dp_init(dev, VLV_DISPLAY_BASE + DP_D, PORT_D);
- }
+ /* eDP not supported on port D, so don't check VBT */
+ if (I915_READ(VLV_DISPLAY_BASE + DP_D) & DP_DETECTED)
+ intel_dp_init(dev, VLV_DISPLAY_BASE + DP_D, PORT_D);
}
intel_dsi_init(dev);
@@ -11236,7 +12428,9 @@ static void intel_setup_outputs(struct drm_device *dev)
if (SUPPORTS_TV(dev))
intel_tv_init(dev);
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+ intel_edp_psr_init(dev);
+
+ for_each_intel_encoder(dev, encoder) {
encoder->base.possible_crtcs = encoder->crtc_mask;
encoder->base.possible_clones =
intel_encoder_clones(encoder);
@@ -11249,11 +12443,14 @@ static void intel_setup_outputs(struct drm_device *dev)
static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
{
+ struct drm_device *dev = fb->dev;
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
drm_framebuffer_cleanup(fb);
+ mutex_lock(&dev->struct_mutex);
WARN_ON(!intel_fb->obj->framebuffer_references--);
- drm_gem_object_unreference_unlocked(&intel_fb->obj->base);
+ drm_gem_object_unreference(&intel_fb->obj->base);
+ mutex_unlock(&dev->struct_mutex);
kfree(intel_fb);
}
@@ -11438,7 +12635,7 @@ static void intel_init_display(struct drm_device *dev)
dev_priv->display.crtc_mode_set = haswell_crtc_mode_set;
dev_priv->display.crtc_enable = haswell_crtc_enable;
dev_priv->display.crtc_disable = haswell_crtc_disable;
- dev_priv->display.off = haswell_crtc_off;
+ dev_priv->display.off = ironlake_crtc_off;
dev_priv->display.update_primary_plane =
ironlake_update_primary_plane;
} else if (HAS_PCH_SPLIT(dev)) {
@@ -11499,29 +12696,27 @@ static void intel_init_display(struct drm_device *dev)
dev_priv->display.get_display_clock_speed =
i830_get_display_clock_speed;
- if (HAS_PCH_SPLIT(dev)) {
- if (IS_GEN5(dev)) {
- dev_priv->display.fdi_link_train = ironlake_fdi_link_train;
- dev_priv->display.write_eld = ironlake_write_eld;
- } else if (IS_GEN6(dev)) {
- dev_priv->display.fdi_link_train = gen6_fdi_link_train;
- dev_priv->display.write_eld = ironlake_write_eld;
- dev_priv->display.modeset_global_resources =
- snb_modeset_global_resources;
- } else if (IS_IVYBRIDGE(dev)) {
- /* FIXME: detect B0+ stepping and use auto training */
- dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
- dev_priv->display.write_eld = ironlake_write_eld;
- dev_priv->display.modeset_global_resources =
- ivb_modeset_global_resources;
- } else if (IS_HASWELL(dev) || IS_GEN8(dev)) {
- dev_priv->display.fdi_link_train = hsw_fdi_link_train;
- dev_priv->display.write_eld = haswell_write_eld;
- dev_priv->display.modeset_global_resources =
- haswell_modeset_global_resources;
- }
- } else if (IS_G4X(dev)) {
+ if (IS_G4X(dev)) {
dev_priv->display.write_eld = g4x_write_eld;
+ } else if (IS_GEN5(dev)) {
+ dev_priv->display.fdi_link_train = ironlake_fdi_link_train;
+ dev_priv->display.write_eld = ironlake_write_eld;
+ } else if (IS_GEN6(dev)) {
+ dev_priv->display.fdi_link_train = gen6_fdi_link_train;
+ dev_priv->display.write_eld = ironlake_write_eld;
+ dev_priv->display.modeset_global_resources =
+ snb_modeset_global_resources;
+ } else if (IS_IVYBRIDGE(dev)) {
+ /* FIXME: detect B0+ stepping and use auto training */
+ dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
+ dev_priv->display.write_eld = ironlake_write_eld;
+ dev_priv->display.modeset_global_resources =
+ ivb_modeset_global_resources;
+ } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+ dev_priv->display.fdi_link_train = hsw_fdi_link_train;
+ dev_priv->display.write_eld = haswell_write_eld;
+ dev_priv->display.modeset_global_resources =
+ haswell_modeset_global_resources;
} else if (IS_VALLEYVIEW(dev)) {
dev_priv->display.modeset_global_resources =
valleyview_modeset_global_resources;
@@ -11555,6 +12750,8 @@ static void intel_init_display(struct drm_device *dev)
}
intel_panel_init_backlight_funcs(dev);
+
+ mutex_init(&dev_priv->pps_mutex);
}
/*
@@ -11570,6 +12767,14 @@ static void quirk_pipea_force(struct drm_device *dev)
DRM_INFO("applying pipe a force quirk\n");
}
+static void quirk_pipeb_force(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->quirks |= QUIRK_PIPEB_FORCE;
+ DRM_INFO("applying pipe b force quirk\n");
+}
+
/*
* Some machines (Lenovo U160) do not work with SSC on LVDS for some reason
*/
@@ -11644,6 +12849,12 @@ static struct intel_quirk intel_quirks[] = {
/* ThinkPad T60 needs pipe A force quirk (bug #16494) */
{ 0x2782, 0x17aa, 0x201a, quirk_pipea_force },
+ /* 830 needs to leave pipe A & dpll A up */
+ { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
+
+ /* 830 needs to leave pipe B & dpll B up */
+ { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipeb_force },
+
/* Lenovo U160 cannot use SSC on LVDS */
{ 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
@@ -11671,6 +12882,12 @@ static struct intel_quirk intel_quirks[] = {
/* Acer C720 and C720P Chromebooks (Celeron 2955U) have backlights */
{ 0x0a06, 0x1025, 0x0a11, quirk_backlight_present },
+ /* Acer C720 Chromebook (Core i3 4005U) */
+ { 0x0a16, 0x1025, 0x0a11, quirk_backlight_present },
+
+ /* Apple Macbook 2,1 (Core 2 T7400) */
+ { 0x27a2, 0x8086, 0x7270, quirk_backlight_present },
+
/* Toshiba CB35 Chromebook (Celeron 2955U) */
{ 0x0a06, 0x1179, 0x0a88, quirk_backlight_present },
@@ -11714,7 +12931,11 @@ static void i915_disable_vga(struct drm_device *dev)
vga_put(dev->pdev, VGA_RSRC_LEGACY_IO);
udelay(300);
- I915_WRITE(vga_reg, VGA_DISP_DISABLE);
+ /*
+ * Fujitsu-Siemens Lifebook S6010 (830) has problems resuming
+ * from S3 without preserving (some of?) the other bits.
+ */
+ I915_WRITE(vga_reg, dev_priv->bios_vgacntr | VGA_DISP_DISABLE);
POSTING_READ(vga_reg);
}
@@ -11722,9 +12943,10 @@ void intel_modeset_init_hw(struct drm_device *dev)
{
intel_prepare_ddi(dev);
- intel_init_clock_gating(dev);
+ if (IS_VALLEYVIEW(dev))
+ vlv_update_cdclk(dev);
- intel_reset_dpio(dev);
+ intel_init_clock_gating(dev);
intel_enable_gt_powersave(dev);
}
@@ -11771,7 +12993,10 @@ void intel_modeset_init(struct drm_device *dev)
dev->mode_config.max_height = 8192;
}
- if (IS_GEN2(dev)) {
+ if (IS_845G(dev) || IS_I865G(dev)) {
+ dev->mode_config.cursor_width = IS_845G(dev) ? 64 : 512;
+ dev->mode_config.cursor_height = 1023;
+ } else if (IS_GEN2(dev)) {
dev->mode_config.cursor_width = GEN2_CURSOR_WIDTH;
dev->mode_config.cursor_height = GEN2_CURSOR_HEIGHT;
} else {
@@ -11785,7 +13010,7 @@ void intel_modeset_init(struct drm_device *dev)
INTEL_INFO(dev)->num_pipes,
INTEL_INFO(dev)->num_pipes > 1 ? "s" : "");
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
intel_crtc_init(dev, pipe);
for_each_sprite(pipe, sprite) {
ret = intel_plane_init(dev, pipe, sprite);
@@ -11796,11 +13021,11 @@ void intel_modeset_init(struct drm_device *dev)
}
intel_init_dpio(dev);
- intel_reset_dpio(dev);
- intel_cpu_pll_init(dev);
intel_shared_dpll_init(dev);
+ /* save the BIOS value before clobbering it */
+ dev_priv->bios_vgacntr = I915_READ(i915_vgacntrl_reg(dev));
/* Just disable it once at startup */
i915_disable_vga(dev);
intel_setup_outputs(dev);
@@ -11840,7 +13065,7 @@ static void intel_enable_pipe_a(struct drm_device *dev)
struct intel_connector *connector;
struct drm_connector *crt = NULL;
struct intel_load_detect_pipe load_detect_temp;
- struct drm_modeset_acquire_ctx ctx;
+ struct drm_modeset_acquire_ctx *ctx = dev->mode_config.acquire_ctx;
/* We can't just switch on the pipe A, we need to set things up with a
* proper mode and output configuration. As a gross hack, enable pipe A
@@ -11857,10 +13082,8 @@ static void intel_enable_pipe_a(struct drm_device *dev)
if (!crt)
return;
- if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, &ctx))
- intel_release_load_detect_pipe(crt, &load_detect_temp, &ctx);
-
-
+ if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, ctx))
+ intel_release_load_detect_pipe(crt, &load_detect_temp);
}
static bool
@@ -11894,9 +13117,10 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK);
/* restore vblank interrupts to correct state */
- if (crtc->active)
+ if (crtc->active) {
+ update_scanline_offset(crtc);
drm_vblank_on(dev, crtc->pipe);
- else
+ } else
drm_vblank_off(dev, crtc->pipe);
/* We need to sanitize the plane -> pipe mapping first because this will
@@ -11979,7 +13203,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
}
}
- if (crtc->active || IS_VALLEYVIEW(dev) || INTEL_INFO(dev)->gen < 5) {
+ if (crtc->active || HAS_GMCH_DISPLAY(dev)) {
/*
* We start out with underrun reporting disabled to avoid races.
* For correct bookkeeping mark this on active crtcs.
@@ -11995,8 +13219,6 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
*/
crtc->cpu_fifo_underrun_disabled = true;
crtc->pch_fifo_underrun_disabled = true;
-
- update_scanline_offset(crtc);
}
}
@@ -12024,6 +13246,8 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
encoder->base.base.id,
encoder->base.name);
encoder->disable(encoder);
+ if (encoder->post_disable)
+ encoder->post_disable(encoder);
}
encoder->base.crtc = NULL;
encoder->connectors_active = false;
@@ -12108,10 +13332,6 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
crtc->active ? "enabled" : "disabled");
}
- /* FIXME: Smash this into the new shared dpll infrastructure. */
- if (HAS_DDI(dev))
- intel_ddi_setup_hw_pll_state(dev);
-
for (i = 0; i < dev_priv->num_shared_dpll; i++) {
struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i];
@@ -12125,10 +13345,12 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
DRM_DEBUG_KMS("%s hw state readout: refcount %i, on %i\n",
pll->name, pll->refcount, pll->on);
+
+ if (pll->refcount)
+ intel_display_power_get(dev_priv, POWER_DOMAIN_PLLS);
}
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
pipe = 0;
if (encoder->get_hw_state(encoder, &pipe)) {
@@ -12192,12 +13414,11 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
}
/* HW state is read out, now we need to sanitize this mess. */
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
intel_sanitize_encoder(encoder);
}
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
intel_sanitize_crtc(crtc);
intel_dump_pipe_config(crtc, &crtc->config, "[setup_hw_state]");
@@ -12225,7 +13446,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
* We need to use raw interfaces for restoring state to avoid
* checking (bogus) intermediate states.
*/
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
struct drm_crtc *crtc =
dev_priv->pipe_to_crtc_mapping[pipe];
@@ -12242,7 +13463,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
void intel_modeset_gem_init(struct drm_device *dev)
{
struct drm_crtc *c;
- struct intel_framebuffer *fb;
+ struct drm_i915_gem_object *obj;
mutex_lock(&dev->struct_mutex);
intel_init_gt_powersave(dev);
@@ -12259,11 +13480,11 @@ void intel_modeset_gem_init(struct drm_device *dev)
*/
mutex_lock(&dev->struct_mutex);
for_each_crtc(dev, c) {
- if (!c->primary->fb)
+ obj = intel_fb_obj(c->primary->fb);
+ if (obj == NULL)
continue;
- fb = to_intel_framebuffer(c->primary->fb);
- if (intel_pin_and_fence_fb_obj(dev, fb->obj, NULL)) {
+ if (intel_pin_and_fence_fb_obj(dev, obj, NULL)) {
DRM_ERROR("failed to pin boot fb on pipe %d\n",
to_intel_crtc(c)->pipe);
drm_framebuffer_unreference(c->primary->fb);
@@ -12278,13 +13499,12 @@ void intel_connector_unregister(struct intel_connector *intel_connector)
struct drm_connector *connector = &intel_connector->base;
intel_panel_destroy_backlight(connector);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
}
void intel_modeset_cleanup(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_crtc *crtc;
struct drm_connector *connector;
/*
@@ -12293,7 +13513,9 @@ void intel_modeset_cleanup(struct drm_device *dev)
* experience fancy races otherwise.
*/
drm_irq_uninstall(dev);
- cancel_work_sync(&dev_priv->hotplug_work);
+ intel_hpd_cancel_work(dev_priv);
+ dev_priv->pm._irqs_disabled = true;
+
/*
* Due to the hpd irq storm handling the hotplug work can re-arm the
* poll handlers. Hence disable polling after hpd handling is shut down.
@@ -12304,14 +13526,6 @@ void intel_modeset_cleanup(struct drm_device *dev)
intel_unregister_dsm_handler();
- for_each_crtc(dev, crtc) {
- /* Skip inactive CRTCs */
- if (!crtc->primary->fb)
- continue;
-
- intel_increase_pllclock(crtc);
- }
-
intel_disable_fbc(dev);
intel_disable_gt_powersave(dev);
@@ -12453,7 +13667,7 @@ intel_display_capture_error_state(struct drm_device *dev)
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER);
- for_each_pipe(i) {
+ for_each_pipe(dev_priv, i) {
error->pipe[i].power_domain_on =
intel_display_power_enabled_unlocked(dev_priv,
POWER_DOMAIN_PIPE(i));
@@ -12479,7 +13693,7 @@ intel_display_capture_error_state(struct drm_device *dev)
error->pipe[i].source = I915_READ(PIPESRC(i));
- if (!HAS_PCH_SPLIT(dev))
+ if (HAS_GMCH_DISPLAY(dev))
error->pipe[i].stat = I915_READ(PIPESTAT(i));
}
@@ -12517,6 +13731,7 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m,
struct drm_device *dev,
struct intel_display_error_state *error)
{
+ struct drm_i915_private *dev_priv = dev->dev_private;
int i;
if (!error)
@@ -12526,7 +13741,7 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m,
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
err_printf(m, "PWR_WELL_CTL2: %08x\n",
error->power_well_driver);
- for_each_pipe(i) {
+ for_each_pipe(dev_priv, i) {
err_printf(m, "Pipe [%d]:\n", i);
err_printf(m, " Power: %s\n",
error->pipe[i].power_domain_on ? "on" : "off");
@@ -12567,3 +13782,25 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m,
err_printf(m, " VSYNC: %08x\n", error->transcoder[i].vsync);
}
}
+
+void intel_modeset_preclose(struct drm_device *dev, struct drm_file *file)
+{
+ struct intel_crtc *crtc;
+
+ for_each_intel_crtc(dev, crtc) {
+ struct intel_unpin_work *work;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&dev->event_lock, irqflags);
+
+ work = crtc->unpin_work;
+
+ if (work && work->event &&
+ work->event->base.file_priv == file) {
+ kfree(work->event);
+ work->event = NULL;
+ }
+
+ spin_unlock_irqrestore(&dev->event_lock, irqflags);
+ }
+}
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 8a1a4fbc06ac..5ad45bfff3fe 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -111,10 +111,10 @@ static struct intel_dp *intel_attached_dp(struct drm_connector *connector)
}
static void intel_dp_link_down(struct intel_dp *intel_dp);
-static bool _edp_panel_vdd_on(struct intel_dp *intel_dp);
+static bool edp_panel_vdd_on(struct intel_dp *intel_dp);
static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
-static int
+int
intel_dp_max_link_bw(struct intel_dp *intel_dp)
{
int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
@@ -290,32 +290,201 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
struct intel_dp *intel_dp,
struct edp_power_seq *out);
+static void pps_lock(struct intel_dp *intel_dp)
+{
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct intel_encoder *encoder = &intel_dig_port->base;
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum intel_display_power_domain power_domain;
+
+ /*
+ * See vlv_power_sequencer_reset() why we need
+ * a power domain reference here.
+ */
+ power_domain = intel_display_port_power_domain(encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
+ mutex_lock(&dev_priv->pps_mutex);
+}
+
+static void pps_unlock(struct intel_dp *intel_dp)
+{
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct intel_encoder *encoder = &intel_dig_port->base;
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum intel_display_power_domain power_domain;
+
+ mutex_unlock(&dev_priv->pps_mutex);
+
+ power_domain = intel_display_port_power_domain(encoder);
+ intel_display_power_put(dev_priv, power_domain);
+}
+
static enum pipe
vlv_power_sequencer_pipe(struct intel_dp *intel_dp)
{
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
- struct drm_crtc *crtc = intel_dig_port->base.base.crtc;
struct drm_device *dev = intel_dig_port->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- enum port port = intel_dig_port->port;
- enum pipe pipe;
+ struct intel_encoder *encoder;
+ unsigned int pipes = (1 << PIPE_A) | (1 << PIPE_B);
+ struct edp_power_seq power_seq;
+
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
+ if (intel_dp->pps_pipe != INVALID_PIPE)
+ return intel_dp->pps_pipe;
+
+ /*
+ * We don't have power sequencer currently.
+ * Pick one that's not used by other ports.
+ */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+ base.head) {
+ struct intel_dp *tmp;
+
+ if (encoder->type != INTEL_OUTPUT_EDP)
+ continue;
+
+ tmp = enc_to_intel_dp(&encoder->base);
+
+ if (tmp->pps_pipe != INVALID_PIPE)
+ pipes &= ~(1 << tmp->pps_pipe);
+ }
+
+ /*
+ * Didn't find one. This should not happen since there
+ * are two power sequencers and up to two eDP ports.
+ */
+ if (WARN_ON(pipes == 0))
+ return PIPE_A;
+
+ intel_dp->pps_pipe = ffs(pipes) - 1;
+
+ DRM_DEBUG_KMS("picked pipe %c power sequencer for port %c\n",
+ pipe_name(intel_dp->pps_pipe),
+ port_name(intel_dig_port->port));
+
+ /* init power sequencer on this pipe and port */
+ intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
+ intel_dp_init_panel_power_sequencer_registers(dev, intel_dp,
+ &power_seq);
+
+ return intel_dp->pps_pipe;
+}
+
+typedef bool (*vlv_pipe_check)(struct drm_i915_private *dev_priv,
+ enum pipe pipe);
+
+static bool vlv_pipe_has_pp_on(struct drm_i915_private *dev_priv,
+ enum pipe pipe)
+{
+ return I915_READ(VLV_PIPE_PP_STATUS(pipe)) & PP_ON;
+}
+
+static bool vlv_pipe_has_vdd_on(struct drm_i915_private *dev_priv,
+ enum pipe pipe)
+{
+ return I915_READ(VLV_PIPE_PP_CONTROL(pipe)) & EDP_FORCE_VDD;
+}
+
+static bool vlv_pipe_any(struct drm_i915_private *dev_priv,
+ enum pipe pipe)
+{
+ return true;
+}
- /* modeset should have pipe */
- if (crtc)
- return to_intel_crtc(crtc)->pipe;
+static enum pipe
+vlv_initial_pps_pipe(struct drm_i915_private *dev_priv,
+ enum port port,
+ vlv_pipe_check pipe_check)
+{
+ enum pipe pipe;
- /* init time, try to find a pipe with this port selected */
for (pipe = PIPE_A; pipe <= PIPE_B; pipe++) {
u32 port_sel = I915_READ(VLV_PIPE_PP_ON_DELAYS(pipe)) &
PANEL_PORT_SELECT_MASK;
- if (port_sel == PANEL_PORT_SELECT_DPB_VLV && port == PORT_B)
- return pipe;
- if (port_sel == PANEL_PORT_SELECT_DPC_VLV && port == PORT_C)
- return pipe;
+
+ if (port_sel != PANEL_PORT_SELECT_VLV(port))
+ continue;
+
+ if (!pipe_check(dev_priv, pipe))
+ continue;
+
+ return pipe;
}
- /* shrug */
- return PIPE_A;
+ return INVALID_PIPE;
+}
+
+static void
+vlv_initial_power_sequencer_setup(struct intel_dp *intel_dp)
+{
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct edp_power_seq power_seq;
+ enum port port = intel_dig_port->port;
+
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
+ /* try to find a pipe with this port selected */
+ /* first pick one where the panel is on */
+ intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port,
+ vlv_pipe_has_pp_on);
+ /* didn't find one? pick one where vdd is on */
+ if (intel_dp->pps_pipe == INVALID_PIPE)
+ intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port,
+ vlv_pipe_has_vdd_on);
+ /* didn't find one? pick one with just the correct port */
+ if (intel_dp->pps_pipe == INVALID_PIPE)
+ intel_dp->pps_pipe = vlv_initial_pps_pipe(dev_priv, port,
+ vlv_pipe_any);
+
+ /* didn't find one? just let vlv_power_sequencer_pipe() pick one when needed */
+ if (intel_dp->pps_pipe == INVALID_PIPE) {
+ DRM_DEBUG_KMS("no initial power sequencer for port %c\n",
+ port_name(port));
+ return;
+ }
+
+ DRM_DEBUG_KMS("initial power sequencer for port %c: pipe %c\n",
+ port_name(port), pipe_name(intel_dp->pps_pipe));
+
+ intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
+ intel_dp_init_panel_power_sequencer_registers(dev, intel_dp,
+ &power_seq);
+}
+
+void vlv_power_sequencer_reset(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = dev_priv->dev;
+ struct intel_encoder *encoder;
+
+ if (WARN_ON(!IS_VALLEYVIEW(dev)))
+ return;
+
+ /*
+ * We can't grab pps_mutex here due to deadlock with power_domain
+ * mutex when power_domain functions are called while holding pps_mutex.
+ * That also means that in order to use pps_pipe the code needs to
+ * hold both a power domain reference and pps_mutex, and the power domain
+ * reference get/put must be done while _not_ holding pps_mutex.
+ * pps_{lock,unlock}() do these steps in the correct order, so one
+ * should use them always.
+ */
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+ struct intel_dp *intel_dp;
+
+ if (encoder->type != INTEL_OUTPUT_EDP)
+ continue;
+
+ intel_dp = enc_to_intel_dp(&encoder->base);
+ intel_dp->pps_pipe = INVALID_PIPE;
+ }
}
static u32 _pp_ctrl_reg(struct intel_dp *intel_dp)
@@ -349,12 +518,15 @@ static int edp_notify_handler(struct notifier_block *this, unsigned long code,
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp_div;
u32 pp_ctrl_reg, pp_div_reg;
- enum pipe pipe = vlv_power_sequencer_pipe(intel_dp);
if (!is_edp(intel_dp) || code != SYS_RESTART)
return 0;
+ pps_lock(intel_dp);
+
if (IS_VALLEYVIEW(dev)) {
+ enum pipe pipe = vlv_power_sequencer_pipe(intel_dp);
+
pp_ctrl_reg = VLV_PIPE_PP_CONTROL(pipe);
pp_div_reg = VLV_PIPE_PP_DIVISOR(pipe);
pp_div = I915_READ(pp_div_reg);
@@ -366,6 +538,8 @@ static int edp_notify_handler(struct notifier_block *this, unsigned long code,
msleep(intel_dp->panel_power_cycle_delay);
}
+ pps_unlock(intel_dp);
+
return 0;
}
@@ -374,6 +548,8 @@ static bool edp_have_panel_power(struct intel_dp *intel_dp)
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
return (I915_READ(_pp_stat_reg(intel_dp)) & PP_ON) != 0;
}
@@ -381,13 +557,10 @@ static bool edp_have_panel_vdd(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
- struct intel_encoder *intel_encoder = &intel_dig_port->base;
- enum intel_display_power_domain power_domain;
- power_domain = intel_display_port_power_domain(intel_encoder);
- return intel_display_power_enabled(dev_priv, power_domain) &&
- (I915_READ(_pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD) != 0;
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
+ return I915_READ(_pp_ctrl_reg(intel_dp)) & EDP_FORCE_VDD;
}
static void
@@ -535,7 +708,15 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
bool has_aux_irq = HAS_AUX_IRQ(dev);
bool vdd;
- vdd = _edp_panel_vdd_on(intel_dp);
+ pps_lock(intel_dp);
+
+ /*
+ * We will be called with VDD already enabled for dpcd/edid/oui reads.
+ * In such cases we want to leave VDD enabled and it's up to upper layers
+ * to turn it off. But for eg. i2c-dev access we need to turn it on/off
+ * ourselves.
+ */
+ vdd = edp_panel_vdd_on(intel_dp);
/* dp aux is extremely sensitive to irq latency, hence request the
* lowest possible wakeup latency and so prevent the cpu from going into
@@ -644,6 +825,8 @@ out:
if (vdd)
edp_panel_vdd_off(intel_dp, false);
+ pps_unlock(intel_dp);
+
return ret;
}
@@ -773,12 +956,29 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector)
{
struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base);
- sysfs_remove_link(&intel_connector->base.kdev->kobj,
- intel_dp->aux.ddc.dev.kobj.name);
+ if (!intel_connector->mst_port)
+ sysfs_remove_link(&intel_connector->base.kdev->kobj,
+ intel_dp->aux.ddc.dev.kobj.name);
intel_connector_unregister(intel_connector);
}
static void
+hsw_dp_set_ddi_pll_sel(struct intel_crtc_config *pipe_config, int link_bw)
+{
+ switch (link_bw) {
+ case DP_LINK_BW_1_62:
+ pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_810;
+ break;
+ case DP_LINK_BW_2_7:
+ pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350;
+ break;
+ case DP_LINK_BW_5_4:
+ pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700;
+ break;
+ }
+}
+
+static void
intel_dp_set_clock(struct intel_encoder *encoder,
struct intel_crtc_config *pipe_config, int link_bw)
{
@@ -789,8 +989,6 @@ intel_dp_set_clock(struct intel_encoder *encoder,
if (IS_G4X(dev)) {
divisor = gen4_dpll;
count = ARRAY_SIZE(gen4_dpll);
- } else if (IS_HASWELL(dev)) {
- /* Haswell has special-purpose DP DDI clocks. */
} else if (HAS_PCH_SPLIT(dev)) {
divisor = pch_dpll;
count = ARRAY_SIZE(pch_dpll);
@@ -813,20 +1011,6 @@ intel_dp_set_clock(struct intel_encoder *encoder,
}
}
-static void
-intel_dp_set_m2_n2(struct intel_crtc *crtc, struct intel_link_m_n *m_n)
-{
- struct drm_device *dev = crtc->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- enum transcoder transcoder = crtc->config.cpu_transcoder;
-
- I915_WRITE(PIPE_DATA_M2(transcoder),
- TU_SIZE(m_n->tu) | m_n->gmch_m);
- I915_WRITE(PIPE_DATA_N2(transcoder), m_n->gmch_n);
- I915_WRITE(PIPE_LINK_M2(transcoder), m_n->link_m);
- I915_WRITE(PIPE_LINK_N2(transcoder), m_n->link_n);
-}
-
bool
intel_dp_compute_config(struct intel_encoder *encoder,
struct intel_crtc_config *pipe_config)
@@ -852,6 +1036,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
pipe_config->has_pch_encoder = true;
pipe_config->has_dp_encoder = true;
+ pipe_config->has_drrs = false;
pipe_config->has_audio = intel_dp->has_audio;
if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) {
@@ -883,23 +1068,15 @@ intel_dp_compute_config(struct intel_encoder *encoder,
bpp = dev_priv->vbt.edp_bpp;
}
- if (IS_BROADWELL(dev)) {
- /* Yes, it's an ugly hack. */
- min_lane_count = max_lane_count;
- DRM_DEBUG_KMS("forcing lane count to max (%u) on BDW\n",
- min_lane_count);
- } else if (dev_priv->vbt.edp_lanes) {
- min_lane_count = min(dev_priv->vbt.edp_lanes,
- max_lane_count);
- DRM_DEBUG_KMS("using min %u lanes per VBT\n",
- min_lane_count);
- }
-
- if (dev_priv->vbt.edp_rate) {
- min_clock = min(dev_priv->vbt.edp_rate >> 3, max_clock);
- DRM_DEBUG_KMS("using min %02x link bw per VBT\n",
- bws[min_clock]);
- }
+ /*
+ * Use the maximum clock and number of lanes the eDP panel
+ * advertizes being capable of. The panels are generally
+ * designed to support only a single clock and lane
+ * configuration, and typically these values correspond to the
+ * native resolution of the panel.
+ */
+ min_lane_count = max_lane_count;
+ min_clock = max_clock;
}
for (; bpp >= 6*3; bpp -= 2*3) {
@@ -955,13 +1132,17 @@ found:
if (intel_connector->panel.downclock_mode != NULL &&
intel_dp->drrs_state.type == SEAMLESS_DRRS_SUPPORT) {
+ pipe_config->has_drrs = true;
intel_link_compute_m_n(bpp, lane_count,
intel_connector->panel.downclock_mode->clock,
pipe_config->port_clock,
&pipe_config->dp_m2_n2);
}
- intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw);
+ if (IS_HASWELL(dev) || IS_BROADWELL(dev))
+ hsw_dp_set_ddi_pll_sel(pipe_config, intel_dp->link_bw);
+ else
+ intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw);
return true;
}
@@ -1092,6 +1273,8 @@ static void wait_panel_status(struct intel_dp *intel_dp,
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp_stat_reg, pp_ctrl_reg;
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
pp_stat_reg = _pp_stat_reg(intel_dp);
pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
@@ -1155,13 +1338,20 @@ static u32 ironlake_get_pp_control(struct intel_dp *intel_dp)
struct drm_i915_private *dev_priv = dev->dev_private;
u32 control;
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
control = I915_READ(_pp_ctrl_reg(intel_dp));
control &= ~PANEL_UNLOCK_MASK;
control |= PANEL_UNLOCK_REGS;
return control;
}
-static bool _edp_panel_vdd_on(struct intel_dp *intel_dp)
+/*
+ * Must be paired with edp_panel_vdd_off().
+ * Must hold pps_mutex around the whole on/off sequence.
+ * Can be nested with intel_edp_panel_vdd_{on,off}() calls.
+ */
+static bool edp_panel_vdd_on(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -1172,6 +1362,8 @@ static bool _edp_panel_vdd_on(struct intel_dp *intel_dp)
u32 pp_stat_reg, pp_ctrl_reg;
bool need_to_disable = !intel_dp->want_panel_vdd;
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
if (!is_edp(intel_dp))
return false;
@@ -1209,66 +1401,103 @@ static bool _edp_panel_vdd_on(struct intel_dp *intel_dp)
return need_to_disable;
}
+/*
+ * Must be paired with intel_edp_panel_vdd_off() or
+ * intel_edp_panel_off().
+ * Nested calls to these functions are not allowed since
+ * we drop the lock. Caller must use some higher level
+ * locking to prevent nested calls from other threads.
+ */
void intel_edp_panel_vdd_on(struct intel_dp *intel_dp)
{
- if (is_edp(intel_dp)) {
- bool vdd = _edp_panel_vdd_on(intel_dp);
+ bool vdd;
- WARN(!vdd, "eDP VDD already requested on\n");
- }
+ if (!is_edp(intel_dp))
+ return;
+
+ pps_lock(intel_dp);
+ vdd = edp_panel_vdd_on(intel_dp);
+ pps_unlock(intel_dp);
+
+ WARN(!vdd, "eDP VDD already requested on\n");
}
static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_digital_port *intel_dig_port =
+ dp_to_dig_port(intel_dp);
+ struct intel_encoder *intel_encoder = &intel_dig_port->base;
+ enum intel_display_power_domain power_domain;
u32 pp;
u32 pp_stat_reg, pp_ctrl_reg;
- WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+ lockdep_assert_held(&dev_priv->pps_mutex);
- if (!intel_dp->want_panel_vdd && edp_have_panel_vdd(intel_dp)) {
- struct intel_digital_port *intel_dig_port =
- dp_to_dig_port(intel_dp);
- struct intel_encoder *intel_encoder = &intel_dig_port->base;
- enum intel_display_power_domain power_domain;
+ WARN_ON(intel_dp->want_panel_vdd);
- DRM_DEBUG_KMS("Turning eDP VDD off\n");
+ if (!edp_have_panel_vdd(intel_dp))
+ return;
- pp = ironlake_get_pp_control(intel_dp);
- pp &= ~EDP_FORCE_VDD;
+ DRM_DEBUG_KMS("Turning eDP VDD off\n");
- pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
- pp_stat_reg = _pp_stat_reg(intel_dp);
+ pp = ironlake_get_pp_control(intel_dp);
+ pp &= ~EDP_FORCE_VDD;
- I915_WRITE(pp_ctrl_reg, pp);
- POSTING_READ(pp_ctrl_reg);
+ pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
+ pp_stat_reg = _pp_stat_reg(intel_dp);
- /* Make sure sequencer is idle before allowing subsequent activity */
- DRM_DEBUG_KMS("PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n",
- I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg));
+ I915_WRITE(pp_ctrl_reg, pp);
+ POSTING_READ(pp_ctrl_reg);
- if ((pp & POWER_TARGET_ON) == 0)
- intel_dp->last_power_cycle = jiffies;
+ /* Make sure sequencer is idle before allowing subsequent activity */
+ DRM_DEBUG_KMS("PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n",
+ I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg));
- power_domain = intel_display_port_power_domain(intel_encoder);
- intel_display_power_put(dev_priv, power_domain);
- }
+ if ((pp & POWER_TARGET_ON) == 0)
+ intel_dp->last_power_cycle = jiffies;
+
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_put(dev_priv, power_domain);
}
static void edp_panel_vdd_work(struct work_struct *__work)
{
struct intel_dp *intel_dp = container_of(to_delayed_work(__work),
struct intel_dp, panel_vdd_work);
- struct drm_device *dev = intel_dp_to_dev(intel_dp);
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
- edp_panel_vdd_off_sync(intel_dp);
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
+ pps_lock(intel_dp);
+ if (!intel_dp->want_panel_vdd)
+ edp_panel_vdd_off_sync(intel_dp);
+ pps_unlock(intel_dp);
+}
+
+static void edp_panel_vdd_schedule_off(struct intel_dp *intel_dp)
+{
+ unsigned long delay;
+
+ /*
+ * Queue the timer to fire a long time from now (relative to the power
+ * down delay) to keep the panel power up across a sequence of
+ * operations.
+ */
+ delay = msecs_to_jiffies(intel_dp->panel_power_cycle_delay * 5);
+ schedule_delayed_work(&intel_dp->panel_vdd_work, delay);
}
+/*
+ * Must be paired with edp_panel_vdd_on().
+ * Must hold pps_mutex around the whole on/off sequence.
+ * Can be nested with intel_edp_panel_vdd_{on,off}() calls.
+ */
static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
{
+ struct drm_i915_private *dev_priv =
+ intel_dp_to_dev(intel_dp)->dev_private;
+
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
if (!is_edp(intel_dp))
return;
@@ -1276,17 +1505,26 @@ static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
intel_dp->want_panel_vdd = false;
- if (sync) {
+ if (sync)
edp_panel_vdd_off_sync(intel_dp);
- } else {
- /*
- * Queue the timer to fire a long
- * time from now (relative to the power down delay)
- * to keep the panel power up across a sequence of operations
- */
- schedule_delayed_work(&intel_dp->panel_vdd_work,
- msecs_to_jiffies(intel_dp->panel_power_cycle_delay * 5));
- }
+ else
+ edp_panel_vdd_schedule_off(intel_dp);
+}
+
+/*
+ * Must be paired with intel_edp_panel_vdd_on().
+ * Nested calls to these functions are not allowed since
+ * we drop the lock. Caller must use some higher level
+ * locking to prevent nested calls from other threads.
+ */
+static void intel_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
+{
+ if (!is_edp(intel_dp))
+ return;
+
+ pps_lock(intel_dp);
+ edp_panel_vdd_off(intel_dp, sync);
+ pps_unlock(intel_dp);
}
void intel_edp_panel_on(struct intel_dp *intel_dp)
@@ -1301,9 +1539,11 @@ void intel_edp_panel_on(struct intel_dp *intel_dp)
DRM_DEBUG_KMS("Turn eDP power on\n");
+ pps_lock(intel_dp);
+
if (edp_have_panel_power(intel_dp)) {
DRM_DEBUG_KMS("eDP power already on\n");
- return;
+ goto out;
}
wait_panel_power_cycle(intel_dp);
@@ -1332,6 +1572,9 @@ void intel_edp_panel_on(struct intel_dp *intel_dp)
I915_WRITE(pp_ctrl_reg, pp);
POSTING_READ(pp_ctrl_reg);
}
+
+ out:
+ pps_unlock(intel_dp);
}
void intel_edp_panel_off(struct intel_dp *intel_dp)
@@ -1349,7 +1592,7 @@ void intel_edp_panel_off(struct intel_dp *intel_dp)
DRM_DEBUG_KMS("Turn eDP power off\n");
- edp_wait_backlight_off(intel_dp);
+ pps_lock(intel_dp);
WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n");
@@ -1372,9 +1615,12 @@ void intel_edp_panel_off(struct intel_dp *intel_dp)
/* We got a reference when we enabled the VDD. */
power_domain = intel_display_port_power_domain(intel_encoder);
intel_display_power_put(dev_priv, power_domain);
+
+ pps_unlock(intel_dp);
}
-void intel_edp_backlight_on(struct intel_dp *intel_dp)
+/* Enable backlight in the panel power control. */
+static void _intel_edp_backlight_on(struct intel_dp *intel_dp)
{
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
struct drm_device *dev = intel_dig_port->base.base.dev;
@@ -1382,10 +1628,6 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp)
u32 pp;
u32 pp_ctrl_reg;
- if (!is_edp(intel_dp))
- return;
-
- DRM_DEBUG_KMS("\n");
/*
* If we enable the backlight right away following a panel power
* on, we may see slight flicker as the panel syncs with the eDP
@@ -1393,6 +1635,9 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp)
* allowing it to appear.
*/
wait_backlight_on(intel_dp);
+
+ pps_lock(intel_dp);
+
pp = ironlake_get_pp_control(intel_dp);
pp |= EDP_BLC_ENABLE;
@@ -1401,10 +1646,23 @@ void intel_edp_backlight_on(struct intel_dp *intel_dp)
I915_WRITE(pp_ctrl_reg, pp);
POSTING_READ(pp_ctrl_reg);
+ pps_unlock(intel_dp);
+}
+
+/* Enable backlight PWM and backlight PP control. */
+void intel_edp_backlight_on(struct intel_dp *intel_dp)
+{
+ if (!is_edp(intel_dp))
+ return;
+
+ DRM_DEBUG_KMS("\n");
+
intel_panel_enable_backlight(intel_dp->attached_connector);
+ _intel_edp_backlight_on(intel_dp);
}
-void intel_edp_backlight_off(struct intel_dp *intel_dp)
+/* Disable backlight in the panel power control. */
+static void _intel_edp_backlight_off(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1414,9 +1672,8 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp)
if (!is_edp(intel_dp))
return;
- intel_panel_disable_backlight(intel_dp->attached_connector);
+ pps_lock(intel_dp);
- DRM_DEBUG_KMS("\n");
pp = ironlake_get_pp_control(intel_dp);
pp &= ~EDP_BLC_ENABLE;
@@ -1424,7 +1681,49 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp)
I915_WRITE(pp_ctrl_reg, pp);
POSTING_READ(pp_ctrl_reg);
+
+ pps_unlock(intel_dp);
+
intel_dp->last_backlight_off = jiffies;
+ edp_wait_backlight_off(intel_dp);
+}
+
+/* Disable backlight PP control and backlight PWM. */
+void intel_edp_backlight_off(struct intel_dp *intel_dp)
+{
+ if (!is_edp(intel_dp))
+ return;
+
+ DRM_DEBUG_KMS("\n");
+
+ _intel_edp_backlight_off(intel_dp);
+ intel_panel_disable_backlight(intel_dp->attached_connector);
+}
+
+/*
+ * Hook for controlling the panel power control backlight through the bl_power
+ * sysfs attribute. Take care to handle multiple calls.
+ */
+static void intel_edp_backlight_power(struct intel_connector *connector,
+ bool enable)
+{
+ struct intel_dp *intel_dp = intel_attached_dp(&connector->base);
+ bool is_enabled;
+
+ pps_lock(intel_dp);
+ is_enabled = ironlake_get_pp_control(intel_dp) & EDP_BLC_ENABLE;
+ pps_unlock(intel_dp);
+
+ if (is_enabled == enable)
+ return;
+
+ DRM_DEBUG_KMS("panel power control backlight %s\n",
+ enable ? "enable" : "disable");
+
+ if (enable)
+ _intel_edp_backlight_on(intel_dp);
+ else
+ _intel_edp_backlight_off(intel_dp);
}
static void ironlake_edp_pll_on(struct intel_dp *intel_dp)
@@ -1490,8 +1789,6 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
if (mode != DRM_MODE_DPMS_ON) {
ret = drm_dp_dpcd_writeb(&intel_dp->aux, DP_SET_POWER,
DP_SET_POWER_D3);
- if (ret != 1)
- DRM_DEBUG_DRIVER("failed to write sink power state\n");
} else {
/*
* When turning on, we need to retry for 1ms to give the sink
@@ -1505,6 +1802,10 @@ void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
msleep(1);
}
}
+
+ if (ret != 1)
+ DRM_DEBUG_KMS("failed to %s sink power state\n",
+ mode == DRM_MODE_DPMS_ON ? "enable" : "disable");
}
static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
@@ -1551,7 +1852,7 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
return true;
}
- for_each_pipe(i) {
+ for_each_pipe(dev_priv, i) {
trans_dp = I915_READ(TRANS_DP_CTL(i));
if ((trans_dp & TRANS_DP_PORT_SEL_MASK) == trans_sel) {
*pipe = i;
@@ -1606,6 +1907,10 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
pipe_config->adjusted_mode.flags |= flags;
+ if (!HAS_PCH_SPLIT(dev) && !IS_VALLEYVIEW(dev) &&
+ tmp & DP_COLOR_RANGE_16_235)
+ pipe_config->limited_color_range = true;
+
pipe_config->has_dp_encoder = true;
intel_dp_get_m_n(crtc, pipe_config);
@@ -1646,11 +1951,9 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
}
}
-static bool is_edp_psr(struct drm_device *dev)
+static bool is_edp_psr(struct intel_dp *intel_dp)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
- return dev_priv->psr.sink_support;
+ return intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED;
}
static bool intel_edp_is_psr_enabled(struct drm_device *dev)
@@ -1698,9 +2001,6 @@ static void intel_edp_psr_setup(struct intel_dp *intel_dp)
struct drm_i915_private *dev_priv = dev->dev_private;
struct edp_vsc_psr psr_vsc;
- if (intel_dp->psr_setup_done)
- return;
-
/* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */
memset(&psr_vsc, 0, sizeof(psr_vsc));
psr_vsc.sdp_header.HB0 = 0;
@@ -1712,22 +2012,25 @@ static void intel_edp_psr_setup(struct intel_dp *intel_dp)
/* Avoid continuous PSR exit by masking memup and hpd */
I915_WRITE(EDP_PSR_DEBUG_CTL(dev), EDP_PSR_DEBUG_MASK_MEMUP |
EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP);
-
- intel_dp->psr_setup_done = true;
}
static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
{
- struct drm_device *dev = intel_dp_to_dev(intel_dp);
+ struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = dig_port->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t aux_clock_divider;
int precharge = 0x3;
int msg_size = 5; /* Header(4) + Message(1) */
+ bool only_standby = false;
aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, 0);
+ if (IS_BROADWELL(dev) && dig_port->port != PORT_A)
+ only_standby = true;
+
/* Enable PSR in sink */
- if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT)
+ if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT || only_standby)
drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG,
DP_PSR_ENABLE & ~DP_PSR_MAIN_LINK_ACTIVE);
else
@@ -1746,18 +2049,24 @@ static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
static void intel_edp_psr_enable_source(struct intel_dp *intel_dp)
{
- struct drm_device *dev = intel_dp_to_dev(intel_dp);
+ struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = dig_port->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t max_sleep_time = 0x1f;
uint32_t idle_frames = 1;
uint32_t val = 0x0;
const uint32_t link_entry_time = EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES;
+ bool only_standby = false;
- if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT) {
+ if (IS_BROADWELL(dev) && dig_port->port != PORT_A)
+ only_standby = true;
+
+ if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT || only_standby) {
val |= EDP_PSR_LINK_STANDBY;
val |= EDP_PSR_TP2_TP3_TIME_0us;
val |= EDP_PSR_TP1_TIME_0us;
val |= EDP_PSR_SKIP_AUX_EXIT;
+ val |= IS_BROADWELL(dev) ? BDW_PSR_SINGLE_FRAME : 0;
} else
val |= EDP_PSR_LINK_DISABLE;
@@ -1775,18 +2084,14 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = dig_port->base.base.crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- struct drm_i915_gem_object *obj = to_intel_framebuffer(crtc->primary->fb)->obj;
- struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base;
- dev_priv->psr.source_ok = false;
+ lockdep_assert_held(&dev_priv->psr.lock);
+ WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+ WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
- if (!HAS_PSR(dev)) {
- DRM_DEBUG_KMS("PSR not supported on this platform\n");
- return false;
- }
+ dev_priv->psr.source_ok = false;
- if ((intel_encoder->type != INTEL_OUTPUT_EDP) ||
- (dig_port->port != PORT_A)) {
+ if (IS_HASWELL(dev) && dig_port->port != PORT_A) {
DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n");
return false;
}
@@ -1796,29 +2101,9 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
return false;
}
- crtc = dig_port->base.base.crtc;
- if (crtc == NULL) {
- DRM_DEBUG_KMS("crtc not active for PSR\n");
- return false;
- }
-
- intel_crtc = to_intel_crtc(crtc);
- if (!intel_crtc_active(crtc)) {
- DRM_DEBUG_KMS("crtc not active for PSR\n");
- return false;
- }
-
- obj = to_intel_framebuffer(crtc->primary->fb)->obj;
- if (obj->tiling_mode != I915_TILING_X ||
- obj->fence_reg == I915_FENCE_REG_NONE) {
- DRM_DEBUG_KMS("PSR condition failed: fb not tiled or fenced\n");
- return false;
- }
-
- if (I915_READ(SPRCTL(intel_crtc->pipe)) & SPRITE_ENABLE) {
- DRM_DEBUG_KMS("PSR condition failed: Sprite is Enabled\n");
- return false;
- }
+ /* Below limitations aren't valid for Broadwell */
+ if (IS_BROADWELL(dev))
+ goto out;
if (I915_READ(HSW_STEREO_3D_CTL(intel_crtc->config.cpu_transcoder)) &
S3D_ENABLE) {
@@ -1831,35 +2116,60 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
return false;
}
+ out:
dev_priv->psr.source_ok = true;
return true;
}
static void intel_edp_psr_do_enable(struct intel_dp *intel_dp)
{
- struct drm_device *dev = intel_dp_to_dev(intel_dp);
-
- if (!intel_edp_psr_match_conditions(intel_dp) ||
- intel_edp_is_psr_enabled(dev))
- return;
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
- /* Setup PSR once */
- intel_edp_psr_setup(intel_dp);
+ WARN_ON(I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE);
+ WARN_ON(dev_priv->psr.active);
+ lockdep_assert_held(&dev_priv->psr.lock);
/* Enable PSR on the panel */
intel_edp_psr_enable_sink(intel_dp);
/* Enable PSR on the host */
intel_edp_psr_enable_source(intel_dp);
+
+ dev_priv->psr.active = true;
}
void intel_edp_psr_enable(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (!HAS_PSR(dev)) {
+ DRM_DEBUG_KMS("PSR not supported on this platform\n");
+ return;
+ }
- if (intel_edp_psr_match_conditions(intel_dp) &&
- !intel_edp_is_psr_enabled(dev))
- intel_edp_psr_do_enable(intel_dp);
+ if (!is_edp_psr(intel_dp)) {
+ DRM_DEBUG_KMS("PSR not supported by this panel\n");
+ return;
+ }
+
+ mutex_lock(&dev_priv->psr.lock);
+ if (dev_priv->psr.enabled) {
+ DRM_DEBUG_KMS("PSR already in use\n");
+ mutex_unlock(&dev_priv->psr.lock);
+ return;
+ }
+
+ dev_priv->psr.busy_frontbuffer_bits = 0;
+
+ /* Setup PSR once */
+ intel_edp_psr_setup(intel_dp);
+
+ if (intel_edp_psr_match_conditions(intel_dp))
+ dev_priv->psr.enabled = intel_dp;
+ mutex_unlock(&dev_priv->psr.lock);
}
void intel_edp_psr_disable(struct intel_dp *intel_dp)
@@ -1867,42 +2177,141 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp)
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
- if (!intel_edp_is_psr_enabled(dev))
+ mutex_lock(&dev_priv->psr.lock);
+ if (!dev_priv->psr.enabled) {
+ mutex_unlock(&dev_priv->psr.lock);
return;
+ }
+
+ if (dev_priv->psr.active) {
+ I915_WRITE(EDP_PSR_CTL(dev),
+ I915_READ(EDP_PSR_CTL(dev)) & ~EDP_PSR_ENABLE);
- I915_WRITE(EDP_PSR_CTL(dev),
- I915_READ(EDP_PSR_CTL(dev)) & ~EDP_PSR_ENABLE);
+ /* Wait till PSR is idle */
+ if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL(dev)) &
+ EDP_PSR_STATUS_STATE_MASK) == 0, 2000, 10))
+ DRM_ERROR("Timed out waiting for PSR Idle State\n");
+
+ dev_priv->psr.active = false;
+ } else {
+ WARN_ON(I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE);
+ }
- /* Wait till PSR is idle */
- if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL(dev)) &
- EDP_PSR_STATUS_STATE_MASK) == 0, 2000, 10))
- DRM_ERROR("Timed out waiting for PSR Idle State\n");
+ dev_priv->psr.enabled = NULL;
+ mutex_unlock(&dev_priv->psr.lock);
+
+ cancel_delayed_work_sync(&dev_priv->psr.work);
}
-void intel_edp_psr_update(struct drm_device *dev)
+static void intel_edp_psr_work(struct work_struct *work)
{
- struct intel_encoder *encoder;
- struct intel_dp *intel_dp = NULL;
+ struct drm_i915_private *dev_priv =
+ container_of(work, typeof(*dev_priv), psr.work.work);
+ struct intel_dp *intel_dp = dev_priv->psr.enabled;
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head)
- if (encoder->type == INTEL_OUTPUT_EDP) {
- intel_dp = enc_to_intel_dp(&encoder->base);
+ mutex_lock(&dev_priv->psr.lock);
+ intel_dp = dev_priv->psr.enabled;
- if (!is_edp_psr(dev))
- return;
+ if (!intel_dp)
+ goto unlock;
- if (!intel_edp_psr_match_conditions(intel_dp))
- intel_edp_psr_disable(intel_dp);
- else
- if (!intel_edp_is_psr_enabled(dev))
- intel_edp_psr_do_enable(intel_dp);
- }
+ /*
+ * The delayed work can race with an invalidate hence we need to
+ * recheck. Since psr_flush first clears this and then reschedules we
+ * won't ever miss a flush when bailing out here.
+ */
+ if (dev_priv->psr.busy_frontbuffer_bits)
+ goto unlock;
+
+ intel_edp_psr_do_enable(intel_dp);
+unlock:
+ mutex_unlock(&dev_priv->psr.lock);
+}
+
+static void intel_edp_psr_do_exit(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->psr.active) {
+ u32 val = I915_READ(EDP_PSR_CTL(dev));
+
+ WARN_ON(!(val & EDP_PSR_ENABLE));
+
+ I915_WRITE(EDP_PSR_CTL(dev), val & ~EDP_PSR_ENABLE);
+
+ dev_priv->psr.active = false;
+ }
+
+}
+
+void intel_edp_psr_invalidate(struct drm_device *dev,
+ unsigned frontbuffer_bits)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc;
+ enum pipe pipe;
+
+ mutex_lock(&dev_priv->psr.lock);
+ if (!dev_priv->psr.enabled) {
+ mutex_unlock(&dev_priv->psr.lock);
+ return;
+ }
+
+ crtc = dp_to_dig_port(dev_priv->psr.enabled)->base.base.crtc;
+ pipe = to_intel_crtc(crtc)->pipe;
+
+ intel_edp_psr_do_exit(dev);
+
+ frontbuffer_bits &= INTEL_FRONTBUFFER_ALL_MASK(pipe);
+
+ dev_priv->psr.busy_frontbuffer_bits |= frontbuffer_bits;
+ mutex_unlock(&dev_priv->psr.lock);
+}
+
+void intel_edp_psr_flush(struct drm_device *dev,
+ unsigned frontbuffer_bits)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_crtc *crtc;
+ enum pipe pipe;
+
+ mutex_lock(&dev_priv->psr.lock);
+ if (!dev_priv->psr.enabled) {
+ mutex_unlock(&dev_priv->psr.lock);
+ return;
+ }
+
+ crtc = dp_to_dig_port(dev_priv->psr.enabled)->base.base.crtc;
+ pipe = to_intel_crtc(crtc)->pipe;
+ dev_priv->psr.busy_frontbuffer_bits &= ~frontbuffer_bits;
+
+ /*
+ * On Haswell sprite plane updates don't result in a psr invalidating
+ * signal in the hardware. Which means we need to manually fake this in
+ * software for all flushes, not just when we've seen a preceding
+ * invalidation through frontbuffer rendering.
+ */
+ if (IS_HASWELL(dev) &&
+ (frontbuffer_bits & INTEL_FRONTBUFFER_SPRITE(pipe)))
+ intel_edp_psr_do_exit(dev);
+
+ if (!dev_priv->psr.active && !dev_priv->psr.busy_frontbuffer_bits)
+ schedule_delayed_work(&dev_priv->psr.work,
+ msecs_to_jiffies(100));
+ mutex_unlock(&dev_priv->psr.lock);
+}
+
+void intel_edp_psr_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ INIT_DELAYED_WORK(&dev_priv->psr.work, intel_edp_psr_work);
+ mutex_init(&dev_priv->psr.lock);
}
static void intel_disable_dp(struct intel_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
- enum port port = dp_to_dig_port(intel_dp)->port;
struct drm_device *dev = encoder->base.dev;
/* Make sure the panel is off before trying to change the mode. But also
@@ -1912,21 +2321,19 @@ static void intel_disable_dp(struct intel_encoder *encoder)
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
intel_edp_panel_off(intel_dp);
- /* cpu edp my only be disable _after_ the cpu pipe/plane is disabled. */
- if (!(port == PORT_A || IS_VALLEYVIEW(dev)))
+ /* disable the port before the pipe on g4x */
+ if (INTEL_INFO(dev)->gen < 5)
intel_dp_link_down(intel_dp);
}
-static void g4x_post_disable_dp(struct intel_encoder *encoder)
+static void ilk_post_disable_dp(struct intel_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
enum port port = dp_to_dig_port(intel_dp)->port;
- if (port != PORT_A)
- return;
-
intel_dp_link_down(intel_dp);
- ironlake_edp_pll_off(intel_dp);
+ if (port == PORT_A)
+ ironlake_edp_pll_off(intel_dp);
}
static void vlv_post_disable_dp(struct intel_encoder *encoder)
@@ -1972,6 +2379,104 @@ static void chv_post_disable_dp(struct intel_encoder *encoder)
mutex_unlock(&dev_priv->dpio_lock);
}
+static void
+_intel_dp_set_link_train(struct intel_dp *intel_dp,
+ uint32_t *DP,
+ uint8_t dp_train_pat)
+{
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum port port = intel_dig_port->port;
+
+ if (HAS_DDI(dev)) {
+ uint32_t temp = I915_READ(DP_TP_CTL(port));
+
+ if (dp_train_pat & DP_LINK_SCRAMBLING_DISABLE)
+ temp |= DP_TP_CTL_SCRAMBLE_DISABLE;
+ else
+ temp &= ~DP_TP_CTL_SCRAMBLE_DISABLE;
+
+ temp &= ~DP_TP_CTL_LINK_TRAIN_MASK;
+ switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
+ case DP_TRAINING_PATTERN_DISABLE:
+ temp |= DP_TP_CTL_LINK_TRAIN_NORMAL;
+
+ break;
+ case DP_TRAINING_PATTERN_1:
+ temp |= DP_TP_CTL_LINK_TRAIN_PAT1;
+ break;
+ case DP_TRAINING_PATTERN_2:
+ temp |= DP_TP_CTL_LINK_TRAIN_PAT2;
+ break;
+ case DP_TRAINING_PATTERN_3:
+ temp |= DP_TP_CTL_LINK_TRAIN_PAT3;
+ break;
+ }
+ I915_WRITE(DP_TP_CTL(port), temp);
+
+ } else if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || port != PORT_A)) {
+ *DP &= ~DP_LINK_TRAIN_MASK_CPT;
+
+ switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
+ case DP_TRAINING_PATTERN_DISABLE:
+ *DP |= DP_LINK_TRAIN_OFF_CPT;
+ break;
+ case DP_TRAINING_PATTERN_1:
+ *DP |= DP_LINK_TRAIN_PAT_1_CPT;
+ break;
+ case DP_TRAINING_PATTERN_2:
+ *DP |= DP_LINK_TRAIN_PAT_2_CPT;
+ break;
+ case DP_TRAINING_PATTERN_3:
+ DRM_ERROR("DP training pattern 3 not supported\n");
+ *DP |= DP_LINK_TRAIN_PAT_2_CPT;
+ break;
+ }
+
+ } else {
+ if (IS_CHERRYVIEW(dev))
+ *DP &= ~DP_LINK_TRAIN_MASK_CHV;
+ else
+ *DP &= ~DP_LINK_TRAIN_MASK;
+
+ switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
+ case DP_TRAINING_PATTERN_DISABLE:
+ *DP |= DP_LINK_TRAIN_OFF;
+ break;
+ case DP_TRAINING_PATTERN_1:
+ *DP |= DP_LINK_TRAIN_PAT_1;
+ break;
+ case DP_TRAINING_PATTERN_2:
+ *DP |= DP_LINK_TRAIN_PAT_2;
+ break;
+ case DP_TRAINING_PATTERN_3:
+ if (IS_CHERRYVIEW(dev)) {
+ *DP |= DP_LINK_TRAIN_PAT_3_CHV;
+ } else {
+ DRM_ERROR("DP training pattern 3 not supported\n");
+ *DP |= DP_LINK_TRAIN_PAT_2;
+ }
+ break;
+ }
+ }
+}
+
+static void intel_dp_enable_port(struct intel_dp *intel_dp)
+{
+ struct drm_device *dev = intel_dp_to_dev(intel_dp);
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ intel_dp->DP |= DP_PORT_EN;
+
+ /* enable with pattern 1 (as per spec) */
+ _intel_dp_set_link_train(intel_dp, &intel_dp->DP,
+ DP_TRAINING_PATTERN_1);
+
+ I915_WRITE(intel_dp->output_reg, intel_dp->DP);
+ POSTING_READ(intel_dp->output_reg);
+}
+
static void intel_enable_dp(struct intel_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
@@ -1982,11 +2487,12 @@ static void intel_enable_dp(struct intel_encoder *encoder)
if (WARN_ON(dp_reg & DP_PORT_EN))
return;
+ intel_dp_enable_port(intel_dp);
intel_edp_panel_vdd_on(intel_dp);
+ intel_edp_panel_on(intel_dp);
+ intel_edp_panel_vdd_off(intel_dp, true);
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
intel_dp_start_link_train(intel_dp);
- intel_edp_panel_on(intel_dp);
- edp_panel_vdd_off(intel_dp, true);
intel_dp_complete_link_train(intel_dp);
intel_dp_stop_link_train(intel_dp);
}
@@ -2020,6 +2526,78 @@ static void g4x_pre_enable_dp(struct intel_encoder *encoder)
}
}
+static void vlv_steal_power_sequencer(struct drm_device *dev,
+ enum pipe pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_encoder *encoder;
+
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+ base.head) {
+ struct intel_dp *intel_dp;
+ enum port port;
+
+ if (encoder->type != INTEL_OUTPUT_EDP)
+ continue;
+
+ intel_dp = enc_to_intel_dp(&encoder->base);
+ port = dp_to_dig_port(intel_dp)->port;
+
+ if (intel_dp->pps_pipe != pipe)
+ continue;
+
+ DRM_DEBUG_KMS("stealing pipe %c power sequencer from port %c\n",
+ pipe_name(pipe), port_name(port));
+
+ /* make sure vdd is off before we steal it */
+ edp_panel_vdd_off_sync(intel_dp);
+
+ intel_dp->pps_pipe = INVALID_PIPE;
+ }
+}
+
+static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp)
+{
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct intel_encoder *encoder = &intel_dig_port->base;
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
+ struct edp_power_seq power_seq;
+
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
+ if (intel_dp->pps_pipe == crtc->pipe)
+ return;
+
+ /*
+ * If another power sequencer was being used on this
+ * port previously make sure to turn off vdd there while
+ * we still have control of it.
+ */
+ if (intel_dp->pps_pipe != INVALID_PIPE)
+ edp_panel_vdd_off_sync(intel_dp);
+
+ /*
+ * We may be stealing the power
+ * sequencer from another port.
+ */
+ vlv_steal_power_sequencer(dev, crtc->pipe);
+
+ /* now it's all ours */
+ intel_dp->pps_pipe = crtc->pipe;
+
+ DRM_DEBUG_KMS("initializing pipe %c power sequencer for port %c\n",
+ pipe_name(intel_dp->pps_pipe), port_name(intel_dig_port->port));
+
+ /* init power sequencer on this pipe and port */
+ intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
+ intel_dp_init_panel_power_sequencer_registers(dev, intel_dp,
+ &power_seq);
+}
+
static void vlv_pre_enable_dp(struct intel_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
@@ -2029,7 +2607,6 @@ static void vlv_pre_enable_dp(struct intel_encoder *encoder)
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
enum dpio_channel port = vlv_dport_to_channel(dport);
int pipe = intel_crtc->pipe;
- struct edp_power_seq power_seq;
u32 val;
mutex_lock(&dev_priv->dpio_lock);
@@ -2048,10 +2625,9 @@ static void vlv_pre_enable_dp(struct intel_encoder *encoder)
mutex_unlock(&dev_priv->dpio_lock);
if (is_edp(intel_dp)) {
- /* init power sequencer on this pipe and port */
- intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
- intel_dp_init_panel_power_sequencer_registers(dev, intel_dp,
- &power_seq);
+ pps_lock(intel_dp);
+ vlv_init_panel_power_sequencer(intel_dp);
+ pps_unlock(intel_dp);
}
intel_enable_dp(encoder);
@@ -2095,7 +2671,6 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder)
struct intel_digital_port *dport = dp_to_dig_port(intel_dp);
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct edp_power_seq power_seq;
struct intel_crtc *intel_crtc =
to_intel_crtc(encoder->base.crtc);
enum dpio_channel ch = vlv_dport_to_channel(dport);
@@ -2141,10 +2716,9 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder)
mutex_unlock(&dev_priv->dpio_lock);
if (is_edp(intel_dp)) {
- /* init power sequencer on this pipe and port */
- intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
- intel_dp_init_panel_power_sequencer_registers(dev, intel_dp,
- &power_seq);
+ pps_lock(intel_dp);
+ vlv_init_panel_power_sequencer(intel_dp);
+ pps_unlock(intel_dp);
}
intel_enable_dp(encoder);
@@ -2152,6 +2726,72 @@ static void chv_pre_enable_dp(struct intel_encoder *encoder)
vlv_wait_port_ready(dev_priv, dport);
}
+static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
+{
+ struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc =
+ to_intel_crtc(encoder->base.crtc);
+ enum dpio_channel ch = vlv_dport_to_channel(dport);
+ enum pipe pipe = intel_crtc->pipe;
+ u32 val;
+
+ intel_dp_prepare(encoder);
+
+ mutex_lock(&dev_priv->dpio_lock);
+
+ /* program left/right clock distribution */
+ if (pipe != PIPE_B) {
+ val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0);
+ val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK);
+ if (ch == DPIO_CH0)
+ val |= CHV_BUFLEFTENA1_FORCE;
+ if (ch == DPIO_CH1)
+ val |= CHV_BUFRIGHTENA1_FORCE;
+ vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val);
+ } else {
+ val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1);
+ val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK);
+ if (ch == DPIO_CH0)
+ val |= CHV_BUFLEFTENA2_FORCE;
+ if (ch == DPIO_CH1)
+ val |= CHV_BUFRIGHTENA2_FORCE;
+ vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val);
+ }
+
+ /* program clock channel usage */
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(ch));
+ val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE;
+ if (pipe != PIPE_B)
+ val &= ~CHV_PCS_USEDCLKCHANNEL;
+ else
+ val |= CHV_PCS_USEDCLKCHANNEL;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW8(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch));
+ val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE;
+ if (pipe != PIPE_B)
+ val &= ~CHV_PCS_USEDCLKCHANNEL;
+ else
+ val |= CHV_PCS_USEDCLKCHANNEL;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val);
+
+ /*
+ * This a a bit weird since generally CL
+ * matches the pipe, but here we need to
+ * pick the CL based on the port.
+ */
+ val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW19(ch));
+ if (pipe != PIPE_B)
+ val &= ~CHV_CMN_USEDCLKCHANNEL;
+ else
+ val |= CHV_CMN_USEDCLKCHANNEL;
+ vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW19(ch), val);
+
+ mutex_unlock(&dev_priv->dpio_lock);
+}
+
/*
* Native read with retry for link status and receiver capability reads for
* cases where the sink may still be asleep.
@@ -2166,6 +2806,13 @@ intel_dp_dpcd_read_wake(struct drm_dp_aux *aux, unsigned int offset,
ssize_t ret;
int i;
+ /*
+ * Sometime we just get the same incorrect byte repeated
+ * over the entire buffer. Doing just one throw away read
+ * initially seems to "solve" it.
+ */
+ drm_dp_dpcd_read(aux, DP_DPCD_REV, buffer, 1);
+
for (i = 0; i < 3; i++) {
ret = drm_dp_dpcd_read(aux, offset, buffer, size);
if (ret == size)
@@ -2189,25 +2836,21 @@ intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_
DP_LINK_STATUS_SIZE) == DP_LINK_STATUS_SIZE;
}
-/*
- * These are source-specific values; current Intel hardware supports
- * a maximum voltage of 800mV and a maximum pre-emphasis of 6dB
- */
-
+/* These are source-specific values. */
static uint8_t
intel_dp_voltage_max(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
enum port port = dp_to_dig_port(intel_dp)->port;
- if (IS_VALLEYVIEW(dev) || IS_BROADWELL(dev))
- return DP_TRAIN_VOLTAGE_SWING_1200;
+ if (IS_VALLEYVIEW(dev))
+ return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
else if (IS_GEN7(dev) && port == PORT_A)
- return DP_TRAIN_VOLTAGE_SWING_800;
+ return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
else if (HAS_PCH_CPT(dev) && port != PORT_A)
- return DP_TRAIN_VOLTAGE_SWING_1200;
+ return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
else
- return DP_TRAIN_VOLTAGE_SWING_800;
+ return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
}
static uint8_t
@@ -2216,62 +2859,51 @@ intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
struct drm_device *dev = intel_dp_to_dev(intel_dp);
enum port port = dp_to_dig_port(intel_dp)->port;
- if (IS_BROADWELL(dev)) {
- switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
- case DP_TRAIN_VOLTAGE_SWING_600:
- return DP_TRAIN_PRE_EMPHASIS_6;
- case DP_TRAIN_VOLTAGE_SWING_800:
- return DP_TRAIN_PRE_EMPHASIS_3_5;
- case DP_TRAIN_VOLTAGE_SWING_1200:
- default:
- return DP_TRAIN_PRE_EMPHASIS_0;
- }
- } else if (IS_HASWELL(dev)) {
+ if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
- return DP_TRAIN_PRE_EMPHASIS_9_5;
- case DP_TRAIN_VOLTAGE_SWING_600:
- return DP_TRAIN_PRE_EMPHASIS_6;
- case DP_TRAIN_VOLTAGE_SWING_800:
- return DP_TRAIN_PRE_EMPHASIS_3_5;
- case DP_TRAIN_VOLTAGE_SWING_1200:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+ return DP_TRAIN_PRE_EMPH_LEVEL_3;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+ return DP_TRAIN_PRE_EMPH_LEVEL_2;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+ return DP_TRAIN_PRE_EMPH_LEVEL_1;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
default:
- return DP_TRAIN_PRE_EMPHASIS_0;
+ return DP_TRAIN_PRE_EMPH_LEVEL_0;
}
} else if (IS_VALLEYVIEW(dev)) {
switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
- return DP_TRAIN_PRE_EMPHASIS_9_5;
- case DP_TRAIN_VOLTAGE_SWING_600:
- return DP_TRAIN_PRE_EMPHASIS_6;
- case DP_TRAIN_VOLTAGE_SWING_800:
- return DP_TRAIN_PRE_EMPHASIS_3_5;
- case DP_TRAIN_VOLTAGE_SWING_1200:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+ return DP_TRAIN_PRE_EMPH_LEVEL_3;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+ return DP_TRAIN_PRE_EMPH_LEVEL_2;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+ return DP_TRAIN_PRE_EMPH_LEVEL_1;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
default:
- return DP_TRAIN_PRE_EMPHASIS_0;
+ return DP_TRAIN_PRE_EMPH_LEVEL_0;
}
} else if (IS_GEN7(dev) && port == PORT_A) {
switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
- return DP_TRAIN_PRE_EMPHASIS_6;
- case DP_TRAIN_VOLTAGE_SWING_600:
- case DP_TRAIN_VOLTAGE_SWING_800:
- return DP_TRAIN_PRE_EMPHASIS_3_5;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+ return DP_TRAIN_PRE_EMPH_LEVEL_2;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+ return DP_TRAIN_PRE_EMPH_LEVEL_1;
default:
- return DP_TRAIN_PRE_EMPHASIS_0;
+ return DP_TRAIN_PRE_EMPH_LEVEL_0;
}
} else {
switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
- return DP_TRAIN_PRE_EMPHASIS_6;
- case DP_TRAIN_VOLTAGE_SWING_600:
- return DP_TRAIN_PRE_EMPHASIS_6;
- case DP_TRAIN_VOLTAGE_SWING_800:
- return DP_TRAIN_PRE_EMPHASIS_3_5;
- case DP_TRAIN_VOLTAGE_SWING_1200:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+ return DP_TRAIN_PRE_EMPH_LEVEL_2;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+ return DP_TRAIN_PRE_EMPH_LEVEL_2;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+ return DP_TRAIN_PRE_EMPH_LEVEL_1;
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
default:
- return DP_TRAIN_PRE_EMPHASIS_0;
+ return DP_TRAIN_PRE_EMPH_LEVEL_0;
}
}
}
@@ -2290,22 +2922,22 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp)
int pipe = intel_crtc->pipe;
switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
- case DP_TRAIN_PRE_EMPHASIS_0:
+ case DP_TRAIN_PRE_EMPH_LEVEL_0:
preemph_reg_value = 0x0004000;
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
demph_reg_value = 0x2B405555;
uniqtranscale_reg_value = 0x552AB83A;
break;
- case DP_TRAIN_VOLTAGE_SWING_600:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
demph_reg_value = 0x2B404040;
uniqtranscale_reg_value = 0x5548B83A;
break;
- case DP_TRAIN_VOLTAGE_SWING_800:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
demph_reg_value = 0x2B245555;
uniqtranscale_reg_value = 0x5560B83A;
break;
- case DP_TRAIN_VOLTAGE_SWING_1200:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
demph_reg_value = 0x2B405555;
uniqtranscale_reg_value = 0x5598DA3A;
break;
@@ -2313,18 +2945,18 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp)
return 0;
}
break;
- case DP_TRAIN_PRE_EMPHASIS_3_5:
+ case DP_TRAIN_PRE_EMPH_LEVEL_1:
preemph_reg_value = 0x0002000;
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
demph_reg_value = 0x2B404040;
uniqtranscale_reg_value = 0x5552B83A;
break;
- case DP_TRAIN_VOLTAGE_SWING_600:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
demph_reg_value = 0x2B404848;
uniqtranscale_reg_value = 0x5580B83A;
break;
- case DP_TRAIN_VOLTAGE_SWING_800:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
demph_reg_value = 0x2B404040;
uniqtranscale_reg_value = 0x55ADDA3A;
break;
@@ -2332,14 +2964,14 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp)
return 0;
}
break;
- case DP_TRAIN_PRE_EMPHASIS_6:
+ case DP_TRAIN_PRE_EMPH_LEVEL_2:
preemph_reg_value = 0x0000000;
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
demph_reg_value = 0x2B305555;
uniqtranscale_reg_value = 0x5570B83A;
break;
- case DP_TRAIN_VOLTAGE_SWING_600:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
demph_reg_value = 0x2B2B4040;
uniqtranscale_reg_value = 0x55ADDA3A;
break;
@@ -2347,10 +2979,10 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp)
return 0;
}
break;
- case DP_TRAIN_PRE_EMPHASIS_9_5:
+ case DP_TRAIN_PRE_EMPH_LEVEL_3:
preemph_reg_value = 0x0006000;
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
demph_reg_value = 0x1B405555;
uniqtranscale_reg_value = 0x55ADDA3A;
break;
@@ -2389,21 +3021,21 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp)
int i;
switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
- case DP_TRAIN_PRE_EMPHASIS_0:
+ case DP_TRAIN_PRE_EMPH_LEVEL_0:
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
deemph_reg_value = 128;
margin_reg_value = 52;
break;
- case DP_TRAIN_VOLTAGE_SWING_600:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
deemph_reg_value = 128;
margin_reg_value = 77;
break;
- case DP_TRAIN_VOLTAGE_SWING_800:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
deemph_reg_value = 128;
margin_reg_value = 102;
break;
- case DP_TRAIN_VOLTAGE_SWING_1200:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
deemph_reg_value = 128;
margin_reg_value = 154;
/* FIXME extra to set for 1200 */
@@ -2412,17 +3044,17 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp)
return 0;
}
break;
- case DP_TRAIN_PRE_EMPHASIS_3_5:
+ case DP_TRAIN_PRE_EMPH_LEVEL_1:
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
deemph_reg_value = 85;
margin_reg_value = 78;
break;
- case DP_TRAIN_VOLTAGE_SWING_600:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
deemph_reg_value = 85;
margin_reg_value = 116;
break;
- case DP_TRAIN_VOLTAGE_SWING_800:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
deemph_reg_value = 85;
margin_reg_value = 154;
break;
@@ -2430,13 +3062,13 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp)
return 0;
}
break;
- case DP_TRAIN_PRE_EMPHASIS_6:
+ case DP_TRAIN_PRE_EMPH_LEVEL_2:
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
deemph_reg_value = 64;
margin_reg_value = 104;
break;
- case DP_TRAIN_VOLTAGE_SWING_600:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
deemph_reg_value = 64;
margin_reg_value = 154;
break;
@@ -2444,9 +3076,9 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp)
return 0;
}
break;
- case DP_TRAIN_PRE_EMPHASIS_9_5:
+ case DP_TRAIN_PRE_EMPH_LEVEL_3:
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
deemph_reg_value = 43;
margin_reg_value = 154;
break;
@@ -2480,8 +3112,8 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp)
/* Program swing margin */
for (i = 0; i < 4; i++) {
val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i));
- val &= ~DPIO_SWING_MARGIN_MASK;
- val |= margin_reg_value << DPIO_SWING_MARGIN_SHIFT;
+ val &= ~DPIO_SWING_MARGIN000_MASK;
+ val |= margin_reg_value << DPIO_SWING_MARGIN000_SHIFT;
vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val);
}
@@ -2493,9 +3125,9 @@ static uint32_t intel_chv_signal_levels(struct intel_dp *intel_dp)
}
if (((train_set & DP_TRAIN_PRE_EMPHASIS_MASK)
- == DP_TRAIN_PRE_EMPHASIS_0) &&
+ == DP_TRAIN_PRE_EMPH_LEVEL_0) &&
((train_set & DP_TRAIN_VOLTAGE_SWING_MASK)
- == DP_TRAIN_VOLTAGE_SWING_1200)) {
+ == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)) {
/*
* The document said it needs to set bit 27 for ch0 and bit 26
@@ -2574,32 +3206,32 @@ intel_gen4_signal_levels(uint8_t train_set)
uint32_t signal_levels = 0;
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
- case DP_TRAIN_VOLTAGE_SWING_400:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
default:
signal_levels |= DP_VOLTAGE_0_4;
break;
- case DP_TRAIN_VOLTAGE_SWING_600:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
signal_levels |= DP_VOLTAGE_0_6;
break;
- case DP_TRAIN_VOLTAGE_SWING_800:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
signal_levels |= DP_VOLTAGE_0_8;
break;
- case DP_TRAIN_VOLTAGE_SWING_1200:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
signal_levels |= DP_VOLTAGE_1_2;
break;
}
switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
- case DP_TRAIN_PRE_EMPHASIS_0:
+ case DP_TRAIN_PRE_EMPH_LEVEL_0:
default:
signal_levels |= DP_PRE_EMPHASIS_0;
break;
- case DP_TRAIN_PRE_EMPHASIS_3_5:
+ case DP_TRAIN_PRE_EMPH_LEVEL_1:
signal_levels |= DP_PRE_EMPHASIS_3_5;
break;
- case DP_TRAIN_PRE_EMPHASIS_6:
+ case DP_TRAIN_PRE_EMPH_LEVEL_2:
signal_levels |= DP_PRE_EMPHASIS_6;
break;
- case DP_TRAIN_PRE_EMPHASIS_9_5:
+ case DP_TRAIN_PRE_EMPH_LEVEL_3:
signal_levels |= DP_PRE_EMPHASIS_9_5;
break;
}
@@ -2613,19 +3245,19 @@ intel_gen6_edp_signal_levels(uint8_t train_set)
int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
DP_TRAIN_PRE_EMPHASIS_MASK);
switch (signal_levels) {
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0:
- case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_0:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_0:
return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B;
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_1:
return EDP_LINK_TRAIN_400MV_3_5DB_SNB_B;
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6:
- case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_2:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_2:
return EDP_LINK_TRAIN_400_600MV_6DB_SNB_B;
- case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5:
- case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_1:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_1:
return EDP_LINK_TRAIN_600_800MV_3_5DB_SNB_B;
- case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0:
- case DP_TRAIN_VOLTAGE_SWING_1200 | DP_TRAIN_PRE_EMPHASIS_0:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_0:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_3 | DP_TRAIN_PRE_EMPH_LEVEL_0:
return EDP_LINK_TRAIN_800_1200MV_0DB_SNB_B;
default:
DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:"
@@ -2641,21 +3273,21 @@ intel_gen7_edp_signal_levels(uint8_t train_set)
int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
DP_TRAIN_PRE_EMPHASIS_MASK);
switch (signal_levels) {
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_0:
return EDP_LINK_TRAIN_400MV_0DB_IVB;
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_1:
return EDP_LINK_TRAIN_400MV_3_5DB_IVB;
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_2:
return EDP_LINK_TRAIN_400MV_6DB_IVB;
- case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_0:
return EDP_LINK_TRAIN_600MV_0DB_IVB;
- case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_1:
return EDP_LINK_TRAIN_600MV_3_5DB_IVB;
- case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_0:
return EDP_LINK_TRAIN_800MV_0DB_IVB;
- case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5:
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_1:
return EDP_LINK_TRAIN_800MV_3_5DB_IVB;
default:
@@ -2672,65 +3304,30 @@ intel_hsw_signal_levels(uint8_t train_set)
int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
DP_TRAIN_PRE_EMPHASIS_MASK);
switch (signal_levels) {
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0:
- return DDI_BUF_EMP_400MV_0DB_HSW;
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5:
- return DDI_BUF_EMP_400MV_3_5DB_HSW;
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6:
- return DDI_BUF_EMP_400MV_6DB_HSW;
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_9_5:
- return DDI_BUF_EMP_400MV_9_5DB_HSW;
-
- case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0:
- return DDI_BUF_EMP_600MV_0DB_HSW;
- case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5:
- return DDI_BUF_EMP_600MV_3_5DB_HSW;
- case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6:
- return DDI_BUF_EMP_600MV_6DB_HSW;
-
- case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0:
- return DDI_BUF_EMP_800MV_0DB_HSW;
- case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5:
- return DDI_BUF_EMP_800MV_3_5DB_HSW;
- default:
- DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:"
- "0x%x\n", signal_levels);
- return DDI_BUF_EMP_400MV_0DB_HSW;
- }
-}
-
-static uint32_t
-intel_bdw_signal_levels(uint8_t train_set)
-{
- int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
- DP_TRAIN_PRE_EMPHASIS_MASK);
- switch (signal_levels) {
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0:
- return DDI_BUF_EMP_400MV_0DB_BDW; /* Sel0 */
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5:
- return DDI_BUF_EMP_400MV_3_5DB_BDW; /* Sel1 */
- case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6:
- return DDI_BUF_EMP_400MV_6DB_BDW; /* Sel2 */
-
- case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0:
- return DDI_BUF_EMP_600MV_0DB_BDW; /* Sel3 */
- case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5:
- return DDI_BUF_EMP_600MV_3_5DB_BDW; /* Sel4 */
- case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6:
- return DDI_BUF_EMP_600MV_6DB_BDW; /* Sel5 */
-
- case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0:
- return DDI_BUF_EMP_800MV_0DB_BDW; /* Sel6 */
- case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5:
- return DDI_BUF_EMP_800MV_3_5DB_BDW; /* Sel7 */
-
- case DP_TRAIN_VOLTAGE_SWING_1200 | DP_TRAIN_PRE_EMPHASIS_0:
- return DDI_BUF_EMP_1200MV_0DB_BDW; /* Sel8 */
-
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_0:
+ return DDI_BUF_TRANS_SELECT(0);
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_1:
+ return DDI_BUF_TRANS_SELECT(1);
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_2:
+ return DDI_BUF_TRANS_SELECT(2);
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | DP_TRAIN_PRE_EMPH_LEVEL_3:
+ return DDI_BUF_TRANS_SELECT(3);
+
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_0:
+ return DDI_BUF_TRANS_SELECT(4);
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_1:
+ return DDI_BUF_TRANS_SELECT(5);
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_1 | DP_TRAIN_PRE_EMPH_LEVEL_2:
+ return DDI_BUF_TRANS_SELECT(6);
+
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_0:
+ return DDI_BUF_TRANS_SELECT(7);
+ case DP_TRAIN_VOLTAGE_SWING_LEVEL_2 | DP_TRAIN_PRE_EMPH_LEVEL_1:
+ return DDI_BUF_TRANS_SELECT(8);
default:
DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:"
"0x%x\n", signal_levels);
- return DDI_BUF_EMP_400MV_0DB_BDW; /* Sel0 */
+ return DDI_BUF_TRANS_SELECT(0);
}
}
@@ -2744,10 +3341,7 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
uint32_t signal_levels, mask;
uint8_t train_set = intel_dp->train_set[0];
- if (IS_BROADWELL(dev)) {
- signal_levels = intel_bdw_signal_levels(train_set);
- mask = DDI_BUF_EMP_MASK;
- } else if (IS_HASWELL(dev)) {
+ if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
signal_levels = intel_hsw_signal_levels(train_set);
mask = DDI_BUF_EMP_MASK;
} else if (IS_CHERRYVIEW(dev)) {
@@ -2780,74 +3374,10 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
struct drm_device *dev = intel_dig_port->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- enum port port = intel_dig_port->port;
uint8_t buf[sizeof(intel_dp->train_set) + 1];
int ret, len;
- if (HAS_DDI(dev)) {
- uint32_t temp = I915_READ(DP_TP_CTL(port));
-
- if (dp_train_pat & DP_LINK_SCRAMBLING_DISABLE)
- temp |= DP_TP_CTL_SCRAMBLE_DISABLE;
- else
- temp &= ~DP_TP_CTL_SCRAMBLE_DISABLE;
-
- temp &= ~DP_TP_CTL_LINK_TRAIN_MASK;
- switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
- case DP_TRAINING_PATTERN_DISABLE:
- temp |= DP_TP_CTL_LINK_TRAIN_NORMAL;
-
- break;
- case DP_TRAINING_PATTERN_1:
- temp |= DP_TP_CTL_LINK_TRAIN_PAT1;
- break;
- case DP_TRAINING_PATTERN_2:
- temp |= DP_TP_CTL_LINK_TRAIN_PAT2;
- break;
- case DP_TRAINING_PATTERN_3:
- temp |= DP_TP_CTL_LINK_TRAIN_PAT3;
- break;
- }
- I915_WRITE(DP_TP_CTL(port), temp);
-
- } else if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || port != PORT_A)) {
- *DP &= ~DP_LINK_TRAIN_MASK_CPT;
-
- switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
- case DP_TRAINING_PATTERN_DISABLE:
- *DP |= DP_LINK_TRAIN_OFF_CPT;
- break;
- case DP_TRAINING_PATTERN_1:
- *DP |= DP_LINK_TRAIN_PAT_1_CPT;
- break;
- case DP_TRAINING_PATTERN_2:
- *DP |= DP_LINK_TRAIN_PAT_2_CPT;
- break;
- case DP_TRAINING_PATTERN_3:
- DRM_ERROR("DP training pattern 3 not supported\n");
- *DP |= DP_LINK_TRAIN_PAT_2_CPT;
- break;
- }
-
- } else {
- *DP &= ~DP_LINK_TRAIN_MASK;
-
- switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
- case DP_TRAINING_PATTERN_DISABLE:
- *DP |= DP_LINK_TRAIN_OFF;
- break;
- case DP_TRAINING_PATTERN_1:
- *DP |= DP_LINK_TRAIN_PAT_1;
- break;
- case DP_TRAINING_PATTERN_2:
- *DP |= DP_LINK_TRAIN_PAT_2;
- break;
- case DP_TRAINING_PATTERN_3:
- DRM_ERROR("DP training pattern 3 not supported\n");
- *DP |= DP_LINK_TRAIN_PAT_2;
- break;
- }
- }
+ _intel_dp_set_link_train(intel_dp, DP, dp_train_pat);
I915_WRITE(intel_dp->output_reg, *DP);
POSTING_READ(intel_dp->output_reg);
@@ -3131,7 +3661,10 @@ intel_dp_link_down(struct intel_dp *intel_dp)
DP &= ~DP_LINK_TRAIN_MASK_CPT;
I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT);
} else {
- DP &= ~DP_LINK_TRAIN_MASK;
+ if (IS_CHERRYVIEW(dev))
+ DP &= ~DP_LINK_TRAIN_MASK_CHV;
+ else
+ DP &= ~DP_LINK_TRAIN_MASK;
I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE);
}
POSTING_READ(intel_dp->output_reg);
@@ -3177,15 +3710,11 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
struct drm_device *dev = dig_port->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3];
-
if (intel_dp_dpcd_read_wake(&intel_dp->aux, 0x000, intel_dp->dpcd,
sizeof(intel_dp->dpcd)) < 0)
return false; /* aux transfer failed */
- hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd),
- 32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false);
- DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump);
+ DRM_DEBUG_KMS("DPCD: %*ph\n", (int) sizeof(intel_dp->dpcd), intel_dp->dpcd);
if (intel_dp->dpcd[DP_DPCD_REV] == 0)
return false; /* DPCD not present */
@@ -3202,11 +3731,12 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
}
}
- /* Training Pattern 3 support */
+ /* Training Pattern 3 support, both source and sink */
if (intel_dp->dpcd[DP_DPCD_REV] >= 0x12 &&
- intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED) {
+ intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED &&
+ (IS_HASWELL(dev_priv) || INTEL_INFO(dev_priv)->gen >= 8)) {
intel_dp->use_tps3 = true;
- DRM_DEBUG_KMS("Displayport TPS3 supported");
+ DRM_DEBUG_KMS("Displayport TPS3 supported\n");
} else
intel_dp->use_tps3 = false;
@@ -3243,7 +3773,34 @@ intel_dp_probe_oui(struct intel_dp *intel_dp)
DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n",
buf[0], buf[1], buf[2]);
- edp_panel_vdd_off(intel_dp, false);
+ intel_edp_panel_vdd_off(intel_dp, false);
+}
+
+static bool
+intel_dp_probe_mst(struct intel_dp *intel_dp)
+{
+ u8 buf[1];
+
+ if (!intel_dp->can_mst)
+ return false;
+
+ if (intel_dp->dpcd[DP_DPCD_REV] < 0x12)
+ return false;
+
+ intel_edp_panel_vdd_on(intel_dp);
+ if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_MSTM_CAP, buf, 1)) {
+ if (buf[0] & DP_MST_CAP) {
+ DRM_DEBUG_KMS("Sink is MST capable\n");
+ intel_dp->is_mst = true;
+ } else {
+ DRM_DEBUG_KMS("Sink is not MST capable\n");
+ intel_dp->is_mst = false;
+ }
+ }
+ intel_edp_panel_vdd_off(intel_dp, false);
+
+ drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
+ return intel_dp->is_mst;
}
int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc)
@@ -3255,21 +3812,21 @@ int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc)
u8 buf[1];
if (drm_dp_dpcd_readb(&intel_dp->aux, DP_TEST_SINK_MISC, buf) < 0)
- return -EAGAIN;
+ return -EIO;
if (!(buf[0] & DP_TEST_CRC_SUPPORTED))
return -ENOTTY;
if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_SINK,
DP_TEST_SINK_START) < 0)
- return -EAGAIN;
+ return -EIO;
/* Wait 2 vblanks to be sure we will have the correct CRC value */
intel_wait_for_vblank(dev, intel_crtc->pipe);
intel_wait_for_vblank(dev, intel_crtc->pipe);
if (drm_dp_dpcd_read(&intel_dp->aux, DP_TEST_CRC_R_CR, crc, 6) < 0)
- return -EAGAIN;
+ return -EIO;
drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_SINK, 0);
return 0;
@@ -3283,6 +3840,20 @@ intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector)
sink_irq_vector, 1) == 1;
}
+static bool
+intel_dp_get_sink_irq_esi(struct intel_dp *intel_dp, u8 *sink_irq_vector)
+{
+ int ret;
+
+ ret = intel_dp_dpcd_read_wake(&intel_dp->aux,
+ DP_SINK_COUNT_ESI,
+ sink_irq_vector, 14);
+ if (ret != 14)
+ return false;
+
+ return true;
+}
+
static void
intel_dp_handle_test_request(struct intel_dp *intel_dp)
{
@@ -3290,6 +3861,63 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp)
drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_RESPONSE, DP_TEST_NAK);
}
+static int
+intel_dp_check_mst_status(struct intel_dp *intel_dp)
+{
+ bool bret;
+
+ if (intel_dp->is_mst) {
+ u8 esi[16] = { 0 };
+ int ret = 0;
+ int retry;
+ bool handled;
+ bret = intel_dp_get_sink_irq_esi(intel_dp, esi);
+go_again:
+ if (bret == true) {
+
+ /* check link status - esi[10] = 0x200c */
+ if (intel_dp->active_mst_links && !drm_dp_channel_eq_ok(&esi[10], intel_dp->lane_count)) {
+ DRM_DEBUG_KMS("channel EQ not ok, retraining\n");
+ intel_dp_start_link_train(intel_dp);
+ intel_dp_complete_link_train(intel_dp);
+ intel_dp_stop_link_train(intel_dp);
+ }
+
+ DRM_DEBUG_KMS("got esi %02x %02x %02x\n", esi[0], esi[1], esi[2]);
+ ret = drm_dp_mst_hpd_irq(&intel_dp->mst_mgr, esi, &handled);
+
+ if (handled) {
+ for (retry = 0; retry < 3; retry++) {
+ int wret;
+ wret = drm_dp_dpcd_write(&intel_dp->aux,
+ DP_SINK_COUNT_ESI+1,
+ &esi[1], 3);
+ if (wret == 3) {
+ break;
+ }
+ }
+
+ bret = intel_dp_get_sink_irq_esi(intel_dp, esi);
+ if (bret == true) {
+ DRM_DEBUG_KMS("got esi2 %02x %02x %02x\n", esi[0], esi[1], esi[2]);
+ goto go_again;
+ }
+ } else
+ ret = 0;
+
+ return ret;
+ } else {
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ DRM_DEBUG_KMS("failed to get ESI - device may have failed\n");
+ intel_dp->is_mst = false;
+ drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
+ /* send a hotplug event */
+ drm_kms_helper_hotplug_event(intel_dig_port->base.base.dev);
+ }
+ }
+ return -EINVAL;
+}
+
/*
* According to DP spec
* 5.1.2:
@@ -3298,21 +3926,25 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp)
* 3. Use Link Training from 2.5.3.3 and 3.5.1.3
* 4. Check link status on receipt of hot-plug interrupt
*/
-
void
intel_dp_check_link_status(struct intel_dp *intel_dp)
{
+ struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base;
u8 sink_irq_vector;
u8 link_status[DP_LINK_STATUS_SIZE];
- /* FIXME: This access isn't protected by any locks. */
+ WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+
if (!intel_encoder->connectors_active)
return;
if (WARN_ON(!intel_encoder->base.crtc))
return;
+ if (!to_intel_crtc(intel_encoder->base.crtc)->active)
+ return;
+
/* Try to read receiver status if the link appears to be up */
if (!intel_dp_get_link_status(intel_dp, link_status)) {
return;
@@ -3397,20 +4029,24 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp)
}
static enum drm_connector_status
+edp_detect(struct intel_dp *intel_dp)
+{
+ struct drm_device *dev = intel_dp_to_dev(intel_dp);
+ enum drm_connector_status status;
+
+ status = intel_panel_detect(dev);
+ if (status == connector_status_unknown)
+ status = connector_status_connected;
+
+ return status;
+}
+
+static enum drm_connector_status
ironlake_dp_detect(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
- enum drm_connector_status status;
-
- /* Can't disconnect eDP, but you can close the lid... */
- if (is_edp(intel_dp)) {
- status = intel_panel_detect(dev);
- if (status == connector_status_unknown)
- status = connector_status_connected;
- return status;
- }
if (!ibx_digital_port_connected(dev_priv, intel_dig_port))
return connector_status_disconnected;
@@ -3418,24 +4054,12 @@ ironlake_dp_detect(struct intel_dp *intel_dp)
return intel_dp_detect_dpcd(intel_dp);
}
-static enum drm_connector_status
-g4x_dp_detect(struct intel_dp *intel_dp)
+static int g4x_digital_port_connected(struct drm_device *dev,
+ struct intel_digital_port *intel_dig_port)
{
- struct drm_device *dev = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
uint32_t bit;
- /* Can't disconnect eDP, but you can close the lid... */
- if (is_edp(intel_dp)) {
- enum drm_connector_status status;
-
- status = intel_panel_detect(dev);
- if (status == connector_status_unknown)
- status = connector_status_connected;
- return status;
- }
-
if (IS_VALLEYVIEW(dev)) {
switch (intel_dig_port->port) {
case PORT_B:
@@ -3448,7 +4072,7 @@ g4x_dp_detect(struct intel_dp *intel_dp)
bit = PORTD_HOTPLUG_LIVE_STATUS_VLV;
break;
default:
- return connector_status_unknown;
+ return -EINVAL;
}
} else {
switch (intel_dig_port->port) {
@@ -3462,20 +4086,45 @@ g4x_dp_detect(struct intel_dp *intel_dp)
bit = PORTD_HOTPLUG_LIVE_STATUS_G4X;
break;
default:
- return connector_status_unknown;
+ return -EINVAL;
}
}
if ((I915_READ(PORT_HOTPLUG_STAT) & bit) == 0)
+ return 0;
+ return 1;
+}
+
+static enum drm_connector_status
+g4x_dp_detect(struct intel_dp *intel_dp)
+{
+ struct drm_device *dev = intel_dp_to_dev(intel_dp);
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ int ret;
+
+ /* Can't disconnect eDP, but you can close the lid... */
+ if (is_edp(intel_dp)) {
+ enum drm_connector_status status;
+
+ status = intel_panel_detect(dev);
+ if (status == connector_status_unknown)
+ status = connector_status_connected;
+ return status;
+ }
+
+ ret = g4x_digital_port_connected(dev, intel_dig_port);
+ if (ret == -EINVAL)
+ return connector_status_unknown;
+ else if (ret == 0)
return connector_status_disconnected;
return intel_dp_detect_dpcd(intel_dp);
}
static struct edid *
-intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
+intel_dp_get_edid(struct intel_dp *intel_dp)
{
- struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct intel_connector *intel_connector = intel_dp->attached_connector;
/* use cached edid if we have one */
if (intel_connector->edid) {
@@ -3484,27 +4133,55 @@ intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
return NULL;
return drm_edid_duplicate(intel_connector->edid);
- }
+ } else
+ return drm_get_edid(&intel_connector->base,
+ &intel_dp->aux.ddc);
+}
- return drm_get_edid(connector, adapter);
+static void
+intel_dp_set_edid(struct intel_dp *intel_dp)
+{
+ struct intel_connector *intel_connector = intel_dp->attached_connector;
+ struct edid *edid;
+
+ edid = intel_dp_get_edid(intel_dp);
+ intel_connector->detect_edid = edid;
+
+ if (intel_dp->force_audio != HDMI_AUDIO_AUTO)
+ intel_dp->has_audio = intel_dp->force_audio == HDMI_AUDIO_ON;
+ else
+ intel_dp->has_audio = drm_detect_monitor_audio(edid);
}
-static int
-intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *adapter)
+static void
+intel_dp_unset_edid(struct intel_dp *intel_dp)
{
- struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct intel_connector *intel_connector = intel_dp->attached_connector;
- /* use cached edid if we have one */
- if (intel_connector->edid) {
- /* invalid edid */
- if (IS_ERR(intel_connector->edid))
- return 0;
+ kfree(intel_connector->detect_edid);
+ intel_connector->detect_edid = NULL;
- return intel_connector_update_modes(connector,
- intel_connector->edid);
- }
+ intel_dp->has_audio = false;
+}
+
+static enum intel_display_power_domain
+intel_dp_power_get(struct intel_dp *dp)
+{
+ struct intel_encoder *encoder = &dp_to_dig_port(dp)->base;
+ enum intel_display_power_domain power_domain;
+
+ power_domain = intel_display_port_power_domain(encoder);
+ intel_display_power_get(to_i915(encoder->base.dev), power_domain);
- return intel_ddc_get_modes(connector, adapter);
+ return power_domain;
+}
+
+static void
+intel_dp_power_put(struct intel_dp *dp,
+ enum intel_display_power_domain power_domain)
+{
+ struct intel_encoder *encoder = &dp_to_dig_port(dp)->base;
+ intel_display_power_put(to_i915(encoder->base.dev), power_domain);
}
static enum drm_connector_status
@@ -3514,110 +4191,117 @@ intel_dp_detect(struct drm_connector *connector, bool force)
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
struct intel_encoder *intel_encoder = &intel_dig_port->base;
struct drm_device *dev = connector->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
enum drm_connector_status status;
enum intel_display_power_domain power_domain;
- struct edid *edid = NULL;
-
- intel_runtime_pm_get(dev_priv);
-
- power_domain = intel_display_port_power_domain(intel_encoder);
- intel_display_power_get(dev_priv, power_domain);
+ bool ret;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id, connector->name);
+ intel_dp_unset_edid(intel_dp);
- intel_dp->has_audio = false;
+ if (intel_dp->is_mst) {
+ /* MST devices are disconnected from a monitor POV */
+ if (intel_encoder->type != INTEL_OUTPUT_EDP)
+ intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
+ return connector_status_disconnected;
+ }
- if (HAS_PCH_SPLIT(dev))
+ power_domain = intel_dp_power_get(intel_dp);
+
+ /* Can't disconnect eDP, but you can close the lid... */
+ if (is_edp(intel_dp))
+ status = edp_detect(intel_dp);
+ else if (HAS_PCH_SPLIT(dev))
status = ironlake_dp_detect(intel_dp);
else
status = g4x_dp_detect(intel_dp);
-
if (status != connector_status_connected)
goto out;
intel_dp_probe_oui(intel_dp);
- if (intel_dp->force_audio != HDMI_AUDIO_AUTO) {
- intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON);
- } else {
- edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc);
- if (edid) {
- intel_dp->has_audio = drm_detect_monitor_audio(edid);
- kfree(edid);
- }
+ ret = intel_dp_probe_mst(intel_dp);
+ if (ret) {
+ /* if we are in MST mode then this connector
+ won't appear connected or have anything with EDID on it */
+ if (intel_encoder->type != INTEL_OUTPUT_EDP)
+ intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
+ status = connector_status_disconnected;
+ goto out;
}
+ intel_dp_set_edid(intel_dp);
+
if (intel_encoder->type != INTEL_OUTPUT_EDP)
intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
status = connector_status_connected;
out:
- intel_display_power_put(dev_priv, power_domain);
-
- intel_runtime_pm_put(dev_priv);
-
+ intel_dp_power_put(intel_dp, power_domain);
return status;
}
-static int intel_dp_get_modes(struct drm_connector *connector)
+static void
+intel_dp_force(struct drm_connector *connector)
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
- struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
- struct intel_encoder *intel_encoder = &intel_dig_port->base;
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct drm_device *dev = connector->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base;
enum intel_display_power_domain power_domain;
- int ret;
- /* We should parse the EDID data and find out if it has an audio sink
- */
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+ connector->base.id, connector->name);
+ intel_dp_unset_edid(intel_dp);
- power_domain = intel_display_port_power_domain(intel_encoder);
- intel_display_power_get(dev_priv, power_domain);
+ if (connector->status != connector_status_connected)
+ return;
- ret = intel_dp_get_edid_modes(connector, &intel_dp->aux.ddc);
- intel_display_power_put(dev_priv, power_domain);
- if (ret)
- return ret;
+ power_domain = intel_dp_power_get(intel_dp);
+
+ intel_dp_set_edid(intel_dp);
+
+ intel_dp_power_put(intel_dp, power_domain);
+
+ if (intel_encoder->type != INTEL_OUTPUT_EDP)
+ intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
+}
+
+static int intel_dp_get_modes(struct drm_connector *connector)
+{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct edid *edid;
+
+ edid = intel_connector->detect_edid;
+ if (edid) {
+ int ret = intel_connector_update_modes(connector, edid);
+ if (ret)
+ return ret;
+ }
/* if eDP has no EDID, fall back to fixed mode */
- if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) {
+ if (is_edp(intel_attached_dp(connector)) &&
+ intel_connector->panel.fixed_mode) {
struct drm_display_mode *mode;
- mode = drm_mode_duplicate(dev,
+
+ mode = drm_mode_duplicate(connector->dev,
intel_connector->panel.fixed_mode);
if (mode) {
drm_mode_probed_add(connector, mode);
return 1;
}
}
+
return 0;
}
static bool
intel_dp_detect_audio(struct drm_connector *connector)
{
- struct intel_dp *intel_dp = intel_attached_dp(connector);
- struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
- struct intel_encoder *intel_encoder = &intel_dig_port->base;
- struct drm_device *dev = connector->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- enum intel_display_power_domain power_domain;
- struct edid *edid;
bool has_audio = false;
+ struct edid *edid;
- power_domain = intel_display_port_power_domain(intel_encoder);
- intel_display_power_get(dev_priv, power_domain);
-
- edid = intel_dp_get_edid(connector, &intel_dp->aux.ddc);
- if (edid) {
+ edid = to_intel_connector(connector)->detect_edid;
+ if (edid)
has_audio = drm_detect_monitor_audio(edid);
- kfree(edid);
- }
-
- intel_display_power_put(dev_priv, power_domain);
return has_audio;
}
@@ -3715,6 +4399,8 @@ intel_dp_connector_destroy(struct drm_connector *connector)
{
struct intel_connector *intel_connector = to_intel_connector(connector);
+ kfree(intel_connector->detect_edid);
+
if (!IS_ERR_OR_NULL(intel_connector->edid))
kfree(intel_connector->edid);
@@ -3731,15 +4417,20 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
{
struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
struct intel_dp *intel_dp = &intel_dig_port->dp;
- struct drm_device *dev = intel_dp_to_dev(intel_dp);
drm_dp_aux_unregister(&intel_dp->aux);
+ intel_dp_mst_encoder_cleanup(intel_dig_port);
drm_encoder_cleanup(encoder);
if (is_edp(intel_dp)) {
cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+ /*
+ * vdd might still be enabled do to the delayed vdd off.
+ * Make sure vdd is actually turned off here.
+ */
+ pps_lock(intel_dp);
edp_panel_vdd_off_sync(intel_dp);
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
+ pps_unlock(intel_dp);
+
if (intel_dp->edp_notifier.notifier_call) {
unregister_reboot_notifier(&intel_dp->edp_notifier);
intel_dp->edp_notifier.notifier_call = NULL;
@@ -3748,9 +4439,31 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
kfree(intel_dig_port);
}
+static void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
+{
+ struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
+
+ if (!is_edp(intel_dp))
+ return;
+
+ /*
+ * vdd might still be enabled do to the delayed vdd off.
+ * Make sure vdd is actually turned off here.
+ */
+ pps_lock(intel_dp);
+ edp_panel_vdd_off_sync(intel_dp);
+ pps_unlock(intel_dp);
+}
+
+static void intel_dp_encoder_reset(struct drm_encoder *encoder)
+{
+ intel_edp_panel_vdd_sanitize(to_intel_encoder(encoder));
+}
+
static const struct drm_connector_funcs intel_dp_connector_funcs = {
.dpms = intel_connector_dpms,
.detect = intel_dp_detect,
+ .force = intel_dp_force,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_dp_set_property,
.destroy = intel_dp_connector_destroy,
@@ -3763,15 +4476,96 @@ static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs =
};
static const struct drm_encoder_funcs intel_dp_enc_funcs = {
+ .reset = intel_dp_encoder_reset,
.destroy = intel_dp_encoder_destroy,
};
-static void
+void
intel_dp_hot_plug(struct intel_encoder *intel_encoder)
{
- struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
+ return;
+}
+
+bool
+intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
+{
+ struct intel_dp *intel_dp = &intel_dig_port->dp;
+ struct intel_encoder *intel_encoder = &intel_dig_port->base;
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum intel_display_power_domain power_domain;
+ bool ret = true;
+
+ if (intel_dig_port->base.type != INTEL_OUTPUT_EDP)
+ intel_dig_port->base.type = INTEL_OUTPUT_DISPLAYPORT;
+
+ if (long_hpd && intel_dig_port->base.type == INTEL_OUTPUT_EDP) {
+ /*
+ * vdd off can generate a long pulse on eDP which
+ * would require vdd on to handle it, and thus we
+ * would end up in an endless cycle of
+ * "vdd off -> long hpd -> vdd on -> detect -> vdd off -> ..."
+ */
+ DRM_DEBUG_KMS("ignoring long hpd on eDP port %c\n",
+ port_name(intel_dig_port->port));
+ return false;
+ }
+
+ DRM_DEBUG_KMS("got hpd irq on port %c - %s\n",
+ port_name(intel_dig_port->port),
+ long_hpd ? "long" : "short");
+
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
+ if (long_hpd) {
+
+ if (HAS_PCH_SPLIT(dev)) {
+ if (!ibx_digital_port_connected(dev_priv, intel_dig_port))
+ goto mst_fail;
+ } else {
+ if (g4x_digital_port_connected(dev, intel_dig_port) != 1)
+ goto mst_fail;
+ }
+
+ if (!intel_dp_get_dpcd(intel_dp)) {
+ goto mst_fail;
+ }
- intel_dp_check_link_status(intel_dp);
+ intel_dp_probe_oui(intel_dp);
+
+ if (!intel_dp_probe_mst(intel_dp))
+ goto mst_fail;
+
+ } else {
+ if (intel_dp->is_mst) {
+ if (intel_dp_check_mst_status(intel_dp) == -EINVAL)
+ goto mst_fail;
+ }
+
+ if (!intel_dp->is_mst) {
+ /*
+ * we'll check the link status via the normal hot plug path later -
+ * but for short hpds we should check it now
+ */
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+ intel_dp_check_link_status(intel_dp);
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
+ }
+ }
+ ret = false;
+ goto put_power;
+mst_fail:
+ /* if we were in MST mode, and device is not there get out of MST mode */
+ if (intel_dp->is_mst) {
+ DRM_DEBUG_KMS("MST device may have disappeared %d vs %d\n", intel_dp->is_mst, intel_dp->mst_mgr.mst_state);
+ intel_dp->is_mst = false;
+ drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
+ }
+put_power:
+ intel_display_power_put(dev_priv, power_domain);
+
+ return ret;
}
/* Return which DP Port should be selected for Transcoder DP control */
@@ -3822,7 +4616,7 @@ bool intel_dp_is_edp(struct drm_device *dev, enum port port)
return false;
}
-static void
+void
intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector)
{
struct intel_connector *intel_connector = to_intel_connector(connector);
@@ -3858,6 +4652,8 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev,
u32 pp_on, pp_off, pp_div, pp;
int pp_ctrl_reg, pp_on_reg, pp_off_reg, pp_div_reg;
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
if (HAS_PCH_SPLIT(dev)) {
pp_ctrl_reg = PCH_PP_CONTROL;
pp_on_reg = PCH_PP_ON_DELAYS;
@@ -3957,6 +4753,9 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
u32 pp_on, pp_off, pp_div, port_sel = 0;
int div = HAS_PCH_SPLIT(dev) ? intel_pch_rawclk(dev) : intel_hrawclk(dev);
int pp_on_reg, pp_off_reg, pp_div_reg;
+ enum port port = dp_to_dig_port(intel_dp)->port;
+
+ lockdep_assert_held(&dev_priv->pps_mutex);
if (HAS_PCH_SPLIT(dev)) {
pp_on_reg = PCH_PP_ON_DELAYS;
@@ -3991,12 +4790,9 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
/* Haswell doesn't have any port selection bits for the panel
* power sequencer any more. */
if (IS_VALLEYVIEW(dev)) {
- if (dp_to_dig_port(intel_dp)->port == PORT_B)
- port_sel = PANEL_PORT_SELECT_DPB_VLV;
- else
- port_sel = PANEL_PORT_SELECT_DPC_VLV;
+ port_sel = PANEL_PORT_SELECT_VLV(port);
} else if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) {
- if (dp_to_dig_port(intel_dp)->port == PORT_A)
+ if (port == PORT_A)
port_sel = PANEL_PORT_SELECT_DPA;
else
port_sel = PANEL_PORT_SELECT_DPD;
@@ -4035,6 +4831,11 @@ void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate)
return;
}
+ /*
+ * FIXME: This needs proper synchronization with psr state. But really
+ * hard to tell without seeing the user of this function of this code.
+ * Check locking and ordering once that lands.
+ */
if (INTEL_INFO(dev)->gen < 8 && intel_edp_is_psr_enabled(dev)) {
DRM_DEBUG_KMS("DRRS is disabled as PSR is enabled\n");
return;
@@ -4075,7 +4876,7 @@ void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate)
val = I915_READ(reg);
if (index > DRRS_HIGH_RR) {
val |= PIPECONF_EDP_RR_MODE_SWITCH;
- intel_dp_set_m2_n2(intel_crtc, &config->dp_m2_n2);
+ intel_dp_set_m_n(intel_crtc);
} else {
val &= ~PIPECONF_EDP_RR_MODE_SWITCH;
}
@@ -4115,7 +4916,7 @@ intel_dp_drrs_init(struct intel_digital_port *intel_dig_port,
}
if (dev_priv->vbt.drrs_type != SEAMLESS_DRRS_SUPPORT) {
- DRM_INFO("VBT doesn't support DRRS\n");
+ DRM_DEBUG_KMS("VBT doesn't support DRRS\n");
return NULL;
}
@@ -4123,7 +4924,7 @@ intel_dp_drrs_init(struct intel_digital_port *intel_dig_port,
(dev, fixed_mode, connector);
if (!downclock_mode) {
- DRM_INFO("DRRS not supported\n");
+ DRM_DEBUG_KMS("DRRS not supported\n");
return NULL;
}
@@ -4134,10 +4935,41 @@ intel_dp_drrs_init(struct intel_digital_port *intel_dig_port,
intel_dp->drrs_state.type = dev_priv->vbt.drrs_type;
intel_dp->drrs_state.refresh_rate_type = DRRS_HIGH_RR;
- DRM_INFO("seamless DRRS supported for eDP panel.\n");
+ DRM_DEBUG_KMS("seamless DRRS supported for eDP panel.\n");
return downclock_mode;
}
+void intel_edp_panel_vdd_sanitize(struct intel_encoder *intel_encoder)
+{
+ struct drm_device *dev = intel_encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_dp *intel_dp;
+ enum intel_display_power_domain power_domain;
+
+ if (intel_encoder->type != INTEL_OUTPUT_EDP)
+ return;
+
+ intel_dp = enc_to_intel_dp(&intel_encoder->base);
+
+ pps_lock(intel_dp);
+
+ if (!edp_have_panel_vdd(intel_dp))
+ goto out;
+ /*
+ * The VDD bit needs a power domain reference, so if the bit is
+ * already enabled when we boot or resume, grab this reference and
+ * schedule a vdd off, so we don't hold on to the reference
+ * indefinitely.
+ */
+ DRM_DEBUG_KMS("VDD left on by BIOS, adjusting state tracking\n");
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
+ edp_panel_vdd_schedule_off(intel_dp);
+ out:
+ pps_unlock(intel_dp);
+}
+
static bool intel_edp_init_connector(struct intel_dp *intel_dp,
struct intel_connector *intel_connector,
struct edp_power_seq *power_seq)
@@ -4158,18 +4990,12 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
if (!is_edp(intel_dp))
return true;
- /* The VDD bit needs a power domain reference, so if the bit is already
- * enabled when we boot, grab this reference. */
- if (edp_have_panel_vdd(intel_dp)) {
- enum intel_display_power_domain power_domain;
- power_domain = intel_display_port_power_domain(intel_encoder);
- intel_display_power_get(dev_priv, power_domain);
- }
+ intel_edp_panel_vdd_sanitize(intel_encoder);
/* Cache DPCD and EDID for edp. */
intel_edp_panel_vdd_on(intel_dp);
has_dpcd = intel_dp_get_dpcd(intel_dp);
- edp_panel_vdd_off(intel_dp, false);
+ intel_edp_panel_vdd_off(intel_dp, false);
if (has_dpcd) {
if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
@@ -4183,7 +5009,9 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
}
/* We now know it's not a ghost, init power sequence regs. */
+ pps_lock(intel_dp);
intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, power_seq);
+ pps_unlock(intel_dp);
mutex_lock(&dev->mode_config.mutex);
edid = drm_get_edid(connector, &intel_dp->aux.ddc);
@@ -4227,6 +5055,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
}
intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
+ intel_connector->panel.backlight_power = intel_edp_backlight_power;
intel_panel_setup_backlight(connector);
return true;
@@ -4245,6 +5074,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
struct edp_power_seq power_seq = { 0 };
int type;
+ intel_dp->pps_pipe = INVALID_PIPE;
+
/* intel_dp vfuncs */
if (IS_VALLEYVIEW(dev))
intel_dp->get_aux_clock_divider = vlv_get_aux_clock_divider;
@@ -4288,7 +5119,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
edp_panel_vdd_work);
intel_connector_attach_encoder(intel_connector, intel_encoder);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
if (HAS_DDI(dev))
intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
@@ -4315,23 +5146,40 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
}
if (is_edp(intel_dp)) {
- intel_dp_init_panel_power_timestamps(intel_dp);
- intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
+ pps_lock(intel_dp);
+ if (IS_VALLEYVIEW(dev)) {
+ vlv_initial_power_sequencer_setup(intel_dp);
+ } else {
+ intel_dp_init_panel_power_timestamps(intel_dp);
+ intel_dp_init_panel_power_sequencer(dev, intel_dp,
+ &power_seq);
+ }
+ pps_unlock(intel_dp);
}
intel_dp_aux_init(intel_dp, intel_connector);
- intel_dp->psr_setup_done = false;
+ /* init MST on ports that can support it */
+ if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+ if (port == PORT_B || port == PORT_C || port == PORT_D) {
+ intel_dp_mst_encoder_init(intel_dig_port,
+ intel_connector->base.base.id);
+ }
+ }
if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
drm_dp_aux_unregister(&intel_dp->aux);
if (is_edp(intel_dp)) {
cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+ /*
+ * vdd might still be enabled do to the delayed vdd off.
+ * Make sure vdd is actually turned off here.
+ */
+ pps_lock(intel_dp);
edp_panel_vdd_off_sync(intel_dp);
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
+ pps_unlock(intel_dp);
}
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
return false;
}
@@ -4353,6 +5201,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
void
intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
{
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_digital_port *intel_dig_port;
struct intel_encoder *intel_encoder;
struct drm_encoder *encoder;
@@ -4378,7 +5227,9 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
intel_encoder->disable = intel_disable_dp;
intel_encoder->get_hw_state = intel_dp_get_hw_state;
intel_encoder->get_config = intel_dp_get_config;
+ intel_encoder->suspend = intel_dp_encoder_suspend;
if (IS_CHERRYVIEW(dev)) {
+ intel_encoder->pre_pll_enable = chv_dp_pre_pll_enable;
intel_encoder->pre_enable = chv_pre_enable_dp;
intel_encoder->enable = vlv_enable_dp;
intel_encoder->post_disable = chv_post_disable_dp;
@@ -4390,7 +5241,8 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
} else {
intel_encoder->pre_enable = g4x_pre_enable_dp;
intel_encoder->enable = g4x_enable_dp;
- intel_encoder->post_disable = g4x_post_disable_dp;
+ if (INTEL_INFO(dev)->gen >= 5)
+ intel_encoder->post_disable = ilk_post_disable_dp;
}
intel_dig_port->port = port;
@@ -4408,9 +5260,55 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
intel_encoder->cloneable = 0;
intel_encoder->hot_plug = intel_dp_hot_plug;
+ intel_dig_port->hpd_pulse = intel_dp_hpd_pulse;
+ dev_priv->hpd_irq_port[port] = intel_dig_port;
+
if (!intel_dp_init_connector(intel_dig_port, intel_connector)) {
drm_encoder_cleanup(encoder);
kfree(intel_dig_port);
kfree(intel_connector);
}
}
+
+void intel_dp_mst_suspend(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int i;
+
+ /* disable MST */
+ for (i = 0; i < I915_MAX_PORTS; i++) {
+ struct intel_digital_port *intel_dig_port = dev_priv->hpd_irq_port[i];
+ if (!intel_dig_port)
+ continue;
+
+ if (intel_dig_port->base.type == INTEL_OUTPUT_DISPLAYPORT) {
+ if (!intel_dig_port->dp.can_mst)
+ continue;
+ if (intel_dig_port->dp.is_mst)
+ drm_dp_mst_topology_mgr_suspend(&intel_dig_port->dp.mst_mgr);
+ }
+ }
+}
+
+void intel_dp_mst_resume(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int i;
+
+ for (i = 0; i < I915_MAX_PORTS; i++) {
+ struct intel_digital_port *intel_dig_port = dev_priv->hpd_irq_port[i];
+ if (!intel_dig_port)
+ continue;
+ if (intel_dig_port->base.type == INTEL_OUTPUT_DISPLAYPORT) {
+ int ret;
+
+ if (!intel_dig_port->dp.can_mst)
+ continue;
+
+ ret = drm_dp_mst_topology_mgr_resume(&intel_dig_port->dp.mst_mgr);
+ if (ret != 0) {
+ intel_dp_check_mst_status(&intel_dig_port->dp);
+ }
+ }
+ }
+}
diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c
new file mode 100644
index 000000000000..d9a7a7865f66
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dp_mst.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright © 2008 Intel Corporation
+ * 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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 <drm/drmP.h>
+#include "i915_drv.h"
+#include "intel_drv.h"
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+
+static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
+ struct intel_crtc_config *pipe_config)
+{
+ struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+ struct intel_digital_port *intel_dig_port = intel_mst->primary;
+ struct intel_dp *intel_dp = &intel_dig_port->dp;
+ struct drm_device *dev = encoder->base.dev;
+ int bpp;
+ int lane_count, slots;
+ struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
+ struct intel_connector *found = NULL, *intel_connector;
+ int mst_pbn;
+
+ pipe_config->dp_encoder_is_mst = true;
+ pipe_config->has_pch_encoder = false;
+ pipe_config->has_dp_encoder = true;
+ bpp = 24;
+ /*
+ * for MST we always configure max link bw - the spec doesn't
+ * seem to suggest we should do otherwise.
+ */
+ lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
+ intel_dp->link_bw = intel_dp_max_link_bw(intel_dp);
+ intel_dp->lane_count = lane_count;
+
+ pipe_config->pipe_bpp = 24;
+ pipe_config->port_clock = drm_dp_bw_code_to_link_rate(intel_dp->link_bw);
+
+ list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) {
+ if (intel_connector->new_encoder == encoder) {
+ found = intel_connector;
+ break;
+ }
+ }
+
+ if (!found) {
+ DRM_ERROR("can't find connector\n");
+ return false;
+ }
+
+ mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->clock, bpp);
+
+ pipe_config->pbn = mst_pbn;
+ slots = drm_dp_find_vcpi_slots(&intel_dp->mst_mgr, mst_pbn);
+
+ intel_link_compute_m_n(bpp, lane_count,
+ adjusted_mode->crtc_clock,
+ pipe_config->port_clock,
+ &pipe_config->dp_m_n);
+
+ pipe_config->dp_m_n.tu = slots;
+ return true;
+
+}
+
+static void intel_mst_disable_dp(struct intel_encoder *encoder)
+{
+ struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+ struct intel_digital_port *intel_dig_port = intel_mst->primary;
+ struct intel_dp *intel_dp = &intel_dig_port->dp;
+ int ret;
+
+ DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+
+ drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, intel_mst->port);
+
+ ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
+ if (ret) {
+ DRM_ERROR("failed to update payload %d\n", ret);
+ }
+}
+
+static void intel_mst_post_disable_dp(struct intel_encoder *encoder)
+{
+ struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+ struct intel_digital_port *intel_dig_port = intel_mst->primary;
+ struct intel_dp *intel_dp = &intel_dig_port->dp;
+
+ DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+
+ /* this can fail */
+ drm_dp_check_act_status(&intel_dp->mst_mgr);
+ /* and this can also fail */
+ drm_dp_update_payload_part2(&intel_dp->mst_mgr);
+
+ drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, intel_mst->port);
+
+ intel_dp->active_mst_links--;
+ intel_mst->port = NULL;
+ if (intel_dp->active_mst_links == 0) {
+ intel_dig_port->base.post_disable(&intel_dig_port->base);
+ intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
+ }
+}
+
+static void intel_mst_pre_enable_dp(struct intel_encoder *encoder)
+{
+ struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+ struct intel_digital_port *intel_dig_port = intel_mst->primary;
+ struct intel_dp *intel_dp = &intel_dig_port->dp;
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum port port = intel_dig_port->port;
+ int ret;
+ uint32_t temp;
+ struct intel_connector *found = NULL, *intel_connector;
+ int slots;
+ struct drm_crtc *crtc = encoder->base.crtc;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+ list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) {
+ if (intel_connector->new_encoder == encoder) {
+ found = intel_connector;
+ break;
+ }
+ }
+
+ if (!found) {
+ DRM_ERROR("can't find connector\n");
+ return;
+ }
+
+ DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+ intel_mst->port = found->port;
+
+ if (intel_dp->active_mst_links == 0) {
+ enum port port = intel_ddi_get_encoder_port(encoder);
+
+ I915_WRITE(PORT_CLK_SEL(port), intel_crtc->config.ddi_pll_sel);
+
+ intel_ddi_init_dp_buf_reg(&intel_dig_port->base);
+
+ intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+
+
+ intel_dp_start_link_train(intel_dp);
+ intel_dp_complete_link_train(intel_dp);
+ intel_dp_stop_link_train(intel_dp);
+ }
+
+ ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr,
+ intel_mst->port, intel_crtc->config.pbn, &slots);
+ if (ret == false) {
+ DRM_ERROR("failed to allocate vcpi\n");
+ return;
+ }
+
+
+ intel_dp->active_mst_links++;
+ temp = I915_READ(DP_TP_STATUS(port));
+ I915_WRITE(DP_TP_STATUS(port), temp);
+
+ ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
+}
+
+static void intel_mst_enable_dp(struct intel_encoder *encoder)
+{
+ struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+ struct intel_digital_port *intel_dig_port = intel_mst->primary;
+ struct intel_dp *intel_dp = &intel_dig_port->dp;
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum port port = intel_dig_port->port;
+ int ret;
+
+ DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+
+ if (wait_for((I915_READ(DP_TP_STATUS(port)) & DP_TP_STATUS_ACT_SENT),
+ 1))
+ DRM_ERROR("Timed out waiting for ACT sent\n");
+
+ ret = drm_dp_check_act_status(&intel_dp->mst_mgr);
+
+ ret = drm_dp_update_payload_part2(&intel_dp->mst_mgr);
+}
+
+static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
+ enum pipe *pipe)
+{
+ struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+ *pipe = intel_mst->pipe;
+ if (intel_mst->port)
+ return true;
+ return false;
+}
+
+static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder,
+ struct intel_crtc_config *pipe_config)
+{
+ struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+ struct intel_digital_port *intel_dig_port = intel_mst->primary;
+ struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum transcoder cpu_transcoder = crtc->config.cpu_transcoder;
+ u32 temp, flags = 0;
+
+ pipe_config->has_dp_encoder = true;
+
+ temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
+ if (temp & TRANS_DDI_PHSYNC)
+ flags |= DRM_MODE_FLAG_PHSYNC;
+ else
+ flags |= DRM_MODE_FLAG_NHSYNC;
+ if (temp & TRANS_DDI_PVSYNC)
+ flags |= DRM_MODE_FLAG_PVSYNC;
+ else
+ flags |= DRM_MODE_FLAG_NVSYNC;
+
+ switch (temp & TRANS_DDI_BPC_MASK) {
+ case TRANS_DDI_BPC_6:
+ pipe_config->pipe_bpp = 18;
+ break;
+ case TRANS_DDI_BPC_8:
+ pipe_config->pipe_bpp = 24;
+ break;
+ case TRANS_DDI_BPC_10:
+ pipe_config->pipe_bpp = 30;
+ break;
+ case TRANS_DDI_BPC_12:
+ pipe_config->pipe_bpp = 36;
+ break;
+ default:
+ break;
+ }
+ pipe_config->adjusted_mode.flags |= flags;
+ intel_dp_get_m_n(crtc, pipe_config);
+
+ intel_ddi_clock_get(&intel_dig_port->base, pipe_config);
+}
+
+static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
+{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct intel_dp *intel_dp = intel_connector->mst_port;
+ struct edid *edid;
+ int ret;
+
+ edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port);
+ if (!edid)
+ return 0;
+
+ ret = intel_connector_update_modes(connector, edid);
+ kfree(edid);
+
+ return ret;
+}
+
+static enum drm_connector_status
+intel_mst_port_dp_detect(struct drm_connector *connector)
+{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct intel_dp *intel_dp = intel_connector->mst_port;
+
+ return drm_dp_mst_detect_port(&intel_dp->mst_mgr, intel_connector->port);
+}
+
+static enum drm_connector_status
+intel_dp_mst_detect(struct drm_connector *connector, bool force)
+{
+ enum drm_connector_status status;
+ status = intel_mst_port_dp_detect(connector);
+ return status;
+}
+
+static int
+intel_dp_mst_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t val)
+{
+ return 0;
+}
+
+static void
+intel_dp_mst_connector_destroy(struct drm_connector *connector)
+{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+
+ if (!IS_ERR_OR_NULL(intel_connector->edid))
+ kfree(intel_connector->edid);
+
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
+ .dpms = intel_connector_dpms,
+ .detect = intel_dp_mst_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = intel_dp_mst_set_property,
+ .destroy = intel_dp_mst_connector_destroy,
+};
+
+static int intel_dp_mst_get_modes(struct drm_connector *connector)
+{
+ return intel_dp_mst_get_ddc_modes(connector);
+}
+
+static enum drm_mode_status
+intel_dp_mst_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ /* TODO - validate mode against available PBN for link */
+ if (mode->clock < 10000)
+ return MODE_CLOCK_LOW;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ return MODE_H_ILLEGAL;
+
+ return MODE_OK;
+}
+
+static struct drm_encoder *intel_mst_best_encoder(struct drm_connector *connector)
+{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct intel_dp *intel_dp = intel_connector->mst_port;
+ return &intel_dp->mst_encoders[0]->base.base;
+}
+
+static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
+ .get_modes = intel_dp_mst_get_modes,
+ .mode_valid = intel_dp_mst_mode_valid,
+ .best_encoder = intel_mst_best_encoder,
+};
+
+static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
+
+ drm_encoder_cleanup(encoder);
+ kfree(intel_mst);
+}
+
+static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = {
+ .destroy = intel_dp_mst_encoder_destroy,
+};
+
+static bool intel_dp_mst_get_hw_state(struct intel_connector *connector)
+{
+ if (connector->encoder) {
+ enum pipe pipe;
+ if (!connector->encoder->get_hw_state(connector->encoder, &pipe))
+ return false;
+ return true;
+ }
+ return false;
+}
+
+static void intel_connector_add_to_fbdev(struct intel_connector *connector)
+{
+#ifdef CONFIG_DRM_I915_FBDEV
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper, &connector->base);
+#endif
+}
+
+static void intel_connector_remove_from_fbdev(struct intel_connector *connector)
+{
+#ifdef CONFIG_DRM_I915_FBDEV
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper, &connector->base);
+#endif
+}
+
+static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, char *pathprop)
+{
+ struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ int i;
+
+ intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
+ if (!intel_connector)
+ return NULL;
+
+ connector = &intel_connector->base;
+ drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort);
+ drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
+
+ intel_connector->unregister = intel_connector_unregister;
+ intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
+ intel_connector->mst_port = intel_dp;
+ intel_connector->port = port;
+
+ for (i = PIPE_A; i <= PIPE_C; i++) {
+ drm_mode_connector_attach_encoder(&intel_connector->base,
+ &intel_dp->mst_encoders[i]->base.base);
+ }
+ intel_dp_add_properties(intel_dp, connector);
+
+ drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0);
+ drm_mode_connector_set_path_property(connector, pathprop);
+ drm_reinit_primary_mode_group(dev);
+ mutex_lock(&dev->mode_config.mutex);
+ intel_connector_add_to_fbdev(intel_connector);
+ mutex_unlock(&dev->mode_config.mutex);
+ drm_connector_register(&intel_connector->base);
+ return connector;
+}
+
+static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_connector *connector)
+{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct drm_device *dev = connector->dev;
+ /* need to nuke the connector */
+ mutex_lock(&dev->mode_config.mutex);
+ intel_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ mutex_unlock(&dev->mode_config.mutex);
+
+ intel_connector->unregister(intel_connector);
+
+ mutex_lock(&dev->mode_config.mutex);
+ intel_connector_remove_from_fbdev(intel_connector);
+ drm_connector_cleanup(connector);
+ mutex_unlock(&dev->mode_config.mutex);
+
+ drm_reinit_primary_mode_group(dev);
+
+ kfree(intel_connector);
+ DRM_DEBUG_KMS("\n");
+}
+
+static void intel_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
+{
+ struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+
+ drm_kms_helper_hotplug_event(dev);
+}
+
+static struct drm_dp_mst_topology_cbs mst_cbs = {
+ .add_connector = intel_dp_add_mst_connector,
+ .destroy_connector = intel_dp_destroy_mst_connector,
+ .hotplug = intel_dp_mst_hotplug,
+};
+
+static struct intel_dp_mst_encoder *
+intel_dp_create_fake_mst_encoder(struct intel_digital_port *intel_dig_port, enum pipe pipe)
+{
+ struct intel_dp_mst_encoder *intel_mst;
+ struct intel_encoder *intel_encoder;
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+
+ intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL);
+
+ if (!intel_mst)
+ return NULL;
+
+ intel_mst->pipe = pipe;
+ intel_encoder = &intel_mst->base;
+ intel_mst->primary = intel_dig_port;
+
+ drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs,
+ DRM_MODE_ENCODER_DPMST);
+
+ intel_encoder->type = INTEL_OUTPUT_DP_MST;
+ intel_encoder->crtc_mask = 0x7;
+ intel_encoder->cloneable = 0;
+
+ intel_encoder->compute_config = intel_dp_mst_compute_config;
+ intel_encoder->disable = intel_mst_disable_dp;
+ intel_encoder->post_disable = intel_mst_post_disable_dp;
+ intel_encoder->pre_enable = intel_mst_pre_enable_dp;
+ intel_encoder->enable = intel_mst_enable_dp;
+ intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state;
+ intel_encoder->get_config = intel_dp_mst_enc_get_config;
+
+ return intel_mst;
+
+}
+
+static bool
+intel_dp_create_fake_mst_encoders(struct intel_digital_port *intel_dig_port)
+{
+ int i;
+ struct intel_dp *intel_dp = &intel_dig_port->dp;
+
+ for (i = PIPE_A; i <= PIPE_C; i++)
+ intel_dp->mst_encoders[i] = intel_dp_create_fake_mst_encoder(intel_dig_port, i);
+ return true;
+}
+
+int
+intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_base_id)
+{
+ struct intel_dp *intel_dp = &intel_dig_port->dp;
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ int ret;
+
+ intel_dp->can_mst = true;
+ intel_dp->mst_mgr.cbs = &mst_cbs;
+
+ /* create encoders */
+ intel_dp_create_fake_mst_encoders(intel_dig_port);
+ ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev->dev, &intel_dp->aux, 16, 3, conn_base_id);
+ if (ret) {
+ intel_dp->can_mst = false;
+ return ret;
+ }
+ return 0;
+}
+
+void
+intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port)
+{
+ struct intel_dp *intel_dp = &intel_dig_port->dp;
+
+ if (!intel_dp->can_mst)
+ return;
+
+ drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
+ /* encoders will get killed by normal cleanup */
+}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index f67340ed2c12..ba715229a540 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -25,6 +25,7 @@
#ifndef __INTEL_DRV_H__
#define __INTEL_DRV_H__
+#include <linux/async.h>
#include <linux/i2c.h>
#include <linux/hdmi.h>
#include <drm/i915_drm.h>
@@ -32,7 +33,10 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/drm_dp_mst_helper.h>
+
+#define DIV_ROUND_CLOSEST_ULL(ll, d) \
+({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; })
/**
* _wait_for - magic (register) wait macro
@@ -100,6 +104,7 @@
#define INTEL_OUTPUT_EDP 8
#define INTEL_OUTPUT_DSI 9
#define INTEL_OUTPUT_UNKNOWN 10
+#define INTEL_OUTPUT_DP_MST 11
#define INTEL_DVO_CHIP_NONE 0
#define INTEL_DVO_CHIP_LVDS 1
@@ -152,6 +157,12 @@ struct intel_encoder {
* be set correctly before calling this function. */
void (*get_config)(struct intel_encoder *,
struct intel_crtc_config *pipe_config);
+ /*
+ * Called during system suspend after all pending requests for the
+ * encoder are flushed (for example for DP AUX transactions) and
+ * device interrupts are disabled.
+ */
+ void (*suspend)(struct intel_encoder *);
int crtc_mask;
enum hpd_pin hpd_pin;
};
@@ -165,12 +176,15 @@ struct intel_panel {
struct {
bool present;
u32 level;
+ u32 min;
u32 max;
bool enabled;
bool combination_mode; /* gen 2/4 only */
bool active_low_pwm;
struct backlight_device *device;
} backlight;
+
+ void (*backlight_power)(struct intel_connector *, bool enable);
};
struct intel_connector {
@@ -203,10 +217,15 @@ struct intel_connector {
/* Cached EDID for eDP and LVDS. May hold ERR_PTR for invalid EDID. */
struct edid *edid;
+ struct edid *detect_edid;
/* since POLL and HPD connectors may use the same HPD line keep the native
state of connector->polled in case hotplug storm detection changes it */
u8 polled;
+
+ void *port; /* store this opaque as its illegal to dereference it */
+
+ struct intel_dp *mst_port;
};
typedef struct dpll {
@@ -307,6 +326,9 @@ struct intel_crtc_config {
/* Selected dpll when shared or DPLL_ID_PRIVATE. */
enum intel_dpll_id shared_dpll;
+ /* PORT_CLK_SEL for DDI ports. */
+ uint32_t ddi_pll_sel;
+
/* Actual register state of the dpll, for shared dpll cross-checking. */
struct intel_dpll_hw_state dpll_hw_state;
@@ -315,6 +337,7 @@ struct intel_crtc_config {
/* m2_n2 for eDP downclock */
struct intel_link_m_n dp_m2_n2;
+ bool has_drrs;
/*
* Frequence the dpll for the port should run at. Differs from the
@@ -338,6 +361,7 @@ struct intel_crtc_config {
u32 pos;
u32 size;
bool enabled;
+ bool force_thru;
} pch_pfit;
/* FDI configuration, only valid if has_pch_encoder is set. */
@@ -347,6 +371,9 @@ struct intel_crtc_config {
bool ips_enabled;
bool double_wide;
+
+ bool dp_encoder_is_mst;
+ int pbn;
};
struct intel_pipe_wm {
@@ -358,6 +385,11 @@ struct intel_pipe_wm {
bool sprites_scaled;
};
+struct intel_mmio_flip {
+ u32 seqno;
+ u32 ring_id;
+};
+
struct intel_crtc {
struct drm_crtc base;
enum pipe pipe;
@@ -384,9 +416,9 @@ struct intel_crtc {
struct drm_i915_gem_object *cursor_bo;
uint32_t cursor_addr;
- int16_t cursor_x, cursor_y;
int16_t cursor_width, cursor_height;
uint32_t cursor_cntl;
+ uint32_t cursor_size;
uint32_t cursor_base;
struct intel_plane_config plane_config;
@@ -394,8 +426,6 @@ struct intel_crtc {
struct intel_crtc_config *new_config;
bool new_enabled;
- uint32_t ddi_pll_sel;
-
/* reset counter value when the last flip was submitted */
unsigned int reset_counter;
@@ -409,13 +439,13 @@ struct intel_crtc {
struct intel_pipe_wm active;
} wm;
- wait_queue_head_t vbl_wait;
-
int scanline_offset;
+ struct intel_mmio_flip mmio_flip;
};
struct intel_plane_wm_parameters {
uint32_t horiz_pixels;
+ uint32_t vert_pixels;
uint8_t bytes_per_pixel;
bool enabled;
bool scaled;
@@ -428,11 +458,11 @@ struct intel_plane {
struct drm_i915_gem_object *obj;
bool can_scale;
int max_downscale;
- u32 lut_r[1024], lut_g[1024], lut_b[1024];
int crtc_x, crtc_y;
unsigned int crtc_w, crtc_h;
uint32_t src_x, src_y;
uint32_t src_w, src_h;
+ unsigned int rotation;
/* Since we need to change the watermarks before/after
* enabling/disabling the planes, we need to store the parameters here
@@ -481,6 +511,7 @@ struct cxsr_latency {
#define to_intel_encoder(x) container_of(x, struct intel_encoder, base)
#define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base)
#define to_intel_plane(x) container_of(x, struct intel_plane, base)
+#define intel_fb_obj(x) (x ? to_intel_framebuffer(x)->obj : NULL)
struct intel_hdmi {
u32 hdmi_reg;
@@ -491,6 +522,7 @@ struct intel_hdmi {
bool has_audio;
enum hdmi_force_audio force_audio;
bool rgb_quant_range_selectable;
+ enum hdmi_picture_aspect aspect_ratio;
void (*write_infoframe)(struct drm_encoder *encoder,
enum hdmi_infoframe_type type,
const void *frame, ssize_t len);
@@ -499,6 +531,7 @@ struct intel_hdmi {
struct drm_display_mode *adjusted_mode);
};
+struct intel_dp_mst_encoder;
#define DP_MAX_DOWNSTREAM_PORTS 0x10
/**
@@ -537,12 +570,26 @@ struct intel_dp {
unsigned long last_power_cycle;
unsigned long last_power_on;
unsigned long last_backlight_off;
- bool psr_setup_done;
+
struct notifier_block edp_notifier;
+ /*
+ * Pipe whose power sequencer is currently locked into
+ * this port. Only relevant on VLV/CHV.
+ */
+ enum pipe pps_pipe;
+
bool use_tps3;
+ bool can_mst; /* this port supports mst */
+ bool is_mst;
+ int active_mst_links;
+ /* connector directly attached - won't be use for modeset in mst world */
struct intel_connector *attached_connector;
+ /* mst connector list */
+ struct intel_dp_mst_encoder *mst_encoders[I915_MAX_PIPES];
+ struct drm_dp_mst_topology_mgr mst_mgr;
+
uint32_t (*get_aux_clock_divider)(struct intel_dp *dp, int index);
/*
* This function returns the value we have to program the AUX_CTL
@@ -566,6 +613,14 @@ struct intel_digital_port {
u32 saved_port_bits;
struct intel_dp dp;
struct intel_hdmi hdmi;
+ bool (*hpd_pulse)(struct intel_digital_port *, bool);
+};
+
+struct intel_dp_mst_encoder {
+ struct intel_encoder base;
+ enum pipe pipe;
+ struct intel_digital_port *primary;
+ void *port; /* store this opaque as its illegal to dereference it */
};
static inline int
@@ -622,6 +677,10 @@ struct intel_unpin_work {
#define INTEL_FLIP_COMPLETE 2
u32 flip_count;
u32 gtt_offset;
+ struct intel_engine_cs *flip_queued_ring;
+ u32 flip_queued_seqno;
+ int flip_queued_vblank;
+ int flip_ready_vblank;
bool enable_stall_check;
};
@@ -652,6 +711,12 @@ enc_to_dig_port(struct drm_encoder *encoder)
return container_of(encoder, struct intel_digital_port, base.base);
}
+static inline struct intel_dp_mst_encoder *
+enc_to_mst(struct drm_encoder *encoder)
+{
+ return container_of(encoder, struct intel_dp_mst_encoder, base.base);
+}
+
static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
{
return &enc_to_dig_port(encoder)->dp;
@@ -676,17 +741,26 @@ bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
enum transcoder pch_transcoder,
bool enable);
-void ilk_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
-void ilk_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
-void snb_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
-void snb_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
-void bdw_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
-void bdw_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void gen5_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void gen5_disable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void gen6_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void gen6_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void gen8_enable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
+void gen8_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask);
void intel_runtime_pm_disable_interrupts(struct drm_device *dev);
void intel_runtime_pm_restore_interrupts(struct drm_device *dev);
+static inline bool intel_irqs_enabled(struct drm_i915_private *dev_priv)
+{
+ /*
+ * We only use drm_irq_uninstall() at unload and VT switch, so
+ * this is the only thing we need to check.
+ */
+ return !dev_priv->pm._irqs_disabled;
+}
+
int intel_get_crtc_scanline(struct intel_crtc *crtc);
void i9xx_check_fifo_underruns(struct drm_device *dev);
-
+void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv);
/* intel_crt.c */
void intel_crt_init(struct drm_device *dev);
@@ -705,10 +779,7 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
enum transcoder cpu_transcoder);
void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc);
void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc);
-void intel_ddi_setup_hw_pll_state(struct drm_device *dev);
bool intel_ddi_pll_select(struct intel_crtc *crtc);
-void intel_ddi_pll_enable(struct intel_crtc *crtc);
-void intel_ddi_put_crtc_pll(struct drm_crtc *crtc);
void intel_ddi_set_pipe_settings(struct drm_crtc *crtc);
void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder);
bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector);
@@ -716,17 +787,46 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc);
void intel_ddi_get_config(struct intel_encoder *encoder,
struct intel_crtc_config *pipe_config);
+void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder);
+void intel_ddi_clock_get(struct intel_encoder *encoder,
+ struct intel_crtc_config *pipe_config);
+void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state);
/* intel_display.c */
const char *intel_output_name(int output);
bool intel_has_pending_fb_unpin(struct drm_device *dev);
int intel_pch_rawclk(struct drm_device *dev);
-int valleyview_cur_cdclk(struct drm_i915_private *dev_priv);
void intel_mark_busy(struct drm_device *dev);
-void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
- struct intel_engine_cs *ring);
+void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
+ struct intel_engine_cs *ring);
+void intel_frontbuffer_flip_prepare(struct drm_device *dev,
+ unsigned frontbuffer_bits);
+void intel_frontbuffer_flip_complete(struct drm_device *dev,
+ unsigned frontbuffer_bits);
+void intel_frontbuffer_flush(struct drm_device *dev,
+ unsigned frontbuffer_bits);
+/**
+ * intel_frontbuffer_flip - prepare frontbuffer flip
+ * @dev: DRM device
+ * @frontbuffer_bits: frontbuffer plane tracking bits
+ *
+ * This function gets called after scheduling a flip on @obj. This is for
+ * synchronous plane updates which will happen on the next vblank and which will
+ * not get delayed by pending gpu rendering.
+ *
+ * Can be called without any locks held.
+ */
+static inline
+void intel_frontbuffer_flip(struct drm_device *dev,
+ unsigned frontbuffer_bits)
+{
+ intel_frontbuffer_flush(dev, frontbuffer_bits);
+}
+
+void intel_fb_obj_flush(struct drm_i915_gem_object *obj, bool retire);
void intel_mark_idle(struct drm_device *dev);
void intel_crtc_restore_mode(struct drm_crtc *crtc);
+void intel_crtc_control(struct drm_crtc *crtc, bool enable);
void intel_crtc_update_dpms(struct drm_crtc *crtc);
void intel_encoder_destroy(struct drm_encoder *encoder);
void intel_connector_dpms(struct drm_connector *, int mode);
@@ -745,7 +845,6 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv,
enum pipe pipe);
void intel_wait_for_vblank(struct drm_device *dev, int pipe);
-void intel_wait_for_pipe_off(struct drm_device *dev, int pipe);
int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp);
void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
struct intel_digital_port *dport);
@@ -754,8 +853,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
struct intel_load_detect_pipe *old,
struct drm_modeset_acquire_ctx *ctx);
void intel_release_load_detect_pipe(struct drm_connector *connector,
- struct intel_load_detect_pipe *old,
- struct drm_modeset_acquire_ctx *ctx);
+ struct intel_load_detect_pipe *old);
int intel_pin_and_fence_fb_obj(struct drm_device *dev,
struct drm_i915_gem_object *obj,
struct intel_engine_cs *pipelined);
@@ -767,12 +865,19 @@ __intel_framebuffer_create(struct drm_device *dev,
void intel_prepare_page_flip(struct drm_device *dev, int plane);
void intel_finish_page_flip(struct drm_device *dev, int pipe);
void intel_finish_page_flip_plane(struct drm_device *dev, int plane);
+void intel_check_page_flip(struct drm_device *dev, int pipe);
+
+/* shared dpll functions */
struct intel_shared_dpll *intel_crtc_to_shared_dpll(struct intel_crtc *crtc);
void assert_shared_dpll(struct drm_i915_private *dev_priv,
struct intel_shared_dpll *pll,
bool state);
#define assert_shared_dpll_enabled(d, p) assert_shared_dpll(d, p, true)
#define assert_shared_dpll_disabled(d, p) assert_shared_dpll(d, p, false)
+struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc);
+void intel_put_shared_dpll(struct intel_crtc *crtc);
+
+/* modesetting asserts */
void assert_pll(struct drm_i915_private *dev_priv,
enum pipe pipe, bool state);
#define assert_pll_enabled(d, p) assert_pll(d, p, true)
@@ -795,6 +900,7 @@ void hsw_enable_pc8(struct drm_i915_private *dev_priv);
void hsw_disable_pc8(struct drm_i915_private *dev_priv);
void intel_dp_get_m_n(struct intel_crtc *crtc,
struct intel_crtc_config *pipe_config);
+void intel_dp_set_m_n(struct intel_crtc *crtc);
int intel_dotclock_calculate(int link_freq, const struct intel_link_m_n *m_n);
void
ironlake_check_encoder_dotclock(const struct intel_crtc_config *pipe_config,
@@ -805,12 +911,11 @@ void hsw_disable_ips(struct intel_crtc *crtc);
void intel_display_set_init_power(struct drm_i915_private *dev, bool enable);
enum intel_display_power_domain
intel_display_port_power_domain(struct intel_encoder *intel_encoder);
-int valleyview_get_vco(struct drm_i915_private *dev_priv);
void intel_mode_from_pipe_config(struct drm_display_mode *mode,
struct intel_crtc_config *pipe_config);
int intel_format_to_fourcc(int format);
void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc);
-
+void intel_modeset_preclose(struct drm_device *dev, struct drm_file *file);
/* intel_dp.c */
void intel_dp_init(struct drm_device *dev, int output_reg, enum port port);
@@ -826,18 +931,35 @@ int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc);
bool intel_dp_compute_config(struct intel_encoder *encoder,
struct intel_crtc_config *pipe_config);
bool intel_dp_is_edp(struct drm_device *dev, enum port port);
+bool intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port,
+ bool long_hpd);
void intel_edp_backlight_on(struct intel_dp *intel_dp);
void intel_edp_backlight_off(struct intel_dp *intel_dp);
void intel_edp_panel_vdd_on(struct intel_dp *intel_dp);
+void intel_edp_panel_vdd_sanitize(struct intel_encoder *intel_encoder);
void intel_edp_panel_on(struct intel_dp *intel_dp);
void intel_edp_panel_off(struct intel_dp *intel_dp);
void intel_edp_psr_enable(struct intel_dp *intel_dp);
void intel_edp_psr_disable(struct intel_dp *intel_dp);
-void intel_edp_psr_update(struct drm_device *dev);
void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate);
-
+void intel_edp_psr_invalidate(struct drm_device *dev,
+ unsigned frontbuffer_bits);
+void intel_edp_psr_flush(struct drm_device *dev,
+ unsigned frontbuffer_bits);
+void intel_edp_psr_init(struct drm_device *dev);
+
+int intel_dp_handle_hpd_irq(struct intel_digital_port *digport, bool long_hpd);
+void intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector);
+void intel_dp_mst_suspend(struct drm_device *dev);
+void intel_dp_mst_resume(struct drm_device *dev);
+int intel_dp_max_link_bw(struct intel_dp *intel_dp);
+void intel_dp_hot_plug(struct intel_encoder *intel_encoder);
+void vlv_power_sequencer_reset(struct drm_i915_private *dev_priv);
+/* intel_dp_mst.c */
+int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
+void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port);
/* intel_dsi.c */
-bool intel_dsi_init(struct drm_device *dev);
+void intel_dsi_init(struct drm_device *dev);
/* intel_dvo.c */
@@ -847,9 +969,9 @@ void intel_dvo_init(struct drm_device *dev);
/* legacy fbdev emulation in intel_fbdev.c */
#ifdef CONFIG_DRM_I915_FBDEV
extern int intel_fbdev_init(struct drm_device *dev);
-extern void intel_fbdev_initial_config(struct drm_device *dev);
+extern void intel_fbdev_initial_config(void *data, async_cookie_t cookie);
extern void intel_fbdev_fini(struct drm_device *dev);
-extern void intel_fbdev_set_suspend(struct drm_device *dev, int state);
+extern void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous);
extern void intel_fbdev_output_poll_changed(struct drm_device *dev);
extern void intel_fbdev_restore_mode(struct drm_device *dev);
#else
@@ -858,7 +980,7 @@ static inline int intel_fbdev_init(struct drm_device *dev)
return 0;
}
-static inline void intel_fbdev_initial_config(struct drm_device *dev)
+static inline void intel_fbdev_initial_config(void *data, async_cookie_t cookie)
{
}
@@ -866,7 +988,7 @@ static inline void intel_fbdev_fini(struct drm_device *dev)
{
}
-static inline void intel_fbdev_set_suspend(struct drm_device *dev, int state)
+static inline void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous)
{
}
@@ -920,8 +1042,8 @@ void intel_pch_panel_fitting(struct intel_crtc *crtc,
void intel_gmch_panel_fitting(struct intel_crtc *crtc,
struct intel_crtc_config *pipe_config,
int fitting_mode);
-void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
- u32 max);
+void intel_panel_set_backlight_acpi(struct intel_connector *connector,
+ u32 level, u32 max);
int intel_panel_setup_backlight(struct drm_connector *connector);
void intel_panel_enable_backlight(struct intel_connector *connector);
void intel_panel_disable_backlight(struct intel_connector *connector);
@@ -940,7 +1062,9 @@ int ilk_wm_max_level(const struct drm_device *dev);
void intel_update_watermarks(struct drm_crtc *crtc);
void intel_update_sprite_watermarks(struct drm_plane *plane,
struct drm_crtc *crtc,
- uint32_t sprite_width, int pixel_size,
+ uint32_t sprite_width,
+ uint32_t sprite_height,
+ int pixel_size,
bool enabled, bool scaled);
void intel_init_pm(struct drm_device *dev);
void intel_pm_setup(struct drm_device *dev);
@@ -963,6 +1087,7 @@ void intel_init_gt_powersave(struct drm_device *dev);
void intel_cleanup_gt_powersave(struct drm_device *dev);
void intel_enable_gt_powersave(struct drm_device *dev);
void intel_disable_gt_powersave(struct drm_device *dev);
+void intel_suspend_gt_powersave(struct drm_device *dev);
void intel_reset_gt_powersave(struct drm_device *dev);
void ironlake_teardown_rc6(struct drm_device *dev);
void gen6_update_ring_freq(struct drm_device *dev);
@@ -976,8 +1101,7 @@ void intel_runtime_pm_put(struct drm_i915_private *dev_priv);
void intel_init_runtime_pm(struct drm_i915_private *dev_priv);
void intel_fini_runtime_pm(struct drm_i915_private *dev_priv);
void ilk_wm_get_hw_state(struct drm_device *dev);
-void __vlv_set_power_well(struct drm_i915_private *dev_priv,
- enum punit_power_well power_well_id, bool enable);
+
/* intel_sdvo.c */
bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob);
@@ -987,7 +1111,10 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob);
int intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane);
void intel_flush_primary_plane(struct drm_i915_private *dev_priv,
enum plane plane);
-void intel_plane_restore(struct drm_plane *plane);
+int intel_plane_set_property(struct drm_plane *plane,
+ struct drm_property *prop,
+ uint64_t val);
+int intel_plane_restore(struct drm_plane *plane);
void intel_plane_disable(struct drm_plane *plane);
int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
struct drm_file *file_priv);
diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
index 3fd082933c87..5bd9e09ad3c5 100644
--- a/drivers/gpu/drm/i915/intel_dsi.c
+++ b/drivers/gpu/drm/i915/intel_dsi.c
@@ -92,6 +92,9 @@ static bool intel_dsi_compute_config(struct intel_encoder *encoder,
if (fixed_mode)
intel_fixed_panel_mode(fixed_mode, adjusted_mode);
+ /* DSI uses short packets for sync events, so clear mode flags for DSI */
+ adjusted_mode->flags = 0;
+
if (intel_dsi->dev.dev_ops->mode_fixup)
return intel_dsi->dev.dev_ops->mode_fixup(&intel_dsi->dev,
mode, adjusted_mode);
@@ -152,6 +155,8 @@ static void intel_dsi_enable(struct intel_encoder *encoder)
if (intel_dsi->dev.dev_ops->enable)
intel_dsi->dev.dev_ops->enable(&intel_dsi->dev);
+ wait_for_dsi_fifo_empty(intel_dsi);
+
/* assert ip_tg_enable signal */
temp = I915_READ(MIPI_PORT_CTRL(pipe)) & ~LANE_CONFIGURATION_MASK;
temp = temp | intel_dsi->port_bits;
@@ -177,6 +182,10 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder)
tmp |= DPLL_REFA_CLK_ENABLE_VLV;
I915_WRITE(DPLL(pipe), tmp);
+ /* update the hw state for DPLL */
+ intel_crtc->config.dpll_hw_state.dpll = DPLL_INTEGRATED_CLOCK_VLV |
+ DPLL_REFA_CLK_ENABLE_VLV;
+
tmp = I915_READ(DSPCLK_GATE_D);
tmp |= DPOUNIT_CLOCK_GATE_DISABLE;
I915_WRITE(DSPCLK_GATE_D, tmp);
@@ -192,6 +201,8 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder)
if (intel_dsi->dev.dev_ops->send_otp_cmds)
intel_dsi->dev.dev_ops->send_otp_cmds(&intel_dsi->dev);
+ wait_for_dsi_fifo_empty(intel_dsi);
+
/* Enable port in pre-enable phase itself because as per hw team
* recommendation, port should be enabled befor plane & pipe */
intel_dsi_enable(encoder);
@@ -232,6 +243,8 @@ static void intel_dsi_disable(struct intel_encoder *encoder)
DRM_DEBUG_KMS("\n");
if (is_vid_mode(intel_dsi)) {
+ wait_for_dsi_fifo_empty(intel_dsi);
+
/* de-assert ip_tg_enable signal */
temp = I915_READ(MIPI_PORT_CTRL(pipe));
I915_WRITE(MIPI_PORT_CTRL(pipe), temp & ~DPI_ENABLE);
@@ -246,8 +259,8 @@ static void intel_dsi_disable(struct intel_encoder *encoder)
temp = I915_READ(MIPI_CTRL(pipe));
temp &= ~ESCAPE_CLOCK_DIVIDER_MASK;
I915_WRITE(MIPI_CTRL(pipe), temp |
- intel_dsi->escape_clk_div <<
- ESCAPE_CLOCK_DIVIDER_SHIFT);
+ intel_dsi->escape_clk_div <<
+ ESCAPE_CLOCK_DIVIDER_SHIFT);
I915_WRITE(MIPI_EOT_DISABLE(pipe), CLOCKSTOP);
@@ -261,6 +274,8 @@ static void intel_dsi_disable(struct intel_encoder *encoder)
* some next enable sequence send turn on packet error is observed */
if (intel_dsi->dev.dev_ops->disable)
intel_dsi->dev.dev_ops->disable(&intel_dsi->dev);
+
+ wait_for_dsi_fifo_empty(intel_dsi);
}
static void intel_dsi_clear_device_ready(struct intel_encoder *encoder)
@@ -282,7 +297,7 @@ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder)
usleep_range(2000, 2500);
if (wait_for(((I915_READ(MIPI_PORT_CTRL(pipe)) & AFE_LATCHOUT)
- == 0x00000), 30))
+ == 0x00000), 30))
DRM_ERROR("DSI LP not going Low\n");
val = I915_READ(MIPI_PORT_CTRL(pipe));
@@ -351,9 +366,21 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
static void intel_dsi_get_config(struct intel_encoder *encoder,
struct intel_crtc_config *pipe_config)
{
+ u32 pclk;
DRM_DEBUG_KMS("\n");
- /* XXX: read flags, set to adjusted_mode */
+ /*
+ * DPLL_MD is not used in case of DSI, reading will get some default value
+ * set dpll_md = 0
+ */
+ pipe_config->dpll_hw_state.dpll_md = 0;
+
+ pclk = vlv_get_dsi_pclk(encoder, pipe_config->pipe_bpp);
+ if (!pclk)
+ return;
+
+ pipe_config->adjusted_mode.crtc_clock = pclk;
+ pipe_config->port_clock = pclk;
}
static enum drm_mode_status
@@ -396,9 +423,11 @@ static u16 txclkesc(u32 divider, unsigned int us)
}
/* return pixels in terms of txbyteclkhs */
-static u16 txbyteclkhs(u16 pixels, int bpp, int lane_count)
+static u16 txbyteclkhs(u16 pixels, int bpp, int lane_count,
+ u16 burst_mode_ratio)
{
- return DIV_ROUND_UP(DIV_ROUND_UP(pixels * bpp, 8), lane_count);
+ return DIV_ROUND_UP(DIV_ROUND_UP(pixels * bpp * burst_mode_ratio,
+ 8 * 100), lane_count);
}
static void set_dsi_timings(struct drm_encoder *encoder,
@@ -424,10 +453,12 @@ static void set_dsi_timings(struct drm_encoder *encoder,
vbp = mode->vtotal - mode->vsync_end;
/* horizontal values are in terms of high speed byte clock */
- hactive = txbyteclkhs(hactive, bpp, lane_count);
- hfp = txbyteclkhs(hfp, bpp, lane_count);
- hsync = txbyteclkhs(hsync, bpp, lane_count);
- hbp = txbyteclkhs(hbp, bpp, lane_count);
+ hactive = txbyteclkhs(hactive, bpp, lane_count,
+ intel_dsi->burst_mode_ratio);
+ hfp = txbyteclkhs(hfp, bpp, lane_count, intel_dsi->burst_mode_ratio);
+ hsync = txbyteclkhs(hsync, bpp, lane_count,
+ intel_dsi->burst_mode_ratio);
+ hbp = txbyteclkhs(hbp, bpp, lane_count, intel_dsi->burst_mode_ratio);
I915_WRITE(MIPI_HACTIVE_AREA_COUNT(pipe), hactive);
I915_WRITE(MIPI_HFP_COUNT(pipe), hfp);
@@ -514,12 +545,14 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder)
intel_dsi->video_mode_format == VIDEO_MODE_BURST) {
I915_WRITE(MIPI_HS_TX_TIMEOUT(pipe),
txbyteclkhs(adjusted_mode->htotal, bpp,
- intel_dsi->lane_count) + 1);
+ intel_dsi->lane_count,
+ intel_dsi->burst_mode_ratio) + 1);
} else {
I915_WRITE(MIPI_HS_TX_TIMEOUT(pipe),
txbyteclkhs(adjusted_mode->vtotal *
adjusted_mode->htotal,
- bpp, intel_dsi->lane_count) + 1);
+ bpp, intel_dsi->lane_count,
+ intel_dsi->burst_mode_ratio) + 1);
}
I915_WRITE(MIPI_LP_RX_TIMEOUT(pipe), intel_dsi->lp_rx_timeout);
I915_WRITE(MIPI_TURN_AROUND_TIMEOUT(pipe), intel_dsi->turn_arnd_val);
@@ -549,7 +582,7 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder)
* XXX: write MIPI_STOP_STATE_STALL?
*/
I915_WRITE(MIPI_HIGH_LOW_SWITCH_COUNT(pipe),
- intel_dsi->hs_to_lp_count);
+ intel_dsi->hs_to_lp_count);
/* XXX: low power clock equivalence in terms of byte clock. the number
* of byte clocks occupied in one low power clock. based on txbyteclkhs
@@ -574,10 +607,10 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder)
* 64 like 1366 x 768. Enable RANDOM resolution support for such
* panels by default */
I915_WRITE(MIPI_VIDEO_MODE_FORMAT(pipe),
- intel_dsi->video_frmt_cfg_bits |
- intel_dsi->video_mode_format |
- IP_TG_CONFIG |
- RANDOM_DPI_DISPLAY_RESOLUTION);
+ intel_dsi->video_frmt_cfg_bits |
+ intel_dsi->video_mode_format |
+ IP_TG_CONFIG |
+ RANDOM_DPI_DISPLAY_RESOLUTION);
}
static void intel_dsi_pre_pll_enable(struct intel_encoder *encoder)
@@ -658,7 +691,7 @@ static const struct drm_connector_funcs intel_dsi_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
};
-bool intel_dsi_init(struct drm_device *dev)
+void intel_dsi_init(struct drm_device *dev)
{
struct intel_dsi *intel_dsi;
struct intel_encoder *intel_encoder;
@@ -674,29 +707,29 @@ bool intel_dsi_init(struct drm_device *dev)
/* There is no detection method for MIPI so rely on VBT */
if (!dev_priv->vbt.has_mipi)
- return false;
+ return;
+
+ if (IS_VALLEYVIEW(dev)) {
+ dev_priv->mipi_mmio_base = VLV_MIPI_BASE;
+ } else {
+ DRM_ERROR("Unsupported Mipi device to reg base");
+ return;
+ }
intel_dsi = kzalloc(sizeof(*intel_dsi), GFP_KERNEL);
if (!intel_dsi)
- return false;
+ return;
intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
if (!intel_connector) {
kfree(intel_dsi);
- return false;
+ return;
}
intel_encoder = &intel_dsi->base;
encoder = &intel_encoder->base;
intel_dsi->attached_connector = intel_connector;
- if (IS_VALLEYVIEW(dev)) {
- dev_priv->mipi_mmio_base = VLV_MIPI_BASE;
- } else {
- DRM_ERROR("Unsupported Mipi device to reg base");
- return false;
- }
-
connector = &intel_connector->base;
drm_encoder_init(dev, encoder, &intel_dsi_funcs, DRM_MODE_ENCODER_DSI);
@@ -743,7 +776,7 @@ bool intel_dsi_init(struct drm_device *dev)
intel_connector_attach_encoder(intel_connector, intel_encoder);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
fixed_mode = dsi->dev_ops->get_modes(&intel_dsi->dev);
if (!fixed_mode) {
@@ -754,12 +787,10 @@ bool intel_dsi_init(struct drm_device *dev)
fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
intel_panel_init(&intel_connector->panel, fixed_mode, NULL);
- return true;
+ return;
err:
drm_encoder_cleanup(&intel_encoder->base);
kfree(intel_dsi);
kfree(intel_connector);
-
- return false;
}
diff --git a/drivers/gpu/drm/i915/intel_dsi.h b/drivers/gpu/drm/i915/intel_dsi.h
index 31db33d3e5cc..657eb5c1b9d8 100644
--- a/drivers/gpu/drm/i915/intel_dsi.h
+++ b/drivers/gpu/drm/i915/intel_dsi.h
@@ -116,6 +116,8 @@ struct intel_dsi {
u16 clk_hs_to_lp_count;
u16 init_count;
+ u32 pclk;
+ u16 burst_mode_ratio;
/* all delays in ms */
u16 backlight_off_delay;
@@ -132,6 +134,7 @@ static inline struct intel_dsi *enc_to_intel_dsi(struct drm_encoder *encoder)
extern void vlv_enable_dsi_pll(struct intel_encoder *encoder);
extern void vlv_disable_dsi_pll(struct intel_encoder *encoder);
+extern u32 vlv_get_dsi_pclk(struct intel_encoder *encoder, int pipe_bpp);
extern struct intel_dsi_dev_ops vbt_generic_dsi_display_ops;
diff --git a/drivers/gpu/drm/i915/intel_dsi_cmd.c b/drivers/gpu/drm/i915/intel_dsi_cmd.c
index 933c86305237..f4767fd2ebeb 100644
--- a/drivers/gpu/drm/i915/intel_dsi_cmd.c
+++ b/drivers/gpu/drm/i915/intel_dsi_cmd.c
@@ -419,3 +419,19 @@ int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd, bool hs)
return 0;
}
+
+void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi)
+{
+ struct drm_encoder *encoder = &intel_dsi->base.base;
+ struct drm_device *dev = encoder->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+ enum pipe pipe = intel_crtc->pipe;
+ u32 mask;
+
+ mask = LP_CTRL_FIFO_EMPTY | HS_CTRL_FIFO_EMPTY |
+ LP_DATA_FIFO_EMPTY | HS_DATA_FIFO_EMPTY;
+
+ if (wait_for((I915_READ(MIPI_GEN_FIFO_STAT(pipe)) & mask) == mask, 100))
+ DRM_ERROR("DPI FIFOs are not empty\n");
+}
diff --git a/drivers/gpu/drm/i915/intel_dsi_cmd.h b/drivers/gpu/drm/i915/intel_dsi_cmd.h
index 9a18cbfa5460..46aa1acc00eb 100644
--- a/drivers/gpu/drm/i915/intel_dsi_cmd.h
+++ b/drivers/gpu/drm/i915/intel_dsi_cmd.h
@@ -51,6 +51,7 @@ int dsi_vc_generic_read(struct intel_dsi *intel_dsi, int channel,
u8 *reqdata, int reqlen, u8 *buf, int buflen);
int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd, bool hs);
+void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi);
/* XXX: questionable write helpers */
static inline int dsi_vc_dcs_write_0(struct intel_dsi *intel_dsi,
diff --git a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
index 21a0d348cedc..f6bdd44069ce 100644
--- a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
+++ b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
@@ -143,7 +143,7 @@ static u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi, u8 *data)
case MIPI_DSI_DCS_LONG_WRITE:
dsi_vc_dcs_write(intel_dsi, vc, data, len);
break;
- };
+ }
data += len;
@@ -271,6 +271,8 @@ static bool generic_init(struct intel_dsi_device *dsi)
u32 ths_prepare_ns, tclk_trail_ns;
u32 tclk_prepare_clkzero, ths_prepare_hszero;
u32 lp_to_hs_switch, hs_to_lp_switch;
+ u32 pclk, computed_ddr;
+ u16 burst_mode_ratio;
DRM_DEBUG_KMS("\n");
@@ -284,8 +286,6 @@ static bool generic_init(struct intel_dsi_device *dsi)
else if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB565)
bits_per_pixel = 16;
- bitrate = (mode->clock * bits_per_pixel) / intel_dsi->lane_count;
-
intel_dsi->operation_mode = mipi_config->is_cmd_mode;
intel_dsi->video_mode_format = mipi_config->video_transfer_mode;
intel_dsi->escape_clk_div = mipi_config->byte_clk_sel;
@@ -294,7 +294,42 @@ static bool generic_init(struct intel_dsi_device *dsi)
intel_dsi->rst_timer_val = mipi_config->device_reset_timer;
intel_dsi->init_count = mipi_config->master_init_timer;
intel_dsi->bw_timer = mipi_config->dbi_bw_timer;
- intel_dsi->video_frmt_cfg_bits = mipi_config->bta_enabled ? DISABLE_VIDEO_BTA : 0;
+ intel_dsi->video_frmt_cfg_bits =
+ mipi_config->bta_enabled ? DISABLE_VIDEO_BTA : 0;
+
+ pclk = mode->clock;
+
+ /* Burst Mode Ratio
+ * Target ddr frequency from VBT / non burst ddr freq
+ * multiply by 100 to preserve remainder
+ */
+ if (intel_dsi->video_mode_format == VIDEO_MODE_BURST) {
+ if (mipi_config->target_burst_mode_freq) {
+ computed_ddr =
+ (pclk * bits_per_pixel) / intel_dsi->lane_count;
+
+ if (mipi_config->target_burst_mode_freq <
+ computed_ddr) {
+ DRM_ERROR("Burst mode freq is less than computed\n");
+ return false;
+ }
+
+ burst_mode_ratio = DIV_ROUND_UP(
+ mipi_config->target_burst_mode_freq * 100,
+ computed_ddr);
+
+ pclk = DIV_ROUND_UP(pclk * burst_mode_ratio, 100);
+ } else {
+ DRM_ERROR("Burst mode target is not set\n");
+ return false;
+ }
+ } else
+ burst_mode_ratio = 100;
+
+ intel_dsi->burst_mode_ratio = burst_mode_ratio;
+ intel_dsi->pclk = pclk;
+
+ bitrate = (pclk * bits_per_pixel) / intel_dsi->lane_count;
switch (intel_dsi->escape_clk_div) {
case 0:
@@ -351,7 +386,8 @@ static bool generic_init(struct intel_dsi_device *dsi)
*
* prepare count
*/
- ths_prepare_ns = max(mipi_config->ths_prepare, mipi_config->tclk_prepare);
+ ths_prepare_ns = max(mipi_config->ths_prepare,
+ mipi_config->tclk_prepare);
prepare_cnt = DIV_ROUND_UP(ths_prepare_ns * ui_den, ui_num * 2);
/* exit zero count */
diff --git a/drivers/gpu/drm/i915/intel_dsi_pll.c b/drivers/gpu/drm/i915/intel_dsi_pll.c
index ba79ec19da3b..fa7a6ca34cd6 100644
--- a/drivers/gpu/drm/i915/intel_dsi_pll.c
+++ b/drivers/gpu/drm/i915/intel_dsi_pll.c
@@ -134,8 +134,7 @@ static u32 dsi_rr_formula(const struct drm_display_mode *mode,
#else
/* Get DSI clock from pixel clock */
-static u32 dsi_clk_from_pclk(const struct drm_display_mode *mode,
- int pixel_format, int lane_count)
+static u32 dsi_clk_from_pclk(u32 pclk, int pixel_format, int lane_count)
{
u32 dsi_clk_khz;
u32 bpp;
@@ -156,7 +155,7 @@ static u32 dsi_clk_from_pclk(const struct drm_display_mode *mode,
/* DSI data rate = pixel clock * bits per pixel / lane count
pixel clock is converted from KHz to Hz */
- dsi_clk_khz = DIV_ROUND_CLOSEST(mode->clock * bpp, lane_count);
+ dsi_clk_khz = DIV_ROUND_CLOSEST(pclk * bpp, lane_count);
return dsi_clk_khz;
}
@@ -191,7 +190,7 @@ static int dsi_calc_mnp(u32 dsi_clk, struct dsi_mnp *dsi_mnp)
for (m = 62; m <= 92; m++) {
for (p = 2; p <= 6; p++) {
/* Find the optimal m and p divisors
- with minimal error +/- the required clock */
+ with minimal error +/- the required clock */
calc_dsi_clk = (m * ref_clk) / p;
if (calc_dsi_clk == target_dsi_clk) {
calc_m = m;
@@ -228,15 +227,13 @@ static int dsi_calc_mnp(u32 dsi_clk, struct dsi_mnp *dsi_mnp)
static void vlv_configure_dsi_pll(struct intel_encoder *encoder)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
- struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
- const struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode;
struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
int ret;
struct dsi_mnp dsi_mnp;
u32 dsi_clk;
- dsi_clk = dsi_clk_from_pclk(mode, intel_dsi->pixel_format,
- intel_dsi->lane_count);
+ dsi_clk = dsi_clk_from_pclk(intel_dsi->pclk, intel_dsi->pixel_format,
+ intel_dsi->lane_count);
ret = dsi_calc_mnp(dsi_clk, &dsi_mnp);
if (ret) {
@@ -298,3 +295,84 @@ void vlv_disable_dsi_pll(struct intel_encoder *encoder)
mutex_unlock(&dev_priv->dpio_lock);
}
+
+static void assert_bpp_mismatch(int pixel_format, int pipe_bpp)
+{
+ int bpp;
+
+ switch (pixel_format) {
+ default:
+ case VID_MODE_FORMAT_RGB888:
+ case VID_MODE_FORMAT_RGB666_LOOSE:
+ bpp = 24;
+ break;
+ case VID_MODE_FORMAT_RGB666:
+ bpp = 18;
+ break;
+ case VID_MODE_FORMAT_RGB565:
+ bpp = 16;
+ break;
+ }
+
+ WARN(bpp != pipe_bpp,
+ "bpp match assertion failure (expected %d, current %d)\n",
+ bpp, pipe_bpp);
+}
+
+u32 vlv_get_dsi_pclk(struct intel_encoder *encoder, int pipe_bpp)
+{
+ struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+ struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+ u32 dsi_clock, pclk;
+ u32 pll_ctl, pll_div;
+ u32 m = 0, p = 0;
+ int refclk = 25000;
+ int i;
+
+ DRM_DEBUG_KMS("\n");
+
+ mutex_lock(&dev_priv->dpio_lock);
+ pll_ctl = vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_CONTROL);
+ pll_div = vlv_cck_read(dev_priv, CCK_REG_DSI_PLL_DIVIDER);
+ mutex_unlock(&dev_priv->dpio_lock);
+
+ /* mask out other bits and extract the P1 divisor */
+ pll_ctl &= DSI_PLL_P1_POST_DIV_MASK;
+ pll_ctl = pll_ctl >> (DSI_PLL_P1_POST_DIV_SHIFT - 2);
+
+ /* mask out the other bits and extract the M1 divisor */
+ pll_div &= DSI_PLL_M1_DIV_MASK;
+ pll_div = pll_div >> DSI_PLL_M1_DIV_SHIFT;
+
+ while (pll_ctl) {
+ pll_ctl = pll_ctl >> 1;
+ p++;
+ }
+ p--;
+
+ if (!p) {
+ DRM_ERROR("wrong P1 divisor\n");
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lfsr_converts); i++) {
+ if (lfsr_converts[i] == pll_div)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(lfsr_converts)) {
+ DRM_ERROR("wrong m_seed programmed\n");
+ return 0;
+ }
+
+ m = i + 62;
+
+ dsi_clock = (m * refclk) / p;
+
+ /* pixel_format and pipe_bpp should agree */
+ assert_bpp_mismatch(intel_dsi->pixel_format, pipe_bpp);
+
+ pclk = DIV_ROUND_CLOSEST(dsi_clock * intel_dsi->lane_count, pipe_bpp);
+
+ return pclk;
+}
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
index a3631c0a5c28..e40e3df33517 100644
--- a/drivers/gpu/drm/i915/intel_dvo.c
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -85,7 +85,7 @@ static const struct intel_dvo_device intel_dvo_devices[] = {
{
.type = INTEL_DVO_CHIP_TMDS,
.name = "ns2501",
- .dvo_reg = DVOC,
+ .dvo_reg = DVOB,
.slave_addr = NS2501_ADDR,
.dev_ops = &ns2501_ops,
}
@@ -112,7 +112,15 @@ static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector)
static bool intel_dvo_connector_get_hw_state(struct intel_connector *connector)
{
+ struct drm_device *dev = connector->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_dvo *intel_dvo = intel_attached_dvo(&connector->base);
+ u32 tmp;
+
+ tmp = I915_READ(intel_dvo->dev.dvo_reg);
+
+ if (!(tmp & DVO_ENABLE))
+ return false;
return intel_dvo->dev.dev_ops->get_hw_state(&intel_dvo->dev);
}
@@ -177,12 +185,13 @@ static void intel_enable_dvo(struct intel_encoder *encoder)
u32 dvo_reg = intel_dvo->dev.dvo_reg;
u32 temp = I915_READ(dvo_reg);
- I915_WRITE(dvo_reg, temp | DVO_ENABLE);
- I915_READ(dvo_reg);
intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev,
&crtc->config.requested_mode,
&crtc->config.adjusted_mode);
+ I915_WRITE(dvo_reg, temp | DVO_ENABLE);
+ I915_READ(dvo_reg);
+
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
}
@@ -218,10 +227,6 @@ static void intel_dvo_dpms(struct drm_connector *connector, int mode)
intel_crtc_update_dpms(crtc);
- intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev,
- &config->requested_mode,
- &config->adjusted_mode);
-
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
} else {
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false);
@@ -558,7 +563,7 @@ void intel_dvo_init(struct drm_device *dev)
intel_dvo->panel_wants_dither = true;
}
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return;
}
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index 088fe9378a4c..9b584f3fbb99 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -24,8 +24,10 @@
* David Airlie
*/
+#include <linux/async.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/console.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
@@ -43,10 +45,36 @@
#include <drm/i915_drm.h>
#include "i915_drv.h"
+static int intel_fbdev_set_par(struct fb_info *info)
+{
+ struct drm_fb_helper *fb_helper = info->par;
+ struct intel_fbdev *ifbdev =
+ container_of(fb_helper, struct intel_fbdev, helper);
+ int ret;
+
+ ret = drm_fb_helper_set_par(info);
+
+ if (ret == 0) {
+ /*
+ * FIXME: fbdev presumes that all callbacks also work from
+ * atomic contexts and relies on that for emergency oops
+ * printing. KMS totally doesn't do that and the locking here is
+ * by far not the only place this goes wrong. Ignore this for
+ * now until we solve this for real.
+ */
+ mutex_lock(&fb_helper->dev->struct_mutex);
+ ret = i915_gem_object_set_to_gtt_domain(ifbdev->fb->obj,
+ true);
+ mutex_unlock(&fb_helper->dev->struct_mutex);
+ }
+
+ return ret;
+}
+
static struct fb_ops intelfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = drm_fb_helper_check_var,
- .fb_set_par = drm_fb_helper_set_par,
+ .fb_set_par = intel_fbdev_set_par,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
@@ -81,7 +109,7 @@ static int intelfb_alloc(struct drm_fb_helper *helper,
sizes->surface_depth);
size = mode_cmd.pitches[0] * mode_cmd.height;
- size = ALIGN(size, PAGE_SIZE);
+ size = PAGE_ALIGN(size);
obj = i915_gem_object_create_stolen(dev, size);
if (obj == NULL)
obj = i915_gem_alloc_object(dev, size);
@@ -305,24 +333,6 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
int num_connectors_enabled = 0;
int num_connectors_detected = 0;
- /*
- * If the user specified any force options, just bail here
- * and use that config.
- */
- for (i = 0; i < fb_helper->connector_count; i++) {
- struct drm_fb_helper_connector *fb_conn;
- struct drm_connector *connector;
-
- fb_conn = fb_helper->connector_info[i];
- connector = fb_conn->connector;
-
- if (!enabled[i])
- continue;
-
- if (connector->force != DRM_FORCE_UNSPECIFIED)
- return false;
- }
-
save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool),
GFP_KERNEL);
if (!save_enabled)
@@ -348,8 +358,18 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
continue;
}
+ if (connector->force == DRM_FORCE_OFF) {
+ DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n",
+ connector->name);
+ enabled[i] = false;
+ continue;
+ }
+
encoder = connector->encoder;
if (!encoder || WARN_ON(!encoder->crtc)) {
+ if (connector->force > DRM_FORCE_OFF)
+ goto bail;
+
DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n",
connector->name);
enabled[i] = false;
@@ -368,8 +388,7 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
for (j = 0; j < fb_helper->connector_count; j++) {
if (crtcs[j] == new_crtc) {
DRM_DEBUG_KMS("fallback: cloned configuration\n");
- fallback = true;
- goto out;
+ goto bail;
}
}
@@ -417,7 +436,7 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
}
crtcs[i] = new_crtc;
- DRM_DEBUG_KMS("connector %s on pipe %d [CRTC:%d]: %dx%d%s\n",
+ DRM_DEBUG_KMS("connector %s on pipe %c [CRTC:%d]: %dx%d%s\n",
connector->name,
pipe_name(to_intel_crtc(encoder->crtc)->pipe),
encoder->crtc->base.id,
@@ -440,8 +459,8 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
fallback = true;
}
-out:
if (fallback) {
+bail:
DRM_DEBUG_KMS("Not using firmware configuration\n");
memcpy(enabled, save_enabled, dev->mode_config.num_connector);
kfree(save_enabled);
@@ -452,7 +471,7 @@ out:
return true;
}
-static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
.initial_config = intel_fb_initial_config,
.gamma_set = intel_crtc_fb_gamma_set,
.gamma_get = intel_crtc_fb_gamma_get,
@@ -610,6 +629,15 @@ out:
return false;
}
+static void intel_fbdev_suspend_worker(struct work_struct *work)
+{
+ intel_fbdev_set_suspend(container_of(work,
+ struct drm_i915_private,
+ fbdev_suspend_work)->dev,
+ FBINFO_STATE_RUNNING,
+ true);
+}
+
int intel_fbdev_init(struct drm_device *dev)
{
struct intel_fbdev *ifbdev;
@@ -623,7 +651,8 @@ int intel_fbdev_init(struct drm_device *dev)
if (ifbdev == NULL)
return -ENOMEM;
- ifbdev->helper.funcs = &intel_fb_helper_funcs;
+ drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs);
+
if (!intel_fbdev_init_bios(dev, ifbdev))
ifbdev->preferred_bpp = 32;
@@ -635,14 +664,16 @@ int intel_fbdev_init(struct drm_device *dev)
}
dev_priv->fbdev = ifbdev;
+ INIT_WORK(&dev_priv->fbdev_suspend_work, intel_fbdev_suspend_worker);
+
drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
return 0;
}
-void intel_fbdev_initial_config(struct drm_device *dev)
+void intel_fbdev_initial_config(void *data, async_cookie_t cookie)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_private *dev_priv = data;
struct intel_fbdev *ifbdev = dev_priv->fbdev;
/* Due to peculiar init order wrt to hpd handling this is separate. */
@@ -655,12 +686,15 @@ void intel_fbdev_fini(struct drm_device *dev)
if (!dev_priv->fbdev)
return;
+ flush_work(&dev_priv->fbdev_suspend_work);
+
+ async_synchronize_full();
intel_fbdev_destroy(dev, dev_priv->fbdev);
kfree(dev_priv->fbdev);
dev_priv->fbdev = NULL;
}
-void intel_fbdev_set_suspend(struct drm_device *dev, int state)
+void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_fbdev *ifbdev = dev_priv->fbdev;
@@ -671,6 +705,33 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state)
info = ifbdev->helper.fbdev;
+ if (synchronous) {
+ /* Flush any pending work to turn the console on, and then
+ * wait to turn it off. It must be synchronous as we are
+ * about to suspend or unload the driver.
+ *
+ * Note that from within the work-handler, we cannot flush
+ * ourselves, so only flush outstanding work upon suspend!
+ */
+ if (state != FBINFO_STATE_RUNNING)
+ flush_work(&dev_priv->fbdev_suspend_work);
+ console_lock();
+ } else {
+ /*
+ * The console lock can be pretty contented on resume due
+ * to all the printk activity. Try to keep it out of the hot
+ * path of resume if possible.
+ */
+ WARN_ON(state != FBINFO_STATE_RUNNING);
+ if (!console_trylock()) {
+ /* Don't block our own workqueue as this can
+ * be run in parallel with other i915.ko tasks.
+ */
+ schedule_work(&dev_priv->fbdev_suspend_work);
+ return;
+ }
+ }
+
/* On resume from hibernation: If the object is shmemfs backed, it has
* been restored from swap. If the object is stolen however, it will be
* full of whatever garbage was left in there.
@@ -679,6 +740,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state)
memset_io(info->screen_base, 0, info->screen_size);
fb_set_suspend(info, state);
+ console_unlock();
}
void intel_fbdev_output_poll_changed(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index eee2bbec2958..29ec1535992d 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -367,6 +367,9 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
union hdmi_infoframe frame;
int ret;
+ /* Set user selected PAR to incoming mode's member */
+ adjusted_mode->picture_aspect_ratio = intel_hdmi->aspect_ratio;
+
ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
adjusted_mode);
if (ret < 0) {
@@ -709,7 +712,8 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder,
struct intel_crtc_config *pipe_config)
{
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
- struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
u32 tmp, flags = 0;
int dotclock;
@@ -728,9 +732,13 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder,
if (tmp & HDMI_MODE_SELECT_HDMI)
pipe_config->has_hdmi_sink = true;
- if (tmp & HDMI_MODE_SELECT_HDMI)
+ if (tmp & SDVO_AUDIO_ENABLE)
pipe_config->has_audio = true;
+ if (!HAS_PCH_SPLIT(dev) &&
+ tmp & HDMI_COLOR_RANGE_16_235)
+ pipe_config->limited_color_range = true;
+
pipe_config->adjusted_mode.flags |= flags;
if ((tmp & SDVO_COLOR_FORMAT_MASK) == HDMI_COLOR_FORMAT_12bpc)
@@ -861,10 +869,15 @@ static enum drm_mode_status
intel_hdmi_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- if (mode->clock > hdmi_portclock_limit(intel_attached_hdmi(connector),
- true))
+ int clock = mode->clock;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ clock *= 2;
+
+ if (clock > hdmi_portclock_limit(intel_attached_hdmi(connector),
+ true))
return MODE_CLOCK_HIGH;
- if (mode->clock < 20000)
+ if (clock < 20000)
return MODE_CLOCK_LOW;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
@@ -879,10 +892,10 @@ static bool hdmi_12bpc_possible(struct intel_crtc *crtc)
struct intel_encoder *encoder;
int count = 0, count_hdmi = 0;
- if (!HAS_PCH_SPLIT(dev))
+ if (HAS_GMCH_DISPLAY(dev))
return false;
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+ for_each_intel_encoder(dev, encoder) {
if (encoder->new_crtc != crtc)
continue;
@@ -918,6 +931,10 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
intel_hdmi->color_range = 0;
}
+ if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) {
+ pipe_config->pixel_multiplier = 2;
+ }
+
if (intel_hdmi->color_range)
pipe_config->limited_color_range = true;
@@ -959,104 +976,117 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
return true;
}
-static enum drm_connector_status
-intel_hdmi_detect(struct drm_connector *connector, bool force)
+static void
+intel_hdmi_unset_edid(struct drm_connector *connector)
{
- struct drm_device *dev = connector->dev;
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
- struct intel_digital_port *intel_dig_port =
- hdmi_to_dig_port(intel_hdmi);
- struct intel_encoder *intel_encoder = &intel_dig_port->base;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct edid *edid;
- enum intel_display_power_domain power_domain;
- enum drm_connector_status status = connector_status_disconnected;
- DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
- connector->base.id, connector->name);
+ intel_hdmi->has_hdmi_sink = false;
+ intel_hdmi->has_audio = false;
+ intel_hdmi->rgb_quant_range_selectable = false;
+
+ kfree(to_intel_connector(connector)->detect_edid);
+ to_intel_connector(connector)->detect_edid = NULL;
+}
+
+static bool
+intel_hdmi_set_edid(struct drm_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->dev);
+ struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
+ struct intel_encoder *intel_encoder =
+ &hdmi_to_dig_port(intel_hdmi)->base;
+ enum intel_display_power_domain power_domain;
+ struct edid *edid;
+ bool connected = false;
power_domain = intel_display_port_power_domain(intel_encoder);
intel_display_power_get(dev_priv, power_domain);
- intel_hdmi->has_hdmi_sink = false;
- intel_hdmi->has_audio = false;
- intel_hdmi->rgb_quant_range_selectable = false;
edid = drm_get_edid(connector,
intel_gmbus_get_adapter(dev_priv,
intel_hdmi->ddc_bus));
- if (edid) {
- if (edid->input & DRM_EDID_INPUT_DIGITAL) {
- status = connector_status_connected;
- if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI)
- intel_hdmi->has_hdmi_sink =
- drm_detect_hdmi_monitor(edid);
- intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
- intel_hdmi->rgb_quant_range_selectable =
- drm_rgb_quant_range_selectable(edid);
- }
- kfree(edid);
- }
+ intel_display_power_put(dev_priv, power_domain);
+
+ to_intel_connector(connector)->detect_edid = edid;
+ if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) {
+ intel_hdmi->rgb_quant_range_selectable =
+ drm_rgb_quant_range_selectable(edid);
- if (status == connector_status_connected) {
+ intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO)
intel_hdmi->has_audio =
- (intel_hdmi->force_audio == HDMI_AUDIO_ON);
- intel_encoder->type = INTEL_OUTPUT_HDMI;
+ intel_hdmi->force_audio == HDMI_AUDIO_ON;
+
+ if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI)
+ intel_hdmi->has_hdmi_sink =
+ drm_detect_hdmi_monitor(edid);
+
+ connected = true;
}
- intel_display_power_put(dev_priv, power_domain);
+ return connected;
+}
+
+static enum drm_connector_status
+intel_hdmi_detect(struct drm_connector *connector, bool force)
+{
+ enum drm_connector_status status;
+
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+ connector->base.id, connector->name);
+
+ intel_hdmi_unset_edid(connector);
+
+ if (intel_hdmi_set_edid(connector)) {
+ struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
+
+ hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI;
+ status = connector_status_connected;
+ } else
+ status = connector_status_disconnected;
return status;
}
-static int intel_hdmi_get_modes(struct drm_connector *connector)
+static void
+intel_hdmi_force(struct drm_connector *connector)
{
- struct intel_encoder *intel_encoder = intel_attached_encoder(connector);
- struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
- struct drm_i915_private *dev_priv = connector->dev->dev_private;
- enum intel_display_power_domain power_domain;
- int ret;
+ struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
- /* We should parse the EDID data and find out if it's an HDMI sink so
- * we can send audio to it.
- */
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+ connector->base.id, connector->name);
- power_domain = intel_display_port_power_domain(intel_encoder);
- intel_display_power_get(dev_priv, power_domain);
+ intel_hdmi_unset_edid(connector);
- ret = intel_ddc_get_modes(connector,
- intel_gmbus_get_adapter(dev_priv,
- intel_hdmi->ddc_bus));
+ if (connector->status != connector_status_connected)
+ return;
- intel_display_power_put(dev_priv, power_domain);
+ intel_hdmi_set_edid(connector);
+ hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI;
+}
- return ret;
+static int intel_hdmi_get_modes(struct drm_connector *connector)
+{
+ struct edid *edid;
+
+ edid = to_intel_connector(connector)->detect_edid;
+ if (edid == NULL)
+ return 0;
+
+ return intel_connector_update_modes(connector, edid);
}
static bool
intel_hdmi_detect_audio(struct drm_connector *connector)
{
- struct intel_encoder *intel_encoder = intel_attached_encoder(connector);
- struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&intel_encoder->base);
- struct drm_i915_private *dev_priv = connector->dev->dev_private;
- enum intel_display_power_domain power_domain;
- struct edid *edid;
bool has_audio = false;
+ struct edid *edid;
- power_domain = intel_display_port_power_domain(intel_encoder);
- intel_display_power_get(dev_priv, power_domain);
-
- edid = drm_get_edid(connector,
- intel_gmbus_get_adapter(dev_priv,
- intel_hdmi->ddc_bus));
- if (edid) {
- if (edid->input & DRM_EDID_INPUT_DIGITAL)
- has_audio = drm_detect_monitor_audio(edid);
- kfree(edid);
- }
-
- intel_display_power_put(dev_priv, power_domain);
+ edid = to_intel_connector(connector)->detect_edid;
+ if (edid && edid->input & DRM_EDID_INPUT_DIGITAL)
+ has_audio = drm_detect_monitor_audio(edid);
return has_audio;
}
@@ -1124,6 +1154,23 @@ intel_hdmi_set_property(struct drm_connector *connector,
goto done;
}
+ if (property == connector->dev->mode_config.aspect_ratio_property) {
+ switch (val) {
+ case DRM_MODE_PICTURE_ASPECT_NONE:
+ intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
+ break;
+ case DRM_MODE_PICTURE_ASPECT_4_3:
+ intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
+ break;
+ case DRM_MODE_PICTURE_ASPECT_16_9:
+ intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
+ break;
+ default:
+ return -EINVAL;
+ }
+ goto done;
+ }
+
return -EINVAL;
done:
@@ -1229,6 +1276,72 @@ static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
mutex_unlock(&dev_priv->dpio_lock);
}
+static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
+{
+ struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc =
+ to_intel_crtc(encoder->base.crtc);
+ enum dpio_channel ch = vlv_dport_to_channel(dport);
+ enum pipe pipe = intel_crtc->pipe;
+ u32 val;
+
+ intel_hdmi_prepare(encoder);
+
+ mutex_lock(&dev_priv->dpio_lock);
+
+ /* program left/right clock distribution */
+ if (pipe != PIPE_B) {
+ val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW5_CH0);
+ val &= ~(CHV_BUFLEFTENA1_MASK | CHV_BUFRIGHTENA1_MASK);
+ if (ch == DPIO_CH0)
+ val |= CHV_BUFLEFTENA1_FORCE;
+ if (ch == DPIO_CH1)
+ val |= CHV_BUFRIGHTENA1_FORCE;
+ vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW5_CH0, val);
+ } else {
+ val = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW1_CH1);
+ val &= ~(CHV_BUFLEFTENA2_MASK | CHV_BUFRIGHTENA2_MASK);
+ if (ch == DPIO_CH0)
+ val |= CHV_BUFLEFTENA2_FORCE;
+ if (ch == DPIO_CH1)
+ val |= CHV_BUFRIGHTENA2_FORCE;
+ vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW1_CH1, val);
+ }
+
+ /* program clock channel usage */
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(ch));
+ val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE;
+ if (pipe != PIPE_B)
+ val &= ~CHV_PCS_USEDCLKCHANNEL;
+ else
+ val |= CHV_PCS_USEDCLKCHANNEL;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW8(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW8(ch));
+ val |= CHV_PCS_USEDCLKCHANNEL_OVRRIDE;
+ if (pipe != PIPE_B)
+ val &= ~CHV_PCS_USEDCLKCHANNEL;
+ else
+ val |= CHV_PCS_USEDCLKCHANNEL;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW8(ch), val);
+
+ /*
+ * This a a bit weird since generally CL
+ * matches the pipe, but here we need to
+ * pick the CL based on the port.
+ */
+ val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW19(ch));
+ if (pipe != PIPE_B)
+ val &= ~CHV_CMN_USEDCLKCHANNEL;
+ else
+ val |= CHV_CMN_USEDCLKCHANNEL;
+ vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW19(ch), val);
+
+ mutex_unlock(&dev_priv->dpio_lock);
+}
+
static void vlv_hdmi_post_disable(struct intel_encoder *encoder)
{
struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
@@ -1345,8 +1458,8 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
for (i = 0; i < 4; i++) {
val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i));
- val &= ~DPIO_SWING_MARGIN_MASK;
- val |= 102 << DPIO_SWING_MARGIN_SHIFT;
+ val &= ~DPIO_SWING_MARGIN000_MASK;
+ val |= 102 << DPIO_SWING_MARGIN000_SHIFT;
vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val);
}
@@ -1393,6 +1506,7 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
static void intel_hdmi_destroy(struct drm_connector *connector)
{
+ kfree(to_intel_connector(connector)->detect_edid);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -1400,6 +1514,7 @@ static void intel_hdmi_destroy(struct drm_connector *connector)
static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
.dpms = intel_connector_dpms,
.detect = intel_hdmi_detect,
+ .force = intel_hdmi_force,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_hdmi_set_property,
.destroy = intel_hdmi_destroy,
@@ -1416,11 +1531,22 @@ static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
};
static void
+intel_attach_aspect_ratio_property(struct drm_connector *connector)
+{
+ if (!drm_mode_create_aspect_ratio_property(connector->dev))
+ drm_object_attach_property(&connector->base,
+ connector->dev->mode_config.aspect_ratio_property,
+ DRM_MODE_PICTURE_ASPECT_NONE);
+}
+
+static void
intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
{
intel_attach_force_audio_property(connector);
intel_attach_broadcast_rgb_property(connector);
intel_hdmi->color_range_auto = true;
+ intel_attach_aspect_ratio_property(connector);
+ intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
}
void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
@@ -1467,7 +1593,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
if (IS_VALLEYVIEW(dev)) {
intel_hdmi->write_infoframe = vlv_write_infoframe;
intel_hdmi->set_infoframes = vlv_set_infoframes;
- } else if (!HAS_PCH_SPLIT(dev)) {
+ } else if (IS_G4X(dev)) {
intel_hdmi->write_infoframe = g4x_write_infoframe;
intel_hdmi->set_infoframes = g4x_set_infoframes;
} else if (HAS_DDI(dev)) {
@@ -1490,7 +1616,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
intel_hdmi_add_properties(intel_hdmi, connector);
intel_connector_attach_encoder(intel_connector, intel_encoder);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
* 0xd. Failure to do so will result in spurious interrupts being
@@ -1528,6 +1654,7 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
intel_encoder->get_config = intel_hdmi_get_config;
if (IS_CHERRYVIEW(dev)) {
+ intel_encoder->pre_pll_enable = chv_hdmi_pre_pll_enable;
intel_encoder->pre_enable = chv_hdmi_pre_enable;
intel_encoder->enable = vlv_enable_hdmi;
intel_encoder->post_disable = chv_hdmi_post_disable;
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index d33b61d0dd33..b31088a551f2 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -34,11 +34,6 @@
#include <drm/i915_drm.h>
#include "i915_drv.h"
-enum disp_clk {
- CDCLK,
- CZCLK
-};
-
struct gmbus_port {
const char *name;
int reg;
@@ -63,60 +58,11 @@ to_intel_gmbus(struct i2c_adapter *i2c)
return container_of(i2c, struct intel_gmbus, adapter);
}
-static int get_disp_clk_div(struct drm_i915_private *dev_priv,
- enum disp_clk clk)
-{
- u32 reg_val;
- int clk_ratio;
-
- reg_val = I915_READ(CZCLK_CDCLK_FREQ_RATIO);
-
- if (clk == CDCLK)
- clk_ratio =
- ((reg_val & CDCLK_FREQ_MASK) >> CDCLK_FREQ_SHIFT) + 1;
- else
- clk_ratio = (reg_val & CZCLK_FREQ_MASK) + 1;
-
- return clk_ratio;
-}
-
-static void gmbus_set_freq(struct drm_i915_private *dev_priv)
-{
- int vco, gmbus_freq = 0, cdclk_div;
-
- BUG_ON(!IS_VALLEYVIEW(dev_priv->dev));
-
- vco = valleyview_get_vco(dev_priv);
-
- /* Get the CDCLK divide ratio */
- cdclk_div = get_disp_clk_div(dev_priv, CDCLK);
-
- /*
- * Program the gmbus_freq based on the cdclk frequency.
- * BSpec erroneously claims we should aim for 4MHz, but
- * in fact 1MHz is the correct frequency.
- */
- if (cdclk_div)
- gmbus_freq = (vco << 1) / cdclk_div;
-
- if (WARN_ON(gmbus_freq == 0))
- return;
-
- I915_WRITE(GMBUSFREQ_VLV, gmbus_freq);
-}
-
void
intel_i2c_reset(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- /*
- * In BIOS-less system, program the correct gmbus frequency
- * before reading edid.
- */
- if (IS_VALLEYVIEW(dev))
- gmbus_set_freq(dev_priv);
-
I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0);
I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0);
}
diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c
new file mode 100644
index 000000000000..bafd38b5703e
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_lrc.c
@@ -0,0 +1,1766 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ * Authors:
+ * Ben Widawsky <ben@bwidawsk.net>
+ * Michel Thierry <michel.thierry@intel.com>
+ * Thomas Daniel <thomas.daniel@intel.com>
+ * Oscar Mateo <oscar.mateo@intel.com>
+ *
+ */
+
+/**
+ * DOC: Logical Rings, Logical Ring Contexts and Execlists
+ *
+ * Motivation:
+ * GEN8 brings an expansion of the HW contexts: "Logical Ring Contexts".
+ * These expanded contexts enable a number of new abilities, especially
+ * "Execlists" (also implemented in this file).
+ *
+ * One of the main differences with the legacy HW contexts is that logical
+ * ring contexts incorporate many more things to the context's state, like
+ * PDPs or ringbuffer control registers:
+ *
+ * The reason why PDPs are included in the context is straightforward: as
+ * PPGTTs (per-process GTTs) are actually per-context, having the PDPs
+ * contained there mean you don't need to do a ppgtt->switch_mm yourself,
+ * instead, the GPU will do it for you on the context switch.
+ *
+ * But, what about the ringbuffer control registers (head, tail, etc..)?
+ * shouldn't we just need a set of those per engine command streamer? This is
+ * where the name "Logical Rings" starts to make sense: by virtualizing the
+ * rings, the engine cs shifts to a new "ring buffer" with every context
+ * switch. When you want to submit a workload to the GPU you: A) choose your
+ * context, B) find its appropriate virtualized ring, C) write commands to it
+ * and then, finally, D) tell the GPU to switch to that context.
+ *
+ * Instead of the legacy MI_SET_CONTEXT, the way you tell the GPU to switch
+ * to a contexts is via a context execution list, ergo "Execlists".
+ *
+ * LRC implementation:
+ * Regarding the creation of contexts, we have:
+ *
+ * - One global default context.
+ * - One local default context for each opened fd.
+ * - One local extra context for each context create ioctl call.
+ *
+ * Now that ringbuffers belong per-context (and not per-engine, like before)
+ * and that contexts are uniquely tied to a given engine (and not reusable,
+ * like before) we need:
+ *
+ * - One ringbuffer per-engine inside each context.
+ * - One backing object per-engine inside each context.
+ *
+ * The global default context starts its life with these new objects fully
+ * allocated and populated. The local default context for each opened fd is
+ * more complex, because we don't know at creation time which engine is going
+ * to use them. To handle this, we have implemented a deferred creation of LR
+ * contexts:
+ *
+ * The local context starts its life as a hollow or blank holder, that only
+ * gets populated for a given engine once we receive an execbuffer. If later
+ * on we receive another execbuffer ioctl for the same context but a different
+ * engine, we allocate/populate a new ringbuffer and context backing object and
+ * so on.
+ *
+ * Finally, regarding local contexts created using the ioctl call: as they are
+ * only allowed with the render ring, we can allocate & populate them right
+ * away (no need to defer anything, at least for now).
+ *
+ * Execlists implementation:
+ * Execlists are the new method by which, on gen8+ hardware, workloads are
+ * submitted for execution (as opposed to the legacy, ringbuffer-based, method).
+ * This method works as follows:
+ *
+ * When a request is committed, its commands (the BB start and any leading or
+ * trailing commands, like the seqno breadcrumbs) are placed in the ringbuffer
+ * for the appropriate context. The tail pointer in the hardware context is not
+ * updated at this time, but instead, kept by the driver in the ringbuffer
+ * structure. A structure representing this request is added to a request queue
+ * for the appropriate engine: this structure contains a copy of the context's
+ * tail after the request was written to the ring buffer and a pointer to the
+ * context itself.
+ *
+ * If the engine's request queue was empty before the request was added, the
+ * queue is processed immediately. Otherwise the queue will be processed during
+ * a context switch interrupt. In any case, elements on the queue will get sent
+ * (in pairs) to the GPU's ExecLists Submit Port (ELSP, for short) with a
+ * globally unique 20-bits submission ID.
+ *
+ * When execution of a request completes, the GPU updates the context status
+ * buffer with a context complete event and generates a context switch interrupt.
+ * During the interrupt handling, the driver examines the events in the buffer:
+ * for each context complete event, if the announced ID matches that on the head
+ * of the request queue, then that request is retired and removed from the queue.
+ *
+ * After processing, if any requests were retired and the queue is not empty
+ * then a new execution list can be submitted. The two requests at the front of
+ * the queue are next to be submitted but since a context may not occur twice in
+ * an execution list, if subsequent requests have the same ID as the first then
+ * the two requests must be combined. This is done simply by discarding requests
+ * at the head of the queue until either only one requests is left (in which case
+ * we use a NULL second context) or the first two requests have unique IDs.
+ *
+ * By always executing the first two requests in the queue the driver ensures
+ * that the GPU is kept as busy as possible. In the case where a single context
+ * completes but a second context is still executing, the request for this second
+ * context will be at the head of the queue when we remove the first one. This
+ * request will then be resubmitted along with a new request for a different context,
+ * which will cause the hardware to continue executing the second request and queue
+ * the new request (the GPU detects the condition of a context getting preempted
+ * with the same context and optimizes the context switch flow by not doing
+ * preemption, but just sampling the new tail pointer).
+ *
+ */
+
+#include <drm/drmP.h>
+#include <drm/i915_drm.h>
+#include "i915_drv.h"
+
+#define GEN8_LR_CONTEXT_RENDER_SIZE (20 * PAGE_SIZE)
+#define GEN8_LR_CONTEXT_OTHER_SIZE (2 * PAGE_SIZE)
+
+#define GEN8_LR_CONTEXT_ALIGN 4096
+
+#define RING_EXECLIST_QFULL (1 << 0x2)
+#define RING_EXECLIST1_VALID (1 << 0x3)
+#define RING_EXECLIST0_VALID (1 << 0x4)
+#define RING_EXECLIST_ACTIVE_STATUS (3 << 0xE)
+#define RING_EXECLIST1_ACTIVE (1 << 0x11)
+#define RING_EXECLIST0_ACTIVE (1 << 0x12)
+
+#define GEN8_CTX_STATUS_IDLE_ACTIVE (1 << 0)
+#define GEN8_CTX_STATUS_PREEMPTED (1 << 1)
+#define GEN8_CTX_STATUS_ELEMENT_SWITCH (1 << 2)
+#define GEN8_CTX_STATUS_ACTIVE_IDLE (1 << 3)
+#define GEN8_CTX_STATUS_COMPLETE (1 << 4)
+#define GEN8_CTX_STATUS_LITE_RESTORE (1 << 15)
+
+#define CTX_LRI_HEADER_0 0x01
+#define CTX_CONTEXT_CONTROL 0x02
+#define CTX_RING_HEAD 0x04
+#define CTX_RING_TAIL 0x06
+#define CTX_RING_BUFFER_START 0x08
+#define CTX_RING_BUFFER_CONTROL 0x0a
+#define CTX_BB_HEAD_U 0x0c
+#define CTX_BB_HEAD_L 0x0e
+#define CTX_BB_STATE 0x10
+#define CTX_SECOND_BB_HEAD_U 0x12
+#define CTX_SECOND_BB_HEAD_L 0x14
+#define CTX_SECOND_BB_STATE 0x16
+#define CTX_BB_PER_CTX_PTR 0x18
+#define CTX_RCS_INDIRECT_CTX 0x1a
+#define CTX_RCS_INDIRECT_CTX_OFFSET 0x1c
+#define CTX_LRI_HEADER_1 0x21
+#define CTX_CTX_TIMESTAMP 0x22
+#define CTX_PDP3_UDW 0x24
+#define CTX_PDP3_LDW 0x26
+#define CTX_PDP2_UDW 0x28
+#define CTX_PDP2_LDW 0x2a
+#define CTX_PDP1_UDW 0x2c
+#define CTX_PDP1_LDW 0x2e
+#define CTX_PDP0_UDW 0x30
+#define CTX_PDP0_LDW 0x32
+#define CTX_LRI_HEADER_2 0x41
+#define CTX_R_PWR_CLK_STATE 0x42
+#define CTX_GPGPU_CSR_BASE_ADDRESS 0x44
+
+#define GEN8_CTX_VALID (1<<0)
+#define GEN8_CTX_FORCE_PD_RESTORE (1<<1)
+#define GEN8_CTX_FORCE_RESTORE (1<<2)
+#define GEN8_CTX_L3LLC_COHERENT (1<<5)
+#define GEN8_CTX_PRIVILEGE (1<<8)
+enum {
+ ADVANCED_CONTEXT = 0,
+ LEGACY_CONTEXT,
+ ADVANCED_AD_CONTEXT,
+ LEGACY_64B_CONTEXT
+};
+#define GEN8_CTX_MODE_SHIFT 3
+enum {
+ FAULT_AND_HANG = 0,
+ FAULT_AND_HALT, /* Debug only */
+ FAULT_AND_STREAM,
+ FAULT_AND_CONTINUE /* Unsupported */
+};
+#define GEN8_CTX_ID_SHIFT 32
+
+/**
+ * intel_sanitize_enable_execlists() - sanitize i915.enable_execlists
+ * @dev: DRM device.
+ * @enable_execlists: value of i915.enable_execlists module parameter.
+ *
+ * Only certain platforms support Execlists (the prerequisites being
+ * support for Logical Ring Contexts and Aliasing PPGTT or better),
+ * and only when enabled via module parameter.
+ *
+ * Return: 1 if Execlists is supported and has to be enabled.
+ */
+int intel_sanitize_enable_execlists(struct drm_device *dev, int enable_execlists)
+{
+ WARN_ON(i915.enable_ppgtt == -1);
+
+ if (enable_execlists == 0)
+ return 0;
+
+ if (HAS_LOGICAL_RING_CONTEXTS(dev) && USES_PPGTT(dev) &&
+ i915.use_mmio_flip >= 0)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * intel_execlists_ctx_id() - get the Execlists Context ID
+ * @ctx_obj: Logical Ring Context backing object.
+ *
+ * Do not confuse with ctx->id! Unfortunately we have a name overload
+ * here: the old context ID we pass to userspace as a handler so that
+ * they can refer to a context, and the new context ID we pass to the
+ * ELSP so that the GPU can inform us of the context status via
+ * interrupts.
+ *
+ * Return: 20-bits globally unique context ID.
+ */
+u32 intel_execlists_ctx_id(struct drm_i915_gem_object *ctx_obj)
+{
+ u32 lrca = i915_gem_obj_ggtt_offset(ctx_obj);
+
+ /* LRCA is required to be 4K aligned so the more significant 20 bits
+ * are globally unique */
+ return lrca >> 12;
+}
+
+static uint64_t execlists_ctx_descriptor(struct drm_i915_gem_object *ctx_obj)
+{
+ uint64_t desc;
+ uint64_t lrca = i915_gem_obj_ggtt_offset(ctx_obj);
+
+ WARN_ON(lrca & 0xFFFFFFFF00000FFFULL);
+
+ desc = GEN8_CTX_VALID;
+ desc |= LEGACY_CONTEXT << GEN8_CTX_MODE_SHIFT;
+ desc |= GEN8_CTX_L3LLC_COHERENT;
+ desc |= GEN8_CTX_PRIVILEGE;
+ desc |= lrca;
+ desc |= (u64)intel_execlists_ctx_id(ctx_obj) << GEN8_CTX_ID_SHIFT;
+
+ /* TODO: WaDisableLiteRestore when we start using semaphore
+ * signalling between Command Streamers */
+ /* desc |= GEN8_CTX_FORCE_RESTORE; */
+
+ return desc;
+}
+
+static void execlists_elsp_write(struct intel_engine_cs *ring,
+ struct drm_i915_gem_object *ctx_obj0,
+ struct drm_i915_gem_object *ctx_obj1)
+{
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ uint64_t temp = 0;
+ uint32_t desc[4];
+ unsigned long flags;
+
+ /* XXX: You must always write both descriptors in the order below. */
+ if (ctx_obj1)
+ temp = execlists_ctx_descriptor(ctx_obj1);
+ else
+ temp = 0;
+ desc[1] = (u32)(temp >> 32);
+ desc[0] = (u32)temp;
+
+ temp = execlists_ctx_descriptor(ctx_obj0);
+ desc[3] = (u32)(temp >> 32);
+ desc[2] = (u32)temp;
+
+ /* Set Force Wakeup bit to prevent GT from entering C6 while ELSP writes
+ * are in progress.
+ *
+ * The other problem is that we can't just call gen6_gt_force_wake_get()
+ * because that function calls intel_runtime_pm_get(), which might sleep.
+ * Instead, we do the runtime_pm_get/put when creating/destroying requests.
+ */
+ spin_lock_irqsave(&dev_priv->uncore.lock, flags);
+ if (IS_CHERRYVIEW(dev_priv->dev)) {
+ if (dev_priv->uncore.fw_rendercount++ == 0)
+ dev_priv->uncore.funcs.force_wake_get(dev_priv,
+ FORCEWAKE_RENDER);
+ if (dev_priv->uncore.fw_mediacount++ == 0)
+ dev_priv->uncore.funcs.force_wake_get(dev_priv,
+ FORCEWAKE_MEDIA);
+ } else {
+ if (dev_priv->uncore.forcewake_count++ == 0)
+ dev_priv->uncore.funcs.force_wake_get(dev_priv,
+ FORCEWAKE_ALL);
+ }
+ spin_unlock_irqrestore(&dev_priv->uncore.lock, flags);
+
+ I915_WRITE(RING_ELSP(ring), desc[1]);
+ I915_WRITE(RING_ELSP(ring), desc[0]);
+ I915_WRITE(RING_ELSP(ring), desc[3]);
+ /* The context is automatically loaded after the following */
+ I915_WRITE(RING_ELSP(ring), desc[2]);
+
+ /* ELSP is a wo register, so use another nearby reg for posting instead */
+ POSTING_READ(RING_EXECLIST_STATUS(ring));
+
+ /* Release Force Wakeup (see the big comment above). */
+ spin_lock_irqsave(&dev_priv->uncore.lock, flags);
+ if (IS_CHERRYVIEW(dev_priv->dev)) {
+ if (--dev_priv->uncore.fw_rendercount == 0)
+ dev_priv->uncore.funcs.force_wake_put(dev_priv,
+ FORCEWAKE_RENDER);
+ if (--dev_priv->uncore.fw_mediacount == 0)
+ dev_priv->uncore.funcs.force_wake_put(dev_priv,
+ FORCEWAKE_MEDIA);
+ } else {
+ if (--dev_priv->uncore.forcewake_count == 0)
+ dev_priv->uncore.funcs.force_wake_put(dev_priv,
+ FORCEWAKE_ALL);
+ }
+
+ spin_unlock_irqrestore(&dev_priv->uncore.lock, flags);
+}
+
+static int execlists_ctx_write_tail(struct drm_i915_gem_object *ctx_obj, u32 tail)
+{
+ struct page *page;
+ uint32_t *reg_state;
+
+ page = i915_gem_object_get_page(ctx_obj, 1);
+ reg_state = kmap_atomic(page);
+
+ reg_state[CTX_RING_TAIL+1] = tail;
+
+ kunmap_atomic(reg_state);
+
+ return 0;
+}
+
+static int execlists_submit_context(struct intel_engine_cs *ring,
+ struct intel_context *to0, u32 tail0,
+ struct intel_context *to1, u32 tail1)
+{
+ struct drm_i915_gem_object *ctx_obj0;
+ struct drm_i915_gem_object *ctx_obj1 = NULL;
+
+ ctx_obj0 = to0->engine[ring->id].state;
+ BUG_ON(!ctx_obj0);
+ WARN_ON(!i915_gem_obj_is_pinned(ctx_obj0));
+
+ execlists_ctx_write_tail(ctx_obj0, tail0);
+
+ if (to1) {
+ ctx_obj1 = to1->engine[ring->id].state;
+ BUG_ON(!ctx_obj1);
+ WARN_ON(!i915_gem_obj_is_pinned(ctx_obj1));
+
+ execlists_ctx_write_tail(ctx_obj1, tail1);
+ }
+
+ execlists_elsp_write(ring, ctx_obj0, ctx_obj1);
+
+ return 0;
+}
+
+static void execlists_context_unqueue(struct intel_engine_cs *ring)
+{
+ struct intel_ctx_submit_request *req0 = NULL, *req1 = NULL;
+ struct intel_ctx_submit_request *cursor = NULL, *tmp = NULL;
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+
+ assert_spin_locked(&ring->execlist_lock);
+
+ if (list_empty(&ring->execlist_queue))
+ return;
+
+ /* Try to read in pairs */
+ list_for_each_entry_safe(cursor, tmp, &ring->execlist_queue,
+ execlist_link) {
+ if (!req0) {
+ req0 = cursor;
+ } else if (req0->ctx == cursor->ctx) {
+ /* Same ctx: ignore first request, as second request
+ * will update tail past first request's workload */
+ cursor->elsp_submitted = req0->elsp_submitted;
+ list_del(&req0->execlist_link);
+ queue_work(dev_priv->wq, &req0->work);
+ req0 = cursor;
+ } else {
+ req1 = cursor;
+ break;
+ }
+ }
+
+ WARN_ON(req1 && req1->elsp_submitted);
+
+ WARN_ON(execlists_submit_context(ring, req0->ctx, req0->tail,
+ req1 ? req1->ctx : NULL,
+ req1 ? req1->tail : 0));
+
+ req0->elsp_submitted++;
+ if (req1)
+ req1->elsp_submitted++;
+}
+
+static bool execlists_check_remove_request(struct intel_engine_cs *ring,
+ u32 request_id)
+{
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ struct intel_ctx_submit_request *head_req;
+
+ assert_spin_locked(&ring->execlist_lock);
+
+ head_req = list_first_entry_or_null(&ring->execlist_queue,
+ struct intel_ctx_submit_request,
+ execlist_link);
+
+ if (head_req != NULL) {
+ struct drm_i915_gem_object *ctx_obj =
+ head_req->ctx->engine[ring->id].state;
+ if (intel_execlists_ctx_id(ctx_obj) == request_id) {
+ WARN(head_req->elsp_submitted == 0,
+ "Never submitted head request\n");
+
+ if (--head_req->elsp_submitted <= 0) {
+ list_del(&head_req->execlist_link);
+ queue_work(dev_priv->wq, &head_req->work);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
+ * intel_execlists_handle_ctx_events() - handle Context Switch interrupts
+ * @ring: Engine Command Streamer to handle.
+ *
+ * Check the unread Context Status Buffers and manage the submission of new
+ * contexts to the ELSP accordingly.
+ */
+void intel_execlists_handle_ctx_events(struct intel_engine_cs *ring)
+{
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ u32 status_pointer;
+ u8 read_pointer;
+ u8 write_pointer;
+ u32 status;
+ u32 status_id;
+ u32 submit_contexts = 0;
+
+ status_pointer = I915_READ(RING_CONTEXT_STATUS_PTR(ring));
+
+ read_pointer = ring->next_context_status_buffer;
+ write_pointer = status_pointer & 0x07;
+ if (read_pointer > write_pointer)
+ write_pointer += 6;
+
+ spin_lock(&ring->execlist_lock);
+
+ while (read_pointer < write_pointer) {
+ read_pointer++;
+ status = I915_READ(RING_CONTEXT_STATUS_BUF(ring) +
+ (read_pointer % 6) * 8);
+ status_id = I915_READ(RING_CONTEXT_STATUS_BUF(ring) +
+ (read_pointer % 6) * 8 + 4);
+
+ if (status & GEN8_CTX_STATUS_PREEMPTED) {
+ if (status & GEN8_CTX_STATUS_LITE_RESTORE) {
+ if (execlists_check_remove_request(ring, status_id))
+ WARN(1, "Lite Restored request removed from queue\n");
+ } else
+ WARN(1, "Preemption without Lite Restore\n");
+ }
+
+ if ((status & GEN8_CTX_STATUS_ACTIVE_IDLE) ||
+ (status & GEN8_CTX_STATUS_ELEMENT_SWITCH)) {
+ if (execlists_check_remove_request(ring, status_id))
+ submit_contexts++;
+ }
+ }
+
+ if (submit_contexts != 0)
+ execlists_context_unqueue(ring);
+
+ spin_unlock(&ring->execlist_lock);
+
+ WARN(submit_contexts > 2, "More than two context complete events?\n");
+ ring->next_context_status_buffer = write_pointer % 6;
+
+ I915_WRITE(RING_CONTEXT_STATUS_PTR(ring),
+ ((u32)ring->next_context_status_buffer & 0x07) << 8);
+}
+
+static void execlists_free_request_task(struct work_struct *work)
+{
+ struct intel_ctx_submit_request *req =
+ container_of(work, struct intel_ctx_submit_request, work);
+ struct drm_device *dev = req->ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ intel_runtime_pm_put(dev_priv);
+
+ mutex_lock(&dev->struct_mutex);
+ i915_gem_context_unreference(req->ctx);
+ mutex_unlock(&dev->struct_mutex);
+
+ kfree(req);
+}
+
+static int execlists_context_queue(struct intel_engine_cs *ring,
+ struct intel_context *to,
+ u32 tail)
+{
+ struct intel_ctx_submit_request *req = NULL, *cursor;
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ unsigned long flags;
+ int num_elements = 0;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (req == NULL)
+ return -ENOMEM;
+ req->ctx = to;
+ i915_gem_context_reference(req->ctx);
+ req->ring = ring;
+ req->tail = tail;
+ INIT_WORK(&req->work, execlists_free_request_task);
+
+ intel_runtime_pm_get(dev_priv);
+
+ spin_lock_irqsave(&ring->execlist_lock, flags);
+
+ list_for_each_entry(cursor, &ring->execlist_queue, execlist_link)
+ if (++num_elements > 2)
+ break;
+
+ if (num_elements > 2) {
+ struct intel_ctx_submit_request *tail_req;
+
+ tail_req = list_last_entry(&ring->execlist_queue,
+ struct intel_ctx_submit_request,
+ execlist_link);
+
+ if (to == tail_req->ctx) {
+ WARN(tail_req->elsp_submitted != 0,
+ "More than 2 already-submitted reqs queued\n");
+ list_del(&tail_req->execlist_link);
+ queue_work(dev_priv->wq, &tail_req->work);
+ }
+ }
+
+ list_add_tail(&req->execlist_link, &ring->execlist_queue);
+ if (num_elements == 0)
+ execlists_context_unqueue(ring);
+
+ spin_unlock_irqrestore(&ring->execlist_lock, flags);
+
+ return 0;
+}
+
+static int logical_ring_invalidate_all_caches(struct intel_ringbuffer *ringbuf)
+{
+ struct intel_engine_cs *ring = ringbuf->ring;
+ uint32_t flush_domains;
+ int ret;
+
+ flush_domains = 0;
+ if (ring->gpu_caches_dirty)
+ flush_domains = I915_GEM_GPU_DOMAINS;
+
+ ret = ring->emit_flush(ringbuf, I915_GEM_GPU_DOMAINS, flush_domains);
+ if (ret)
+ return ret;
+
+ ring->gpu_caches_dirty = false;
+ return 0;
+}
+
+static int execlists_move_to_gpu(struct intel_ringbuffer *ringbuf,
+ struct list_head *vmas)
+{
+ struct intel_engine_cs *ring = ringbuf->ring;
+ struct i915_vma *vma;
+ uint32_t flush_domains = 0;
+ bool flush_chipset = false;
+ int ret;
+
+ list_for_each_entry(vma, vmas, exec_list) {
+ struct drm_i915_gem_object *obj = vma->obj;
+
+ ret = i915_gem_object_sync(obj, ring);
+ if (ret)
+ return ret;
+
+ if (obj->base.write_domain & I915_GEM_DOMAIN_CPU)
+ flush_chipset |= i915_gem_clflush_object(obj, false);
+
+ flush_domains |= obj->base.write_domain;
+ }
+
+ if (flush_domains & I915_GEM_DOMAIN_GTT)
+ wmb();
+
+ /* Unconditionally invalidate gpu caches and ensure that we do flush
+ * any residual writes from the previous batch.
+ */
+ return logical_ring_invalidate_all_caches(ringbuf);
+}
+
+/**
+ * execlists_submission() - submit a batchbuffer for execution, Execlists style
+ * @dev: DRM device.
+ * @file: DRM file.
+ * @ring: Engine Command Streamer to submit to.
+ * @ctx: Context to employ for this submission.
+ * @args: execbuffer call arguments.
+ * @vmas: list of vmas.
+ * @batch_obj: the batchbuffer to submit.
+ * @exec_start: batchbuffer start virtual address pointer.
+ * @flags: translated execbuffer call flags.
+ *
+ * This is the evil twin version of i915_gem_ringbuffer_submission. It abstracts
+ * away the submission details of the execbuffer ioctl call.
+ *
+ * Return: non-zero if the submission fails.
+ */
+int intel_execlists_submission(struct drm_device *dev, struct drm_file *file,
+ struct intel_engine_cs *ring,
+ struct intel_context *ctx,
+ struct drm_i915_gem_execbuffer2 *args,
+ struct list_head *vmas,
+ struct drm_i915_gem_object *batch_obj,
+ u64 exec_start, u32 flags)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_ringbuffer *ringbuf = ctx->engine[ring->id].ringbuf;
+ int instp_mode;
+ u32 instp_mask;
+ int ret;
+
+ instp_mode = args->flags & I915_EXEC_CONSTANTS_MASK;
+ instp_mask = I915_EXEC_CONSTANTS_MASK;
+ switch (instp_mode) {
+ case I915_EXEC_CONSTANTS_REL_GENERAL:
+ case I915_EXEC_CONSTANTS_ABSOLUTE:
+ case I915_EXEC_CONSTANTS_REL_SURFACE:
+ if (instp_mode != 0 && ring != &dev_priv->ring[RCS]) {
+ DRM_DEBUG("non-0 rel constants mode on non-RCS\n");
+ return -EINVAL;
+ }
+
+ if (instp_mode != dev_priv->relative_constants_mode) {
+ if (instp_mode == I915_EXEC_CONSTANTS_REL_SURFACE) {
+ DRM_DEBUG("rel surface constants mode invalid on gen5+\n");
+ return -EINVAL;
+ }
+
+ /* The HW changed the meaning on this bit on gen6 */
+ instp_mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE;
+ }
+ break;
+ default:
+ DRM_DEBUG("execbuf with unknown constants: %d\n", instp_mode);
+ return -EINVAL;
+ }
+
+ if (args->num_cliprects != 0) {
+ DRM_DEBUG("clip rectangles are only valid on pre-gen5\n");
+ return -EINVAL;
+ } else {
+ if (args->DR4 == 0xffffffff) {
+ DRM_DEBUG("UXA submitting garbage DR4, fixing up\n");
+ args->DR4 = 0;
+ }
+
+ if (args->DR1 || args->DR4 || args->cliprects_ptr) {
+ DRM_DEBUG("0 cliprects but dirt in cliprects fields\n");
+ return -EINVAL;
+ }
+ }
+
+ if (args->flags & I915_EXEC_GEN7_SOL_RESET) {
+ DRM_DEBUG("sol reset is gen7 only\n");
+ return -EINVAL;
+ }
+
+ ret = execlists_move_to_gpu(ringbuf, vmas);
+ if (ret)
+ return ret;
+
+ if (ring == &dev_priv->ring[RCS] &&
+ instp_mode != dev_priv->relative_constants_mode) {
+ ret = intel_logical_ring_begin(ringbuf, 4);
+ if (ret)
+ return ret;
+
+ intel_logical_ring_emit(ringbuf, MI_NOOP);
+ intel_logical_ring_emit(ringbuf, MI_LOAD_REGISTER_IMM(1));
+ intel_logical_ring_emit(ringbuf, INSTPM);
+ intel_logical_ring_emit(ringbuf, instp_mask << 16 | instp_mode);
+ intel_logical_ring_advance(ringbuf);
+
+ dev_priv->relative_constants_mode = instp_mode;
+ }
+
+ ret = ring->emit_bb_start(ringbuf, exec_start, flags);
+ if (ret)
+ return ret;
+
+ i915_gem_execbuffer_move_to_active(vmas, ring);
+ i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj);
+
+ return 0;
+}
+
+void intel_logical_ring_stop(struct intel_engine_cs *ring)
+{
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ int ret;
+
+ if (!intel_ring_initialized(ring))
+ return;
+
+ ret = intel_ring_idle(ring);
+ if (ret && !i915_reset_in_progress(&to_i915(ring->dev)->gpu_error))
+ DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n",
+ ring->name, ret);
+
+ /* TODO: Is this correct with Execlists enabled? */
+ I915_WRITE_MODE(ring, _MASKED_BIT_ENABLE(STOP_RING));
+ if (wait_for_atomic((I915_READ_MODE(ring) & MODE_IDLE) != 0, 1000)) {
+ DRM_ERROR("%s :timed out trying to stop ring\n", ring->name);
+ return;
+ }
+ I915_WRITE_MODE(ring, _MASKED_BIT_DISABLE(STOP_RING));
+}
+
+int logical_ring_flush_all_caches(struct intel_ringbuffer *ringbuf)
+{
+ struct intel_engine_cs *ring = ringbuf->ring;
+ int ret;
+
+ if (!ring->gpu_caches_dirty)
+ return 0;
+
+ ret = ring->emit_flush(ringbuf, 0, I915_GEM_GPU_DOMAINS);
+ if (ret)
+ return ret;
+
+ ring->gpu_caches_dirty = false;
+ return 0;
+}
+
+/**
+ * intel_logical_ring_advance_and_submit() - advance the tail and submit the workload
+ * @ringbuf: Logical Ringbuffer to advance.
+ *
+ * The tail is updated in our logical ringbuffer struct, not in the actual context. What
+ * really happens during submission is that the context and current tail will be placed
+ * on a queue waiting for the ELSP to be ready to accept a new context submission. At that
+ * point, the tail *inside* the context is updated and the ELSP written to.
+ */
+void intel_logical_ring_advance_and_submit(struct intel_ringbuffer *ringbuf)
+{
+ struct intel_engine_cs *ring = ringbuf->ring;
+ struct intel_context *ctx = ringbuf->FIXME_lrc_ctx;
+
+ intel_logical_ring_advance(ringbuf);
+
+ if (intel_ring_stopped(ring))
+ return;
+
+ execlists_context_queue(ring, ctx, ringbuf->tail);
+}
+
+static int logical_ring_alloc_seqno(struct intel_engine_cs *ring,
+ struct intel_context *ctx)
+{
+ if (ring->outstanding_lazy_seqno)
+ return 0;
+
+ if (ring->preallocated_lazy_request == NULL) {
+ struct drm_i915_gem_request *request;
+
+ request = kmalloc(sizeof(*request), GFP_KERNEL);
+ if (request == NULL)
+ return -ENOMEM;
+
+ /* Hold a reference to the context this request belongs to
+ * (we will need it when the time comes to emit/retire the
+ * request).
+ */
+ request->ctx = ctx;
+ i915_gem_context_reference(request->ctx);
+
+ ring->preallocated_lazy_request = request;
+ }
+
+ return i915_gem_get_seqno(ring->dev, &ring->outstanding_lazy_seqno);
+}
+
+static int logical_ring_wait_request(struct intel_ringbuffer *ringbuf,
+ int bytes)
+{
+ struct intel_engine_cs *ring = ringbuf->ring;
+ struct drm_i915_gem_request *request;
+ u32 seqno = 0;
+ int ret;
+
+ if (ringbuf->last_retired_head != -1) {
+ ringbuf->head = ringbuf->last_retired_head;
+ ringbuf->last_retired_head = -1;
+
+ ringbuf->space = intel_ring_space(ringbuf);
+ if (ringbuf->space >= bytes)
+ return 0;
+ }
+
+ list_for_each_entry(request, &ring->request_list, list) {
+ if (__intel_ring_space(request->tail, ringbuf->tail,
+ ringbuf->size) >= bytes) {
+ seqno = request->seqno;
+ break;
+ }
+ }
+
+ if (seqno == 0)
+ return -ENOSPC;
+
+ ret = i915_wait_seqno(ring, seqno);
+ if (ret)
+ return ret;
+
+ i915_gem_retire_requests_ring(ring);
+ ringbuf->head = ringbuf->last_retired_head;
+ ringbuf->last_retired_head = -1;
+
+ ringbuf->space = intel_ring_space(ringbuf);
+ return 0;
+}
+
+static int logical_ring_wait_for_space(struct intel_ringbuffer *ringbuf,
+ int bytes)
+{
+ struct intel_engine_cs *ring = ringbuf->ring;
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long end;
+ int ret;
+
+ ret = logical_ring_wait_request(ringbuf, bytes);
+ if (ret != -ENOSPC)
+ return ret;
+
+ /* Force the context submission in case we have been skipping it */
+ intel_logical_ring_advance_and_submit(ringbuf);
+
+ /* With GEM the hangcheck timer should kick us out of the loop,
+ * leaving it early runs the risk of corrupting GEM state (due
+ * to running on almost untested codepaths). But on resume
+ * timers don't work yet, so prevent a complete hang in that
+ * case by choosing an insanely large timeout. */
+ end = jiffies + 60 * HZ;
+
+ do {
+ ringbuf->head = I915_READ_HEAD(ring);
+ ringbuf->space = intel_ring_space(ringbuf);
+ if (ringbuf->space >= bytes) {
+ ret = 0;
+ break;
+ }
+
+ msleep(1);
+
+ if (dev_priv->mm.interruptible && signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ ret = i915_gem_check_wedge(&dev_priv->gpu_error,
+ dev_priv->mm.interruptible);
+ if (ret)
+ break;
+
+ if (time_after(jiffies, end)) {
+ ret = -EBUSY;
+ break;
+ }
+ } while (1);
+
+ return ret;
+}
+
+static int logical_ring_wrap_buffer(struct intel_ringbuffer *ringbuf)
+{
+ uint32_t __iomem *virt;
+ int rem = ringbuf->size - ringbuf->tail;
+
+ if (ringbuf->space < rem) {
+ int ret = logical_ring_wait_for_space(ringbuf, rem);
+
+ if (ret)
+ return ret;
+ }
+
+ virt = ringbuf->virtual_start + ringbuf->tail;
+ rem /= 4;
+ while (rem--)
+ iowrite32(MI_NOOP, virt++);
+
+ ringbuf->tail = 0;
+ ringbuf->space = intel_ring_space(ringbuf);
+
+ return 0;
+}
+
+static int logical_ring_prepare(struct intel_ringbuffer *ringbuf, int bytes)
+{
+ int ret;
+
+ if (unlikely(ringbuf->tail + bytes > ringbuf->effective_size)) {
+ ret = logical_ring_wrap_buffer(ringbuf);
+ if (unlikely(ret))
+ return ret;
+ }
+
+ if (unlikely(ringbuf->space < bytes)) {
+ ret = logical_ring_wait_for_space(ringbuf, bytes);
+ if (unlikely(ret))
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * intel_logical_ring_begin() - prepare the logical ringbuffer to accept some commands
+ *
+ * @ringbuf: Logical ringbuffer.
+ * @num_dwords: number of DWORDs that we plan to write to the ringbuffer.
+ *
+ * The ringbuffer might not be ready to accept the commands right away (maybe it needs to
+ * be wrapped, or wait a bit for the tail to be updated). This function takes care of that
+ * and also preallocates a request (every workload submission is still mediated through
+ * requests, same as it did with legacy ringbuffer submission).
+ *
+ * Return: non-zero if the ringbuffer is not ready to be written to.
+ */
+int intel_logical_ring_begin(struct intel_ringbuffer *ringbuf, int num_dwords)
+{
+ struct intel_engine_cs *ring = ringbuf->ring;
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
+ ret = i915_gem_check_wedge(&dev_priv->gpu_error,
+ dev_priv->mm.interruptible);
+ if (ret)
+ return ret;
+
+ ret = logical_ring_prepare(ringbuf, num_dwords * sizeof(uint32_t));
+ if (ret)
+ return ret;
+
+ /* Preallocate the olr before touching the ring */
+ ret = logical_ring_alloc_seqno(ring, ringbuf->FIXME_lrc_ctx);
+ if (ret)
+ return ret;
+
+ ringbuf->space -= num_dwords * sizeof(uint32_t);
+ return 0;
+}
+
+static int gen8_init_common_ring(struct intel_engine_cs *ring)
+{
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE_IMR(ring, ~(ring->irq_enable_mask | ring->irq_keep_mask));
+ I915_WRITE(RING_HWSTAM(ring->mmio_base), 0xffffffff);
+
+ I915_WRITE(RING_MODE_GEN7(ring),
+ _MASKED_BIT_DISABLE(GFX_REPLAY_MODE) |
+ _MASKED_BIT_ENABLE(GFX_RUN_LIST_ENABLE));
+ POSTING_READ(RING_MODE_GEN7(ring));
+ DRM_DEBUG_DRIVER("Execlists enabled for %s\n", ring->name);
+
+ memset(&ring->hangcheck, 0, sizeof(ring->hangcheck));
+
+ return 0;
+}
+
+static int gen8_init_render_ring(struct intel_engine_cs *ring)
+{
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
+ ret = gen8_init_common_ring(ring);
+ if (ret)
+ return ret;
+
+ /* We need to disable the AsyncFlip performance optimisations in order
+ * to use MI_WAIT_FOR_EVENT within the CS. It should already be
+ * programmed to '1' on all products.
+ *
+ * WaDisableAsyncFlipPerfMode:snb,ivb,hsw,vlv,bdw,chv
+ */
+ I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(ASYNC_FLIP_PERF_DISABLE));
+
+ ret = intel_init_pipe_control(ring);
+ if (ret)
+ return ret;
+
+ I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_FORCE_ORDERING));
+
+ return ret;
+}
+
+static int gen8_emit_bb_start(struct intel_ringbuffer *ringbuf,
+ u64 offset, unsigned flags)
+{
+ bool ppgtt = !(flags & I915_DISPATCH_SECURE);
+ int ret;
+
+ ret = intel_logical_ring_begin(ringbuf, 4);
+ if (ret)
+ return ret;
+
+ /* FIXME(BDW): Address space and security selectors. */
+ intel_logical_ring_emit(ringbuf, MI_BATCH_BUFFER_START_GEN8 | (ppgtt<<8));
+ intel_logical_ring_emit(ringbuf, lower_32_bits(offset));
+ intel_logical_ring_emit(ringbuf, upper_32_bits(offset));
+ intel_logical_ring_emit(ringbuf, MI_NOOP);
+ intel_logical_ring_advance(ringbuf);
+
+ return 0;
+}
+
+static bool gen8_logical_ring_get_irq(struct intel_engine_cs *ring)
+{
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long flags;
+
+ if (!dev->irq_enabled)
+ return false;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, flags);
+ if (ring->irq_refcount++ == 0) {
+ I915_WRITE_IMR(ring, ~(ring->irq_enable_mask | ring->irq_keep_mask));
+ POSTING_READ(RING_IMR(ring->mmio_base));
+ }
+ spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+
+ return true;
+}
+
+static void gen8_logical_ring_put_irq(struct intel_engine_cs *ring)
+{
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, flags);
+ if (--ring->irq_refcount == 0) {
+ I915_WRITE_IMR(ring, ~ring->irq_keep_mask);
+ POSTING_READ(RING_IMR(ring->mmio_base));
+ }
+ spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+}
+
+static int gen8_emit_flush(struct intel_ringbuffer *ringbuf,
+ u32 invalidate_domains,
+ u32 unused)
+{
+ struct intel_engine_cs *ring = ringbuf->ring;
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t cmd;
+ int ret;
+
+ ret = intel_logical_ring_begin(ringbuf, 4);
+ if (ret)
+ return ret;
+
+ cmd = MI_FLUSH_DW + 1;
+
+ if (ring == &dev_priv->ring[VCS]) {
+ if (invalidate_domains & I915_GEM_GPU_DOMAINS)
+ cmd |= MI_INVALIDATE_TLB | MI_INVALIDATE_BSD |
+ MI_FLUSH_DW_STORE_INDEX |
+ MI_FLUSH_DW_OP_STOREDW;
+ } else {
+ if (invalidate_domains & I915_GEM_DOMAIN_RENDER)
+ cmd |= MI_INVALIDATE_TLB | MI_FLUSH_DW_STORE_INDEX |
+ MI_FLUSH_DW_OP_STOREDW;
+ }
+
+ intel_logical_ring_emit(ringbuf, cmd);
+ intel_logical_ring_emit(ringbuf,
+ I915_GEM_HWS_SCRATCH_ADDR |
+ MI_FLUSH_DW_USE_GTT);
+ intel_logical_ring_emit(ringbuf, 0); /* upper addr */
+ intel_logical_ring_emit(ringbuf, 0); /* value */
+ intel_logical_ring_advance(ringbuf);
+
+ return 0;
+}
+
+static int gen8_emit_flush_render(struct intel_ringbuffer *ringbuf,
+ u32 invalidate_domains,
+ u32 flush_domains)
+{
+ struct intel_engine_cs *ring = ringbuf->ring;
+ u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES;
+ u32 flags = 0;
+ int ret;
+
+ flags |= PIPE_CONTROL_CS_STALL;
+
+ if (flush_domains) {
+ flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
+ flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
+ }
+
+ if (invalidate_domains) {
+ flags |= PIPE_CONTROL_TLB_INVALIDATE;
+ flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_QW_WRITE;
+ flags |= PIPE_CONTROL_GLOBAL_GTT_IVB;
+ }
+
+ ret = intel_logical_ring_begin(ringbuf, 6);
+ if (ret)
+ return ret;
+
+ intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
+ intel_logical_ring_emit(ringbuf, flags);
+ intel_logical_ring_emit(ringbuf, scratch_addr);
+ intel_logical_ring_emit(ringbuf, 0);
+ intel_logical_ring_emit(ringbuf, 0);
+ intel_logical_ring_emit(ringbuf, 0);
+ intel_logical_ring_advance(ringbuf);
+
+ return 0;
+}
+
+static u32 gen8_get_seqno(struct intel_engine_cs *ring, bool lazy_coherency)
+{
+ return intel_read_status_page(ring, I915_GEM_HWS_INDEX);
+}
+
+static void gen8_set_seqno(struct intel_engine_cs *ring, u32 seqno)
+{
+ intel_write_status_page(ring, I915_GEM_HWS_INDEX, seqno);
+}
+
+static int gen8_emit_request(struct intel_ringbuffer *ringbuf)
+{
+ struct intel_engine_cs *ring = ringbuf->ring;
+ u32 cmd;
+ int ret;
+
+ ret = intel_logical_ring_begin(ringbuf, 6);
+ if (ret)
+ return ret;
+
+ cmd = MI_STORE_DWORD_IMM_GEN8;
+ cmd |= MI_GLOBAL_GTT;
+
+ intel_logical_ring_emit(ringbuf, cmd);
+ intel_logical_ring_emit(ringbuf,
+ (ring->status_page.gfx_addr +
+ (I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT)));
+ intel_logical_ring_emit(ringbuf, 0);
+ intel_logical_ring_emit(ringbuf, ring->outstanding_lazy_seqno);
+ intel_logical_ring_emit(ringbuf, MI_USER_INTERRUPT);
+ intel_logical_ring_emit(ringbuf, MI_NOOP);
+ intel_logical_ring_advance_and_submit(ringbuf);
+
+ return 0;
+}
+
+/**
+ * intel_logical_ring_cleanup() - deallocate the Engine Command Streamer
+ *
+ * @ring: Engine Command Streamer.
+ *
+ */
+void intel_logical_ring_cleanup(struct intel_engine_cs *ring)
+{
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+
+ if (!intel_ring_initialized(ring))
+ return;
+
+ intel_logical_ring_stop(ring);
+ WARN_ON((I915_READ_MODE(ring) & MODE_IDLE) == 0);
+ ring->preallocated_lazy_request = NULL;
+ ring->outstanding_lazy_seqno = 0;
+
+ if (ring->cleanup)
+ ring->cleanup(ring);
+
+ i915_cmd_parser_fini_ring(ring);
+
+ if (ring->status_page.obj) {
+ kunmap(sg_page(ring->status_page.obj->pages->sgl));
+ ring->status_page.obj = NULL;
+ }
+}
+
+static int logical_ring_init(struct drm_device *dev, struct intel_engine_cs *ring)
+{
+ int ret;
+
+ /* Intentionally left blank. */
+ ring->buffer = NULL;
+
+ ring->dev = dev;
+ INIT_LIST_HEAD(&ring->active_list);
+ INIT_LIST_HEAD(&ring->request_list);
+ init_waitqueue_head(&ring->irq_queue);
+
+ INIT_LIST_HEAD(&ring->execlist_queue);
+ spin_lock_init(&ring->execlist_lock);
+ ring->next_context_status_buffer = 0;
+
+ ret = i915_cmd_parser_init_ring(ring);
+ if (ret)
+ return ret;
+
+ if (ring->init) {
+ ret = ring->init(ring);
+ if (ret)
+ return ret;
+ }
+
+ ret = intel_lr_context_deferred_create(ring->default_context, ring);
+
+ return ret;
+}
+
+static int logical_render_ring_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring = &dev_priv->ring[RCS];
+
+ ring->name = "render ring";
+ ring->id = RCS;
+ ring->mmio_base = RENDER_RING_BASE;
+ ring->irq_enable_mask =
+ GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT;
+ ring->irq_keep_mask =
+ GT_CONTEXT_SWITCH_INTERRUPT << GEN8_RCS_IRQ_SHIFT;
+ if (HAS_L3_DPF(dev))
+ ring->irq_keep_mask |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
+
+ ring->init = gen8_init_render_ring;
+ ring->cleanup = intel_fini_pipe_control;
+ ring->get_seqno = gen8_get_seqno;
+ ring->set_seqno = gen8_set_seqno;
+ ring->emit_request = gen8_emit_request;
+ ring->emit_flush = gen8_emit_flush_render;
+ ring->irq_get = gen8_logical_ring_get_irq;
+ ring->irq_put = gen8_logical_ring_put_irq;
+ ring->emit_bb_start = gen8_emit_bb_start;
+
+ return logical_ring_init(dev, ring);
+}
+
+static int logical_bsd_ring_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring = &dev_priv->ring[VCS];
+
+ ring->name = "bsd ring";
+ ring->id = VCS;
+ ring->mmio_base = GEN6_BSD_RING_BASE;
+ ring->irq_enable_mask =
+ GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT;
+ ring->irq_keep_mask =
+ GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS1_IRQ_SHIFT;
+
+ ring->init = gen8_init_common_ring;
+ ring->get_seqno = gen8_get_seqno;
+ ring->set_seqno = gen8_set_seqno;
+ ring->emit_request = gen8_emit_request;
+ ring->emit_flush = gen8_emit_flush;
+ ring->irq_get = gen8_logical_ring_get_irq;
+ ring->irq_put = gen8_logical_ring_put_irq;
+ ring->emit_bb_start = gen8_emit_bb_start;
+
+ return logical_ring_init(dev, ring);
+}
+
+static int logical_bsd2_ring_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring = &dev_priv->ring[VCS2];
+
+ ring->name = "bds2 ring";
+ ring->id = VCS2;
+ ring->mmio_base = GEN8_BSD2_RING_BASE;
+ ring->irq_enable_mask =
+ GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT;
+ ring->irq_keep_mask =
+ GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VCS2_IRQ_SHIFT;
+
+ ring->init = gen8_init_common_ring;
+ ring->get_seqno = gen8_get_seqno;
+ ring->set_seqno = gen8_set_seqno;
+ ring->emit_request = gen8_emit_request;
+ ring->emit_flush = gen8_emit_flush;
+ ring->irq_get = gen8_logical_ring_get_irq;
+ ring->irq_put = gen8_logical_ring_put_irq;
+ ring->emit_bb_start = gen8_emit_bb_start;
+
+ return logical_ring_init(dev, ring);
+}
+
+static int logical_blt_ring_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring = &dev_priv->ring[BCS];
+
+ ring->name = "blitter ring";
+ ring->id = BCS;
+ ring->mmio_base = BLT_RING_BASE;
+ ring->irq_enable_mask =
+ GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT;
+ ring->irq_keep_mask =
+ GT_CONTEXT_SWITCH_INTERRUPT << GEN8_BCS_IRQ_SHIFT;
+
+ ring->init = gen8_init_common_ring;
+ ring->get_seqno = gen8_get_seqno;
+ ring->set_seqno = gen8_set_seqno;
+ ring->emit_request = gen8_emit_request;
+ ring->emit_flush = gen8_emit_flush;
+ ring->irq_get = gen8_logical_ring_get_irq;
+ ring->irq_put = gen8_logical_ring_put_irq;
+ ring->emit_bb_start = gen8_emit_bb_start;
+
+ return logical_ring_init(dev, ring);
+}
+
+static int logical_vebox_ring_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring = &dev_priv->ring[VECS];
+
+ ring->name = "video enhancement ring";
+ ring->id = VECS;
+ ring->mmio_base = VEBOX_RING_BASE;
+ ring->irq_enable_mask =
+ GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT;
+ ring->irq_keep_mask =
+ GT_CONTEXT_SWITCH_INTERRUPT << GEN8_VECS_IRQ_SHIFT;
+
+ ring->init = gen8_init_common_ring;
+ ring->get_seqno = gen8_get_seqno;
+ ring->set_seqno = gen8_set_seqno;
+ ring->emit_request = gen8_emit_request;
+ ring->emit_flush = gen8_emit_flush;
+ ring->irq_get = gen8_logical_ring_get_irq;
+ ring->irq_put = gen8_logical_ring_put_irq;
+ ring->emit_bb_start = gen8_emit_bb_start;
+
+ return logical_ring_init(dev, ring);
+}
+
+/**
+ * intel_logical_rings_init() - allocate, populate and init the Engine Command Streamers
+ * @dev: DRM device.
+ *
+ * This function inits the engines for an Execlists submission style (the equivalent in the
+ * legacy ringbuffer submission world would be i915_gem_init_rings). It does it only for
+ * those engines that are present in the hardware.
+ *
+ * Return: non-zero if the initialization failed.
+ */
+int intel_logical_rings_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
+ ret = logical_render_ring_init(dev);
+ if (ret)
+ return ret;
+
+ if (HAS_BSD(dev)) {
+ ret = logical_bsd_ring_init(dev);
+ if (ret)
+ goto cleanup_render_ring;
+ }
+
+ if (HAS_BLT(dev)) {
+ ret = logical_blt_ring_init(dev);
+ if (ret)
+ goto cleanup_bsd_ring;
+ }
+
+ if (HAS_VEBOX(dev)) {
+ ret = logical_vebox_ring_init(dev);
+ if (ret)
+ goto cleanup_blt_ring;
+ }
+
+ if (HAS_BSD2(dev)) {
+ ret = logical_bsd2_ring_init(dev);
+ if (ret)
+ goto cleanup_vebox_ring;
+ }
+
+ ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000));
+ if (ret)
+ goto cleanup_bsd2_ring;
+
+ return 0;
+
+cleanup_bsd2_ring:
+ intel_logical_ring_cleanup(&dev_priv->ring[VCS2]);
+cleanup_vebox_ring:
+ intel_logical_ring_cleanup(&dev_priv->ring[VECS]);
+cleanup_blt_ring:
+ intel_logical_ring_cleanup(&dev_priv->ring[BCS]);
+cleanup_bsd_ring:
+ intel_logical_ring_cleanup(&dev_priv->ring[VCS]);
+cleanup_render_ring:
+ intel_logical_ring_cleanup(&dev_priv->ring[RCS]);
+
+ return ret;
+}
+
+int intel_lr_context_render_state_init(struct intel_engine_cs *ring,
+ struct intel_context *ctx)
+{
+ struct intel_ringbuffer *ringbuf = ctx->engine[ring->id].ringbuf;
+ struct render_state so;
+ struct drm_i915_file_private *file_priv = ctx->file_priv;
+ struct drm_file *file = file_priv ? file_priv->file : NULL;
+ int ret;
+
+ ret = i915_gem_render_state_prepare(ring, &so);
+ if (ret)
+ return ret;
+
+ if (so.rodata == NULL)
+ return 0;
+
+ ret = ring->emit_bb_start(ringbuf,
+ so.ggtt_offset,
+ I915_DISPATCH_SECURE);
+ if (ret)
+ goto out;
+
+ i915_vma_move_to_active(i915_gem_obj_to_ggtt(so.obj), ring);
+
+ ret = __i915_add_request(ring, file, so.obj, NULL);
+ /* intel_logical_ring_add_request moves object to inactive if it
+ * fails */
+out:
+ i915_gem_render_state_fini(&so);
+ return ret;
+}
+
+static int
+populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_obj,
+ struct intel_engine_cs *ring, struct intel_ringbuffer *ringbuf)
+{
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *ring_obj = ringbuf->obj;
+ struct i915_hw_ppgtt *ppgtt = ctx->ppgtt;
+ struct page *page;
+ uint32_t *reg_state;
+ int ret;
+
+ if (!ppgtt)
+ ppgtt = dev_priv->mm.aliasing_ppgtt;
+
+ ret = i915_gem_object_set_to_cpu_domain(ctx_obj, true);
+ if (ret) {
+ DRM_DEBUG_DRIVER("Could not set to CPU domain\n");
+ return ret;
+ }
+
+ ret = i915_gem_object_get_pages(ctx_obj);
+ if (ret) {
+ DRM_DEBUG_DRIVER("Could not get object pages\n");
+ return ret;
+ }
+
+ i915_gem_object_pin_pages(ctx_obj);
+
+ /* The second page of the context object contains some fields which must
+ * be set up prior to the first execution. */
+ page = i915_gem_object_get_page(ctx_obj, 1);
+ reg_state = kmap_atomic(page);
+
+ /* A context is actually a big batch buffer with several MI_LOAD_REGISTER_IMM
+ * commands followed by (reg, value) pairs. The values we are setting here are
+ * only for the first context restore: on a subsequent save, the GPU will
+ * recreate this batchbuffer with new values (including all the missing
+ * MI_LOAD_REGISTER_IMM commands that we are not initializing here). */
+ if (ring->id == RCS)
+ reg_state[CTX_LRI_HEADER_0] = MI_LOAD_REGISTER_IMM(14);
+ else
+ reg_state[CTX_LRI_HEADER_0] = MI_LOAD_REGISTER_IMM(11);
+ reg_state[CTX_LRI_HEADER_0] |= MI_LRI_FORCE_POSTED;
+ reg_state[CTX_CONTEXT_CONTROL] = RING_CONTEXT_CONTROL(ring);
+ reg_state[CTX_CONTEXT_CONTROL+1] =
+ _MASKED_BIT_ENABLE((1<<3) | MI_RESTORE_INHIBIT);
+ reg_state[CTX_RING_HEAD] = RING_HEAD(ring->mmio_base);
+ reg_state[CTX_RING_HEAD+1] = 0;
+ reg_state[CTX_RING_TAIL] = RING_TAIL(ring->mmio_base);
+ reg_state[CTX_RING_TAIL+1] = 0;
+ reg_state[CTX_RING_BUFFER_START] = RING_START(ring->mmio_base);
+ reg_state[CTX_RING_BUFFER_START+1] = i915_gem_obj_ggtt_offset(ring_obj);
+ reg_state[CTX_RING_BUFFER_CONTROL] = RING_CTL(ring->mmio_base);
+ reg_state[CTX_RING_BUFFER_CONTROL+1] =
+ ((ringbuf->size - PAGE_SIZE) & RING_NR_PAGES) | RING_VALID;
+ reg_state[CTX_BB_HEAD_U] = ring->mmio_base + 0x168;
+ reg_state[CTX_BB_HEAD_U+1] = 0;
+ reg_state[CTX_BB_HEAD_L] = ring->mmio_base + 0x140;
+ reg_state[CTX_BB_HEAD_L+1] = 0;
+ reg_state[CTX_BB_STATE] = ring->mmio_base + 0x110;
+ reg_state[CTX_BB_STATE+1] = (1<<5);
+ reg_state[CTX_SECOND_BB_HEAD_U] = ring->mmio_base + 0x11c;
+ reg_state[CTX_SECOND_BB_HEAD_U+1] = 0;
+ reg_state[CTX_SECOND_BB_HEAD_L] = ring->mmio_base + 0x114;
+ reg_state[CTX_SECOND_BB_HEAD_L+1] = 0;
+ reg_state[CTX_SECOND_BB_STATE] = ring->mmio_base + 0x118;
+ reg_state[CTX_SECOND_BB_STATE+1] = 0;
+ if (ring->id == RCS) {
+ /* TODO: according to BSpec, the register state context
+ * for CHV does not have these. OTOH, these registers do
+ * exist in CHV. I'm waiting for a clarification */
+ reg_state[CTX_BB_PER_CTX_PTR] = ring->mmio_base + 0x1c0;
+ reg_state[CTX_BB_PER_CTX_PTR+1] = 0;
+ reg_state[CTX_RCS_INDIRECT_CTX] = ring->mmio_base + 0x1c4;
+ reg_state[CTX_RCS_INDIRECT_CTX+1] = 0;
+ reg_state[CTX_RCS_INDIRECT_CTX_OFFSET] = ring->mmio_base + 0x1c8;
+ reg_state[CTX_RCS_INDIRECT_CTX_OFFSET+1] = 0;
+ }
+ reg_state[CTX_LRI_HEADER_1] = MI_LOAD_REGISTER_IMM(9);
+ reg_state[CTX_LRI_HEADER_1] |= MI_LRI_FORCE_POSTED;
+ reg_state[CTX_CTX_TIMESTAMP] = ring->mmio_base + 0x3a8;
+ reg_state[CTX_CTX_TIMESTAMP+1] = 0;
+ reg_state[CTX_PDP3_UDW] = GEN8_RING_PDP_UDW(ring, 3);
+ reg_state[CTX_PDP3_LDW] = GEN8_RING_PDP_LDW(ring, 3);
+ reg_state[CTX_PDP2_UDW] = GEN8_RING_PDP_UDW(ring, 2);
+ reg_state[CTX_PDP2_LDW] = GEN8_RING_PDP_LDW(ring, 2);
+ reg_state[CTX_PDP1_UDW] = GEN8_RING_PDP_UDW(ring, 1);
+ reg_state[CTX_PDP1_LDW] = GEN8_RING_PDP_LDW(ring, 1);
+ reg_state[CTX_PDP0_UDW] = GEN8_RING_PDP_UDW(ring, 0);
+ reg_state[CTX_PDP0_LDW] = GEN8_RING_PDP_LDW(ring, 0);
+ reg_state[CTX_PDP3_UDW+1] = upper_32_bits(ppgtt->pd_dma_addr[3]);
+ reg_state[CTX_PDP3_LDW+1] = lower_32_bits(ppgtt->pd_dma_addr[3]);
+ reg_state[CTX_PDP2_UDW+1] = upper_32_bits(ppgtt->pd_dma_addr[2]);
+ reg_state[CTX_PDP2_LDW+1] = lower_32_bits(ppgtt->pd_dma_addr[2]);
+ reg_state[CTX_PDP1_UDW+1] = upper_32_bits(ppgtt->pd_dma_addr[1]);
+ reg_state[CTX_PDP1_LDW+1] = lower_32_bits(ppgtt->pd_dma_addr[1]);
+ reg_state[CTX_PDP0_UDW+1] = upper_32_bits(ppgtt->pd_dma_addr[0]);
+ reg_state[CTX_PDP0_LDW+1] = lower_32_bits(ppgtt->pd_dma_addr[0]);
+ if (ring->id == RCS) {
+ reg_state[CTX_LRI_HEADER_2] = MI_LOAD_REGISTER_IMM(1);
+ reg_state[CTX_R_PWR_CLK_STATE] = 0x20c8;
+ reg_state[CTX_R_PWR_CLK_STATE+1] = 0;
+ }
+
+ kunmap_atomic(reg_state);
+
+ ctx_obj->dirty = 1;
+ set_page_dirty(page);
+ i915_gem_object_unpin_pages(ctx_obj);
+
+ return 0;
+}
+
+/**
+ * intel_lr_context_free() - free the LRC specific bits of a context
+ * @ctx: the LR context to free.
+ *
+ * The real context freeing is done in i915_gem_context_free: this only
+ * takes care of the bits that are LRC related: the per-engine backing
+ * objects and the logical ringbuffer.
+ */
+void intel_lr_context_free(struct intel_context *ctx)
+{
+ int i;
+
+ for (i = 0; i < I915_NUM_RINGS; i++) {
+ struct drm_i915_gem_object *ctx_obj = ctx->engine[i].state;
+ struct intel_ringbuffer *ringbuf = ctx->engine[i].ringbuf;
+
+ if (ctx_obj) {
+ intel_destroy_ringbuffer_obj(ringbuf);
+ kfree(ringbuf);
+ i915_gem_object_ggtt_unpin(ctx_obj);
+ drm_gem_object_unreference(&ctx_obj->base);
+ }
+ }
+}
+
+static uint32_t get_lr_context_size(struct intel_engine_cs *ring)
+{
+ int ret = 0;
+
+ WARN_ON(INTEL_INFO(ring->dev)->gen != 8);
+
+ switch (ring->id) {
+ case RCS:
+ ret = GEN8_LR_CONTEXT_RENDER_SIZE;
+ break;
+ case VCS:
+ case BCS:
+ case VECS:
+ case VCS2:
+ ret = GEN8_LR_CONTEXT_OTHER_SIZE;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * intel_lr_context_deferred_create() - create the LRC specific bits of a context
+ * @ctx: LR context to create.
+ * @ring: engine to be used with the context.
+ *
+ * This function can be called more than once, with different engines, if we plan
+ * to use the context with them. The context backing objects and the ringbuffers
+ * (specially the ringbuffer backing objects) suck a lot of memory up, and that's why
+ * the creation is a deferred call: it's better to make sure first that we need to use
+ * a given ring with the context.
+ *
+ * Return: non-zero on eror.
+ */
+int intel_lr_context_deferred_create(struct intel_context *ctx,
+ struct intel_engine_cs *ring)
+{
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_gem_object *ctx_obj;
+ uint32_t context_size;
+ struct intel_ringbuffer *ringbuf;
+ int ret;
+
+ WARN_ON(ctx->legacy_hw_ctx.rcs_state != NULL);
+ if (ctx->engine[ring->id].state)
+ return 0;
+
+ context_size = round_up(get_lr_context_size(ring), 4096);
+
+ ctx_obj = i915_gem_alloc_context_obj(dev, context_size);
+ if (IS_ERR(ctx_obj)) {
+ ret = PTR_ERR(ctx_obj);
+ DRM_DEBUG_DRIVER("Alloc LRC backing obj failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = i915_gem_obj_ggtt_pin(ctx_obj, GEN8_LR_CONTEXT_ALIGN, 0);
+ if (ret) {
+ DRM_DEBUG_DRIVER("Pin LRC backing obj failed: %d\n", ret);
+ drm_gem_object_unreference(&ctx_obj->base);
+ return ret;
+ }
+
+ ringbuf = kzalloc(sizeof(*ringbuf), GFP_KERNEL);
+ if (!ringbuf) {
+ DRM_DEBUG_DRIVER("Failed to allocate ringbuffer %s\n",
+ ring->name);
+ i915_gem_object_ggtt_unpin(ctx_obj);
+ drm_gem_object_unreference(&ctx_obj->base);
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ ringbuf->ring = ring;
+ ringbuf->FIXME_lrc_ctx = ctx;
+
+ ringbuf->size = 32 * PAGE_SIZE;
+ ringbuf->effective_size = ringbuf->size;
+ ringbuf->head = 0;
+ ringbuf->tail = 0;
+ ringbuf->space = ringbuf->size;
+ ringbuf->last_retired_head = -1;
+
+ /* TODO: For now we put this in the mappable region so that we can reuse
+ * the existing ringbuffer code which ioremaps it. When we start
+ * creating many contexts, this will no longer work and we must switch
+ * to a kmapish interface.
+ */
+ ret = intel_alloc_ringbuffer_obj(dev, ringbuf);
+ if (ret) {
+ DRM_DEBUG_DRIVER("Failed to allocate ringbuffer obj %s: %d\n",
+ ring->name, ret);
+ goto error;
+ }
+
+ ret = populate_lr_context(ctx, ctx_obj, ring, ringbuf);
+ if (ret) {
+ DRM_DEBUG_DRIVER("Failed to populate LRC: %d\n", ret);
+ intel_destroy_ringbuffer_obj(ringbuf);
+ goto error;
+ }
+
+ ctx->engine[ring->id].ringbuf = ringbuf;
+ ctx->engine[ring->id].state = ctx_obj;
+
+ if (ctx == ring->default_context) {
+ /* The status page is offset 0 from the default context object
+ * in LRC mode. */
+ ring->status_page.gfx_addr = i915_gem_obj_ggtt_offset(ctx_obj);
+ ring->status_page.page_addr =
+ kmap(sg_page(ctx_obj->pages->sgl));
+ if (ring->status_page.page_addr == NULL)
+ return -ENOMEM;
+ ring->status_page.obj = ctx_obj;
+ }
+
+ if (ring->id == RCS && !ctx->rcs_initialized) {
+ ret = intel_lr_context_render_state_init(ring, ctx);
+ if (ret) {
+ DRM_ERROR("Init render state failed: %d\n", ret);
+ ctx->engine[ring->id].ringbuf = NULL;
+ ctx->engine[ring->id].state = NULL;
+ intel_destroy_ringbuffer_obj(ringbuf);
+ goto error;
+ }
+ ctx->rcs_initialized = true;
+ }
+
+ return 0;
+
+error:
+ kfree(ringbuf);
+ i915_gem_object_ggtt_unpin(ctx_obj);
+ drm_gem_object_unreference(&ctx_obj->base);
+ return ret;
+}
diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h
new file mode 100644
index 000000000000..33c3b4bf28c5
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_lrc.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ */
+
+#ifndef _INTEL_LRC_H_
+#define _INTEL_LRC_H_
+
+/* Execlists regs */
+#define RING_ELSP(ring) ((ring)->mmio_base+0x230)
+#define RING_EXECLIST_STATUS(ring) ((ring)->mmio_base+0x234)
+#define RING_CONTEXT_CONTROL(ring) ((ring)->mmio_base+0x244)
+#define RING_CONTEXT_STATUS_BUF(ring) ((ring)->mmio_base+0x370)
+#define RING_CONTEXT_STATUS_PTR(ring) ((ring)->mmio_base+0x3a0)
+
+/* Logical Rings */
+void intel_logical_ring_stop(struct intel_engine_cs *ring);
+void intel_logical_ring_cleanup(struct intel_engine_cs *ring);
+int intel_logical_rings_init(struct drm_device *dev);
+
+int logical_ring_flush_all_caches(struct intel_ringbuffer *ringbuf);
+void intel_logical_ring_advance_and_submit(struct intel_ringbuffer *ringbuf);
+/**
+ * intel_logical_ring_advance() - advance the ringbuffer tail
+ * @ringbuf: Ringbuffer to advance.
+ *
+ * The tail is only updated in our logical ringbuffer struct.
+ */
+static inline void intel_logical_ring_advance(struct intel_ringbuffer *ringbuf)
+{
+ ringbuf->tail &= ringbuf->size - 1;
+}
+/**
+ * intel_logical_ring_emit() - write a DWORD to the ringbuffer.
+ * @ringbuf: Ringbuffer to write to.
+ * @data: DWORD to write.
+ */
+static inline void intel_logical_ring_emit(struct intel_ringbuffer *ringbuf,
+ u32 data)
+{
+ iowrite32(data, ringbuf->virtual_start + ringbuf->tail);
+ ringbuf->tail += 4;
+}
+int intel_logical_ring_begin(struct intel_ringbuffer *ringbuf, int num_dwords);
+
+/* Logical Ring Contexts */
+int intel_lr_context_render_state_init(struct intel_engine_cs *ring,
+ struct intel_context *ctx);
+void intel_lr_context_free(struct intel_context *ctx);
+int intel_lr_context_deferred_create(struct intel_context *ctx,
+ struct intel_engine_cs *ring);
+
+/* Execlists */
+int intel_sanitize_enable_execlists(struct drm_device *dev, int enable_execlists);
+int intel_execlists_submission(struct drm_device *dev, struct drm_file *file,
+ struct intel_engine_cs *ring,
+ struct intel_context *ctx,
+ struct drm_i915_gem_execbuffer2 *args,
+ struct list_head *vmas,
+ struct drm_i915_gem_object *batch_obj,
+ u64 exec_start, u32 flags);
+u32 intel_execlists_ctx_id(struct drm_i915_gem_object *ctx_obj);
+
+/**
+ * struct intel_ctx_submit_request - queued context submission request
+ * @ctx: Context to submit to the ELSP.
+ * @ring: Engine to submit it to.
+ * @tail: how far in the context's ringbuffer this request goes to.
+ * @execlist_link: link in the submission queue.
+ * @work: workqueue for processing this request in a bottom half.
+ * @elsp_submitted: no. of times this request has been sent to the ELSP.
+ *
+ * The ELSP only accepts two elements at a time, so we queue context/tail
+ * pairs on a given queue (ring->execlist_queue) until the hardware is
+ * available. The queue serves a double purpose: we also use it to keep track
+ * of the up to 2 contexts currently in the hardware (usually one in execution
+ * and the other queued up by the GPU): We only remove elements from the head
+ * of the queue when the hardware informs us that an element has been
+ * completed.
+ *
+ * All accesses to the queue are mediated by a spinlock (ring->execlist_lock).
+ */
+struct intel_ctx_submit_request {
+ struct intel_context *ctx;
+ struct intel_engine_cs *ring;
+ u32 tail;
+
+ struct list_head execlist_link;
+ struct work_struct work;
+
+ int elsp_submitted;
+};
+
+void intel_execlists_handle_ctx_events(struct intel_engine_cs *ring);
+
+#endif /* _INTEL_LRC_H_ */
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 5e5a72fca5fb..a6bd1422e38f 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -51,6 +51,7 @@ struct intel_lvds_encoder {
bool is_dual_link;
u32 reg;
+ u32 a3_power;
struct intel_lvds_connector *attached_connector;
};
@@ -71,8 +72,13 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
+ enum intel_display_power_domain power_domain;
u32 tmp;
+ power_domain = intel_display_port_power_domain(encoder);
+ if (!intel_display_power_enabled(dev_priv, power_domain))
+ return false;
+
tmp = I915_READ(lvds_encoder->reg);
if (!(tmp & LVDS_PORT_EN))
@@ -172,8 +178,11 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder)
/* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
* appropriately here, but we need to look more thoroughly into how
- * panels behave in the two modes.
+ * panels behave in the two modes. For now, let's just maintain the
+ * value we got from the BIOS.
*/
+ temp &= ~LVDS_A3_POWER_MASK;
+ temp |= lvds_encoder->a3_power;
/* Set the dithering flag on LVDS as needed, note that there is no
* special lvds dither control bit on pch-split platforms, dithering is
@@ -271,7 +280,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
struct intel_crtc_config *pipe_config)
{
struct drm_device *dev = intel_encoder->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_lvds_encoder *lvds_encoder =
to_lvds_encoder(&intel_encoder->base);
struct intel_connector *intel_connector =
@@ -286,8 +294,7 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
return false;
}
- if ((I915_READ(lvds_encoder->reg) & LVDS_A3_POWER_MASK) ==
- LVDS_A3_POWER_UP)
+ if (lvds_encoder->a3_power == LVDS_A3_POWER_UP)
lvds_bpp = 8*3;
else
lvds_bpp = 6*3;
@@ -531,7 +538,7 @@ static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
.destroy = intel_encoder_destroy,
};
-static int __init intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
+static int intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
{
DRM_INFO("Skipping LVDS initialization for %s\n", id->ident);
return 1;
@@ -816,8 +823,7 @@ bool intel_is_dual_link_lvds(struct drm_device *dev)
struct intel_encoder *encoder;
struct intel_lvds_encoder *lvds_encoder;
- list_for_each_entry(encoder, &dev->mode_config.encoder_list,
- base.head) {
+ for_each_intel_encoder(dev, encoder) {
if (encoder->type == INTEL_OUTPUT_LVDS) {
lvds_encoder = to_lvds_encoder(&encoder->base);
@@ -1088,6 +1094,9 @@ out:
DRM_DEBUG_KMS("detected %s-link lvds configuration\n",
lvds_encoder->is_dual_link ? "dual" : "single");
+ lvds_encoder->a3_power = I915_READ(lvds_encoder->reg) &
+ LVDS_A3_POWER_MASK;
+
/*
* Unlock registers and just
* leave them unlocked
@@ -1104,7 +1113,7 @@ out:
DRM_DEBUG_KMS("lid notifier registration failed\n");
lvds_connector->lid_notifier.notifier_call = NULL;
}
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
intel_panel_setup_backlight(connector);
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index 4f6b53998d79..d8de1d5140a7 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -352,6 +352,7 @@ int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder,
case INTEL_OUTPUT_UNKNOWN:
case INTEL_OUTPUT_DISPLAYPORT:
case INTEL_OUTPUT_HDMI:
+ case INTEL_OUTPUT_DP_MST:
type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL;
break;
case INTEL_OUTPUT_EDP:
@@ -395,6 +396,16 @@ int intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state)
return -EINVAL;
}
+/*
+ * If the vendor backlight interface is not in use and ACPI backlight interface
+ * is broken, do not bother processing backlight change requests from firmware.
+ */
+static bool should_ignore_backlight_request(void)
+{
+ return acpi_video_backlight_support() &&
+ !acpi_video_verify_backlight_support();
+}
+
static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -403,11 +414,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp);
- /*
- * If the acpi_video interface is not supposed to be used, don't
- * bother processing backlight level change requests from firmware.
- */
- if (!acpi_video_verify_backlight_support()) {
+ if (should_ignore_backlight_request()) {
DRM_DEBUG_KMS("opregion backlight request ignored\n");
return 0;
}
@@ -427,7 +434,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
*/
DRM_DEBUG_KMS("updating opregion backlight %d/255\n", bclp);
list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head)
- intel_panel_set_backlight(intel_connector, bclp, 255);
+ intel_panel_set_backlight_acpi(intel_connector, bclp, 255);
iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv);
drm_modeset_unlock(&dev->mode_config.connection_mutex);
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index daa118978eec..dc2f4f26c961 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -415,6 +415,10 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
}
intel_overlay_release_old_vid_tail(overlay);
+
+
+ i915_gem_track_fb(overlay->old_vid_bo, NULL,
+ INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe));
return 0;
}
@@ -686,6 +690,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
bool scale_changed = false;
struct drm_device *dev = overlay->dev;
u32 swidth, swidthsw, sheight, ostride;
+ enum pipe pipe = overlay->crtc->pipe;
BUG_ON(!mutex_is_locked(&dev->struct_mutex));
BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
@@ -713,7 +718,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
oconfig = OCONF_CC_OUT_8BIT;
if (IS_GEN4(overlay->dev))
oconfig |= OCONF_CSC_MODE_BT709;
- oconfig |= overlay->crtc->pipe == 0 ?
+ oconfig |= pipe == 0 ?
OCONF_PIPE_A : OCONF_PIPE_B;
iowrite32(oconfig, &regs->OCONFIG);
intel_overlay_unmap_regs(overlay, regs);
@@ -776,9 +781,15 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
if (ret)
goto out_unpin;
+ i915_gem_track_fb(overlay->vid_bo, new_bo,
+ INTEL_FRONTBUFFER_OVERLAY(pipe));
+
overlay->old_vid_bo = overlay->vid_bo;
overlay->vid_bo = new_bo;
+ intel_frontbuffer_flip(dev,
+ INTEL_FRONTBUFFER_OVERLAY(pipe));
+
return 0;
out_unpin:
@@ -1028,7 +1039,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
struct drm_intel_overlay_put_image *put_image_rec = data;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_overlay *overlay;
- struct drm_mode_object *drmmode_obj;
+ struct drm_crtc *drmmode_crtc;
struct intel_crtc *crtc;
struct drm_i915_gem_object *new_bo;
struct put_image_params *params;
@@ -1057,13 +1068,12 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
if (!params)
return -ENOMEM;
- drmmode_obj = drm_mode_object_find(dev, put_image_rec->crtc_id,
- DRM_MODE_OBJECT_CRTC);
- if (!drmmode_obj) {
+ drmmode_crtc = drm_crtc_find(dev, put_image_rec->crtc_id);
+ if (!drmmode_crtc) {
ret = -ENOENT;
goto out_free;
}
- crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
+ crtc = to_intel_crtc(drmmode_crtc);
new_bo = to_intel_bo(drm_gem_object_lookup(dev, file_priv,
put_image_rec->bo_handle));
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 12b02fe1d0ae..41b3be217493 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -398,6 +398,68 @@ intel_panel_detect(struct drm_device *dev)
}
}
+/**
+ * scale - scale values from one range to another
+ *
+ * @source_val: value in range [@source_min..@source_max]
+ *
+ * Return @source_val in range [@source_min..@source_max] scaled to range
+ * [@target_min..@target_max].
+ */
+static uint32_t scale(uint32_t source_val,
+ uint32_t source_min, uint32_t source_max,
+ uint32_t target_min, uint32_t target_max)
+{
+ uint64_t target_val;
+
+ WARN_ON(source_min > source_max);
+ WARN_ON(target_min > target_max);
+
+ /* defensive */
+ source_val = clamp(source_val, source_min, source_max);
+
+ /* avoid overflows */
+ target_val = DIV_ROUND_CLOSEST_ULL((uint64_t)(source_val - source_min) *
+ (target_max - target_min), source_max - source_min);
+ target_val += target_min;
+
+ return target_val;
+}
+
+/* Scale user_level in range [0..user_max] to [hw_min..hw_max]. */
+static inline u32 scale_user_to_hw(struct intel_connector *connector,
+ u32 user_level, u32 user_max)
+{
+ struct intel_panel *panel = &connector->panel;
+
+ return scale(user_level, 0, user_max,
+ panel->backlight.min, panel->backlight.max);
+}
+
+/* Scale user_level in range [0..user_max] to [0..hw_max], clamping the result
+ * to [hw_min..hw_max]. */
+static inline u32 clamp_user_to_hw(struct intel_connector *connector,
+ u32 user_level, u32 user_max)
+{
+ struct intel_panel *panel = &connector->panel;
+ u32 hw_level;
+
+ hw_level = scale(user_level, 0, user_max, 0, panel->backlight.max);
+ hw_level = clamp(hw_level, panel->backlight.min, panel->backlight.max);
+
+ return hw_level;
+}
+
+/* Scale hw_level in range [hw_min..hw_max] to [0..user_max]. */
+static inline u32 scale_hw_to_user(struct intel_connector *connector,
+ u32 hw_level, u32 user_max)
+{
+ struct intel_panel *panel = &connector->panel;
+
+ return scale(hw_level, panel->backlight.min, panel->backlight.max,
+ 0, user_max);
+}
+
static u32 intel_panel_compute_brightness(struct intel_connector *connector,
u32 val)
{
@@ -557,17 +619,16 @@ intel_panel_actually_set_backlight(struct intel_connector *connector, u32 level)
dev_priv->display.set_backlight(connector, level);
}
-/* set backlight brightness to level in range [0..max] */
-void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
- u32 max)
+/* set backlight brightness to level in range [0..max], scaling wrt hw min */
+static void intel_panel_set_backlight(struct intel_connector *connector,
+ u32 user_level, u32 user_max)
{
struct drm_device *dev = connector->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_panel *panel = &connector->panel;
enum pipe pipe = intel_get_pipe_from_connector(connector);
- u32 freq;
+ u32 hw_level;
unsigned long flags;
- u64 n;
if (!panel->backlight.present || pipe == INVALID_PIPE)
return;
@@ -576,18 +637,46 @@ void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
WARN_ON(panel->backlight.max == 0);
- /* scale to hardware max, but be careful to not overflow */
- freq = panel->backlight.max;
- n = (u64)level * freq;
- do_div(n, max);
- level = n;
+ hw_level = scale_user_to_hw(connector, user_level, user_max);
+ panel->backlight.level = hw_level;
+
+ if (panel->backlight.enabled)
+ intel_panel_actually_set_backlight(connector, hw_level);
+
+ spin_unlock_irqrestore(&dev_priv->backlight_lock, flags);
+}
+
+/* set backlight brightness to level in range [0..max], assuming hw min is
+ * respected.
+ */
+void intel_panel_set_backlight_acpi(struct intel_connector *connector,
+ u32 user_level, u32 user_max)
+{
+ struct drm_device *dev = connector->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_panel *panel = &connector->panel;
+ enum pipe pipe = intel_get_pipe_from_connector(connector);
+ u32 hw_level;
+ unsigned long flags;
+
+ if (!panel->backlight.present || pipe == INVALID_PIPE)
+ return;
+
+ spin_lock_irqsave(&dev_priv->backlight_lock, flags);
+
+ WARN_ON(panel->backlight.max == 0);
+
+ hw_level = clamp_user_to_hw(connector, user_level, user_max);
+ panel->backlight.level = hw_level;
- panel->backlight.level = level;
if (panel->backlight.device)
- panel->backlight.device->props.brightness = level;
+ panel->backlight.device->props.brightness =
+ scale_hw_to_user(connector,
+ panel->backlight.level,
+ panel->backlight.device->props.max_brightness);
if (panel->backlight.enabled)
- intel_panel_actually_set_backlight(connector, level);
+ intel_panel_actually_set_backlight(connector, hw_level);
spin_unlock_irqrestore(&dev_priv->backlight_lock, flags);
}
@@ -661,6 +750,8 @@ void intel_panel_disable_backlight(struct intel_connector *connector)
spin_lock_irqsave(&dev_priv->backlight_lock, flags);
+ if (panel->backlight.device)
+ panel->backlight.device->props.power = FB_BLANK_POWERDOWN;
panel->backlight.enabled = false;
dev_priv->display.disable_backlight(connector);
@@ -711,7 +802,7 @@ static void pch_enable_backlight(struct intel_connector *connector)
cpu_ctl2 = I915_READ(BLC_PWM_CPU_CTL2);
if (cpu_ctl2 & BLM_PWM_ENABLE) {
- WARN(1, "cpu backlight already enabled\n");
+ DRM_DEBUG_KMS("cpu backlight already enabled\n");
cpu_ctl2 &= ~BLM_PWM_ENABLE;
I915_WRITE(BLC_PWM_CPU_CTL2, cpu_ctl2);
}
@@ -755,7 +846,7 @@ static void i9xx_enable_backlight(struct intel_connector *connector)
ctl = I915_READ(BLC_PWM_CTL);
if (ctl & BACKLIGHT_DUTY_CYCLE_MASK_PNV) {
- WARN(1, "backlight already enabled\n");
+ DRM_DEBUG_KMS("backlight already enabled\n");
I915_WRITE(BLC_PWM_CTL, 0);
}
@@ -786,7 +877,7 @@ static void i965_enable_backlight(struct intel_connector *connector)
ctl2 = I915_READ(BLC_PWM_CTL2);
if (ctl2 & BLM_PWM_ENABLE) {
- WARN(1, "backlight already enabled\n");
+ DRM_DEBUG_KMS("backlight already enabled\n");
ctl2 &= ~BLM_PWM_ENABLE;
I915_WRITE(BLC_PWM_CTL2, ctl2);
}
@@ -820,7 +911,7 @@ static void vlv_enable_backlight(struct intel_connector *connector)
ctl2 = I915_READ(VLV_BLC_PWM_CTL2(pipe));
if (ctl2 & BLM_PWM_ENABLE) {
- WARN(1, "backlight already enabled\n");
+ DRM_DEBUG_KMS("backlight already enabled\n");
ctl2 &= ~BLM_PWM_ENABLE;
I915_WRITE(VLV_BLC_PWM_CTL2(pipe), ctl2);
}
@@ -860,11 +951,15 @@ void intel_panel_enable_backlight(struct intel_connector *connector)
panel->backlight.level = panel->backlight.max;
if (panel->backlight.device)
panel->backlight.device->props.brightness =
- panel->backlight.level;
+ scale_hw_to_user(connector,
+ panel->backlight.level,
+ panel->backlight.device->props.max_brightness);
}
dev_priv->display.enable_backlight(connector);
panel->backlight.enabled = true;
+ if (panel->backlight.device)
+ panel->backlight.device->props.power = FB_BLANK_UNBLANK;
spin_unlock_irqrestore(&dev_priv->backlight_lock, flags);
}
@@ -873,6 +968,7 @@ void intel_panel_enable_backlight(struct intel_connector *connector)
static int intel_backlight_device_update_status(struct backlight_device *bd)
{
struct intel_connector *connector = bl_get_data(bd);
+ struct intel_panel *panel = &connector->panel;
struct drm_device *dev = connector->base.dev;
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
@@ -880,6 +976,23 @@ static int intel_backlight_device_update_status(struct backlight_device *bd)
bd->props.brightness, bd->props.max_brightness);
intel_panel_set_backlight(connector, bd->props.brightness,
bd->props.max_brightness);
+
+ /*
+ * Allow flipping bl_power as a sub-state of enabled. Sadly the
+ * backlight class device does not make it easy to to differentiate
+ * between callbacks for brightness and bl_power, so our backlight_power
+ * callback needs to take this into account.
+ */
+ if (panel->backlight.enabled) {
+ if (panel->backlight_power) {
+ bool enable = bd->props.power == FB_BLANK_UNBLANK &&
+ bd->props.brightness != 0;
+ panel->backlight_power(connector, enable);
+ }
+ } else {
+ bd->props.power = FB_BLANK_POWERDOWN;
+ }
+
drm_modeset_unlock(&dev->mode_config.connection_mutex);
return 0;
}
@@ -889,11 +1002,15 @@ static int intel_backlight_device_get_brightness(struct backlight_device *bd)
struct intel_connector *connector = bl_get_data(bd);
struct drm_device *dev = connector->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 hw_level;
int ret;
intel_runtime_pm_get(dev_priv);
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
- ret = intel_panel_get_backlight(connector);
+
+ hw_level = intel_panel_get_backlight(connector);
+ ret = scale_hw_to_user(connector, hw_level, bd->props.max_brightness);
+
drm_modeset_unlock(&dev->mode_config.connection_mutex);
intel_runtime_pm_put(dev_priv);
@@ -913,12 +1030,24 @@ static int intel_backlight_device_register(struct intel_connector *connector)
if (WARN_ON(panel->backlight.device))
return -ENODEV;
- BUG_ON(panel->backlight.max == 0);
+ WARN_ON(panel->backlight.max == 0);
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_RAW;
- props.brightness = panel->backlight.level;
+
+ /*
+ * Note: Everything should work even if the backlight device max
+ * presented to the userspace is arbitrarily chosen.
+ */
props.max_brightness = panel->backlight.max;
+ props.brightness = scale_hw_to_user(connector,
+ panel->backlight.level,
+ props.max_brightness);
+
+ if (panel->backlight.enabled)
+ props.power = FB_BLANK_UNBLANK;
+ else
+ props.power = FB_BLANK_POWERDOWN;
/*
* Note: using the same name independent of the connector prevents
@@ -964,6 +1093,32 @@ static void intel_backlight_device_unregister(struct intel_connector *connector)
* XXX: Query mode clock or hardware clock and program PWM modulation frequency
* appropriately when it's 0. Use VBT and/or sane defaults.
*/
+static u32 get_backlight_min_vbt(struct intel_connector *connector)
+{
+ struct drm_device *dev = connector->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_panel *panel = &connector->panel;
+ int min;
+
+ WARN_ON(panel->backlight.max == 0);
+
+ /*
+ * XXX: If the vbt value is 255, it makes min equal to max, which leads
+ * to problems. There are such machines out there. Either our
+ * interpretation is wrong or the vbt has bogus data. Or both. Safeguard
+ * against this by letting the minimum be at most (arbitrarily chosen)
+ * 25% of the max.
+ */
+ min = clamp_t(int, dev_priv->vbt.backlight.min_brightness, 0, 64);
+ if (min != dev_priv->vbt.backlight.min_brightness) {
+ DRM_DEBUG_KMS("clamping VBT min backlight %d/255 to %d/255\n",
+ dev_priv->vbt.backlight.min_brightness, min);
+ }
+
+ /* vbt value is a coefficient in range [0..255] */
+ return scale(min, 0, 255, 0, panel->backlight.max);
+}
+
static int bdw_setup_backlight(struct intel_connector *connector)
{
struct drm_device *dev = connector->base.dev;
@@ -979,6 +1134,8 @@ static int bdw_setup_backlight(struct intel_connector *connector)
if (!panel->backlight.max)
return -ENODEV;
+ panel->backlight.min = get_backlight_min_vbt(connector);
+
val = bdw_get_backlight(connector);
panel->backlight.level = intel_panel_compute_brightness(connector, val);
@@ -1003,6 +1160,8 @@ static int pch_setup_backlight(struct intel_connector *connector)
if (!panel->backlight.max)
return -ENODEV;
+ panel->backlight.min = get_backlight_min_vbt(connector);
+
val = pch_get_backlight(connector);
panel->backlight.level = intel_panel_compute_brightness(connector, val);
@@ -1035,6 +1194,8 @@ static int i9xx_setup_backlight(struct intel_connector *connector)
if (!panel->backlight.max)
return -ENODEV;
+ panel->backlight.min = get_backlight_min_vbt(connector);
+
val = i9xx_get_backlight(connector);
panel->backlight.level = intel_panel_compute_brightness(connector, val);
@@ -1062,6 +1223,8 @@ static int i965_setup_backlight(struct intel_connector *connector)
if (!panel->backlight.max)
return -ENODEV;
+ panel->backlight.min = get_backlight_min_vbt(connector);
+
val = i9xx_get_backlight(connector);
panel->backlight.level = intel_panel_compute_brightness(connector, val);
@@ -1079,7 +1242,7 @@ static int vlv_setup_backlight(struct intel_connector *connector)
enum pipe pipe;
u32 ctl, ctl2, val;
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
u32 cur_val = I915_READ(VLV_BLC_PWM_CTL(pipe));
/* Skip if the modulation freq is already set */
@@ -1099,6 +1262,8 @@ static int vlv_setup_backlight(struct intel_connector *connector)
if (!panel->backlight.max)
return -ENODEV;
+ panel->backlight.min = get_backlight_min_vbt(connector);
+
val = _vlv_get_backlight(dev, PIPE_A);
panel->backlight.level = intel_panel_compute_brightness(connector, val);
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index f1233f544f3e..c27b6140bfd1 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -93,8 +93,7 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_framebuffer *fb = crtc->primary->fb;
- struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
- struct drm_i915_gem_object *obj = intel_fb->obj;
+ struct drm_i915_gem_object *obj = intel_fb_obj(fb);
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int cfb_pitch;
int i;
@@ -150,8 +149,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_framebuffer *fb = crtc->primary->fb;
- struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
- struct drm_i915_gem_object *obj = intel_fb->obj;
+ struct drm_i915_gem_object *obj = intel_fb_obj(fb);
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
u32 dpfc_ctl;
@@ -222,16 +220,26 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_framebuffer *fb = crtc->primary->fb;
- struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
- struct drm_i915_gem_object *obj = intel_fb->obj;
+ struct drm_i915_gem_object *obj = intel_fb_obj(fb);
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
u32 dpfc_ctl;
dpfc_ctl = DPFC_CTL_PLANE(intel_crtc->plane);
if (drm_format_plane_cpp(fb->pixel_format, 0) == 2)
+ dev_priv->fbc.threshold++;
+
+ switch (dev_priv->fbc.threshold) {
+ case 4:
+ case 3:
+ dpfc_ctl |= DPFC_CTL_LIMIT_4X;
+ break;
+ case 2:
dpfc_ctl |= DPFC_CTL_LIMIT_2X;
- else
+ break;
+ case 1:
dpfc_ctl |= DPFC_CTL_LIMIT_1X;
+ break;
+ }
dpfc_ctl |= DPFC_CTL_FENCE_EN;
if (IS_GEN5(dev))
dpfc_ctl |= obj->fence_reg;
@@ -278,18 +286,32 @@ static void gen7_enable_fbc(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_framebuffer *fb = crtc->primary->fb;
- struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
- struct drm_i915_gem_object *obj = intel_fb->obj;
+ struct drm_i915_gem_object *obj = intel_fb_obj(fb);
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
u32 dpfc_ctl;
dpfc_ctl = IVB_DPFC_CTL_PLANE(intel_crtc->plane);
if (drm_format_plane_cpp(fb->pixel_format, 0) == 2)
+ dev_priv->fbc.threshold++;
+
+ switch (dev_priv->fbc.threshold) {
+ case 4:
+ case 3:
+ dpfc_ctl |= DPFC_CTL_LIMIT_4X;
+ break;
+ case 2:
dpfc_ctl |= DPFC_CTL_LIMIT_2X;
- else
+ break;
+ case 1:
dpfc_ctl |= DPFC_CTL_LIMIT_1X;
+ break;
+ }
+
dpfc_ctl |= IVB_DPFC_CTL_FENCE_EN;
+ if (dev_priv->fbc.false_color)
+ dpfc_ctl |= FBC_CTL_FALSE_COLOR;
+
I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN);
if (IS_IVYBRIDGE(dev)) {
@@ -323,6 +345,16 @@ bool intel_fbc_enabled(struct drm_device *dev)
return dev_priv->display.fbc_enabled(dev);
}
+void gen8_fbc_sw_flush(struct drm_device *dev, u32 value)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (!IS_GEN8(dev))
+ return;
+
+ I915_WRITE(MSG_FBC_REND_STATE, value);
+}
+
static void intel_fbc_work_fn(struct work_struct *__work)
{
struct intel_fbc_work *work =
@@ -462,7 +494,6 @@ void intel_update_fbc(struct drm_device *dev)
struct drm_crtc *crtc = NULL, *tmp_crtc;
struct intel_crtc *intel_crtc;
struct drm_framebuffer *fb;
- struct intel_framebuffer *intel_fb;
struct drm_i915_gem_object *obj;
const struct drm_display_mode *adjusted_mode;
unsigned int max_width, max_height;
@@ -507,8 +538,7 @@ void intel_update_fbc(struct drm_device *dev)
intel_crtc = to_intel_crtc(crtc);
fb = crtc->primary->fb;
- intel_fb = to_intel_framebuffer(fb);
- obj = intel_fb->obj;
+ obj = intel_fb_obj(fb);
adjusted_mode = &intel_crtc->config.adjusted_mode;
if (i915.enable_fbc < 0) {
@@ -529,7 +559,10 @@ void intel_update_fbc(struct drm_device *dev)
goto out_disable;
}
- if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
+ if (INTEL_INFO(dev)->gen >= 8 || IS_HASWELL(dev)) {
+ max_width = 4096;
+ max_height = 4096;
+ } else if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) {
max_width = 4096;
max_height = 2048;
} else {
@@ -558,12 +591,19 @@ void intel_update_fbc(struct drm_device *dev)
DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n");
goto out_disable;
}
+ if (INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev) &&
+ to_intel_plane(crtc->primary)->rotation != BIT(DRM_ROTATE_0)) {
+ if (set_no_fbc_reason(dev_priv, FBC_UNSUPPORTED_MODE))
+ DRM_DEBUG_KMS("Rotation unsupported, disabling\n");
+ goto out_disable;
+ }
/* If the kernel debugger is active, always disable compression */
if (in_dbg_master())
goto out_disable;
- if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size)) {
+ if (i915_gem_stolen_setup_compression(dev, obj->base.size,
+ drm_format_plane_cpp(fb->pixel_format, 0))) {
if (set_no_fbc_reason(dev_priv, FBC_STOLEN_TOO_SMALL))
DRM_DEBUG_KMS("framebuffer too large, disabling compression\n");
goto out_disable;
@@ -789,12 +829,33 @@ static const struct cxsr_latency *intel_get_cxsr_latency(int is_desktop,
return NULL;
}
-static void pineview_disable_cxsr(struct drm_device *dev)
+void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_device *dev = dev_priv->dev;
+ u32 val;
- /* deactivate cxsr */
- I915_WRITE(DSPFW3, I915_READ(DSPFW3) & ~PINEVIEW_SELF_REFRESH_EN);
+ if (IS_VALLEYVIEW(dev)) {
+ I915_WRITE(FW_BLC_SELF_VLV, enable ? FW_CSPWRDWNEN : 0);
+ } else if (IS_G4X(dev) || IS_CRESTLINE(dev)) {
+ I915_WRITE(FW_BLC_SELF, enable ? FW_BLC_SELF_EN : 0);
+ } else if (IS_PINEVIEW(dev)) {
+ val = I915_READ(DSPFW3) & ~PINEVIEW_SELF_REFRESH_EN;
+ val |= enable ? PINEVIEW_SELF_REFRESH_EN : 0;
+ I915_WRITE(DSPFW3, val);
+ } else if (IS_I945G(dev) || IS_I945GM(dev)) {
+ val = enable ? _MASKED_BIT_ENABLE(FW_BLC_SELF_EN) :
+ _MASKED_BIT_DISABLE(FW_BLC_SELF_EN);
+ I915_WRITE(FW_BLC_SELF, val);
+ } else if (IS_I915GM(dev)) {
+ val = enable ? _MASKED_BIT_ENABLE(INSTPM_SELF_EN) :
+ _MASKED_BIT_DISABLE(INSTPM_SELF_EN);
+ I915_WRITE(INSTPM, val);
+ } else {
+ return;
+ }
+
+ DRM_DEBUG_KMS("memory self-refresh is %s\n",
+ enable ? "enabled" : "disabled");
}
/*
@@ -811,7 +872,7 @@ static void pineview_disable_cxsr(struct drm_device *dev)
* A value of 5us seems to be a good balance; safe for very low end
* platforms but not overly aggressive on lower latency configs.
*/
-static const int latency_ns = 5000;
+static const int pessimal_latency_ns = 5000;
static int i9xx_get_fifo_size(struct drm_device *dev, int plane)
{
@@ -864,95 +925,102 @@ static int i845_get_fifo_size(struct drm_device *dev, int plane)
/* Pineview has different values for various configs */
static const struct intel_watermark_params pineview_display_wm = {
- PINEVIEW_DISPLAY_FIFO,
- PINEVIEW_MAX_WM,
- PINEVIEW_DFT_WM,
- PINEVIEW_GUARD_WM,
- PINEVIEW_FIFO_LINE_SIZE
+ .fifo_size = PINEVIEW_DISPLAY_FIFO,
+ .max_wm = PINEVIEW_MAX_WM,
+ .default_wm = PINEVIEW_DFT_WM,
+ .guard_size = PINEVIEW_GUARD_WM,
+ .cacheline_size = PINEVIEW_FIFO_LINE_SIZE,
};
static const struct intel_watermark_params pineview_display_hplloff_wm = {
- PINEVIEW_DISPLAY_FIFO,
- PINEVIEW_MAX_WM,
- PINEVIEW_DFT_HPLLOFF_WM,
- PINEVIEW_GUARD_WM,
- PINEVIEW_FIFO_LINE_SIZE
+ .fifo_size = PINEVIEW_DISPLAY_FIFO,
+ .max_wm = PINEVIEW_MAX_WM,
+ .default_wm = PINEVIEW_DFT_HPLLOFF_WM,
+ .guard_size = PINEVIEW_GUARD_WM,
+ .cacheline_size = PINEVIEW_FIFO_LINE_SIZE,
};
static const struct intel_watermark_params pineview_cursor_wm = {
- PINEVIEW_CURSOR_FIFO,
- PINEVIEW_CURSOR_MAX_WM,
- PINEVIEW_CURSOR_DFT_WM,
- PINEVIEW_CURSOR_GUARD_WM,
- PINEVIEW_FIFO_LINE_SIZE,
+ .fifo_size = PINEVIEW_CURSOR_FIFO,
+ .max_wm = PINEVIEW_CURSOR_MAX_WM,
+ .default_wm = PINEVIEW_CURSOR_DFT_WM,
+ .guard_size = PINEVIEW_CURSOR_GUARD_WM,
+ .cacheline_size = PINEVIEW_FIFO_LINE_SIZE,
};
static const struct intel_watermark_params pineview_cursor_hplloff_wm = {
- PINEVIEW_CURSOR_FIFO,
- PINEVIEW_CURSOR_MAX_WM,
- PINEVIEW_CURSOR_DFT_WM,
- PINEVIEW_CURSOR_GUARD_WM,
- PINEVIEW_FIFO_LINE_SIZE
+ .fifo_size = PINEVIEW_CURSOR_FIFO,
+ .max_wm = PINEVIEW_CURSOR_MAX_WM,
+ .default_wm = PINEVIEW_CURSOR_DFT_WM,
+ .guard_size = PINEVIEW_CURSOR_GUARD_WM,
+ .cacheline_size = PINEVIEW_FIFO_LINE_SIZE,
};
static const struct intel_watermark_params g4x_wm_info = {
- G4X_FIFO_SIZE,
- G4X_MAX_WM,
- G4X_MAX_WM,
- 2,
- G4X_FIFO_LINE_SIZE,
+ .fifo_size = G4X_FIFO_SIZE,
+ .max_wm = G4X_MAX_WM,
+ .default_wm = G4X_MAX_WM,
+ .guard_size = 2,
+ .cacheline_size = G4X_FIFO_LINE_SIZE,
};
static const struct intel_watermark_params g4x_cursor_wm_info = {
- I965_CURSOR_FIFO,
- I965_CURSOR_MAX_WM,
- I965_CURSOR_DFT_WM,
- 2,
- G4X_FIFO_LINE_SIZE,
+ .fifo_size = I965_CURSOR_FIFO,
+ .max_wm = I965_CURSOR_MAX_WM,
+ .default_wm = I965_CURSOR_DFT_WM,
+ .guard_size = 2,
+ .cacheline_size = G4X_FIFO_LINE_SIZE,
};
static const struct intel_watermark_params valleyview_wm_info = {
- VALLEYVIEW_FIFO_SIZE,
- VALLEYVIEW_MAX_WM,
- VALLEYVIEW_MAX_WM,
- 2,
- G4X_FIFO_LINE_SIZE,
+ .fifo_size = VALLEYVIEW_FIFO_SIZE,
+ .max_wm = VALLEYVIEW_MAX_WM,
+ .default_wm = VALLEYVIEW_MAX_WM,
+ .guard_size = 2,
+ .cacheline_size = G4X_FIFO_LINE_SIZE,
};
static const struct intel_watermark_params valleyview_cursor_wm_info = {
- I965_CURSOR_FIFO,
- VALLEYVIEW_CURSOR_MAX_WM,
- I965_CURSOR_DFT_WM,
- 2,
- G4X_FIFO_LINE_SIZE,
+ .fifo_size = I965_CURSOR_FIFO,
+ .max_wm = VALLEYVIEW_CURSOR_MAX_WM,
+ .default_wm = I965_CURSOR_DFT_WM,
+ .guard_size = 2,
+ .cacheline_size = G4X_FIFO_LINE_SIZE,
};
static const struct intel_watermark_params i965_cursor_wm_info = {
- I965_CURSOR_FIFO,
- I965_CURSOR_MAX_WM,
- I965_CURSOR_DFT_WM,
- 2,
- I915_FIFO_LINE_SIZE,
+ .fifo_size = I965_CURSOR_FIFO,
+ .max_wm = I965_CURSOR_MAX_WM,
+ .default_wm = I965_CURSOR_DFT_WM,
+ .guard_size = 2,
+ .cacheline_size = I915_FIFO_LINE_SIZE,
};
static const struct intel_watermark_params i945_wm_info = {
- I945_FIFO_SIZE,
- I915_MAX_WM,
- 1,
- 2,
- I915_FIFO_LINE_SIZE
+ .fifo_size = I945_FIFO_SIZE,
+ .max_wm = I915_MAX_WM,
+ .default_wm = 1,
+ .guard_size = 2,
+ .cacheline_size = I915_FIFO_LINE_SIZE,
};
static const struct intel_watermark_params i915_wm_info = {
- I915_FIFO_SIZE,
- I915_MAX_WM,
- 1,
- 2,
- I915_FIFO_LINE_SIZE
+ .fifo_size = I915_FIFO_SIZE,
+ .max_wm = I915_MAX_WM,
+ .default_wm = 1,
+ .guard_size = 2,
+ .cacheline_size = I915_FIFO_LINE_SIZE,
};
-static const struct intel_watermark_params i830_wm_info = {
- I855GM_FIFO_SIZE,
- I915_MAX_WM,
- 1,
- 2,
- I830_FIFO_LINE_SIZE
+static const struct intel_watermark_params i830_a_wm_info = {
+ .fifo_size = I855GM_FIFO_SIZE,
+ .max_wm = I915_MAX_WM,
+ .default_wm = 1,
+ .guard_size = 2,
+ .cacheline_size = I830_FIFO_LINE_SIZE,
+};
+static const struct intel_watermark_params i830_bc_wm_info = {
+ .fifo_size = I855GM_FIFO_SIZE,
+ .max_wm = I915_MAX_WM/2,
+ .default_wm = 1,
+ .guard_size = 2,
+ .cacheline_size = I830_FIFO_LINE_SIZE,
};
static const struct intel_watermark_params i845_wm_info = {
- I830_FIFO_SIZE,
- I915_MAX_WM,
- 1,
- 2,
- I830_FIFO_LINE_SIZE
+ .fifo_size = I830_FIFO_SIZE,
+ .max_wm = I915_MAX_WM,
+ .default_wm = 1,
+ .guard_size = 2,
+ .cacheline_size = I830_FIFO_LINE_SIZE,
};
/**
@@ -1002,6 +1070,17 @@ static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
wm_size = wm->max_wm;
if (wm_size <= 0)
wm_size = wm->default_wm;
+
+ /*
+ * Bspec seems to indicate that the value shouldn't be lower than
+ * 'burst size + 1'. Certainly 830 is quite unhappy with low values.
+ * Lets go for 8 which is the burst size since certain platforms
+ * already use a hardcoded 8 (which is what the spec says should be
+ * done).
+ */
+ if (wm_size <= 8)
+ wm_size = 8;
+
return wm_size;
}
@@ -1033,7 +1112,7 @@ static void pineview_update_wm(struct drm_crtc *unused_crtc)
dev_priv->fsb_freq, dev_priv->mem_freq);
if (!latency) {
DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n");
- pineview_disable_cxsr(dev);
+ intel_set_memory_cxsr(dev_priv, false);
return;
}
@@ -1084,13 +1163,9 @@ static void pineview_update_wm(struct drm_crtc *unused_crtc)
I915_WRITE(DSPFW3, reg);
DRM_DEBUG_KMS("DSPFW3 register is %x\n", reg);
- /* activate cxsr */
- I915_WRITE(DSPFW3,
- I915_READ(DSPFW3) | PINEVIEW_SELF_REFRESH_EN);
- DRM_DEBUG_KMS("Self-refresh is enabled\n");
+ intel_set_memory_cxsr(dev_priv, true);
} else {
- pineview_disable_cxsr(dev);
- DRM_DEBUG_KMS("Self-refresh is disabled\n");
+ intel_set_memory_cxsr(dev_priv, false);
}
}
@@ -1230,34 +1305,27 @@ static bool g4x_compute_srwm(struct drm_device *dev,
display, cursor);
}
-static bool vlv_compute_drain_latency(struct drm_device *dev,
- int plane,
- int *plane_prec_mult,
- int *plane_dl,
- int *cursor_prec_mult,
- int *cursor_dl)
+static bool vlv_compute_drain_latency(struct drm_crtc *crtc,
+ int pixel_size,
+ int *prec_mult,
+ int *drain_latency)
{
- struct drm_crtc *crtc;
- int clock, pixel_size;
int entries;
+ int clock = to_intel_crtc(crtc)->config.adjusted_mode.crtc_clock;
- crtc = intel_get_crtc_for_plane(dev, plane);
- if (!intel_crtc_active(crtc))
+ if (WARN(clock == 0, "Pixel clock is zero!\n"))
return false;
- clock = to_intel_crtc(crtc)->config.adjusted_mode.crtc_clock;
- pixel_size = crtc->primary->fb->bits_per_pixel / 8; /* BPP */
+ if (WARN(pixel_size == 0, "Pixel size is zero!\n"))
+ return false;
- entries = (clock / 1000) * pixel_size;
- *plane_prec_mult = (entries > 256) ?
- DRAIN_LATENCY_PRECISION_32 : DRAIN_LATENCY_PRECISION_16;
- *plane_dl = (64 * (*plane_prec_mult) * 4) / ((clock / 1000) *
- pixel_size);
+ entries = DIV_ROUND_UP(clock, 1000) * pixel_size;
+ *prec_mult = (entries > 128) ? DRAIN_LATENCY_PRECISION_64 :
+ DRAIN_LATENCY_PRECISION_32;
+ *drain_latency = (64 * (*prec_mult) * 4) / entries;
- entries = (clock / 1000) * 4; /* BPP is always 4 for cursor */
- *cursor_prec_mult = (entries > 256) ?
- DRAIN_LATENCY_PRECISION_32 : DRAIN_LATENCY_PRECISION_16;
- *cursor_dl = (64 * (*cursor_prec_mult) * 4) / ((clock / 1000) * 4);
+ if (*drain_latency > DRAIN_LATENCY_MASK)
+ *drain_latency = DRAIN_LATENCY_MASK;
return true;
}
@@ -1270,39 +1338,48 @@ static bool vlv_compute_drain_latency(struct drm_device *dev,
* latency value.
*/
-static void vlv_update_drain_latency(struct drm_device *dev)
+static void vlv_update_drain_latency(struct drm_crtc *crtc)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
- int planea_prec, planea_dl, planeb_prec, planeb_dl;
- int cursora_prec, cursora_dl, cursorb_prec, cursorb_dl;
- int plane_prec_mult, cursor_prec_mult; /* Precision multiplier is
- either 16 or 32 */
+ struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pixel_size;
+ int drain_latency;
+ enum pipe pipe = intel_crtc->pipe;
+ int plane_prec, prec_mult, plane_dl;
- /* For plane A, Cursor A */
- if (vlv_compute_drain_latency(dev, 0, &plane_prec_mult, &planea_dl,
- &cursor_prec_mult, &cursora_dl)) {
- cursora_prec = (cursor_prec_mult == DRAIN_LATENCY_PRECISION_32) ?
- DDL_CURSORA_PRECISION_32 : DDL_CURSORA_PRECISION_16;
- planea_prec = (plane_prec_mult == DRAIN_LATENCY_PRECISION_32) ?
- DDL_PLANEA_PRECISION_32 : DDL_PLANEA_PRECISION_16;
+ plane_dl = I915_READ(VLV_DDL(pipe)) & ~(DDL_PLANE_PRECISION_64 |
+ DRAIN_LATENCY_MASK | DDL_CURSOR_PRECISION_64 |
+ (DRAIN_LATENCY_MASK << DDL_CURSOR_SHIFT));
+
+ if (!intel_crtc_active(crtc)) {
+ I915_WRITE(VLV_DDL(pipe), plane_dl);
+ return;
+ }
- I915_WRITE(VLV_DDL1, cursora_prec |
- (cursora_dl << DDL_CURSORA_SHIFT) |
- planea_prec | planea_dl);
+ /* Primary plane Drain Latency */
+ pixel_size = crtc->primary->fb->bits_per_pixel / 8; /* BPP */
+ if (vlv_compute_drain_latency(crtc, pixel_size, &prec_mult, &drain_latency)) {
+ plane_prec = (prec_mult == DRAIN_LATENCY_PRECISION_64) ?
+ DDL_PLANE_PRECISION_64 :
+ DDL_PLANE_PRECISION_32;
+ plane_dl |= plane_prec | drain_latency;
}
- /* For plane B, Cursor B */
- if (vlv_compute_drain_latency(dev, 1, &plane_prec_mult, &planeb_dl,
- &cursor_prec_mult, &cursorb_dl)) {
- cursorb_prec = (cursor_prec_mult == DRAIN_LATENCY_PRECISION_32) ?
- DDL_CURSORB_PRECISION_32 : DDL_CURSORB_PRECISION_16;
- planeb_prec = (plane_prec_mult == DRAIN_LATENCY_PRECISION_32) ?
- DDL_PLANEB_PRECISION_32 : DDL_PLANEB_PRECISION_16;
+ /* Cursor Drain Latency
+ * BPP is always 4 for cursor
+ */
+ pixel_size = 4;
- I915_WRITE(VLV_DDL2, cursorb_prec |
- (cursorb_dl << DDL_CURSORB_SHIFT) |
- planeb_prec | planeb_dl);
+ /* Program cursor DL only if it is enabled */
+ if (intel_crtc->cursor_base &&
+ vlv_compute_drain_latency(crtc, pixel_size, &prec_mult, &drain_latency)) {
+ plane_prec = (prec_mult == DRAIN_LATENCY_PRECISION_64) ?
+ DDL_CURSOR_PRECISION_64 :
+ DDL_CURSOR_PRECISION_32;
+ plane_dl |= plane_prec | (drain_latency << DDL_CURSOR_SHIFT);
}
+
+ I915_WRITE(VLV_DDL(pipe), plane_dl);
}
#define single_plane_enabled(mask) is_power_of_2(mask)
@@ -1316,21 +1393,94 @@ static void valleyview_update_wm(struct drm_crtc *crtc)
int plane_sr, cursor_sr;
int ignore_plane_sr, ignore_cursor_sr;
unsigned int enabled = 0;
+ bool cxsr_enabled;
+
+ vlv_update_drain_latency(crtc);
+
+ if (g4x_compute_wm0(dev, PIPE_A,
+ &valleyview_wm_info, pessimal_latency_ns,
+ &valleyview_cursor_wm_info, pessimal_latency_ns,
+ &planea_wm, &cursora_wm))
+ enabled |= 1 << PIPE_A;
+
+ if (g4x_compute_wm0(dev, PIPE_B,
+ &valleyview_wm_info, pessimal_latency_ns,
+ &valleyview_cursor_wm_info, pessimal_latency_ns,
+ &planeb_wm, &cursorb_wm))
+ enabled |= 1 << PIPE_B;
+
+ if (single_plane_enabled(enabled) &&
+ g4x_compute_srwm(dev, ffs(enabled) - 1,
+ sr_latency_ns,
+ &valleyview_wm_info,
+ &valleyview_cursor_wm_info,
+ &plane_sr, &ignore_cursor_sr) &&
+ g4x_compute_srwm(dev, ffs(enabled) - 1,
+ 2*sr_latency_ns,
+ &valleyview_wm_info,
+ &valleyview_cursor_wm_info,
+ &ignore_plane_sr, &cursor_sr)) {
+ cxsr_enabled = true;
+ } else {
+ cxsr_enabled = false;
+ intel_set_memory_cxsr(dev_priv, false);
+ plane_sr = cursor_sr = 0;
+ }
+
+ DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, "
+ "B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n",
+ planea_wm, cursora_wm,
+ planeb_wm, cursorb_wm,
+ plane_sr, cursor_sr);
+
+ I915_WRITE(DSPFW1,
+ (plane_sr << DSPFW_SR_SHIFT) |
+ (cursorb_wm << DSPFW_CURSORB_SHIFT) |
+ (planeb_wm << DSPFW_PLANEB_SHIFT) |
+ (planea_wm << DSPFW_PLANEA_SHIFT));
+ I915_WRITE(DSPFW2,
+ (I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) |
+ (cursora_wm << DSPFW_CURSORA_SHIFT));
+ I915_WRITE(DSPFW3,
+ (I915_READ(DSPFW3) & ~DSPFW_CURSOR_SR_MASK) |
+ (cursor_sr << DSPFW_CURSOR_SR_SHIFT));
+
+ if (cxsr_enabled)
+ intel_set_memory_cxsr(dev_priv, true);
+}
+
+static void cherryview_update_wm(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ static const int sr_latency_ns = 12000;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int planea_wm, planeb_wm, planec_wm;
+ int cursora_wm, cursorb_wm, cursorc_wm;
+ int plane_sr, cursor_sr;
+ int ignore_plane_sr, ignore_cursor_sr;
+ unsigned int enabled = 0;
+ bool cxsr_enabled;
- vlv_update_drain_latency(dev);
+ vlv_update_drain_latency(crtc);
if (g4x_compute_wm0(dev, PIPE_A,
- &valleyview_wm_info, latency_ns,
- &valleyview_cursor_wm_info, latency_ns,
+ &valleyview_wm_info, pessimal_latency_ns,
+ &valleyview_cursor_wm_info, pessimal_latency_ns,
&planea_wm, &cursora_wm))
enabled |= 1 << PIPE_A;
if (g4x_compute_wm0(dev, PIPE_B,
- &valleyview_wm_info, latency_ns,
- &valleyview_cursor_wm_info, latency_ns,
+ &valleyview_wm_info, pessimal_latency_ns,
+ &valleyview_cursor_wm_info, pessimal_latency_ns,
&planeb_wm, &cursorb_wm))
enabled |= 1 << PIPE_B;
+ if (g4x_compute_wm0(dev, PIPE_C,
+ &valleyview_wm_info, pessimal_latency_ns,
+ &valleyview_cursor_wm_info, pessimal_latency_ns,
+ &planec_wm, &cursorc_wm))
+ enabled |= 1 << PIPE_C;
+
if (single_plane_enabled(enabled) &&
g4x_compute_srwm(dev, ffs(enabled) - 1,
sr_latency_ns,
@@ -1342,29 +1492,71 @@ static void valleyview_update_wm(struct drm_crtc *crtc)
&valleyview_wm_info,
&valleyview_cursor_wm_info,
&ignore_plane_sr, &cursor_sr)) {
- I915_WRITE(FW_BLC_SELF_VLV, FW_CSPWRDWNEN);
+ cxsr_enabled = true;
} else {
- I915_WRITE(FW_BLC_SELF_VLV,
- I915_READ(FW_BLC_SELF_VLV) & ~FW_CSPWRDWNEN);
+ cxsr_enabled = false;
+ intel_set_memory_cxsr(dev_priv, false);
plane_sr = cursor_sr = 0;
}
- DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n",
+ DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, "
+ "B: plane=%d, cursor=%d, C: plane=%d, cursor=%d, "
+ "SR: plane=%d, cursor=%d\n",
planea_wm, cursora_wm,
planeb_wm, cursorb_wm,
+ planec_wm, cursorc_wm,
plane_sr, cursor_sr);
I915_WRITE(DSPFW1,
(plane_sr << DSPFW_SR_SHIFT) |
(cursorb_wm << DSPFW_CURSORB_SHIFT) |
(planeb_wm << DSPFW_PLANEB_SHIFT) |
- planea_wm);
+ (planea_wm << DSPFW_PLANEA_SHIFT));
I915_WRITE(DSPFW2,
(I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) |
(cursora_wm << DSPFW_CURSORA_SHIFT));
I915_WRITE(DSPFW3,
(I915_READ(DSPFW3) & ~DSPFW_CURSOR_SR_MASK) |
(cursor_sr << DSPFW_CURSOR_SR_SHIFT));
+ I915_WRITE(DSPFW9_CHV,
+ (I915_READ(DSPFW9_CHV) & ~(DSPFW_PLANEC_MASK |
+ DSPFW_CURSORC_MASK)) |
+ (planec_wm << DSPFW_PLANEC_SHIFT) |
+ (cursorc_wm << DSPFW_CURSORC_SHIFT));
+
+ if (cxsr_enabled)
+ intel_set_memory_cxsr(dev_priv, true);
+}
+
+static void valleyview_update_sprite_wm(struct drm_plane *plane,
+ struct drm_crtc *crtc,
+ uint32_t sprite_width,
+ uint32_t sprite_height,
+ int pixel_size,
+ bool enabled, bool scaled)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int pipe = to_intel_plane(plane)->pipe;
+ int sprite = to_intel_plane(plane)->plane;
+ int drain_latency;
+ int plane_prec;
+ int sprite_dl;
+ int prec_mult;
+
+ sprite_dl = I915_READ(VLV_DDL(pipe)) & ~(DDL_SPRITE_PRECISION_64(sprite) |
+ (DRAIN_LATENCY_MASK << DDL_SPRITE_SHIFT(sprite)));
+
+ if (enabled && vlv_compute_drain_latency(crtc, pixel_size, &prec_mult,
+ &drain_latency)) {
+ plane_prec = (prec_mult == DRAIN_LATENCY_PRECISION_64) ?
+ DDL_SPRITE_PRECISION_64(sprite) :
+ DDL_SPRITE_PRECISION_32(sprite);
+ sprite_dl |= plane_prec |
+ (drain_latency << DDL_SPRITE_SHIFT(sprite));
+ }
+
+ I915_WRITE(VLV_DDL(pipe), sprite_dl);
}
static void g4x_update_wm(struct drm_crtc *crtc)
@@ -1375,16 +1567,17 @@ static void g4x_update_wm(struct drm_crtc *crtc)
int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
int plane_sr, cursor_sr;
unsigned int enabled = 0;
+ bool cxsr_enabled;
if (g4x_compute_wm0(dev, PIPE_A,
- &g4x_wm_info, latency_ns,
- &g4x_cursor_wm_info, latency_ns,
+ &g4x_wm_info, pessimal_latency_ns,
+ &g4x_cursor_wm_info, pessimal_latency_ns,
&planea_wm, &cursora_wm))
enabled |= 1 << PIPE_A;
if (g4x_compute_wm0(dev, PIPE_B,
- &g4x_wm_info, latency_ns,
- &g4x_cursor_wm_info, latency_ns,
+ &g4x_wm_info, pessimal_latency_ns,
+ &g4x_cursor_wm_info, pessimal_latency_ns,
&planeb_wm, &cursorb_wm))
enabled |= 1 << PIPE_B;
@@ -1394,14 +1587,15 @@ static void g4x_update_wm(struct drm_crtc *crtc)
&g4x_wm_info,
&g4x_cursor_wm_info,
&plane_sr, &cursor_sr)) {
- I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
+ cxsr_enabled = true;
} else {
- I915_WRITE(FW_BLC_SELF,
- I915_READ(FW_BLC_SELF) & ~FW_BLC_SELF_EN);
+ cxsr_enabled = false;
+ intel_set_memory_cxsr(dev_priv, false);
plane_sr = cursor_sr = 0;
}
- DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n",
+ DRM_DEBUG_KMS("Setting FIFO watermarks - A: plane=%d, cursor=%d, "
+ "B: plane=%d, cursor=%d, SR: plane=%d, cursor=%d\n",
planea_wm, cursora_wm,
planeb_wm, cursorb_wm,
plane_sr, cursor_sr);
@@ -1410,7 +1604,7 @@ static void g4x_update_wm(struct drm_crtc *crtc)
(plane_sr << DSPFW_SR_SHIFT) |
(cursorb_wm << DSPFW_CURSORB_SHIFT) |
(planeb_wm << DSPFW_PLANEB_SHIFT) |
- planea_wm);
+ (planea_wm << DSPFW_PLANEA_SHIFT));
I915_WRITE(DSPFW2,
(I915_READ(DSPFW2) & ~DSPFW_CURSORA_MASK) |
(cursora_wm << DSPFW_CURSORA_SHIFT));
@@ -1418,6 +1612,9 @@ static void g4x_update_wm(struct drm_crtc *crtc)
I915_WRITE(DSPFW3,
(I915_READ(DSPFW3) & ~(DSPFW_HPLL_SR_EN | DSPFW_CURSOR_SR_MASK)) |
(cursor_sr << DSPFW_CURSOR_SR_SHIFT));
+
+ if (cxsr_enabled)
+ intel_set_memory_cxsr(dev_priv, true);
}
static void i965_update_wm(struct drm_crtc *unused_crtc)
@@ -1427,6 +1624,7 @@ static void i965_update_wm(struct drm_crtc *unused_crtc)
struct drm_crtc *crtc;
int srwm = 1;
int cursor_sr = 16;
+ bool cxsr_enabled;
/* Calc sr entries for one plane configs */
crtc = single_enabled_crtc(dev);
@@ -1468,13 +1666,11 @@ static void i965_update_wm(struct drm_crtc *unused_crtc)
DRM_DEBUG_KMS("self-refresh watermark: display plane %d "
"cursor %d\n", srwm, cursor_sr);
- if (IS_CRESTLINE(dev))
- I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
+ cxsr_enabled = true;
} else {
+ cxsr_enabled = false;
/* Turn off self refresh if both pipes are enabled */
- if (IS_CRESTLINE(dev))
- I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
- & ~FW_BLC_SELF_EN);
+ intel_set_memory_cxsr(dev_priv, false);
}
DRM_DEBUG_KMS("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR %d\n",
@@ -1482,10 +1678,16 @@ static void i965_update_wm(struct drm_crtc *unused_crtc)
/* 965 has limitations... */
I915_WRITE(DSPFW1, (srwm << DSPFW_SR_SHIFT) |
- (8 << 16) | (8 << 8) | (8 << 0));
- I915_WRITE(DSPFW2, (8 << 8) | (8 << 0));
+ (8 << DSPFW_CURSORB_SHIFT) |
+ (8 << DSPFW_PLANEB_SHIFT) |
+ (8 << DSPFW_PLANEA_SHIFT));
+ I915_WRITE(DSPFW2, (8 << DSPFW_CURSORA_SHIFT) |
+ (8 << DSPFW_PLANEC_SHIFT_OLD));
/* update cursor SR watermark */
I915_WRITE(DSPFW3, (cursor_sr << DSPFW_CURSOR_SR_SHIFT));
+
+ if (cxsr_enabled)
+ intel_set_memory_cxsr(dev_priv, true);
}
static void i9xx_update_wm(struct drm_crtc *unused_crtc)
@@ -1505,7 +1707,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
else if (!IS_GEN2(dev))
wm_info = &i915_wm_info;
else
- wm_info = &i830_wm_info;
+ wm_info = &i830_a_wm_info;
fifo_size = dev_priv->display.get_fifo_size(dev, 0);
crtc = intel_get_crtc_for_plane(dev, 0);
@@ -1518,10 +1720,16 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode;
planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock,
wm_info, fifo_size, cpp,
- latency_ns);
+ pessimal_latency_ns);
enabled = crtc;
- } else
+ } else {
planea_wm = fifo_size - wm_info->guard_size;
+ if (planea_wm > (long)wm_info->max_wm)
+ planea_wm = wm_info->max_wm;
+ }
+
+ if (IS_GEN2(dev))
+ wm_info = &i830_bc_wm_info;
fifo_size = dev_priv->display.get_fifo_size(dev, 1);
crtc = intel_get_crtc_for_plane(dev, 1);
@@ -1534,23 +1742,26 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
adjusted_mode = &to_intel_crtc(crtc)->config.adjusted_mode;
planeb_wm = intel_calculate_wm(adjusted_mode->crtc_clock,
wm_info, fifo_size, cpp,
- latency_ns);
+ pessimal_latency_ns);
if (enabled == NULL)
enabled = crtc;
else
enabled = NULL;
- } else
+ } else {
planeb_wm = fifo_size - wm_info->guard_size;
+ if (planeb_wm > (long)wm_info->max_wm)
+ planeb_wm = wm_info->max_wm;
+ }
DRM_DEBUG_KMS("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm);
if (IS_I915GM(dev) && enabled) {
- struct intel_framebuffer *fb;
+ struct drm_i915_gem_object *obj;
- fb = to_intel_framebuffer(enabled->primary->fb);
+ obj = intel_fb_obj(enabled->primary->fb);
/* self-refresh seems busted with untiled */
- if (fb->obj->tiling_mode == I915_TILING_NONE)
+ if (obj->tiling_mode == I915_TILING_NONE)
enabled = NULL;
}
@@ -1560,10 +1771,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
cwm = 2;
/* Play safe and disable self-refresh before adjusting watermarks. */
- if (IS_I945G(dev) || IS_I945GM(dev))
- I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN_MASK | 0);
- else if (IS_I915GM(dev))
- I915_WRITE(INSTPM, _MASKED_BIT_DISABLE(INSTPM_SELF_EN));
+ intel_set_memory_cxsr(dev_priv, false);
/* Calc sr entries for one plane configs */
if (HAS_FW_BLC(dev) && enabled) {
@@ -1609,17 +1817,8 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
I915_WRITE(FW_BLC, fwater_lo);
I915_WRITE(FW_BLC2, fwater_hi);
- if (HAS_FW_BLC(dev)) {
- if (enabled) {
- if (IS_I945G(dev) || IS_I945GM(dev))
- I915_WRITE(FW_BLC_SELF,
- FW_BLC_SELF_EN_MASK | FW_BLC_SELF_EN);
- else if (IS_I915GM(dev))
- I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_SELF_EN));
- DRM_DEBUG_KMS("memory self refresh enabled\n");
- } else
- DRM_DEBUG_KMS("memory self refresh disabled\n");
- }
+ if (enabled)
+ intel_set_memory_cxsr(dev_priv, true);
}
static void i845_update_wm(struct drm_crtc *unused_crtc)
@@ -1639,7 +1838,7 @@ static void i845_update_wm(struct drm_crtc *unused_crtc)
planea_wm = intel_calculate_wm(adjusted_mode->crtc_clock,
&i845_wm_info,
dev_priv->display.get_fifo_size(dev, 0),
- 4, latency_ns);
+ 4, pessimal_latency_ns);
fwater_lo = I915_READ(FW_BLC) & ~0xfff;
fwater_lo |= (3<<8) | planea_wm;
@@ -2492,7 +2691,7 @@ static struct intel_pipe_wm *ilk_find_best_result(struct drm_device *dev,
#define WM_DIRTY_FBC (1 << 24)
#define WM_DIRTY_DDB (1 << 25)
-static unsigned int ilk_compute_wm_dirty(struct drm_device *dev,
+static unsigned int ilk_compute_wm_dirty(struct drm_i915_private *dev_priv,
const struct ilk_wm_values *old,
const struct ilk_wm_values *new)
{
@@ -2500,7 +2699,7 @@ static unsigned int ilk_compute_wm_dirty(struct drm_device *dev,
enum pipe pipe;
int wm_lp;
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
if (old->wm_linetime[pipe] != new->wm_linetime[pipe]) {
dirty |= WM_DIRTY_LINETIME(pipe);
/* Must disable LP1+ watermarks too */
@@ -2586,7 +2785,7 @@ static void ilk_write_wm_values(struct drm_i915_private *dev_priv,
unsigned int dirty;
uint32_t val;
- dirty = ilk_compute_wm_dirty(dev, previous, results);
+ dirty = ilk_compute_wm_dirty(dev_priv, previous, results);
if (!dirty)
return;
@@ -2707,10 +2906,11 @@ static void ilk_update_wm(struct drm_crtc *crtc)
ilk_write_wm_values(dev_priv, &results);
}
-static void ilk_update_sprite_wm(struct drm_plane *plane,
- struct drm_crtc *crtc,
- uint32_t sprite_width, int pixel_size,
- bool enabled, bool scaled)
+static void
+ilk_update_sprite_wm(struct drm_plane *plane,
+ struct drm_crtc *crtc,
+ uint32_t sprite_width, uint32_t sprite_height,
+ int pixel_size, bool enabled, bool scaled)
{
struct drm_device *dev = plane->dev;
struct intel_plane *intel_plane = to_intel_plane(plane);
@@ -2718,6 +2918,7 @@ static void ilk_update_sprite_wm(struct drm_plane *plane,
intel_plane->wm.enabled = enabled;
intel_plane->wm.scaled = scaled;
intel_plane->wm.horiz_pixels = sprite_width;
+ intel_plane->wm.vert_pixels = sprite_width;
intel_plane->wm.bytes_per_pixel = pixel_size;
/*
@@ -2852,13 +3053,16 @@ void intel_update_watermarks(struct drm_crtc *crtc)
void intel_update_sprite_watermarks(struct drm_plane *plane,
struct drm_crtc *crtc,
- uint32_t sprite_width, int pixel_size,
+ uint32_t sprite_width,
+ uint32_t sprite_height,
+ int pixel_size,
bool enabled, bool scaled)
{
struct drm_i915_private *dev_priv = plane->dev->dev_private;
if (dev_priv->display.update_sprite_wm)
- dev_priv->display.update_sprite_wm(plane, crtc, sprite_width,
+ dev_priv->display.update_sprite_wm(plane, crtc,
+ sprite_width, sprite_height,
pixel_size, enabled, scaled);
}
@@ -3147,6 +3351,9 @@ static u32 gen6_rps_pm_mask(struct drm_i915_private *dev_priv, u8 val)
if (val < dev_priv->rps.max_freq_softlimit)
mask |= GEN6_PM_RP_UP_THRESHOLD;
+ mask |= dev_priv->pm_rps_events & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED);
+ mask &= dev_priv->pm_rps_events;
+
/* IVB and SNB hard hangs on looping batchbuffer
* if GEN6_PM_UP_EI_EXPIRED is masked.
*/
@@ -3250,7 +3457,9 @@ void gen6_rps_idle(struct drm_i915_private *dev_priv)
mutex_lock(&dev_priv->rps.hw_lock);
if (dev_priv->rps.enabled) {
- if (IS_VALLEYVIEW(dev))
+ if (IS_CHERRYVIEW(dev))
+ valleyview_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit);
+ else if (IS_VALLEYVIEW(dev))
vlv_set_rps_idle(dev_priv);
else
gen6_set_rps(dev_priv->dev, dev_priv->rps.min_freq_softlimit);
@@ -3282,13 +3491,18 @@ void valleyview_set_rps(struct drm_device *dev, u8 val)
WARN_ON(val > dev_priv->rps.max_freq_softlimit);
WARN_ON(val < dev_priv->rps.min_freq_softlimit);
- DRM_DEBUG_DRIVER("GPU freq request from %d MHz (%u) to %d MHz (%u)\n",
- vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq),
- dev_priv->rps.cur_freq,
- vlv_gpu_freq(dev_priv, val), val);
+ if (WARN_ONCE(IS_CHERRYVIEW(dev) && (val & 1),
+ "Odd GPU freq value\n"))
+ val &= ~1;
+
+ if (val != dev_priv->rps.cur_freq) {
+ DRM_DEBUG_DRIVER("GPU freq request from %d MHz (%u) to %d MHz (%u)\n",
+ vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq),
+ dev_priv->rps.cur_freq,
+ vlv_gpu_freq(dev_priv, val), val);
- if (val != dev_priv->rps.cur_freq)
vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val);
+ }
I915_WRITE(GEN6_PMINTRMSK, gen6_rps_pm_mask(dev_priv, val));
@@ -3348,12 +3562,27 @@ static void gen6_disable_rps(struct drm_device *dev)
gen6_disable_rps_interrupts(dev);
}
+static void cherryview_disable_rps(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(GEN6_RC_CONTROL, 0);
+
+ gen8_disable_rps_interrupts(dev);
+}
+
static void valleyview_disable_rps(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ /* we're doing forcewake before Disabling RC6,
+ * This what the BIOS expects when going into suspend */
+ gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL);
+
I915_WRITE(GEN6_RC_CONTROL, 0);
+ gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL);
+
gen6_disable_rps_interrupts(dev);
}
@@ -3365,10 +3594,10 @@ static void intel_print_rc6_info(struct drm_device *dev, u32 mode)
else
mode = 0;
}
- DRM_INFO("Enabling RC6 states: RC6 %s, RC6p %s, RC6pp %s\n",
- (mode & GEN6_RC_CTL_RC6_ENABLE) ? "on" : "off",
- (mode & GEN6_RC_CTL_RC6p_ENABLE) ? "on" : "off",
- (mode & GEN6_RC_CTL_RC6pp_ENABLE) ? "on" : "off");
+ DRM_DEBUG_KMS("Enabling RC6 states: RC6 %s, RC6p %s, RC6pp %s\n",
+ (mode & GEN6_RC_CTL_RC6_ENABLE) ? "on" : "off",
+ (mode & GEN6_RC_CTL_RC6p_ENABLE) ? "on" : "off",
+ (mode & GEN6_RC_CTL_RC6pp_ENABLE) ? "on" : "off");
}
static int sanitize_rc6_option(const struct drm_device *dev, int enable_rc6)
@@ -3392,8 +3621,8 @@ static int sanitize_rc6_option(const struct drm_device *dev, int enable_rc6)
mask = INTEL_RC6_ENABLE;
if ((enable_rc6 & mask) != enable_rc6)
- DRM_INFO("Adjusting RC6 mask to %d (requested %d, valid %d)\n",
- enable_rc6 & mask, enable_rc6, mask);
+ DRM_DEBUG_KMS("Adjusting RC6 mask to %d (requested %d, valid %d)\n",
+ enable_rc6 & mask, enable_rc6, mask);
return enable_rc6 & mask;
}
@@ -3419,7 +3648,7 @@ static void gen8_enable_rps_interrupts(struct drm_device *dev)
spin_lock_irq(&dev_priv->irq_lock);
WARN_ON(dev_priv->rps.pm_iir);
- bdw_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
+ gen8_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
I915_WRITE(GEN8_GT_IIR(2), dev_priv->pm_rps_events);
spin_unlock_irq(&dev_priv->irq_lock);
}
@@ -3430,7 +3659,7 @@ static void gen6_enable_rps_interrupts(struct drm_device *dev)
spin_lock_irq(&dev_priv->irq_lock);
WARN_ON(dev_priv->rps.pm_iir);
- snb_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
+ gen6_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
I915_WRITE(GEN6_PMIIR, dev_priv->pm_rps_events);
spin_unlock_irq(&dev_priv->irq_lock);
}
@@ -3483,15 +3712,23 @@ static void gen8_enable_rps(struct drm_device *dev)
for_each_ring(ring, dev_priv, unused)
I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10);
I915_WRITE(GEN6_RC_SLEEP, 0);
- I915_WRITE(GEN6_RC6_THRESHOLD, 50000); /* 50/125ms per EI */
+ if (IS_BROADWELL(dev))
+ I915_WRITE(GEN6_RC6_THRESHOLD, 625); /* 800us/1.28 for TO */
+ else
+ I915_WRITE(GEN6_RC6_THRESHOLD, 50000); /* 50/125ms per EI */
/* 3: Enable RC6 */
if (intel_enable_rc6(dev) & INTEL_RC6_ENABLE)
rc6_mask = GEN6_RC_CTL_RC6_ENABLE;
intel_print_rc6_info(dev, rc6_mask);
- I915_WRITE(GEN6_RC_CONTROL, GEN6_RC_CTL_HW_ENABLE |
- GEN6_RC_CTL_EI_MODE(1) |
- rc6_mask);
+ if (IS_BROADWELL(dev))
+ I915_WRITE(GEN6_RC_CONTROL, GEN6_RC_CTL_HW_ENABLE |
+ GEN7_RC_CTL_TO_MODE |
+ rc6_mask);
+ else
+ I915_WRITE(GEN6_RC_CONTROL, GEN6_RC_CTL_HW_ENABLE |
+ GEN6_RC_CTL_EI_MODE(1) |
+ rc6_mask);
/* 4 Program defaults and thresholds for RPS*/
I915_WRITE(GEN6_RPNSWREQ,
@@ -3536,7 +3773,6 @@ static void gen6_enable_rps(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_engine_cs *ring;
u32 rp_state_cap;
- u32 gt_perf_status;
u32 rc6vids, pcu_mbox = 0, rc6_mask = 0;
u32 gtfifodbg;
int rc6_mode;
@@ -3561,7 +3797,6 @@ static void gen6_enable_rps(struct drm_device *dev)
gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL);
rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
- gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
parse_rp_state_cap(dev_priv, rp_state_cap);
@@ -3727,7 +3962,57 @@ void gen6_update_ring_freq(struct drm_device *dev)
mutex_unlock(&dev_priv->rps.hw_lock);
}
-int valleyview_rps_max_freq(struct drm_i915_private *dev_priv)
+static int cherryview_rps_max_freq(struct drm_i915_private *dev_priv)
+{
+ u32 val, rp0;
+
+ val = vlv_punit_read(dev_priv, PUNIT_GPU_STATUS_REG);
+ rp0 = (val >> PUNIT_GPU_STATUS_MAX_FREQ_SHIFT) & PUNIT_GPU_STATUS_MAX_FREQ_MASK;
+
+ return rp0;
+}
+
+static int cherryview_rps_rpe_freq(struct drm_i915_private *dev_priv)
+{
+ u32 val, rpe;
+
+ val = vlv_punit_read(dev_priv, PUNIT_GPU_DUTYCYCLE_REG);
+ rpe = (val >> PUNIT_GPU_DUTYCYCLE_RPE_FREQ_SHIFT) & PUNIT_GPU_DUTYCYCLE_RPE_FREQ_MASK;
+
+ return rpe;
+}
+
+static int cherryview_rps_guar_freq(struct drm_i915_private *dev_priv)
+{
+ u32 val, rp1;
+
+ val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
+ rp1 = (val >> PUNIT_GPU_STATUS_MAX_FREQ_SHIFT) & PUNIT_GPU_STATUS_MAX_FREQ_MASK;
+
+ return rp1;
+}
+
+static int cherryview_rps_min_freq(struct drm_i915_private *dev_priv)
+{
+ u32 val, rpn;
+
+ val = vlv_punit_read(dev_priv, PUNIT_GPU_STATUS_REG);
+ rpn = (val >> PUNIT_GPU_STATIS_GFX_MIN_FREQ_SHIFT) & PUNIT_GPU_STATUS_GFX_MIN_FREQ_MASK;
+ return rpn;
+}
+
+static int valleyview_rps_guar_freq(struct drm_i915_private *dev_priv)
+{
+ u32 val, rp1;
+
+ val = vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FREQ_FUSE);
+
+ rp1 = (val & FB_GFX_FGUARANTEED_FREQ_FUSE_MASK) >> FB_GFX_FGUARANTEED_FREQ_FUSE_SHIFT;
+
+ return rp1;
+}
+
+static int valleyview_rps_max_freq(struct drm_i915_private *dev_priv)
{
u32 val, rp0;
@@ -3752,7 +4037,7 @@ static int valleyview_rps_rpe_freq(struct drm_i915_private *dev_priv)
return rpe;
}
-int valleyview_rps_min_freq(struct drm_i915_private *dev_priv)
+static int valleyview_rps_min_freq(struct drm_i915_private *dev_priv)
{
return vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM) & 0xff;
}
@@ -3766,6 +4051,35 @@ static void valleyview_check_pctx(struct drm_i915_private *dev_priv)
dev_priv->vlv_pctx->stolen->start);
}
+
+/* Check that the pcbr address is not empty. */
+static void cherryview_check_pctx(struct drm_i915_private *dev_priv)
+{
+ unsigned long pctx_addr = I915_READ(VLV_PCBR) & ~4095;
+
+ WARN_ON((pctx_addr >> VLV_PCBR_ADDR_SHIFT) == 0);
+}
+
+static void cherryview_setup_pctx(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ unsigned long pctx_paddr, paddr;
+ struct i915_gtt *gtt = &dev_priv->gtt;
+ u32 pcbr;
+ int pctx_size = 32*1024;
+
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+ pcbr = I915_READ(VLV_PCBR);
+ if ((pcbr >> VLV_PCBR_ADDR_SHIFT) == 0) {
+ paddr = (dev_priv->mm.stolen_base +
+ (gtt->stolen_size - pctx_size));
+
+ pctx_paddr = (paddr & (~4095));
+ I915_WRITE(VLV_PCBR, pctx_paddr);
+ }
+}
+
static void valleyview_setup_pctx(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3824,11 +4138,27 @@ static void valleyview_cleanup_pctx(struct drm_device *dev)
static void valleyview_init_gt_powersave(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 val;
valleyview_setup_pctx(dev);
mutex_lock(&dev_priv->rps.hw_lock);
+ val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
+ switch ((val >> 6) & 3) {
+ case 0:
+ case 1:
+ dev_priv->mem_freq = 800;
+ break;
+ case 2:
+ dev_priv->mem_freq = 1066;
+ break;
+ case 3:
+ dev_priv->mem_freq = 1333;
+ break;
+ }
+ DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq);
+
dev_priv->rps.max_freq = valleyview_rps_max_freq(dev_priv);
dev_priv->rps.rp0_freq = dev_priv->rps.max_freq;
DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n",
@@ -3840,6 +4170,11 @@ static void valleyview_init_gt_powersave(struct drm_device *dev)
vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq),
dev_priv->rps.efficient_freq);
+ dev_priv->rps.rp1_freq = valleyview_rps_guar_freq(dev_priv);
+ DRM_DEBUG_DRIVER("RP1(Guar Freq) GPU freq: %d MHz (%u)\n",
+ vlv_gpu_freq(dev_priv, dev_priv->rps.rp1_freq),
+ dev_priv->rps.rp1_freq);
+
dev_priv->rps.min_freq = valleyview_rps_min_freq(dev_priv);
DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n",
vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq),
@@ -3855,11 +4190,175 @@ static void valleyview_init_gt_powersave(struct drm_device *dev)
mutex_unlock(&dev_priv->rps.hw_lock);
}
+static void cherryview_init_gt_powersave(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 val;
+
+ cherryview_setup_pctx(dev);
+
+ mutex_lock(&dev_priv->rps.hw_lock);
+
+ val = vlv_punit_read(dev_priv, CCK_FUSE_REG);
+ switch ((val >> 2) & 0x7) {
+ case 0:
+ case 1:
+ dev_priv->rps.cz_freq = 200;
+ dev_priv->mem_freq = 1600;
+ break;
+ case 2:
+ dev_priv->rps.cz_freq = 267;
+ dev_priv->mem_freq = 1600;
+ break;
+ case 3:
+ dev_priv->rps.cz_freq = 333;
+ dev_priv->mem_freq = 2000;
+ break;
+ case 4:
+ dev_priv->rps.cz_freq = 320;
+ dev_priv->mem_freq = 1600;
+ break;
+ case 5:
+ dev_priv->rps.cz_freq = 400;
+ dev_priv->mem_freq = 1600;
+ break;
+ }
+ DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq);
+
+ dev_priv->rps.max_freq = cherryview_rps_max_freq(dev_priv);
+ dev_priv->rps.rp0_freq = dev_priv->rps.max_freq;
+ DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n",
+ vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq),
+ dev_priv->rps.max_freq);
+
+ dev_priv->rps.efficient_freq = cherryview_rps_rpe_freq(dev_priv);
+ DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n",
+ vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq),
+ dev_priv->rps.efficient_freq);
+
+ dev_priv->rps.rp1_freq = cherryview_rps_guar_freq(dev_priv);
+ DRM_DEBUG_DRIVER("RP1(Guar) GPU freq: %d MHz (%u)\n",
+ vlv_gpu_freq(dev_priv, dev_priv->rps.rp1_freq),
+ dev_priv->rps.rp1_freq);
+
+ dev_priv->rps.min_freq = cherryview_rps_min_freq(dev_priv);
+ DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n",
+ vlv_gpu_freq(dev_priv, dev_priv->rps.min_freq),
+ dev_priv->rps.min_freq);
+
+ WARN_ONCE((dev_priv->rps.max_freq |
+ dev_priv->rps.efficient_freq |
+ dev_priv->rps.rp1_freq |
+ dev_priv->rps.min_freq) & 1,
+ "Odd GPU freq values\n");
+
+ /* Preserve min/max settings in case of re-init */
+ if (dev_priv->rps.max_freq_softlimit == 0)
+ dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq;
+
+ if (dev_priv->rps.min_freq_softlimit == 0)
+ dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq;
+
+ mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
static void valleyview_cleanup_gt_powersave(struct drm_device *dev)
{
valleyview_cleanup_pctx(dev);
}
+static void cherryview_enable_rps(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring;
+ u32 gtfifodbg, val, rc6_mode = 0, pcbr;
+ int i;
+
+ WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock));
+
+ gtfifodbg = I915_READ(GTFIFODBG);
+ if (gtfifodbg) {
+ DRM_DEBUG_DRIVER("GT fifo had a previous error %x\n",
+ gtfifodbg);
+ I915_WRITE(GTFIFODBG, gtfifodbg);
+ }
+
+ cherryview_check_pctx(dev_priv);
+
+ /* 1a & 1b: Get forcewake during program sequence. Although the driver
+ * hasn't enabled a state yet where we need forcewake, BIOS may have.*/
+ gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL);
+
+ /* 2a: Program RC6 thresholds.*/
+ I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16);
+ I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); /* 12500 * 1280ns */
+ I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25); /* 25 * 1280ns */
+
+ for_each_ring(ring, dev_priv, i)
+ I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10);
+ I915_WRITE(GEN6_RC_SLEEP, 0);
+
+ I915_WRITE(GEN6_RC6_THRESHOLD, 50000); /* 50/125ms per EI */
+
+ /* allows RC6 residency counter to work */
+ I915_WRITE(VLV_COUNTER_CONTROL,
+ _MASKED_BIT_ENABLE(VLV_COUNT_RANGE_HIGH |
+ VLV_MEDIA_RC6_COUNT_EN |
+ VLV_RENDER_RC6_COUNT_EN));
+
+ /* For now we assume BIOS is allocating and populating the PCBR */
+ pcbr = I915_READ(VLV_PCBR);
+
+ DRM_DEBUG_DRIVER("PCBR offset : 0x%x\n", pcbr);
+
+ /* 3: Enable RC6 */
+ if ((intel_enable_rc6(dev) & INTEL_RC6_ENABLE) &&
+ (pcbr >> VLV_PCBR_ADDR_SHIFT))
+ rc6_mode = GEN6_RC_CTL_EI_MODE(1);
+
+ I915_WRITE(GEN6_RC_CONTROL, rc6_mode);
+
+ /* 4 Program defaults and thresholds for RPS*/
+ I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400);
+ I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000);
+ I915_WRITE(GEN6_RP_UP_EI, 66000);
+ I915_WRITE(GEN6_RP_DOWN_EI, 350000);
+
+ I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
+
+ /* WaDisablePwrmtrEvent:chv (pre-production hw) */
+ I915_WRITE(0xA80C, I915_READ(0xA80C) & 0x00ffffff);
+ I915_WRITE(0xA810, I915_READ(0xA810) & 0xffffff00);
+
+ /* 5: Enable RPS */
+ I915_WRITE(GEN6_RP_CONTROL,
+ GEN6_RP_MEDIA_HW_NORMAL_MODE |
+ GEN6_RP_MEDIA_IS_GFX | /* WaSetMaskForGfxBusyness:chv (pre-production hw ?) */
+ GEN6_RP_ENABLE |
+ GEN6_RP_UP_BUSY_AVG |
+ GEN6_RP_DOWN_IDLE_AVG);
+
+ val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
+
+ DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & 0x10 ? "yes" : "no");
+ DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val);
+
+ dev_priv->rps.cur_freq = (val >> 8) & 0xff;
+ DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n",
+ vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq),
+ dev_priv->rps.cur_freq);
+
+ DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n",
+ vlv_gpu_freq(dev_priv, dev_priv->rps.efficient_freq),
+ dev_priv->rps.efficient_freq);
+
+ valleyview_set_rps(dev_priv->dev, dev_priv->rps.efficient_freq);
+
+ gen8_enable_rps_interrupts(dev);
+
+ gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL);
+}
+
static void valleyview_enable_rps(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3886,6 +4385,7 @@ static void valleyview_enable_rps(struct drm_device *dev)
I915_WRITE(GEN6_RP_DOWN_EI, 350000);
I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
+ I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 0xf4240);
I915_WRITE(GEN6_RP_CONTROL,
GEN6_RP_MEDIA_TURBO |
@@ -3906,9 +4406,11 @@ static void valleyview_enable_rps(struct drm_device *dev)
/* allows RC6 residency counter to work */
I915_WRITE(VLV_COUNTER_CONTROL,
- _MASKED_BIT_ENABLE(VLV_COUNT_RANGE_HIGH |
+ _MASKED_BIT_ENABLE(VLV_MEDIA_RC0_COUNT_EN |
+ VLV_RENDER_RC0_COUNT_EN |
VLV_MEDIA_RC6_COUNT_EN |
VLV_RENDER_RC6_COUNT_EN));
+
if (intel_enable_rc6(dev) & INTEL_RC6_ENABLE)
rc6_mode = GEN7_RC_CTL_TO_MODE | VLV_RC_CTL_CTX_RST_PARALLEL;
@@ -4666,33 +5168,60 @@ void intel_init_gt_powersave(struct drm_device *dev)
{
i915.enable_rc6 = sanitize_rc6_option(dev, i915.enable_rc6);
- if (IS_VALLEYVIEW(dev))
+ if (IS_CHERRYVIEW(dev))
+ cherryview_init_gt_powersave(dev);
+ else if (IS_VALLEYVIEW(dev))
valleyview_init_gt_powersave(dev);
}
void intel_cleanup_gt_powersave(struct drm_device *dev)
{
- if (IS_VALLEYVIEW(dev))
+ if (IS_CHERRYVIEW(dev))
+ return;
+ else if (IS_VALLEYVIEW(dev))
valleyview_cleanup_gt_powersave(dev);
}
+/**
+ * intel_suspend_gt_powersave - suspend PM work and helper threads
+ * @dev: drm device
+ *
+ * We don't want to disable RC6 or other features here, we just want
+ * to make sure any work we've queued has finished and won't bother
+ * us while we're suspended.
+ */
+void intel_suspend_gt_powersave(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /* Interrupts should be disabled already to avoid re-arming. */
+ WARN_ON(intel_irqs_enabled(dev_priv));
+
+ flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
+ cancel_work_sync(&dev_priv->rps.work);
+
+ /* Force GPU to min freq during suspend */
+ gen6_rps_idle(dev_priv);
+}
+
void intel_disable_gt_powersave(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
/* Interrupts should be disabled already to avoid re-arming. */
- WARN_ON(dev->irq_enabled);
+ WARN_ON(intel_irqs_enabled(dev_priv));
if (IS_IRONLAKE_M(dev)) {
ironlake_disable_drps(dev);
ironlake_disable_rc6(dev);
- } else if (IS_GEN6(dev) || IS_GEN7(dev) || IS_BROADWELL(dev)) {
- if (cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work))
- intel_runtime_pm_put(dev_priv);
+ } else if (INTEL_INFO(dev)->gen >= 6) {
+ intel_suspend_gt_powersave(dev);
- cancel_work_sync(&dev_priv->rps.work);
mutex_lock(&dev_priv->rps.hw_lock);
- if (IS_VALLEYVIEW(dev))
+ if (IS_CHERRYVIEW(dev))
+ cherryview_disable_rps(dev);
+ else if (IS_VALLEYVIEW(dev))
valleyview_disable_rps(dev);
else
gen6_disable_rps(dev);
@@ -4710,7 +5239,9 @@ static void intel_gen6_powersave_work(struct work_struct *work)
mutex_lock(&dev_priv->rps.hw_lock);
- if (IS_VALLEYVIEW(dev)) {
+ if (IS_CHERRYVIEW(dev)) {
+ cherryview_enable_rps(dev);
+ } else if (IS_VALLEYVIEW(dev)) {
valleyview_enable_rps(dev);
} else if (IS_BROADWELL(dev)) {
gen8_enable_rps(dev);
@@ -4735,7 +5266,7 @@ void intel_enable_gt_powersave(struct drm_device *dev)
ironlake_enable_rc6(dev);
intel_init_emon(dev);
mutex_unlock(&dev->struct_mutex);
- } else if (IS_GEN6(dev) || IS_GEN7(dev) || IS_BROADWELL(dev)) {
+ } else if (INTEL_INFO(dev)->gen >= 6) {
/*
* PCU communication is slow and this doesn't need to be
* done at any specific time, so do this out of our fast path
@@ -4779,7 +5310,7 @@ static void g4x_disable_trickle_feed(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
I915_WRITE(DSPCNTR(pipe),
I915_READ(DSPCNTR(pipe)) |
DISPPLANE_TRICKLE_FEED_DISABLE);
@@ -4894,7 +5425,7 @@ static void cpt_init_clock_gating(struct drm_device *dev)
/* The below fixes the weird display corruption, a few pixels shifted
* downward, on (only) LVDS of some HP laptops with IVY.
*/
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
val = I915_READ(TRANS_CHICKEN2(pipe));
val |= TRANS_CHICKEN2_TIMING_OVERRIDE;
val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED;
@@ -4906,7 +5437,7 @@ static void cpt_init_clock_gating(struct drm_device *dev)
I915_WRITE(TRANS_CHICKEN2(pipe), val);
}
/* WADP0ClockGatingDisable */
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
I915_WRITE(TRANS_CHICKEN1(pipe),
TRANS_CHICKEN1_DP0UNIT_GC_DISABLE);
}
@@ -4918,11 +5449,9 @@ static void gen6_check_mch_setup(struct drm_device *dev)
uint32_t tmp;
tmp = I915_READ(MCH_SSKPD);
- if ((tmp & MCH_SSKPD_WM0_MASK) != MCH_SSKPD_WM0_VAL) {
- DRM_INFO("Wrong MCH_SSKPD value: 0x%08x\n", tmp);
- DRM_INFO("This can cause pipe underruns and display issues.\n");
- DRM_INFO("Please upgrade your BIOS to fix this.\n");
- }
+ if ((tmp & MCH_SSKPD_WM0_MASK) != MCH_SSKPD_WM0_VAL)
+ DRM_DEBUG_KMS("Wrong MCH_SSKPD value: 0x%08x This can cause underruns.\n",
+ tmp);
}
static void gen6_init_clock_gating(struct drm_device *dev)
@@ -5076,7 +5605,7 @@ static void lpt_suspend_hw(struct drm_device *dev)
}
}
-static void gen8_init_clock_gating(struct drm_device *dev)
+static void broadwell_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
enum pipe pipe;
@@ -5088,37 +5617,12 @@ static void gen8_init_clock_gating(struct drm_device *dev)
/* FIXME(BDW): Check all the w/a, some might only apply to
* pre-production hw. */
- /* WaDisablePartialInstShootdown:bdw */
- I915_WRITE(GEN8_ROW_CHICKEN,
- _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE));
-
- /* WaDisableThreadStallDopClockGating:bdw */
- /* FIXME: Unclear whether we really need this on production bdw. */
- I915_WRITE(GEN8_ROW_CHICKEN,
- _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE));
- /*
- * This GEN8_CENTROID_PIXEL_OPT_DIS W/A is only needed for
- * pre-production hardware
- */
- I915_WRITE(HALF_SLICE_CHICKEN3,
- _MASKED_BIT_ENABLE(GEN8_CENTROID_PIXEL_OPT_DIS));
- I915_WRITE(HALF_SLICE_CHICKEN3,
- _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS));
I915_WRITE(GAMTARBMODE, _MASKED_BIT_ENABLE(ARB_MODE_BWGTLB_DISABLE));
I915_WRITE(_3D_CHICKEN3,
- _3D_CHICKEN_SDE_LIMIT_FIFO_POLY_DEPTH(2));
-
- I915_WRITE(COMMON_SLICE_CHICKEN2,
- _MASKED_BIT_ENABLE(GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE));
+ _MASKED_BIT_ENABLE(_3D_CHICKEN_SDE_LIMIT_FIFO_POLY_DEPTH(2)));
- I915_WRITE(GEN7_HALF_SLICE_CHICKEN1,
- _MASKED_BIT_ENABLE(GEN7_SINGLE_SUBSCAN_DISPATCH_ENABLE));
-
- /* WaDisableDopClockGating:bdw May not be needed for production */
- I915_WRITE(GEN7_ROW_CHICKEN2,
- _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE));
/* WaSwitchSolVfFArbitrationPriority:bdw */
I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL);
@@ -5128,37 +5632,18 @@ static void gen8_init_clock_gating(struct drm_device *dev)
I915_READ(CHICKEN_PAR1_1) | DPA_MASK_VBLANK_SRD);
/* WaPsrDPRSUnmaskVBlankInSRD:bdw */
- for_each_pipe(pipe) {
+ for_each_pipe(dev_priv, pipe) {
I915_WRITE(CHICKEN_PIPESL_1(pipe),
I915_READ(CHICKEN_PIPESL_1(pipe)) |
BDW_DPRS_MASK_VBLANK_SRD);
}
- /* Use Force Non-Coherent whenever executing a 3D context. This is a
- * workaround for for a possible hang in the unlikely event a TLB
- * invalidation occurs during a PSD flush.
- */
- I915_WRITE(HDC_CHICKEN0,
- I915_READ(HDC_CHICKEN0) |
- _MASKED_BIT_ENABLE(HDC_FORCE_NON_COHERENT));
-
/* WaVSRefCountFullforceMissDisable:bdw */
/* WaDSRefCountFullforceMissDisable:bdw */
I915_WRITE(GEN7_FF_THREAD_MODE,
I915_READ(GEN7_FF_THREAD_MODE) &
~(GEN8_FF_DS_REF_CNT_FFME | GEN7_FF_VS_REF_CNT_FFME));
- /*
- * BSpec recommends 8x4 when MSAA is used,
- * however in practice 16x4 seems fastest.
- *
- * Note that PS/WM thread counts depend on the WIZ hashing
- * disable bit, which we don't touch here, but it's good
- * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM).
- */
- I915_WRITE(GEN7_GT_MODE,
- GEN6_WIZ_HASHING_MASK | GEN6_WIZ_HASHING_16x4);
-
I915_WRITE(GEN6_RC_SLEEP_PSMI_CONTROL,
_MASKED_BIT_ENABLE(GEN8_RC_SEMA_IDLE_MSG_DISABLE));
@@ -5166,9 +5651,7 @@ static void gen8_init_clock_gating(struct drm_device *dev)
I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) |
GEN8_SDEUNIT_CLOCK_GATE_DISABLE);
- /* Wa4x4STCOptimizationDisable:bdw */
- I915_WRITE(CACHE_MODE_1,
- _MASKED_BIT_ENABLE(GEN8_4x4_STC_OPTIMIZATION_DISABLE));
+ lpt_init_clock_gating(dev);
}
static void haswell_init_clock_gating(struct drm_device *dev)
@@ -5324,28 +5807,6 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
static void valleyview_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 val;
-
- mutex_lock(&dev_priv->rps.hw_lock);
- val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
- mutex_unlock(&dev_priv->rps.hw_lock);
- switch ((val >> 6) & 3) {
- case 0:
- case 1:
- dev_priv->mem_freq = 800;
- break;
- case 2:
- dev_priv->mem_freq = 1066;
- break;
- case 3:
- dev_priv->mem_freq = 1333;
- break;
- }
- DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq);
-
- dev_priv->vlv_cdclk_freq = valleyview_cur_cdclk(dev_priv);
- DRM_DEBUG_DRIVER("Current CD clock rate: %d MHz",
- dev_priv->vlv_cdclk_freq);
I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE);
@@ -5426,14 +5887,6 @@ static void cherryview_init_clock_gating(struct drm_device *dev)
I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE);
- /* WaDisablePartialInstShootdown:chv */
- I915_WRITE(GEN8_ROW_CHICKEN,
- _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE));
-
- /* WaDisableThreadStallDopClockGating:chv */
- I915_WRITE(GEN8_ROW_CHICKEN,
- _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE));
-
/* WaVSRefCountFullforceMissDisable:chv */
/* WaDSRefCountFullforceMissDisable:chv */
I915_WRITE(GEN7_FF_THREAD_MODE,
@@ -5452,10 +5905,6 @@ static void cherryview_init_clock_gating(struct drm_device *dev)
I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) |
GEN8_SDEUNIT_CLOCK_GATE_DISABLE);
- /* WaDisableSamplerPowerBypass:chv (pre-production hw) */
- I915_WRITE(HALF_SLICE_CHICKEN3,
- _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS));
-
/* WaDisableGunitClockGating:chv (pre-production hw) */
I915_WRITE(VLV_GUNIT_CLOCK_GATE, I915_READ(VLV_GUNIT_CLOCK_GATE) |
GINT_DIS);
@@ -5465,8 +5914,6 @@ static void cherryview_init_clock_gating(struct drm_device *dev)
_MASKED_BIT_ENABLE(GEN8_FF_DOP_CLOCK_GATE_DISABLE));
/* WaDisableDopClockGating:chv (pre-production hw) */
- I915_WRITE(GEN7_ROW_CHICKEN2,
- _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE));
I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) |
GEN6_EU_TCUNIT_CLOCK_GATE_DISABLE);
}
@@ -5551,6 +5998,9 @@ static void gen3_init_clock_gating(struct drm_device *dev)
/* On GEN3 we really need to make sure the ARB C3 LP bit is set */
I915_WRITE(MI_ARB_STATE, _MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE));
+
+ I915_WRITE(MI_ARB_STATE,
+ _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE));
}
static void i85x_init_clock_gating(struct drm_device *dev)
@@ -5562,6 +6012,9 @@ static void i85x_init_clock_gating(struct drm_device *dev)
/* interrupts should cause a wake up from C3 */
I915_WRITE(MI_STATE, _MASKED_BIT_ENABLE(MI_AGPBUSY_INT_EN) |
_MASKED_BIT_DISABLE(MI_AGPBUSY_830_MODE));
+
+ I915_WRITE(MEM_MODE,
+ _MASKED_BIT_ENABLE(MEM_DISPLAY_TRICKLE_FEED_DISABLE));
}
static void i830_init_clock_gating(struct drm_device *dev)
@@ -5569,6 +6022,10 @@ static void i830_init_clock_gating(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE);
+
+ I915_WRITE(MEM_MODE,
+ _MASKED_BIT_ENABLE(MEM_DISPLAY_A_TRICKLE_FEED_DISABLE) |
+ _MASKED_BIT_ENABLE(MEM_DISPLAY_B_TRICKLE_FEED_DISABLE));
}
void intel_init_clock_gating(struct drm_device *dev)
@@ -5661,7 +6118,6 @@ bool intel_display_power_enabled(struct drm_i915_private *dev_priv,
static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
- unsigned long irqflags;
/*
* After we re-enable the power well, if we touch VGA register 0x3d5
@@ -5677,21 +6133,8 @@ static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv)
outb(inb(VGA_MSR_READ), VGA_MSR_WRITE);
vga_put(dev->pdev, VGA_RSRC_LEGACY_IO);
- if (IS_BROADWELL(dev)) {
- spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- I915_WRITE(GEN8_DE_PIPE_IMR(PIPE_B),
- dev_priv->de_irq_mask[PIPE_B]);
- I915_WRITE(GEN8_DE_PIPE_IER(PIPE_B),
- ~dev_priv->de_irq_mask[PIPE_B] |
- GEN8_PIPE_VBLANK);
- I915_WRITE(GEN8_DE_PIPE_IMR(PIPE_C),
- dev_priv->de_irq_mask[PIPE_C]);
- I915_WRITE(GEN8_DE_PIPE_IER(PIPE_C),
- ~dev_priv->de_irq_mask[PIPE_C] |
- GEN8_PIPE_VBLANK);
- POSTING_READ(GEN8_DE_PIPE_IER(PIPE_C));
- spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
- }
+ if (IS_BROADWELL(dev))
+ gen8_irq_power_well_post_enable(dev_priv);
}
static void hsw_set_power_well(struct drm_i915_private *dev_priv,
@@ -5762,34 +6205,13 @@ static bool i9xx_always_on_power_well_enabled(struct drm_i915_private *dev_priv,
return true;
}
-void __vlv_set_power_well(struct drm_i915_private *dev_priv,
- enum punit_power_well power_well_id, bool enable)
+static void vlv_set_power_well(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well, bool enable)
{
- struct drm_device *dev = dev_priv->dev;
+ enum punit_power_well power_well_id = power_well->data;
u32 mask;
u32 state;
u32 ctrl;
- enum pipe pipe;
-
- if (power_well_id == PUNIT_POWER_WELL_DPIO_CMN_BC) {
- if (enable) {
- /*
- * Enable the CRI clock source so we can get at the
- * display and the reference clock for VGA
- * hotplug / manual detection.
- */
- I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) |
- DPLL_REFA_CLK_ENABLE_VLV |
- DPLL_INTEGRATED_CRI_CLK_VLV);
- udelay(1); /* >10ns for cmnreset, >0ns for sidereset */
- } else {
- for_each_pipe(pipe)
- assert_pll_disabled(dev_priv, pipe);
- /* Assert common reset */
- I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) &
- ~DPIO_CMNRST);
- }
- }
mask = PUNIT_PWRGT_MASK(power_well_id);
state = enable ? PUNIT_PWRGT_PWR_ON(power_well_id) :
@@ -5817,28 +6239,6 @@ void __vlv_set_power_well(struct drm_i915_private *dev_priv,
out:
mutex_unlock(&dev_priv->rps.hw_lock);
-
- /*
- * From VLV2A0_DP_eDP_DPIO_driver_vbios_notes_10.docx -
- * 6. De-assert cmn_reset/side_reset. Same as VLV X0.
- * a. GUnit 0x2110 bit[0] set to 1 (def 0)
- * b. The other bits such as sfr settings / modesel may all
- * be set to 0.
- *
- * This should only be done on init and resume from S3 with
- * both PLLs disabled, or we risk losing DPIO and PLL
- * synchronization.
- */
- if (power_well_id == PUNIT_POWER_WELL_DPIO_CMN_BC && enable)
- I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) | DPIO_CMNRST);
-}
-
-static void vlv_set_power_well(struct drm_i915_private *dev_priv,
- struct i915_power_well *power_well, bool enable)
-{
- enum punit_power_well power_well_id = power_well->data;
-
- __vlv_set_power_well(dev_priv, power_well_id, enable);
}
static void vlv_power_well_sync_hw(struct drm_i915_private *dev_priv,
@@ -5928,6 +6328,201 @@ static void vlv_display_power_well_disable(struct drm_i915_private *dev_priv,
spin_unlock_irq(&dev_priv->irq_lock);
vlv_set_power_well(dev_priv, power_well, false);
+
+ vlv_power_sequencer_reset(dev_priv);
+}
+
+static void vlv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC);
+
+ /*
+ * Enable the CRI clock source so we can get at the
+ * display and the reference clock for VGA
+ * hotplug / manual detection.
+ */
+ I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) |
+ DPLL_REFA_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV);
+ udelay(1); /* >10ns for cmnreset, >0ns for sidereset */
+
+ vlv_set_power_well(dev_priv, power_well, true);
+
+ /*
+ * From VLV2A0_DP_eDP_DPIO_driver_vbios_notes_10.docx -
+ * 6. De-assert cmn_reset/side_reset. Same as VLV X0.
+ * a. GUnit 0x2110 bit[0] set to 1 (def 0)
+ * b. The other bits such as sfr settings / modesel may all
+ * be set to 0.
+ *
+ * This should only be done on init and resume from S3 with
+ * both PLLs disabled, or we risk losing DPIO and PLL
+ * synchronization.
+ */
+ I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) | DPIO_CMNRST);
+}
+
+static void vlv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ enum pipe pipe;
+
+ WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC);
+
+ for_each_pipe(dev_priv, pipe)
+ assert_pll_disabled(dev_priv, pipe);
+
+ /* Assert common reset */
+ I915_WRITE(DPIO_CTL, I915_READ(DPIO_CTL) & ~DPIO_CMNRST);
+
+ vlv_set_power_well(dev_priv, power_well, false);
+}
+
+static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ enum dpio_phy phy;
+
+ WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC &&
+ power_well->data != PUNIT_POWER_WELL_DPIO_CMN_D);
+
+ /*
+ * Enable the CRI clock source so we can get at the
+ * display and the reference clock for VGA
+ * hotplug / manual detection.
+ */
+ if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) {
+ phy = DPIO_PHY0;
+ I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) |
+ DPLL_REFA_CLK_ENABLE_VLV);
+ I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) |
+ DPLL_REFA_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV);
+ } else {
+ phy = DPIO_PHY1;
+ I915_WRITE(DPLL(PIPE_C), I915_READ(DPLL(PIPE_C)) |
+ DPLL_REFA_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV);
+ }
+ udelay(1); /* >10ns for cmnreset, >0ns for sidereset */
+ vlv_set_power_well(dev_priv, power_well, true);
+
+ /* Poll for phypwrgood signal */
+ if (wait_for(I915_READ(DISPLAY_PHY_STATUS) & PHY_POWERGOOD(phy), 1))
+ DRM_ERROR("Display PHY %d is not power up\n", phy);
+
+ I915_WRITE(DISPLAY_PHY_CONTROL, I915_READ(DISPLAY_PHY_CONTROL) |
+ PHY_COM_LANE_RESET_DEASSERT(phy));
+}
+
+static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ enum dpio_phy phy;
+
+ WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC &&
+ power_well->data != PUNIT_POWER_WELL_DPIO_CMN_D);
+
+ if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) {
+ phy = DPIO_PHY0;
+ assert_pll_disabled(dev_priv, PIPE_A);
+ assert_pll_disabled(dev_priv, PIPE_B);
+ } else {
+ phy = DPIO_PHY1;
+ assert_pll_disabled(dev_priv, PIPE_C);
+ }
+
+ I915_WRITE(DISPLAY_PHY_CONTROL, I915_READ(DISPLAY_PHY_CONTROL) &
+ ~PHY_COM_LANE_RESET_DEASSERT(phy));
+
+ vlv_set_power_well(dev_priv, power_well, false);
+}
+
+static bool chv_pipe_power_well_enabled(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ enum pipe pipe = power_well->data;
+ bool enabled;
+ u32 state, ctrl;
+
+ mutex_lock(&dev_priv->rps.hw_lock);
+
+ state = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) & DP_SSS_MASK(pipe);
+ /*
+ * We only ever set the power-on and power-gate states, anything
+ * else is unexpected.
+ */
+ WARN_ON(state != DP_SSS_PWR_ON(pipe) && state != DP_SSS_PWR_GATE(pipe));
+ enabled = state == DP_SSS_PWR_ON(pipe);
+
+ /*
+ * A transient state at this point would mean some unexpected party
+ * is poking at the power controls too.
+ */
+ ctrl = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) & DP_SSC_MASK(pipe);
+ WARN_ON(ctrl << 16 != state);
+
+ mutex_unlock(&dev_priv->rps.hw_lock);
+
+ return enabled;
+}
+
+static void chv_set_pipe_power_well(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well,
+ bool enable)
+{
+ enum pipe pipe = power_well->data;
+ u32 state;
+ u32 ctrl;
+
+ state = enable ? DP_SSS_PWR_ON(pipe) : DP_SSS_PWR_GATE(pipe);
+
+ mutex_lock(&dev_priv->rps.hw_lock);
+
+#define COND \
+ ((vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) & DP_SSS_MASK(pipe)) == state)
+
+ if (COND)
+ goto out;
+
+ ctrl = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ);
+ ctrl &= ~DP_SSC_MASK(pipe);
+ ctrl |= enable ? DP_SSC_PWR_ON(pipe) : DP_SSC_PWR_GATE(pipe);
+ vlv_punit_write(dev_priv, PUNIT_REG_DSPFREQ, ctrl);
+
+ if (wait_for(COND, 100))
+ DRM_ERROR("timout setting power well state %08x (%08x)\n",
+ state,
+ vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ));
+
+#undef COND
+
+out:
+ mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
+static void chv_pipe_power_well_sync_hw(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ chv_set_pipe_power_well(dev_priv, power_well, power_well->count > 0);
+}
+
+static void chv_pipe_power_well_enable(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ WARN_ON_ONCE(power_well->data != PIPE_A &&
+ power_well->data != PIPE_B &&
+ power_well->data != PIPE_C);
+
+ chv_set_pipe_power_well(dev_priv, power_well, true);
+}
+
+static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv,
+ struct i915_power_well *power_well)
+{
+ WARN_ON_ONCE(power_well->data != PIPE_A &&
+ power_well->data != PIPE_B &&
+ power_well->data != PIPE_C);
+
+ chv_set_pipe_power_well(dev_priv, power_well, false);
}
static void check_power_well_state(struct drm_i915_private *dev_priv,
@@ -6079,6 +6674,7 @@ EXPORT_SYMBOL_GPL(i915_get_cdclk_freq);
BIT(POWER_DOMAIN_PORT_DDI_D_2_LANES) | \
BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) | \
BIT(POWER_DOMAIN_PORT_CRT) | \
+ BIT(POWER_DOMAIN_PLLS) | \
BIT(POWER_DOMAIN_INIT))
#define HSW_DISPLAY_POWER_DOMAINS ( \
(POWER_DOMAIN_MASK & ~HSW_ALWAYS_ON_POWER_DOMAINS) | \
@@ -6120,6 +6716,39 @@ EXPORT_SYMBOL_GPL(i915_get_cdclk_freq);
BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) | \
BIT(POWER_DOMAIN_INIT))
+#define CHV_PIPE_A_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PIPE_A) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define CHV_PIPE_B_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PIPE_B) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define CHV_PIPE_C_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PIPE_C) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define CHV_DPIO_CMN_BC_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define CHV_DPIO_CMN_D_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PORT_DDI_D_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define CHV_DPIO_TX_D_LANES_01_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PORT_DDI_D_2_LANES) | \
+ BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) | \
+ BIT(POWER_DOMAIN_INIT))
+
+#define CHV_DPIO_TX_D_LANES_23_POWER_DOMAINS ( \
+ BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) | \
+ BIT(POWER_DOMAIN_INIT))
+
static const struct i915_power_well_ops i9xx_always_on_power_well_ops = {
.sync_hw = i9xx_always_on_power_well_noop,
.enable = i9xx_always_on_power_well_noop,
@@ -6127,6 +6756,20 @@ static const struct i915_power_well_ops i9xx_always_on_power_well_ops = {
.is_enabled = i9xx_always_on_power_well_enabled,
};
+static const struct i915_power_well_ops chv_pipe_power_well_ops = {
+ .sync_hw = chv_pipe_power_well_sync_hw,
+ .enable = chv_pipe_power_well_enable,
+ .disable = chv_pipe_power_well_disable,
+ .is_enabled = chv_pipe_power_well_enabled,
+};
+
+static const struct i915_power_well_ops chv_dpio_cmn_power_well_ops = {
+ .sync_hw = vlv_power_well_sync_hw,
+ .enable = chv_dpio_cmn_power_well_enable,
+ .disable = chv_dpio_cmn_power_well_disable,
+ .is_enabled = vlv_power_well_enabled,
+};
+
static struct i915_power_well i9xx_always_on_power_well[] = {
{
.name = "always-on",
@@ -6178,6 +6821,13 @@ static const struct i915_power_well_ops vlv_display_power_well_ops = {
.is_enabled = vlv_power_well_enabled,
};
+static const struct i915_power_well_ops vlv_dpio_cmn_power_well_ops = {
+ .sync_hw = vlv_power_well_sync_hw,
+ .enable = vlv_dpio_cmn_power_well_enable,
+ .disable = vlv_dpio_cmn_power_well_disable,
+ .is_enabled = vlv_power_well_enabled,
+};
+
static const struct i915_power_well_ops vlv_dpio_power_well_ops = {
.sync_hw = vlv_power_well_sync_hw,
.enable = vlv_power_well_enable,
@@ -6238,10 +6888,126 @@ static struct i915_power_well vlv_power_wells[] = {
.name = "dpio-common",
.domains = VLV_DPIO_CMN_BC_POWER_DOMAINS,
.data = PUNIT_POWER_WELL_DPIO_CMN_BC,
+ .ops = &vlv_dpio_cmn_power_well_ops,
+ },
+};
+
+static struct i915_power_well chv_power_wells[] = {
+ {
+ .name = "always-on",
+ .always_on = 1,
+ .domains = VLV_ALWAYS_ON_POWER_DOMAINS,
+ .ops = &i9xx_always_on_power_well_ops,
+ },
+#if 0
+ {
+ .name = "display",
+ .domains = VLV_DISPLAY_POWER_DOMAINS,
+ .data = PUNIT_POWER_WELL_DISP2D,
+ .ops = &vlv_display_power_well_ops,
+ },
+ {
+ .name = "pipe-a",
+ .domains = CHV_PIPE_A_POWER_DOMAINS,
+ .data = PIPE_A,
+ .ops = &chv_pipe_power_well_ops,
+ },
+ {
+ .name = "pipe-b",
+ .domains = CHV_PIPE_B_POWER_DOMAINS,
+ .data = PIPE_B,
+ .ops = &chv_pipe_power_well_ops,
+ },
+ {
+ .name = "pipe-c",
+ .domains = CHV_PIPE_C_POWER_DOMAINS,
+ .data = PIPE_C,
+ .ops = &chv_pipe_power_well_ops,
+ },
+#endif
+ {
+ .name = "dpio-common-bc",
+ /*
+ * XXX: cmnreset for one PHY seems to disturb the other.
+ * As a workaround keep both powered on at the same
+ * time for now.
+ */
+ .domains = CHV_DPIO_CMN_BC_POWER_DOMAINS | CHV_DPIO_CMN_D_POWER_DOMAINS,
+ .data = PUNIT_POWER_WELL_DPIO_CMN_BC,
+ .ops = &chv_dpio_cmn_power_well_ops,
+ },
+ {
+ .name = "dpio-common-d",
+ /*
+ * XXX: cmnreset for one PHY seems to disturb the other.
+ * As a workaround keep both powered on at the same
+ * time for now.
+ */
+ .domains = CHV_DPIO_CMN_BC_POWER_DOMAINS | CHV_DPIO_CMN_D_POWER_DOMAINS,
+ .data = PUNIT_POWER_WELL_DPIO_CMN_D,
+ .ops = &chv_dpio_cmn_power_well_ops,
+ },
+#if 0
+ {
+ .name = "dpio-tx-b-01",
+ .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS,
+ .ops = &vlv_dpio_power_well_ops,
+ .data = PUNIT_POWER_WELL_DPIO_TX_B_LANES_01,
+ },
+ {
+ .name = "dpio-tx-b-23",
+ .domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS,
+ .ops = &vlv_dpio_power_well_ops,
+ .data = PUNIT_POWER_WELL_DPIO_TX_B_LANES_23,
+ },
+ {
+ .name = "dpio-tx-c-01",
+ .domains = VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS,
.ops = &vlv_dpio_power_well_ops,
+ .data = PUNIT_POWER_WELL_DPIO_TX_C_LANES_01,
},
+ {
+ .name = "dpio-tx-c-23",
+ .domains = VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS |
+ VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS,
+ .ops = &vlv_dpio_power_well_ops,
+ .data = PUNIT_POWER_WELL_DPIO_TX_C_LANES_23,
+ },
+ {
+ .name = "dpio-tx-d-01",
+ .domains = CHV_DPIO_TX_D_LANES_01_POWER_DOMAINS |
+ CHV_DPIO_TX_D_LANES_23_POWER_DOMAINS,
+ .ops = &vlv_dpio_power_well_ops,
+ .data = PUNIT_POWER_WELL_DPIO_TX_D_LANES_01,
+ },
+ {
+ .name = "dpio-tx-d-23",
+ .domains = CHV_DPIO_TX_D_LANES_01_POWER_DOMAINS |
+ CHV_DPIO_TX_D_LANES_23_POWER_DOMAINS,
+ .ops = &vlv_dpio_power_well_ops,
+ .data = PUNIT_POWER_WELL_DPIO_TX_D_LANES_23,
+ },
+#endif
};
+static struct i915_power_well *lookup_power_well(struct drm_i915_private *dev_priv,
+ enum punit_power_well power_well_id)
+{
+ struct i915_power_domains *power_domains = &dev_priv->power_domains;
+ struct i915_power_well *power_well;
+ int i;
+
+ for_each_power_well(i, power_well, POWER_DOMAIN_MASK, power_domains) {
+ if (power_well->data == power_well_id)
+ return power_well;
+ }
+
+ return NULL;
+}
+
#define set_power_wells(power_domains, __power_wells) ({ \
(power_domains)->power_wells = (__power_wells); \
(power_domains)->power_well_count = ARRAY_SIZE(__power_wells); \
@@ -6263,6 +7029,8 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv)
} else if (IS_BROADWELL(dev_priv->dev)) {
set_power_wells(power_domains, bdw_power_wells);
hsw_pwr = power_domains;
+ } else if (IS_CHERRYVIEW(dev_priv->dev)) {
+ set_power_wells(power_domains, chv_power_wells);
} else if (IS_VALLEYVIEW(dev_priv->dev)) {
set_power_wells(power_domains, vlv_power_wells);
} else {
@@ -6292,11 +7060,50 @@ static void intel_power_domains_resume(struct drm_i915_private *dev_priv)
mutex_unlock(&power_domains->lock);
}
+static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv)
+{
+ struct i915_power_well *cmn =
+ lookup_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC);
+ struct i915_power_well *disp2d =
+ lookup_power_well(dev_priv, PUNIT_POWER_WELL_DISP2D);
+
+ /* nothing to do if common lane is already off */
+ if (!cmn->ops->is_enabled(dev_priv, cmn))
+ return;
+
+ /* If the display might be already active skip this */
+ if (disp2d->ops->is_enabled(dev_priv, disp2d) &&
+ I915_READ(DPIO_CTL) & DPIO_CMNRST)
+ return;
+
+ DRM_DEBUG_KMS("toggling display PHY side reset\n");
+
+ /* cmnlane needs DPLL registers */
+ disp2d->ops->enable(dev_priv, disp2d);
+
+ /*
+ * From VLV2A0_DP_eDP_HDMI_DPIO_driver_vbios_notes_11.docx:
+ * Need to assert and de-assert PHY SB reset by gating the
+ * common lane power, then un-gating it.
+ * Simply ungating isn't enough to reset the PHY enough to get
+ * ports and lanes running.
+ */
+ cmn->ops->disable(dev_priv, cmn);
+}
+
void intel_power_domains_init_hw(struct drm_i915_private *dev_priv)
{
+ struct drm_device *dev = dev_priv->dev;
struct i915_power_domains *power_domains = &dev_priv->power_domains;
power_domains->initializing = true;
+
+ if (IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev)) {
+ mutex_lock(&power_domains->lock);
+ vlv_cmnlane_wa(dev_priv);
+ mutex_unlock(&power_domains->lock);
+ }
+
/* For now, we need the power well to be always enabled. */
intel_display_set_init_power(dev_priv, true);
intel_power_domains_resume(dev_priv);
@@ -6449,13 +7256,15 @@ void intel_init_pm(struct drm_device *dev)
else if (IS_HASWELL(dev))
dev_priv->display.init_clock_gating = haswell_init_clock_gating;
else if (INTEL_INFO(dev)->gen == 8)
- dev_priv->display.init_clock_gating = gen8_init_clock_gating;
+ dev_priv->display.init_clock_gating = broadwell_init_clock_gating;
} else if (IS_CHERRYVIEW(dev)) {
- dev_priv->display.update_wm = valleyview_update_wm;
+ dev_priv->display.update_wm = cherryview_update_wm;
+ dev_priv->display.update_sprite_wm = valleyview_update_sprite_wm;
dev_priv->display.init_clock_gating =
cherryview_init_clock_gating;
} else if (IS_VALLEYVIEW(dev)) {
dev_priv->display.update_wm = valleyview_update_wm;
+ dev_priv->display.update_sprite_wm = valleyview_update_sprite_wm;
dev_priv->display.init_clock_gating =
valleyview_init_clock_gating;
} else if (IS_PINEVIEW(dev)) {
@@ -6469,7 +7278,7 @@ void intel_init_pm(struct drm_device *dev)
(dev_priv->is_ddr3 == 1) ? "3" : "2",
dev_priv->fsb_freq, dev_priv->mem_freq);
/* Disable CxSR and never update its watermark again */
- pineview_disable_cxsr(dev);
+ intel_set_memory_cxsr(dev_priv, false);
dev_priv->display.update_wm = NULL;
} else
dev_priv->display.update_wm = pineview_update_wm;
@@ -6552,7 +7361,7 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val)
return 0;
}
-int vlv_gpu_freq(struct drm_i915_private *dev_priv, int val)
+static int byt_gpu_freq(struct drm_i915_private *dev_priv, int val)
{
int div;
@@ -6574,7 +7383,7 @@ int vlv_gpu_freq(struct drm_i915_private *dev_priv, int val)
return DIV_ROUND_CLOSEST(dev_priv->mem_freq * (val + 6 - 0xbd), 4 * div);
}
-int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val)
+static int byt_freq_opcode(struct drm_i915_private *dev_priv, int val)
{
int mul;
@@ -6596,6 +7405,81 @@ int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val)
return DIV_ROUND_CLOSEST(4 * mul * val, dev_priv->mem_freq) + 0xbd - 6;
}
+static int chv_gpu_freq(struct drm_i915_private *dev_priv, int val)
+{
+ int div, freq;
+
+ switch (dev_priv->rps.cz_freq) {
+ case 200:
+ div = 5;
+ break;
+ case 267:
+ div = 6;
+ break;
+ case 320:
+ case 333:
+ case 400:
+ div = 8;
+ break;
+ default:
+ return -1;
+ }
+
+ freq = (DIV_ROUND_CLOSEST((dev_priv->rps.cz_freq * val), 2 * div) / 2);
+
+ return freq;
+}
+
+static int chv_freq_opcode(struct drm_i915_private *dev_priv, int val)
+{
+ int mul, opcode;
+
+ switch (dev_priv->rps.cz_freq) {
+ case 200:
+ mul = 5;
+ break;
+ case 267:
+ mul = 6;
+ break;
+ case 320:
+ case 333:
+ case 400:
+ mul = 8;
+ break;
+ default:
+ return -1;
+ }
+
+ /* CHV needs even values */
+ opcode = (DIV_ROUND_CLOSEST((val * 2 * mul), dev_priv->rps.cz_freq) * 2);
+
+ return opcode;
+}
+
+int vlv_gpu_freq(struct drm_i915_private *dev_priv, int val)
+{
+ int ret = -1;
+
+ if (IS_CHERRYVIEW(dev_priv->dev))
+ ret = chv_gpu_freq(dev_priv, val);
+ else if (IS_VALLEYVIEW(dev_priv->dev))
+ ret = byt_gpu_freq(dev_priv, val);
+
+ return ret;
+}
+
+int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val)
+{
+ int ret = -1;
+
+ if (IS_CHERRYVIEW(dev_priv->dev))
+ ret = chv_freq_opcode(dev_priv, val);
+ else if (IS_VALLEYVIEW(dev_priv->dev))
+ ret = byt_freq_opcode(dev_priv, val);
+
+ return ret;
+}
+
void intel_pm_setup(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -6606,5 +7490,5 @@ void intel_pm_setup(struct drm_device *dev)
intel_gen6_powersave_work);
dev_priv->pm.suspended = false;
- dev_priv->pm.irqs_disabled = false;
+ dev_priv->pm._irqs_disabled = false;
}
diff --git a/drivers/gpu/drm/i915/intel_renderstate.h b/drivers/gpu/drm/i915/intel_renderstate.h
index a5e783a9928a..6c792d3a9c9c 100644
--- a/drivers/gpu/drm/i915/intel_renderstate.h
+++ b/drivers/gpu/drm/i915/intel_renderstate.h
@@ -24,14 +24,7 @@
#ifndef _INTEL_RENDERSTATE_H
#define _INTEL_RENDERSTATE_H
-#include <linux/types.h>
-
-struct intel_renderstate_rodata {
- const u32 *reloc;
- const u32 reloc_items;
- const u32 *batch;
- const u32 batch_items;
-};
+#include "i915_drv.h"
extern const struct intel_renderstate_rodata gen6_null_state;
extern const struct intel_renderstate_rodata gen7_null_state;
@@ -40,7 +33,6 @@ extern const struct intel_renderstate_rodata gen8_null_state;
#define RO_RENDERSTATE(_g) \
const struct intel_renderstate_rodata gen ## _g ## _null_state = { \
.reloc = gen ## _g ## _null_state_relocs, \
- .reloc_items = sizeof(gen ## _g ## _null_state_relocs)/4, \
.batch = gen ## _g ## _null_state_batch, \
.batch_items = sizeof(gen ## _g ## _null_state_batch)/4, \
}
diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen6.c b/drivers/gpu/drm/i915/intel_renderstate_gen6.c
index 740538ad0977..56c1429d8a60 100644
--- a/drivers/gpu/drm/i915/intel_renderstate_gen6.c
+++ b/drivers/gpu/drm/i915/intel_renderstate_gen6.c
@@ -6,6 +6,7 @@ static const u32 gen6_null_state_relocs[] = {
0x0000002c,
0x000001e0,
0x000001e4,
+ -1,
};
static const u32 gen6_null_state_batch[] = {
diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen7.c b/drivers/gpu/drm/i915/intel_renderstate_gen7.c
index 6fa7ff2a1298..419e35a7b0ff 100644
--- a/drivers/gpu/drm/i915/intel_renderstate_gen7.c
+++ b/drivers/gpu/drm/i915/intel_renderstate_gen7.c
@@ -5,6 +5,7 @@ static const u32 gen7_null_state_relocs[] = {
0x00000010,
0x00000018,
0x000001ec,
+ -1,
};
static const u32 gen7_null_state_batch[] = {
diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen8.c b/drivers/gpu/drm/i915/intel_renderstate_gen8.c
index 5c875615d42a..75ef1b5de45c 100644
--- a/drivers/gpu/drm/i915/intel_renderstate_gen8.c
+++ b/drivers/gpu/drm/i915/intel_renderstate_gen8.c
@@ -5,6 +5,7 @@ static const u32 gen8_null_state_relocs[] = {
0x00000050,
0x00000060,
0x000003ec,
+ -1,
};
static const u32 gen8_null_state_batch[] = {
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 279488addf3f..0a80e419b589 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -33,14 +33,24 @@
#include "i915_trace.h"
#include "intel_drv.h"
-/* Early gen2 devices have a cacheline of just 32 bytes, using 64 is overkill,
- * but keeps the logic simple. Indeed, the whole purpose of this macro is just
- * to give some inclination as to some of the magic values used in the various
- * workarounds!
- */
-#define CACHELINE_BYTES 64
+bool
+intel_ring_initialized(struct intel_engine_cs *ring)
+{
+ struct drm_device *dev = ring->dev;
+
+ if (!dev)
+ return false;
-static inline int __ring_space(int head, int tail, int size)
+ if (i915.enable_execlists) {
+ struct intel_context *dctx = ring->default_context;
+ struct intel_ringbuffer *ringbuf = dctx->engine[ring->id].ringbuf;
+
+ return ringbuf->obj;
+ } else
+ return ring->buffer && ring->buffer->obj;
+}
+
+int __intel_ring_space(int head, int tail, int size)
{
int space = head - (tail + I915_RING_FREE_SPACE);
if (space < 0)
@@ -48,13 +58,13 @@ static inline int __ring_space(int head, int tail, int size)
return space;
}
-static inline int ring_space(struct intel_engine_cs *ring)
+int intel_ring_space(struct intel_ringbuffer *ringbuf)
{
- struct intel_ringbuffer *ringbuf = ring->buffer;
- return __ring_space(ringbuf->head & HEAD_ADDR, ringbuf->tail, ringbuf->size);
+ return __intel_ring_space(ringbuf->head & HEAD_ADDR,
+ ringbuf->tail, ringbuf->size);
}
-static bool intel_ring_stopped(struct intel_engine_cs *ring)
+bool intel_ring_stopped(struct intel_engine_cs *ring)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
return dev_priv->gpu_error.stop_rings & intel_ring_flag(ring);
@@ -381,6 +391,27 @@ gen7_render_ring_flush(struct intel_engine_cs *ring,
}
static int
+gen8_emit_pipe_control(struct intel_engine_cs *ring,
+ u32 flags, u32 scratch_addr)
+{
+ int ret;
+
+ ret = intel_ring_begin(ring, 6);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6));
+ intel_ring_emit(ring, flags);
+ intel_ring_emit(ring, scratch_addr);
+ intel_ring_emit(ring, 0);
+ intel_ring_emit(ring, 0);
+ intel_ring_emit(ring, 0);
+ intel_ring_advance(ring);
+
+ return 0;
+}
+
+static int
gen8_render_ring_flush(struct intel_engine_cs *ring,
u32 invalidate_domains, u32 flush_domains)
{
@@ -403,22 +434,24 @@ gen8_render_ring_flush(struct intel_engine_cs *ring,
flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE;
flags |= PIPE_CONTROL_QW_WRITE;
flags |= PIPE_CONTROL_GLOBAL_GTT_IVB;
+
+ /* WaCsStallBeforeStateCacheInvalidate:bdw,chv */
+ ret = gen8_emit_pipe_control(ring,
+ PIPE_CONTROL_CS_STALL |
+ PIPE_CONTROL_STALL_AT_SCOREBOARD,
+ 0);
+ if (ret)
+ return ret;
}
- ret = intel_ring_begin(ring, 6);
+ ret = gen8_emit_pipe_control(ring, flags, scratch_addr);
if (ret)
return ret;
- intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6));
- intel_ring_emit(ring, flags);
- intel_ring_emit(ring, scratch_addr);
- intel_ring_emit(ring, 0);
- intel_ring_emit(ring, 0);
- intel_ring_emit(ring, 0);
- intel_ring_advance(ring);
+ if (!invalidate_domains && flush_domains)
+ return gen7_ring_fbc_flush(ring, FBC_REND_NUKE);
return 0;
-
}
static void ring_write_tail(struct intel_engine_cs *ring,
@@ -461,9 +494,14 @@ static bool stop_ring(struct intel_engine_cs *ring)
if (!IS_GEN2(ring->dev)) {
I915_WRITE_MODE(ring, _MASKED_BIT_ENABLE(STOP_RING));
- if (wait_for_atomic((I915_READ_MODE(ring) & MODE_IDLE) != 0, 1000)) {
- DRM_ERROR("%s :timed out trying to stop ring\n", ring->name);
- return false;
+ if (wait_for((I915_READ_MODE(ring) & MODE_IDLE) != 0, 1000)) {
+ DRM_ERROR("%s : timed out trying to stop ring\n", ring->name);
+ /* Sometimes we observe that the idle flag is not
+ * set even though the ring is empty. So double
+ * check before giving up.
+ */
+ if (I915_READ_HEAD(ring) != I915_READ_TAIL(ring))
+ return false;
}
}
@@ -517,11 +555,22 @@ static int init_ring_common(struct intel_engine_cs *ring)
else
ring_setup_phys_status_page(ring);
+ /* Enforce ordering by reading HEAD register back */
+ I915_READ_HEAD(ring);
+
/* Initialize the ring. This must happen _after_ we've cleared the ring
* registers with the above sequence (the readback of the HEAD registers
* also enforces ordering), otherwise the hw might lose the new ring
* register values. */
I915_WRITE_START(ring, i915_gem_obj_ggtt_offset(obj));
+
+ /* WaClearRingBufHeadRegAtInit:ctg,elk */
+ if (I915_READ_HEAD(ring))
+ DRM_DEBUG("%s initialization failed [head=%08x], fudging\n",
+ ring->name, I915_READ_HEAD(ring));
+ I915_WRITE_HEAD(ring, 0);
+ (void)I915_READ_HEAD(ring);
+
I915_WRITE_CTL(ring,
((ringbuf->size - PAGE_SIZE) & RING_NR_PAGES)
| RING_VALID);
@@ -545,7 +594,7 @@ static int init_ring_common(struct intel_engine_cs *ring)
else {
ringbuf->head = I915_READ_HEAD(ring);
ringbuf->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
- ringbuf->space = ring_space(ring);
+ ringbuf->space = intel_ring_space(ringbuf);
ringbuf->last_retired_head = -1;
}
@@ -557,8 +606,25 @@ out:
return ret;
}
-static int
-init_pipe_control(struct intel_engine_cs *ring)
+void
+intel_fini_pipe_control(struct intel_engine_cs *ring)
+{
+ struct drm_device *dev = ring->dev;
+
+ if (ring->scratch.obj == NULL)
+ return;
+
+ if (INTEL_INFO(dev)->gen >= 5) {
+ kunmap(sg_page(ring->scratch.obj->pages->sgl));
+ i915_gem_object_ggtt_unpin(ring->scratch.obj);
+ }
+
+ drm_gem_object_unreference(&ring->scratch.obj->base);
+ ring->scratch.obj = NULL;
+}
+
+int
+intel_init_pipe_control(struct intel_engine_cs *ring)
{
int ret;
@@ -599,11 +665,142 @@ err:
return ret;
}
+static inline void intel_ring_emit_wa(struct intel_engine_cs *ring,
+ u32 addr, u32 value)
+{
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (WARN_ON(dev_priv->num_wa_regs >= I915_MAX_WA_REGS))
+ return;
+
+ intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+ intel_ring_emit(ring, addr);
+ intel_ring_emit(ring, value);
+
+ dev_priv->intel_wa_regs[dev_priv->num_wa_regs].addr = addr;
+ dev_priv->intel_wa_regs[dev_priv->num_wa_regs].mask = value & 0xFFFF;
+ /* value is updated with the status of remaining bits of this
+ * register when it is read from debugfs file
+ */
+ dev_priv->intel_wa_regs[dev_priv->num_wa_regs].value = value;
+ dev_priv->num_wa_regs++;
+
+ return;
+}
+
+static int bdw_init_workarounds(struct intel_engine_cs *ring)
+{
+ int ret;
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /*
+ * workarounds applied in this fn are part of register state context,
+ * they need to be re-initialized followed by gpu reset, suspend/resume,
+ * module reload.
+ */
+ dev_priv->num_wa_regs = 0;
+ memset(dev_priv->intel_wa_regs, 0, sizeof(dev_priv->intel_wa_regs));
+
+ /*
+ * update the number of dwords required based on the
+ * actual number of workarounds applied
+ */
+ ret = intel_ring_begin(ring, 18);
+ if (ret)
+ return ret;
+
+ /* WaDisablePartialInstShootdown:bdw */
+ /* WaDisableThreadStallDopClockGating:bdw */
+ /* FIXME: Unclear whether we really need this on production bdw. */
+ intel_ring_emit_wa(ring, GEN8_ROW_CHICKEN,
+ _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE
+ | STALL_DOP_GATING_DISABLE));
+
+ /* WaDisableDopClockGating:bdw May not be needed for production */
+ intel_ring_emit_wa(ring, GEN7_ROW_CHICKEN2,
+ _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE));
+
+ intel_ring_emit_wa(ring, HALF_SLICE_CHICKEN3,
+ _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS));
+
+ /* Use Force Non-Coherent whenever executing a 3D context. This is a
+ * workaround for for a possible hang in the unlikely event a TLB
+ * invalidation occurs during a PSD flush.
+ */
+ intel_ring_emit_wa(ring, HDC_CHICKEN0,
+ _MASKED_BIT_ENABLE(HDC_FORCE_NON_COHERENT));
+
+ /* Wa4x4STCOptimizationDisable:bdw */
+ intel_ring_emit_wa(ring, CACHE_MODE_1,
+ _MASKED_BIT_ENABLE(GEN8_4x4_STC_OPTIMIZATION_DISABLE));
+
+ /*
+ * BSpec recommends 8x4 when MSAA is used,
+ * however in practice 16x4 seems fastest.
+ *
+ * Note that PS/WM thread counts depend on the WIZ hashing
+ * disable bit, which we don't touch here, but it's good
+ * to keep in mind (see 3DSTATE_PS and 3DSTATE_WM).
+ */
+ intel_ring_emit_wa(ring, GEN7_GT_MODE,
+ GEN6_WIZ_HASHING_MASK | GEN6_WIZ_HASHING_16x4);
+
+ intel_ring_advance(ring);
+
+ DRM_DEBUG_DRIVER("Number of Workarounds applied: %d\n",
+ dev_priv->num_wa_regs);
+
+ return 0;
+}
+
+static int chv_init_workarounds(struct intel_engine_cs *ring)
+{
+ int ret;
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /*
+ * workarounds applied in this fn are part of register state context,
+ * they need to be re-initialized followed by gpu reset, suspend/resume,
+ * module reload.
+ */
+ dev_priv->num_wa_regs = 0;
+ memset(dev_priv->intel_wa_regs, 0, sizeof(dev_priv->intel_wa_regs));
+
+ ret = intel_ring_begin(ring, 12);
+ if (ret)
+ return ret;
+
+ /* WaDisablePartialInstShootdown:chv */
+ intel_ring_emit_wa(ring, GEN8_ROW_CHICKEN,
+ _MASKED_BIT_ENABLE(PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE));
+
+ /* WaDisableThreadStallDopClockGating:chv */
+ intel_ring_emit_wa(ring, GEN8_ROW_CHICKEN,
+ _MASKED_BIT_ENABLE(STALL_DOP_GATING_DISABLE));
+
+ /* WaDisableDopClockGating:chv (pre-production hw) */
+ intel_ring_emit_wa(ring, GEN7_ROW_CHICKEN2,
+ _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE));
+
+ /* WaDisableSamplerPowerBypass:chv (pre-production hw) */
+ intel_ring_emit_wa(ring, HALF_SLICE_CHICKEN3,
+ _MASKED_BIT_ENABLE(GEN8_SAMPLER_POWER_BYPASS_DIS));
+
+ intel_ring_advance(ring);
+
+ return 0;
+}
+
static int init_render_ring(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int ret = init_ring_common(ring);
+ if (ret)
+ return ret;
/* WaTimedSingleVertexDispatch:cl,bw,ctg,elk,ilk,snb */
if (INTEL_INFO(dev)->gen >= 4 && INTEL_INFO(dev)->gen < 7)
@@ -631,7 +828,7 @@ static int init_render_ring(struct intel_engine_cs *ring)
_MASKED_BIT_ENABLE(GFX_REPLAY_MODE));
if (INTEL_INFO(dev)->gen >= 5) {
- ret = init_pipe_control(ring);
+ ret = intel_init_pipe_control(ring);
if (ret)
return ret;
}
@@ -658,17 +855,89 @@ static int init_render_ring(struct intel_engine_cs *ring)
static void render_ring_cleanup(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
- if (ring->scratch.obj == NULL)
- return;
+ if (dev_priv->semaphore_obj) {
+ i915_gem_object_ggtt_unpin(dev_priv->semaphore_obj);
+ drm_gem_object_unreference(&dev_priv->semaphore_obj->base);
+ dev_priv->semaphore_obj = NULL;
+ }
- if (INTEL_INFO(dev)->gen >= 5) {
- kunmap(sg_page(ring->scratch.obj->pages->sgl));
- i915_gem_object_ggtt_unpin(ring->scratch.obj);
+ intel_fini_pipe_control(ring);
+}
+
+static int gen8_rcs_signal(struct intel_engine_cs *signaller,
+ unsigned int num_dwords)
+{
+#define MBOX_UPDATE_DWORDS 8
+ struct drm_device *dev = signaller->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *waiter;
+ int i, ret, num_rings;
+
+ num_rings = hweight32(INTEL_INFO(dev)->ring_mask);
+ num_dwords += (num_rings-1) * MBOX_UPDATE_DWORDS;
+#undef MBOX_UPDATE_DWORDS
+
+ ret = intel_ring_begin(signaller, num_dwords);
+ if (ret)
+ return ret;
+
+ for_each_ring(waiter, dev_priv, i) {
+ u64 gtt_offset = signaller->semaphore.signal_ggtt[i];
+ if (gtt_offset == MI_SEMAPHORE_SYNC_INVALID)
+ continue;
+
+ intel_ring_emit(signaller, GFX_OP_PIPE_CONTROL(6));
+ intel_ring_emit(signaller, PIPE_CONTROL_GLOBAL_GTT_IVB |
+ PIPE_CONTROL_QW_WRITE |
+ PIPE_CONTROL_FLUSH_ENABLE);
+ intel_ring_emit(signaller, lower_32_bits(gtt_offset));
+ intel_ring_emit(signaller, upper_32_bits(gtt_offset));
+ intel_ring_emit(signaller, signaller->outstanding_lazy_seqno);
+ intel_ring_emit(signaller, 0);
+ intel_ring_emit(signaller, MI_SEMAPHORE_SIGNAL |
+ MI_SEMAPHORE_TARGET(waiter->id));
+ intel_ring_emit(signaller, 0);
}
- drm_gem_object_unreference(&ring->scratch.obj->base);
- ring->scratch.obj = NULL;
+ return 0;
+}
+
+static int gen8_xcs_signal(struct intel_engine_cs *signaller,
+ unsigned int num_dwords)
+{
+#define MBOX_UPDATE_DWORDS 6
+ struct drm_device *dev = signaller->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *waiter;
+ int i, ret, num_rings;
+
+ num_rings = hweight32(INTEL_INFO(dev)->ring_mask);
+ num_dwords += (num_rings-1) * MBOX_UPDATE_DWORDS;
+#undef MBOX_UPDATE_DWORDS
+
+ ret = intel_ring_begin(signaller, num_dwords);
+ if (ret)
+ return ret;
+
+ for_each_ring(waiter, dev_priv, i) {
+ u64 gtt_offset = signaller->semaphore.signal_ggtt[i];
+ if (gtt_offset == MI_SEMAPHORE_SYNC_INVALID)
+ continue;
+
+ intel_ring_emit(signaller, (MI_FLUSH_DW + 1) |
+ MI_FLUSH_DW_OP_STOREDW);
+ intel_ring_emit(signaller, lower_32_bits(gtt_offset) |
+ MI_FLUSH_DW_USE_GTT);
+ intel_ring_emit(signaller, upper_32_bits(gtt_offset));
+ intel_ring_emit(signaller, signaller->outstanding_lazy_seqno);
+ intel_ring_emit(signaller, MI_SEMAPHORE_SIGNAL |
+ MI_SEMAPHORE_TARGET(waiter->id));
+ intel_ring_emit(signaller, 0);
+ }
+
+ return 0;
}
static int gen6_signal(struct intel_engine_cs *signaller,
@@ -677,23 +946,16 @@ static int gen6_signal(struct intel_engine_cs *signaller,
struct drm_device *dev = signaller->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_engine_cs *useless;
- int i, ret;
+ int i, ret, num_rings;
- /* NB: In order to be able to do semaphore MBOX updates for varying
- * number of rings, it's easiest if we round up each individual update
- * to a multiple of 2 (since ring updates must always be a multiple of
- * 2) even though the actual update only requires 3 dwords.
- */
-#define MBOX_UPDATE_DWORDS 4
- if (i915_semaphore_is_enabled(dev))
- num_dwords += ((I915_NUM_RINGS-1) * MBOX_UPDATE_DWORDS);
- else
- return intel_ring_begin(signaller, num_dwords);
+#define MBOX_UPDATE_DWORDS 3
+ num_rings = hweight32(INTEL_INFO(dev)->ring_mask);
+ num_dwords += round_up((num_rings-1) * MBOX_UPDATE_DWORDS, 2);
+#undef MBOX_UPDATE_DWORDS
ret = intel_ring_begin(signaller, num_dwords);
if (ret)
return ret;
-#undef MBOX_UPDATE_DWORDS
for_each_ring(useless, dev_priv, i) {
u32 mbox_reg = signaller->semaphore.mbox.signal[i];
@@ -701,15 +963,13 @@ static int gen6_signal(struct intel_engine_cs *signaller,
intel_ring_emit(signaller, MI_LOAD_REGISTER_IMM(1));
intel_ring_emit(signaller, mbox_reg);
intel_ring_emit(signaller, signaller->outstanding_lazy_seqno);
- intel_ring_emit(signaller, MI_NOOP);
- } else {
- intel_ring_emit(signaller, MI_NOOP);
- intel_ring_emit(signaller, MI_NOOP);
- intel_ring_emit(signaller, MI_NOOP);
- intel_ring_emit(signaller, MI_NOOP);
}
}
+ /* If num_dwords was rounded, make sure the tail pointer is correct */
+ if (num_rings % 2 == 0)
+ intel_ring_emit(signaller, MI_NOOP);
+
return 0;
}
@@ -727,7 +987,11 @@ gen6_add_request(struct intel_engine_cs *ring)
{
int ret;
- ret = ring->semaphore.signal(ring, 4);
+ if (ring->semaphore.signal)
+ ret = ring->semaphore.signal(ring, 4);
+ else
+ ret = intel_ring_begin(ring, 4);
+
if (ret)
return ret;
@@ -754,6 +1018,32 @@ static inline bool i915_gem_has_seqno_wrapped(struct drm_device *dev,
* @signaller - ring which has, or will signal
* @seqno - seqno which the waiter will block on
*/
+
+static int
+gen8_ring_sync(struct intel_engine_cs *waiter,
+ struct intel_engine_cs *signaller,
+ u32 seqno)
+{
+ struct drm_i915_private *dev_priv = waiter->dev->dev_private;
+ int ret;
+
+ ret = intel_ring_begin(waiter, 4);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(waiter, MI_SEMAPHORE_WAIT |
+ MI_SEMAPHORE_GLOBAL_GTT |
+ MI_SEMAPHORE_POLL |
+ MI_SEMAPHORE_SAD_GTE_SDD);
+ intel_ring_emit(waiter, seqno);
+ intel_ring_emit(waiter,
+ lower_32_bits(GEN8_WAIT_OFFSET(waiter, signaller->id)));
+ intel_ring_emit(waiter,
+ upper_32_bits(GEN8_WAIT_OFFSET(waiter, signaller->id)));
+ intel_ring_advance(waiter);
+ return 0;
+}
+
static int
gen6_ring_sync(struct intel_engine_cs *waiter,
struct intel_engine_cs *signaller,
@@ -901,7 +1191,7 @@ gen5_ring_get_irq(struct intel_engine_cs *ring)
spin_lock_irqsave(&dev_priv->irq_lock, flags);
if (ring->irq_refcount++ == 0)
- ilk_enable_gt_irq(dev_priv, ring->irq_enable_mask);
+ gen5_enable_gt_irq(dev_priv, ring->irq_enable_mask);
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
return true;
@@ -916,7 +1206,7 @@ gen5_ring_put_irq(struct intel_engine_cs *ring)
spin_lock_irqsave(&dev_priv->irq_lock, flags);
if (--ring->irq_refcount == 0)
- ilk_disable_gt_irq(dev_priv, ring->irq_enable_mask);
+ gen5_disable_gt_irq(dev_priv, ring->irq_enable_mask);
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
}
@@ -1109,7 +1399,7 @@ gen6_ring_get_irq(struct intel_engine_cs *ring)
GT_PARITY_ERROR(dev)));
else
I915_WRITE_IMR(ring, ~ring->irq_enable_mask);
- ilk_enable_gt_irq(dev_priv, ring->irq_enable_mask);
+ gen5_enable_gt_irq(dev_priv, ring->irq_enable_mask);
}
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
@@ -1129,7 +1419,7 @@ gen6_ring_put_irq(struct intel_engine_cs *ring)
I915_WRITE_IMR(ring, ~GT_PARITY_ERROR(dev));
else
I915_WRITE_IMR(ring, ~0);
- ilk_disable_gt_irq(dev_priv, ring->irq_enable_mask);
+ gen5_disable_gt_irq(dev_priv, ring->irq_enable_mask);
}
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
}
@@ -1147,7 +1437,7 @@ hsw_vebox_get_irq(struct intel_engine_cs *ring)
spin_lock_irqsave(&dev_priv->irq_lock, flags);
if (ring->irq_refcount++ == 0) {
I915_WRITE_IMR(ring, ~ring->irq_enable_mask);
- snb_enable_pm_irq(dev_priv, ring->irq_enable_mask);
+ gen6_enable_pm_irq(dev_priv, ring->irq_enable_mask);
}
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
@@ -1167,7 +1457,7 @@ hsw_vebox_put_irq(struct intel_engine_cs *ring)
spin_lock_irqsave(&dev_priv->irq_lock, flags);
if (--ring->irq_refcount == 0) {
I915_WRITE_IMR(ring, ~0);
- snb_disable_pm_irq(dev_priv, ring->irq_enable_mask);
+ gen6_disable_pm_irq(dev_priv, ring->irq_enable_mask);
}
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
}
@@ -1241,54 +1531,66 @@ i965_dispatch_execbuffer(struct intel_engine_cs *ring,
/* Just userspace ABI convention to limit the wa batch bo to a resonable size */
#define I830_BATCH_LIMIT (256*1024)
+#define I830_TLB_ENTRIES (2)
+#define I830_WA_SIZE max(I830_TLB_ENTRIES*4096, I830_BATCH_LIMIT)
static int
i830_dispatch_execbuffer(struct intel_engine_cs *ring,
u64 offset, u32 len,
unsigned flags)
{
+ u32 cs_offset = ring->scratch.gtt_offset;
int ret;
- if (flags & I915_DISPATCH_PINNED) {
- ret = intel_ring_begin(ring, 4);
- if (ret)
- return ret;
+ ret = intel_ring_begin(ring, 6);
+ if (ret)
+ return ret;
- intel_ring_emit(ring, MI_BATCH_BUFFER);
- intel_ring_emit(ring, offset | (flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE));
- intel_ring_emit(ring, offset + len - 8);
- intel_ring_emit(ring, MI_NOOP);
- intel_ring_advance(ring);
- } else {
- u32 cs_offset = ring->scratch.gtt_offset;
+ /* Evict the invalid PTE TLBs */
+ intel_ring_emit(ring, COLOR_BLT_CMD | BLT_WRITE_RGBA);
+ intel_ring_emit(ring, BLT_DEPTH_32 | BLT_ROP_COLOR_COPY | 4096);
+ intel_ring_emit(ring, I830_TLB_ENTRIES << 16 | 4); /* load each page */
+ intel_ring_emit(ring, cs_offset);
+ intel_ring_emit(ring, 0xdeadbeef);
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_advance(ring);
+ if ((flags & I915_DISPATCH_PINNED) == 0) {
if (len > I830_BATCH_LIMIT)
return -ENOSPC;
- ret = intel_ring_begin(ring, 9+3);
+ ret = intel_ring_begin(ring, 6 + 2);
if (ret)
return ret;
- /* Blit the batch (which has now all relocs applied) to the stable batch
- * scratch bo area (so that the CS never stumbles over its tlb
- * invalidation bug) ... */
- intel_ring_emit(ring, XY_SRC_COPY_BLT_CMD |
- XY_SRC_COPY_BLT_WRITE_ALPHA |
- XY_SRC_COPY_BLT_WRITE_RGB);
- intel_ring_emit(ring, BLT_DEPTH_32 | BLT_ROP_GXCOPY | 4096);
- intel_ring_emit(ring, 0);
- intel_ring_emit(ring, (DIV_ROUND_UP(len, 4096) << 16) | 1024);
+
+ /* Blit the batch (which has now all relocs applied) to the
+ * stable batch scratch bo area (so that the CS never
+ * stumbles over its tlb invalidation bug) ...
+ */
+ intel_ring_emit(ring, SRC_COPY_BLT_CMD | BLT_WRITE_RGBA);
+ intel_ring_emit(ring, BLT_DEPTH_32 | BLT_ROP_SRC_COPY | 4096);
+ intel_ring_emit(ring, DIV_ROUND_UP(len, 4096) << 16 | 4096);
intel_ring_emit(ring, cs_offset);
- intel_ring_emit(ring, 0);
intel_ring_emit(ring, 4096);
intel_ring_emit(ring, offset);
+
intel_ring_emit(ring, MI_FLUSH);
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_advance(ring);
/* ... and execute it. */
- intel_ring_emit(ring, MI_BATCH_BUFFER);
- intel_ring_emit(ring, cs_offset | (flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE));
- intel_ring_emit(ring, cs_offset + len - 8);
- intel_ring_advance(ring);
+ offset = cs_offset;
}
+ ret = intel_ring_begin(ring, 4);
+ if (ret)
+ return ret;
+
+ intel_ring_emit(ring, MI_BATCH_BUFFER);
+ intel_ring_emit(ring, offset | (flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE));
+ intel_ring_emit(ring, offset + len - 8);
+ intel_ring_emit(ring, MI_NOOP);
+ intel_ring_advance(ring);
+
return 0;
}
@@ -1329,6 +1631,7 @@ static int init_status_page(struct intel_engine_cs *ring)
struct drm_i915_gem_object *obj;
if ((obj = ring->status_page.obj) == NULL) {
+ unsigned flags;
int ret;
obj = i915_gem_alloc_object(ring->dev, 4096);
@@ -1341,7 +1644,20 @@ static int init_status_page(struct intel_engine_cs *ring)
if (ret)
goto err_unref;
- ret = i915_gem_obj_ggtt_pin(obj, 4096, 0);
+ flags = 0;
+ if (!HAS_LLC(ring->dev))
+ /* On g33, we cannot place HWS above 256MiB, so
+ * restrict its pinning to the low mappable arena.
+ * Though this restriction is not documented for
+ * gen4, gen5, or byt, they also behave similarly
+ * and hang if the HWS is placed at the top of the
+ * GTT. To generalise, it appears that all !llc
+ * platforms have issues with us placing the HWS
+ * above the mappable region (even though we never
+ * actualy map it).
+ */
+ flags |= PIN_MAPPABLE;
+ ret = i915_gem_obj_ggtt_pin(obj, 4096, flags);
if (ret) {
err_unref:
drm_gem_object_unreference(&obj->base);
@@ -1378,15 +1694,25 @@ static int init_phys_status_page(struct intel_engine_cs *ring)
return 0;
}
-static int allocate_ring_buffer(struct intel_engine_cs *ring)
+void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf)
+{
+ if (!ringbuf->obj)
+ return;
+
+ iounmap(ringbuf->virtual_start);
+ i915_gem_object_ggtt_unpin(ringbuf->obj);
+ drm_gem_object_unreference(&ringbuf->obj->base);
+ ringbuf->obj = NULL;
+}
+
+int intel_alloc_ringbuffer_obj(struct drm_device *dev,
+ struct intel_ringbuffer *ringbuf)
{
- struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
- struct intel_ringbuffer *ringbuf = ring->buffer;
struct drm_i915_gem_object *obj;
int ret;
- if (intel_ring_initialized(ring))
+ if (ringbuf->obj)
return 0;
obj = NULL;
@@ -1397,6 +1723,9 @@ static int allocate_ring_buffer(struct intel_engine_cs *ring)
if (obj == NULL)
return -ENOMEM;
+ /* mark ring buffers as read-only from GPU side by default */
+ obj->gt_ro = 1;
+
ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, PIN_MAPPABLE);
if (ret)
goto err_unref;
@@ -1439,7 +1768,9 @@ static int intel_init_ring_buffer(struct drm_device *dev,
ring->dev = dev;
INIT_LIST_HEAD(&ring->active_list);
INIT_LIST_HEAD(&ring->request_list);
+ INIT_LIST_HEAD(&ring->execlist_queue);
ringbuf->size = 32 * PAGE_SIZE;
+ ringbuf->ring = ring;
memset(ring->semaphore.sync_seqno, 0, sizeof(ring->semaphore.sync_seqno));
init_waitqueue_head(&ring->irq_queue);
@@ -1455,7 +1786,7 @@ static int intel_init_ring_buffer(struct drm_device *dev,
goto error;
}
- ret = allocate_ring_buffer(ring);
+ ret = intel_alloc_ringbuffer_obj(dev, ringbuf);
if (ret) {
DRM_ERROR("Failed to allocate ringbuffer %s: %d\n", ring->name, ret);
goto error;
@@ -1496,11 +1827,7 @@ void intel_cleanup_ring_buffer(struct intel_engine_cs *ring)
intel_stop_ring_buffer(ring);
WARN_ON(!IS_GEN2(ring->dev) && (I915_READ_MODE(ring) & MODE_IDLE) == 0);
- iounmap(ringbuf->virtual_start);
-
- i915_gem_object_ggtt_unpin(ringbuf->obj);
- drm_gem_object_unreference(&ringbuf->obj->base);
- ringbuf->obj = NULL;
+ intel_destroy_ringbuffer_obj(ringbuf);
ring->preallocated_lazy_request = NULL;
ring->outstanding_lazy_seqno = 0;
@@ -1526,13 +1853,14 @@ static int intel_ring_wait_request(struct intel_engine_cs *ring, int n)
ringbuf->head = ringbuf->last_retired_head;
ringbuf->last_retired_head = -1;
- ringbuf->space = ring_space(ring);
+ ringbuf->space = intel_ring_space(ringbuf);
if (ringbuf->space >= n)
return 0;
}
list_for_each_entry(request, &ring->request_list, list) {
- if (__ring_space(request->tail, ringbuf->tail, ringbuf->size) >= n) {
+ if (__intel_ring_space(request->tail, ringbuf->tail,
+ ringbuf->size) >= n) {
seqno = request->seqno;
break;
}
@@ -1549,7 +1877,7 @@ static int intel_ring_wait_request(struct intel_engine_cs *ring, int n)
ringbuf->head = ringbuf->last_retired_head;
ringbuf->last_retired_head = -1;
- ringbuf->space = ring_space(ring);
+ ringbuf->space = intel_ring_space(ringbuf);
return 0;
}
@@ -1578,7 +1906,7 @@ static int ring_wait_for_space(struct intel_engine_cs *ring, int n)
trace_i915_ring_wait_begin(ring);
do {
ringbuf->head = I915_READ_HEAD(ring);
- ringbuf->space = ring_space(ring);
+ ringbuf->space = intel_ring_space(ringbuf);
if (ringbuf->space >= n) {
ret = 0;
break;
@@ -1630,7 +1958,7 @@ static int intel_wrap_ring_buffer(struct intel_engine_cs *ring)
iowrite32(MI_NOOP, virt++);
ringbuf->tail = 0;
- ringbuf->space = ring_space(ring);
+ ringbuf->space = intel_ring_space(ringbuf);
return 0;
}
@@ -1746,14 +2074,15 @@ int intel_ring_cacheline_align(struct intel_engine_cs *ring)
void intel_ring_init_seqno(struct intel_engine_cs *ring, u32 seqno)
{
- struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ struct drm_device *dev = ring->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
BUG_ON(ring->outstanding_lazy_seqno);
- if (INTEL_INFO(ring->dev)->gen >= 6) {
+ if (INTEL_INFO(dev)->gen == 6 || INTEL_INFO(dev)->gen == 7) {
I915_WRITE(RING_SYNC_0(ring->mmio_base), 0);
I915_WRITE(RING_SYNC_1(ring->mmio_base), 0);
- if (HAS_VEBOX(ring->dev))
+ if (HAS_VEBOX(dev))
I915_WRITE(RING_SYNC_2(ring->mmio_base), 0);
}
@@ -1834,9 +2163,7 @@ gen8_ring_dispatch_execbuffer(struct intel_engine_cs *ring,
u64 offset, u32 len,
unsigned flags)
{
- struct drm_i915_private *dev_priv = ring->dev->dev_private;
- bool ppgtt = dev_priv->mm.aliasing_ppgtt != NULL &&
- !(flags & I915_DISPATCH_SECURE);
+ bool ppgtt = USES_PPGTT(ring->dev) && !(flags & I915_DISPATCH_SECURE);
int ret;
ret = intel_ring_begin(ring, 4);
@@ -1865,8 +2192,9 @@ hsw_ring_dispatch_execbuffer(struct intel_engine_cs *ring,
return ret;
intel_ring_emit(ring,
- MI_BATCH_BUFFER_START | MI_BATCH_PPGTT_HSW |
- (flags & I915_DISPATCH_SECURE ? 0 : MI_BATCH_NON_SECURE_HSW));
+ MI_BATCH_BUFFER_START |
+ (flags & I915_DISPATCH_SECURE ?
+ 0 : MI_BATCH_PPGTT_HSW | MI_BATCH_NON_SECURE_HSW));
/* bit0-7 is the length on GEN6+ */
intel_ring_emit(ring, offset);
intel_ring_advance(ring);
@@ -1941,45 +2269,78 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_engine_cs *ring = &dev_priv->ring[RCS];
+ struct drm_i915_gem_object *obj;
+ int ret;
ring->name = "render ring";
ring->id = RCS;
ring->mmio_base = RENDER_RING_BASE;
- if (INTEL_INFO(dev)->gen >= 6) {
+ if (INTEL_INFO(dev)->gen >= 8) {
+ if (i915_semaphore_is_enabled(dev)) {
+ obj = i915_gem_alloc_object(dev, 4096);
+ if (obj == NULL) {
+ DRM_ERROR("Failed to allocate semaphore bo. Disabling semaphores\n");
+ i915.semaphores = 0;
+ } else {
+ i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
+ ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_NONBLOCK);
+ if (ret != 0) {
+ drm_gem_object_unreference(&obj->base);
+ DRM_ERROR("Failed to pin semaphore bo. Disabling semaphores\n");
+ i915.semaphores = 0;
+ } else
+ dev_priv->semaphore_obj = obj;
+ }
+ }
+ if (IS_CHERRYVIEW(dev))
+ ring->init_context = chv_init_workarounds;
+ else
+ ring->init_context = bdw_init_workarounds;
+ ring->add_request = gen6_add_request;
+ ring->flush = gen8_render_ring_flush;
+ ring->irq_get = gen8_ring_get_irq;
+ ring->irq_put = gen8_ring_put_irq;
+ ring->irq_enable_mask = GT_RENDER_USER_INTERRUPT;
+ ring->get_seqno = gen6_ring_get_seqno;
+ ring->set_seqno = ring_set_seqno;
+ if (i915_semaphore_is_enabled(dev)) {
+ WARN_ON(!dev_priv->semaphore_obj);
+ ring->semaphore.sync_to = gen8_ring_sync;
+ ring->semaphore.signal = gen8_rcs_signal;
+ GEN8_RING_SEMAPHORE_INIT;
+ }
+ } else if (INTEL_INFO(dev)->gen >= 6) {
ring->add_request = gen6_add_request;
ring->flush = gen7_render_ring_flush;
if (INTEL_INFO(dev)->gen == 6)
ring->flush = gen6_render_ring_flush;
- if (INTEL_INFO(dev)->gen >= 8) {
- ring->flush = gen8_render_ring_flush;
- ring->irq_get = gen8_ring_get_irq;
- ring->irq_put = gen8_ring_put_irq;
- } else {
- ring->irq_get = gen6_ring_get_irq;
- ring->irq_put = gen6_ring_put_irq;
- }
+ ring->irq_get = gen6_ring_get_irq;
+ ring->irq_put = gen6_ring_put_irq;
ring->irq_enable_mask = GT_RENDER_USER_INTERRUPT;
ring->get_seqno = gen6_ring_get_seqno;
ring->set_seqno = ring_set_seqno;
- ring->semaphore.sync_to = gen6_ring_sync;
- ring->semaphore.signal = gen6_signal;
- /*
- * The current semaphore is only applied on pre-gen8 platform.
- * And there is no VCS2 ring on the pre-gen8 platform. So the
- * semaphore between RCS and VCS2 is initialized as INVALID.
- * Gen8 will initialize the sema between VCS2 and RCS later.
- */
- ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_RV;
- ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_RB;
- ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_RVE;
- ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC;
- ring->semaphore.mbox.signal[VCS] = GEN6_VRSYNC;
- ring->semaphore.mbox.signal[BCS] = GEN6_BRSYNC;
- ring->semaphore.mbox.signal[VECS] = GEN6_VERSYNC;
- ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
+ if (i915_semaphore_is_enabled(dev)) {
+ ring->semaphore.sync_to = gen6_ring_sync;
+ ring->semaphore.signal = gen6_signal;
+ /*
+ * The current semaphore is only applied on pre-gen8
+ * platform. And there is no VCS2 ring on the pre-gen8
+ * platform. So the semaphore between RCS and VCS2 is
+ * initialized as INVALID. Gen8 will initialize the
+ * sema between VCS2 and RCS later.
+ */
+ ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID;
+ ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_RV;
+ ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_RB;
+ ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_RVE;
+ ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
+ ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC;
+ ring->semaphore.mbox.signal[VCS] = GEN6_VRSYNC;
+ ring->semaphore.mbox.signal[BCS] = GEN6_BRSYNC;
+ ring->semaphore.mbox.signal[VECS] = GEN6_VERSYNC;
+ ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
+ }
} else if (IS_GEN5(dev)) {
ring->add_request = pc_render_add_request;
ring->flush = gen4_render_ring_flush;
@@ -2007,6 +2368,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
ring->irq_enable_mask = I915_USER_INTERRUPT;
}
ring->write_tail = ring_write_tail;
+
if (IS_HASWELL(dev))
ring->dispatch_execbuffer = hsw_ring_dispatch_execbuffer;
else if (IS_GEN8(dev))
@@ -2024,10 +2386,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
/* Workaround batchbuffer to combat CS tlb bug. */
if (HAS_BROKEN_CS_TLB(dev)) {
- struct drm_i915_gem_object *obj;
- int ret;
-
- obj = i915_gem_alloc_object(dev, I830_BATCH_LIMIT);
+ obj = i915_gem_alloc_object(dev, I830_WA_SIZE);
if (obj == NULL) {
DRM_ERROR("Failed to allocate batch bo\n");
return -ENOMEM;
@@ -2157,31 +2516,32 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
ring->irq_put = gen8_ring_put_irq;
ring->dispatch_execbuffer =
gen8_ring_dispatch_execbuffer;
+ if (i915_semaphore_is_enabled(dev)) {
+ ring->semaphore.sync_to = gen8_ring_sync;
+ ring->semaphore.signal = gen8_xcs_signal;
+ GEN8_RING_SEMAPHORE_INIT;
+ }
} else {
ring->irq_enable_mask = GT_BSD_USER_INTERRUPT;
ring->irq_get = gen6_ring_get_irq;
ring->irq_put = gen6_ring_put_irq;
ring->dispatch_execbuffer =
gen6_ring_dispatch_execbuffer;
+ if (i915_semaphore_is_enabled(dev)) {
+ ring->semaphore.sync_to = gen6_ring_sync;
+ ring->semaphore.signal = gen6_signal;
+ ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_VR;
+ ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID;
+ ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_VB;
+ ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_VVE;
+ ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
+ ring->semaphore.mbox.signal[RCS] = GEN6_RVSYNC;
+ ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC;
+ ring->semaphore.mbox.signal[BCS] = GEN6_BVSYNC;
+ ring->semaphore.mbox.signal[VECS] = GEN6_VEVSYNC;
+ ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
+ }
}
- ring->semaphore.sync_to = gen6_ring_sync;
- ring->semaphore.signal = gen6_signal;
- /*
- * The current semaphore is only applied on pre-gen8 platform.
- * And there is no VCS2 ring on the pre-gen8 platform. So the
- * semaphore between VCS and VCS2 is initialized as INVALID.
- * Gen8 will initialize the sema between VCS2 and VCS later.
- */
- ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_VR;
- ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_VB;
- ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_VVE;
- ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.signal[RCS] = GEN6_RVSYNC;
- ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC;
- ring->semaphore.mbox.signal[BCS] = GEN6_BVSYNC;
- ring->semaphore.mbox.signal[VECS] = GEN6_VEVSYNC;
- ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
} else {
ring->mmio_base = BSD_RING_BASE;
ring->flush = bsd_ring_flush;
@@ -2218,7 +2578,7 @@ int intel_init_bsd2_ring_buffer(struct drm_device *dev)
return -EINVAL;
}
- ring->name = "bds2_ring";
+ ring->name = "bsd2 ring";
ring->id = VCS2;
ring->write_tail = ring_write_tail;
@@ -2233,25 +2593,11 @@ int intel_init_bsd2_ring_buffer(struct drm_device *dev)
ring->irq_put = gen8_ring_put_irq;
ring->dispatch_execbuffer =
gen8_ring_dispatch_execbuffer;
- ring->semaphore.sync_to = gen6_ring_sync;
- ring->semaphore.signal = gen6_signal;
- /*
- * The current semaphore is only applied on the pre-gen8. And there
- * is no bsd2 ring on the pre-gen8. So now the semaphore_register
- * between VCS2 and other ring is initialized as invalid.
- * Gen8 will initialize the sema between VCS2 and other ring later.
- */
- ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.signal[RCS] = GEN6_NOSYNC;
- ring->semaphore.mbox.signal[VCS] = GEN6_NOSYNC;
- ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC;
- ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC;
- ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
-
+ if (i915_semaphore_is_enabled(dev)) {
+ ring->semaphore.sync_to = gen8_ring_sync;
+ ring->semaphore.signal = gen8_xcs_signal;
+ GEN8_RING_SEMAPHORE_INIT;
+ }
ring->init = init_ring_common;
return intel_init_ring_buffer(dev, ring);
@@ -2277,30 +2623,38 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
ring->irq_get = gen8_ring_get_irq;
ring->irq_put = gen8_ring_put_irq;
ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer;
+ if (i915_semaphore_is_enabled(dev)) {
+ ring->semaphore.sync_to = gen8_ring_sync;
+ ring->semaphore.signal = gen8_xcs_signal;
+ GEN8_RING_SEMAPHORE_INIT;
+ }
} else {
ring->irq_enable_mask = GT_BLT_USER_INTERRUPT;
ring->irq_get = gen6_ring_get_irq;
ring->irq_put = gen6_ring_put_irq;
ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
+ if (i915_semaphore_is_enabled(dev)) {
+ ring->semaphore.signal = gen6_signal;
+ ring->semaphore.sync_to = gen6_ring_sync;
+ /*
+ * The current semaphore is only applied on pre-gen8
+ * platform. And there is no VCS2 ring on the pre-gen8
+ * platform. So the semaphore between BCS and VCS2 is
+ * initialized as INVALID. Gen8 will initialize the
+ * sema between BCS and VCS2 later.
+ */
+ ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_BR;
+ ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_BV;
+ ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID;
+ ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_BVE;
+ ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
+ ring->semaphore.mbox.signal[RCS] = GEN6_RBSYNC;
+ ring->semaphore.mbox.signal[VCS] = GEN6_VBSYNC;
+ ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC;
+ ring->semaphore.mbox.signal[VECS] = GEN6_VEBSYNC;
+ ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
+ }
}
- ring->semaphore.sync_to = gen6_ring_sync;
- ring->semaphore.signal = gen6_signal;
- /*
- * The current semaphore is only applied on pre-gen8 platform. And
- * there is no VCS2 ring on the pre-gen8 platform. So the semaphore
- * between BCS and VCS2 is initialized as INVALID.
- * Gen8 will initialize the sema between BCS and VCS2 later.
- */
- ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_BR;
- ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_BV;
- ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_BVE;
- ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.signal[RCS] = GEN6_RBSYNC;
- ring->semaphore.mbox.signal[VCS] = GEN6_VBSYNC;
- ring->semaphore.mbox.signal[BCS] = GEN6_NOSYNC;
- ring->semaphore.mbox.signal[VECS] = GEN6_VEBSYNC;
- ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
ring->init = init_ring_common;
return intel_init_ring_buffer(dev, ring);
@@ -2327,24 +2681,31 @@ int intel_init_vebox_ring_buffer(struct drm_device *dev)
ring->irq_get = gen8_ring_get_irq;
ring->irq_put = gen8_ring_put_irq;
ring->dispatch_execbuffer = gen8_ring_dispatch_execbuffer;
+ if (i915_semaphore_is_enabled(dev)) {
+ ring->semaphore.sync_to = gen8_ring_sync;
+ ring->semaphore.signal = gen8_xcs_signal;
+ GEN8_RING_SEMAPHORE_INIT;
+ }
} else {
ring->irq_enable_mask = PM_VEBOX_USER_INTERRUPT;
ring->irq_get = hsw_vebox_get_irq;
ring->irq_put = hsw_vebox_put_irq;
ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
+ if (i915_semaphore_is_enabled(dev)) {
+ ring->semaphore.sync_to = gen6_ring_sync;
+ ring->semaphore.signal = gen6_signal;
+ ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_VER;
+ ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_VEV;
+ ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_VEB;
+ ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID;
+ ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
+ ring->semaphore.mbox.signal[RCS] = GEN6_RVESYNC;
+ ring->semaphore.mbox.signal[VCS] = GEN6_VVESYNC;
+ ring->semaphore.mbox.signal[BCS] = GEN6_BVESYNC;
+ ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC;
+ ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
+ }
}
- ring->semaphore.sync_to = gen6_ring_sync;
- ring->semaphore.signal = gen6_signal;
- ring->semaphore.mbox.wait[RCS] = MI_SEMAPHORE_SYNC_VER;
- ring->semaphore.mbox.wait[VCS] = MI_SEMAPHORE_SYNC_VEV;
- ring->semaphore.mbox.wait[BCS] = MI_SEMAPHORE_SYNC_VEB;
- ring->semaphore.mbox.wait[VECS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.wait[VCS2] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore.mbox.signal[RCS] = GEN6_RVESYNC;
- ring->semaphore.mbox.signal[VCS] = GEN6_VVESYNC;
- ring->semaphore.mbox.signal[BCS] = GEN6_BVESYNC;
- ring->semaphore.mbox.signal[VECS] = GEN6_NOSYNC;
- ring->semaphore.mbox.signal[VCS2] = GEN6_NOSYNC;
ring->init = init_ring_common;
return intel_init_ring_buffer(dev, ring);
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index e72017bdcd7f..96479c89f4bd 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -5,6 +5,13 @@
#define I915_CMD_HASH_ORDER 9
+/* Early gen2 devices have a cacheline of just 32 bytes, using 64 is overkill,
+ * but keeps the logic simple. Indeed, the whole purpose of this macro is just
+ * to give some inclination as to some of the magic values used in the various
+ * workarounds!
+ */
+#define CACHELINE_BYTES 64
+
/*
* Gen2 BSpec "1. Programming Environment" / 1.4.4.6 "Ring Buffer Use"
* Gen3 BSpec "vol1c Memory Interface Functions" / 2.3.4.5 "Ring Buffer Use"
@@ -40,10 +47,37 @@ struct intel_hw_status_page {
#define I915_READ_MODE(ring) I915_READ(RING_MI_MODE((ring)->mmio_base))
#define I915_WRITE_MODE(ring, val) I915_WRITE(RING_MI_MODE((ring)->mmio_base), val)
+/* seqno size is actually only a uint32, but since we plan to use MI_FLUSH_DW to
+ * do the writes, and that must have qw aligned offsets, simply pretend it's 8b.
+ */
+#define i915_semaphore_seqno_size sizeof(uint64_t)
+#define GEN8_SIGNAL_OFFSET(__ring, to) \
+ (i915_gem_obj_ggtt_offset(dev_priv->semaphore_obj) + \
+ ((__ring)->id * I915_NUM_RINGS * i915_semaphore_seqno_size) + \
+ (i915_semaphore_seqno_size * (to)))
+
+#define GEN8_WAIT_OFFSET(__ring, from) \
+ (i915_gem_obj_ggtt_offset(dev_priv->semaphore_obj) + \
+ ((from) * I915_NUM_RINGS * i915_semaphore_seqno_size) + \
+ (i915_semaphore_seqno_size * (__ring)->id))
+
+#define GEN8_RING_SEMAPHORE_INIT do { \
+ if (!dev_priv->semaphore_obj) { \
+ break; \
+ } \
+ ring->semaphore.signal_ggtt[RCS] = GEN8_SIGNAL_OFFSET(ring, RCS); \
+ ring->semaphore.signal_ggtt[VCS] = GEN8_SIGNAL_OFFSET(ring, VCS); \
+ ring->semaphore.signal_ggtt[BCS] = GEN8_SIGNAL_OFFSET(ring, BCS); \
+ ring->semaphore.signal_ggtt[VECS] = GEN8_SIGNAL_OFFSET(ring, VECS); \
+ ring->semaphore.signal_ggtt[VCS2] = GEN8_SIGNAL_OFFSET(ring, VCS2); \
+ ring->semaphore.signal_ggtt[ring->id] = MI_SEMAPHORE_SYNC_INVALID; \
+ } while(0)
+
enum intel_ring_hangcheck_action {
HANGCHECK_IDLE = 0,
HANGCHECK_WAIT,
HANGCHECK_ACTIVE,
+ HANGCHECK_ACTIVE_LOOP,
HANGCHECK_KICK,
HANGCHECK_HUNG,
};
@@ -52,6 +86,7 @@ enum intel_ring_hangcheck_action {
struct intel_ring_hangcheck {
u64 acthd;
+ u64 max_acthd;
u32 seqno;
int score;
enum intel_ring_hangcheck_action action;
@@ -62,6 +97,15 @@ struct intel_ringbuffer {
struct drm_i915_gem_object *obj;
void __iomem *virtual_start;
+ struct intel_engine_cs *ring;
+
+ /*
+ * FIXME: This backpointer is an artifact of the history of how the
+ * execlist patches came into being. It will get removed once the basic
+ * code has landed.
+ */
+ struct intel_context *FIXME_lrc_ctx;
+
u32 head;
u32 tail;
int space;
@@ -104,6 +148,8 @@ struct intel_engine_cs {
int (*init)(struct intel_engine_cs *ring);
+ int (*init_context)(struct intel_engine_cs *ring);
+
void (*write_tail)(struct intel_engine_cs *ring,
u32 value);
int __must_check (*flush)(struct intel_engine_cs *ring,
@@ -127,15 +173,55 @@ struct intel_engine_cs {
#define I915_DISPATCH_PINNED 0x2
void (*cleanup)(struct intel_engine_cs *ring);
+ /* GEN8 signal/wait table - never trust comments!
+ * signal to signal to signal to signal to signal to
+ * RCS VCS BCS VECS VCS2
+ * --------------------------------------------------------------------
+ * RCS | NOP (0x00) | VCS (0x08) | BCS (0x10) | VECS (0x18) | VCS2 (0x20) |
+ * |-------------------------------------------------------------------
+ * VCS | RCS (0x28) | NOP (0x30) | BCS (0x38) | VECS (0x40) | VCS2 (0x48) |
+ * |-------------------------------------------------------------------
+ * BCS | RCS (0x50) | VCS (0x58) | NOP (0x60) | VECS (0x68) | VCS2 (0x70) |
+ * |-------------------------------------------------------------------
+ * VECS | RCS (0x78) | VCS (0x80) | BCS (0x88) | NOP (0x90) | VCS2 (0x98) |
+ * |-------------------------------------------------------------------
+ * VCS2 | RCS (0xa0) | VCS (0xa8) | BCS (0xb0) | VECS (0xb8) | NOP (0xc0) |
+ * |-------------------------------------------------------------------
+ *
+ * Generalization:
+ * f(x, y) := (x->id * NUM_RINGS * seqno_size) + (seqno_size * y->id)
+ * ie. transpose of g(x, y)
+ *
+ * sync from sync from sync from sync from sync from
+ * RCS VCS BCS VECS VCS2
+ * --------------------------------------------------------------------
+ * RCS | NOP (0x00) | VCS (0x28) | BCS (0x50) | VECS (0x78) | VCS2 (0xa0) |
+ * |-------------------------------------------------------------------
+ * VCS | RCS (0x08) | NOP (0x30) | BCS (0x58) | VECS (0x80) | VCS2 (0xa8) |
+ * |-------------------------------------------------------------------
+ * BCS | RCS (0x10) | VCS (0x38) | NOP (0x60) | VECS (0x88) | VCS2 (0xb0) |
+ * |-------------------------------------------------------------------
+ * VECS | RCS (0x18) | VCS (0x40) | BCS (0x68) | NOP (0x90) | VCS2 (0xb8) |
+ * |-------------------------------------------------------------------
+ * VCS2 | RCS (0x20) | VCS (0x48) | BCS (0x70) | VECS (0x98) | NOP (0xc0) |
+ * |-------------------------------------------------------------------
+ *
+ * Generalization:
+ * g(x, y) := (y->id * NUM_RINGS * seqno_size) + (seqno_size * x->id)
+ * ie. transpose of f(x, y)
+ */
struct {
u32 sync_seqno[I915_NUM_RINGS-1];
- struct {
- /* our mbox written by others */
- u32 wait[I915_NUM_RINGS];
- /* mboxes this ring signals to */
- u32 signal[I915_NUM_RINGS];
- } mbox;
+ union {
+ struct {
+ /* our mbox written by others */
+ u32 wait[I915_NUM_RINGS];
+ /* mboxes this ring signals to */
+ u32 signal[I915_NUM_RINGS];
+ } mbox;
+ u64 signal_ggtt[I915_NUM_RINGS];
+ };
/* AKA wait() */
int (*sync_to)(struct intel_engine_cs *ring,
@@ -146,6 +232,18 @@ struct intel_engine_cs {
unsigned int num_dwords);
} semaphore;
+ /* Execlists */
+ spinlock_t execlist_lock;
+ struct list_head execlist_queue;
+ u8 next_context_status_buffer;
+ u32 irq_keep_mask; /* bitmask for interrupts that should not be masked */
+ int (*emit_request)(struct intel_ringbuffer *ringbuf);
+ int (*emit_flush)(struct intel_ringbuffer *ringbuf,
+ u32 invalidate_domains,
+ u32 flush_domains);
+ int (*emit_bb_start)(struct intel_ringbuffer *ringbuf,
+ u64 offset, unsigned flags);
+
/**
* List of objects currently involved in rendering from the
* ringbuffer.
@@ -219,11 +317,7 @@ struct intel_engine_cs {
u32 (*get_cmd_length_mask)(u32 cmd_header);
};
-static inline bool
-intel_ring_initialized(struct intel_engine_cs *ring)
-{
- return ring->buffer && ring->buffer->obj;
-}
+bool intel_ring_initialized(struct intel_engine_cs *ring);
static inline unsigned
intel_ring_flag(struct intel_engine_cs *ring)
@@ -238,9 +332,11 @@ intel_ring_sync_index(struct intel_engine_cs *ring,
int idx;
/*
- * cs -> 0 = vcs, 1 = bcs
- * vcs -> 0 = bcs, 1 = cs,
- * bcs -> 0 = cs, 1 = vcs.
+ * rcs -> 0 = vcs, 1 = bcs, 2 = vecs, 3 = vcs2;
+ * vcs -> 0 = bcs, 1 = vecs, 2 = vcs2, 3 = rcs;
+ * bcs -> 0 = vecs, 1 = vcs2. 2 = rcs, 3 = vcs;
+ * vecs -> 0 = vcs2, 1 = rcs, 2 = vcs, 3 = bcs;
+ * vcs2 -> 0 = rcs, 1 = vcs, 2 = bcs, 3 = vecs;
*/
idx = (other - ring) - 1;
@@ -285,6 +381,10 @@ intel_write_status_page(struct intel_engine_cs *ring,
#define I915_GEM_HWS_SCRATCH_INDEX 0x30
#define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH_INDEX << MI_STORE_DWORD_INDEX_SHIFT)
+void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf);
+int intel_alloc_ringbuffer_obj(struct drm_device *dev,
+ struct intel_ringbuffer *ringbuf);
+
void intel_stop_ring_buffer(struct intel_engine_cs *ring);
void intel_cleanup_ring_buffer(struct intel_engine_cs *ring);
@@ -302,6 +402,9 @@ static inline void intel_ring_advance(struct intel_engine_cs *ring)
struct intel_ringbuffer *ringbuf = ring->buffer;
ringbuf->tail &= ringbuf->size - 1;
}
+int __intel_ring_space(int head, int tail, int size);
+int intel_ring_space(struct intel_ringbuffer *ringbuf);
+bool intel_ring_stopped(struct intel_engine_cs *ring);
void __intel_ring_advance(struct intel_engine_cs *ring);
int __must_check intel_ring_idle(struct intel_engine_cs *ring);
@@ -309,6 +412,9 @@ void intel_ring_init_seqno(struct intel_engine_cs *ring, u32 seqno);
int intel_ring_flush_all_caches(struct intel_engine_cs *ring);
int intel_ring_invalidate_all_caches(struct intel_engine_cs *ring);
+void intel_fini_pipe_control(struct intel_engine_cs *ring);
+int intel_init_pipe_control(struct intel_engine_cs *ring);
+
int intel_init_render_ring_buffer(struct drm_device *dev);
int intel_init_bsd_ring_buffer(struct drm_device *dev);
int intel_init_bsd2_ring_buffer(struct drm_device *dev);
@@ -318,9 +424,9 @@ int intel_init_vebox_ring_buffer(struct drm_device *dev);
u64 intel_ring_get_active_head(struct intel_engine_cs *ring);
void intel_ring_setup_status_page(struct intel_engine_cs *ring);
-static inline u32 intel_ring_get_tail(struct intel_engine_cs *ring)
+static inline u32 intel_ring_get_tail(struct intel_ringbuffer *ringbuf)
{
- return ring->buffer->tail;
+ return ringbuf->tail;
}
static inline u32 intel_ring_get_seqno(struct intel_engine_cs *ring)
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 20375cc7f82d..9350edd6728d 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2433,7 +2433,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
connector->base.unregister = intel_sdvo_connector_unregister;
intel_connector_attach_encoder(&connector->base, &encoder->base);
- ret = drm_sysfs_connector_add(drm_connector);
+ ret = drm_connector_register(drm_connector);
if (ret < 0)
goto err1;
@@ -2446,7 +2446,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
return 0;
err2:
- drm_sysfs_connector_remove(drm_connector);
+ drm_connector_unregister(drm_connector);
err1:
drm_connector_cleanup(drm_connector);
@@ -2559,7 +2559,7 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
return true;
err:
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
intel_sdvo_destroy(connector);
return false;
}
@@ -2638,7 +2638,7 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
return true;
err:
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
intel_sdvo_destroy(connector);
return false;
}
@@ -2711,7 +2711,7 @@ static void intel_sdvo_output_cleanup(struct intel_sdvo *intel_sdvo)
list_for_each_entry_safe(connector, tmp,
&dev->mode_config.connector_list, head) {
if (intel_attached_encoder(connector) == &intel_sdvo->base) {
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
intel_sdvo_destroy(connector);
}
}
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 9a17b4e92ef4..07a74ef589bd 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -53,6 +53,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl
enum pipe pipe = crtc->pipe;
long timeout = msecs_to_jiffies_timeout(1);
int scanline, min, max, vblank_start;
+ wait_queue_head_t *wq = drm_crtc_vblank_waitqueue(&crtc->base);
DEFINE_WAIT(wait);
WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex));
@@ -81,7 +82,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl
* other CPUs can see the task state update by the time we
* read the scanline.
*/
- prepare_to_wait(&crtc->vbl_wait, &wait, TASK_UNINTERRUPTIBLE);
+ prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE);
scanline = intel_get_crtc_scanline(crtc);
if (scanline < min || scanline > max)
@@ -100,7 +101,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl
local_irq_disable();
}
- finish_wait(&crtc->vbl_wait, &wait);
+ finish_wait(wq, &wait);
drm_vblank_put(dev, pipe);
@@ -163,6 +164,7 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
sprctl &= ~SP_PIXFORMAT_MASK;
sprctl &= ~SP_YUV_BYTE_ORDER_MASK;
sprctl &= ~SP_TILED;
+ sprctl &= ~SP_ROTATE_180;
switch (fb->pixel_format) {
case DRM_FORMAT_YUYV:
@@ -218,7 +220,8 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
sprctl |= SP_ENABLE;
- intel_update_sprite_watermarks(dplane, crtc, src_w, pixel_size, true,
+ intel_update_sprite_watermarks(dplane, crtc, src_w, src_h,
+ pixel_size, true,
src_w != crtc_w || src_h != crtc_h);
/* Sizes are 0 based */
@@ -234,6 +237,14 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
fb->pitches[0]);
linear_offset -= sprsurf_offset;
+ if (intel_plane->rotation == BIT(DRM_ROTATE_180)) {
+ sprctl |= SP_ROTATE_180;
+
+ x += src_w;
+ y += src_h;
+ linear_offset += src_h * fb->pitches[0] + src_w * pixel_size;
+ }
+
atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
intel_update_primary_plane(intel_crtc);
@@ -283,7 +294,7 @@ vlv_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc)
if (atomic_update)
intel_pipe_update_end(intel_crtc, start_vbl_count);
- intel_update_sprite_watermarks(dplane, crtc, 0, 0, false, false);
+ intel_update_sprite_watermarks(dplane, crtc, 0, 0, 0, false, false);
}
static int
@@ -363,6 +374,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
sprctl &= ~SPRITE_RGB_ORDER_RGBX;
sprctl &= ~SPRITE_YUV_BYTE_ORDER_MASK;
sprctl &= ~SPRITE_TILED;
+ sprctl &= ~SPRITE_ROTATE_180;
switch (fb->pixel_format) {
case DRM_FORMAT_XBGR8888:
@@ -406,7 +418,8 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
sprctl |= SPRITE_PIPE_CSC_ENABLE;
- intel_update_sprite_watermarks(plane, crtc, src_w, pixel_size, true,
+ intel_update_sprite_watermarks(plane, crtc, src_w, src_h, pixel_size,
+ true,
src_w != crtc_w || src_h != crtc_h);
/* Sizes are 0 based */
@@ -424,6 +437,18 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
pixel_size, fb->pitches[0]);
linear_offset -= sprsurf_offset;
+ if (intel_plane->rotation == BIT(DRM_ROTATE_180)) {
+ sprctl |= SPRITE_ROTATE_180;
+
+ /* HSW and BDW does this automagically in hardware */
+ if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
+ x += src_w;
+ y += src_h;
+ linear_offset += src_h * fb->pitches[0] +
+ src_w * pixel_size;
+ }
+ }
+
atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
intel_update_primary_plane(intel_crtc);
@@ -486,7 +511,7 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
*/
intel_wait_for_vblank(dev, pipe);
- intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false);
+ intel_update_sprite_watermarks(plane, crtc, 0, 0, 0, false, false);
}
static int
@@ -569,6 +594,7 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
dvscntr &= ~DVS_RGB_ORDER_XBGR;
dvscntr &= ~DVS_YUV_BYTE_ORDER_MASK;
dvscntr &= ~DVS_TILED;
+ dvscntr &= ~DVS_ROTATE_180;
switch (fb->pixel_format) {
case DRM_FORMAT_XBGR8888:
@@ -606,7 +632,8 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
dvscntr |= DVS_TRICKLE_FEED_DISABLE; /* must disable */
dvscntr |= DVS_ENABLE;
- intel_update_sprite_watermarks(plane, crtc, src_w, pixel_size, true,
+ intel_update_sprite_watermarks(plane, crtc, src_w, src_h,
+ pixel_size, true,
src_w != crtc_w || src_h != crtc_h);
/* Sizes are 0 based */
@@ -625,6 +652,14 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
pixel_size, fb->pitches[0]);
linear_offset -= dvssurf_offset;
+ if (intel_plane->rotation == BIT(DRM_ROTATE_180)) {
+ dvscntr |= DVS_ROTATE_180;
+
+ x += src_w;
+ y += src_h;
+ linear_offset += src_h * fb->pitches[0] + src_w * pixel_size;
+ }
+
atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
intel_update_primary_plane(intel_crtc);
@@ -681,7 +716,7 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
*/
intel_wait_for_vblank(dev, pipe);
- intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false);
+ intel_update_sprite_watermarks(plane, crtc, 0, 0, 0, false, false);
}
static void
@@ -819,6 +854,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_device *dev = plane->dev;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_plane *intel_plane = to_intel_plane(plane);
+ enum pipe pipe = intel_crtc->pipe;
struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
struct drm_i915_gem_object *obj = intel_fb->obj;
struct drm_i915_gem_object *old_obj = intel_plane->obj;
@@ -891,6 +927,9 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
max_scale = intel_plane->max_downscale << 16;
min_scale = intel_plane->can_scale ? 1 : (1 << 16);
+ drm_rect_rotate(&src, fb->width << 16, fb->height << 16,
+ intel_plane->rotation);
+
hscale = drm_rect_calc_hscale_relaxed(&src, &dst, min_scale, max_scale);
BUG_ON(hscale < 0);
@@ -929,6 +968,9 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
drm_rect_width(&dst) * hscale - drm_rect_width(&src),
drm_rect_height(&dst) * vscale - drm_rect_height(&src));
+ drm_rect_rotate_inv(&src, fb->width << 16, fb->height << 16,
+ intel_plane->rotation);
+
/* sanity check to make sure the src viewport wasn't enlarged */
WARN_ON(src.x1 < (int) src_x ||
src.y1 < (int) src_y ||
@@ -1006,6 +1048,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
*/
ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
+ i915_gem_track_fb(old_obj, obj,
+ INTEL_FRONTBUFFER_SPRITE(pipe));
mutex_unlock(&dev->struct_mutex);
if (ret)
@@ -1039,6 +1083,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
else
intel_plane->disable_plane(plane, crtc);
+ intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_SPRITE(pipe));
+
if (!primary_was_enabled && primary_enabled)
intel_post_enable_primary(crtc);
}
@@ -1068,6 +1114,7 @@ intel_disable_plane(struct drm_plane *plane)
struct drm_device *dev = plane->dev;
struct intel_plane *intel_plane = to_intel_plane(plane);
struct intel_crtc *intel_crtc;
+ enum pipe pipe;
if (!plane->fb)
return 0;
@@ -1076,6 +1123,7 @@ intel_disable_plane(struct drm_plane *plane)
return -EINVAL;
intel_crtc = to_intel_crtc(plane->crtc);
+ pipe = intel_crtc->pipe;
if (intel_crtc->active) {
bool primary_was_enabled = intel_crtc->primary_enabled;
@@ -1094,6 +1142,8 @@ intel_disable_plane(struct drm_plane *plane)
mutex_lock(&dev->struct_mutex);
intel_unpin_fb_obj(intel_plane->obj);
+ i915_gem_track_fb(intel_plane->obj, NULL,
+ INTEL_FRONTBUFFER_SPRITE(pipe));
mutex_unlock(&dev->struct_mutex);
intel_plane->obj = NULL;
@@ -1114,7 +1164,6 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_intel_sprite_colorkey *set = data;
- struct drm_mode_object *obj;
struct drm_plane *plane;
struct intel_plane *intel_plane;
int ret = 0;
@@ -1128,13 +1177,12 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE);
- if (!obj) {
+ plane = drm_plane_find(dev, set->plane_id);
+ if (!plane) {
ret = -ENOENT;
goto out_unlock;
}
- plane = obj_to_plane(obj);
intel_plane = to_intel_plane(plane);
ret = intel_plane->update_colorkey(plane, set);
@@ -1147,7 +1195,6 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_intel_sprite_colorkey *get = data;
- struct drm_mode_object *obj;
struct drm_plane *plane;
struct intel_plane *intel_plane;
int ret = 0;
@@ -1157,13 +1204,12 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE);
- if (!obj) {
+ plane = drm_plane_find(dev, get->plane_id);
+ if (!plane) {
ret = -ENOENT;
goto out_unlock;
}
- plane = obj_to_plane(obj);
intel_plane = to_intel_plane(plane);
intel_plane->get_colorkey(plane, get);
@@ -1172,18 +1218,45 @@ out_unlock:
return ret;
}
-void intel_plane_restore(struct drm_plane *plane)
+int intel_plane_set_property(struct drm_plane *plane,
+ struct drm_property *prop,
+ uint64_t val)
+{
+ struct drm_device *dev = plane->dev;
+ struct intel_plane *intel_plane = to_intel_plane(plane);
+ uint64_t old_val;
+ int ret = -ENOENT;
+
+ if (prop == dev->mode_config.rotation_property) {
+ /* exactly one rotation angle please */
+ if (hweight32(val & 0xf) != 1)
+ return -EINVAL;
+
+ if (intel_plane->rotation == val)
+ return 0;
+
+ old_val = intel_plane->rotation;
+ intel_plane->rotation = val;
+ ret = intel_plane_restore(plane);
+ if (ret)
+ intel_plane->rotation = old_val;
+ }
+
+ return ret;
+}
+
+int intel_plane_restore(struct drm_plane *plane)
{
struct intel_plane *intel_plane = to_intel_plane(plane);
if (!plane->crtc || !plane->fb)
- return;
+ return 0;
- intel_update_plane(plane, plane->crtc, plane->fb,
- intel_plane->crtc_x, intel_plane->crtc_y,
- intel_plane->crtc_w, intel_plane->crtc_h,
- intel_plane->src_x, intel_plane->src_y,
- intel_plane->src_w, intel_plane->src_h);
+ return plane->funcs->update_plane(plane, plane->crtc, plane->fb,
+ intel_plane->crtc_x, intel_plane->crtc_y,
+ intel_plane->crtc_w, intel_plane->crtc_h,
+ intel_plane->src_x, intel_plane->src_y,
+ intel_plane->src_w, intel_plane->src_h);
}
void intel_plane_disable(struct drm_plane *plane)
@@ -1198,6 +1271,7 @@ static const struct drm_plane_funcs intel_plane_funcs = {
.update_plane = intel_update_plane,
.disable_plane = intel_disable_plane,
.destroy = intel_destroy_plane,
+ .set_property = intel_plane_set_property,
};
static uint32_t ilk_plane_formats[] = {
@@ -1302,13 +1376,28 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane)
intel_plane->pipe = pipe;
intel_plane->plane = plane;
+ intel_plane->rotation = BIT(DRM_ROTATE_0);
possible_crtcs = (1 << pipe);
- ret = drm_plane_init(dev, &intel_plane->base, possible_crtcs,
- &intel_plane_funcs,
- plane_formats, num_plane_formats,
- false);
- if (ret)
+ ret = drm_universal_plane_init(dev, &intel_plane->base, possible_crtcs,
+ &intel_plane_funcs,
+ plane_formats, num_plane_formats,
+ DRM_PLANE_TYPE_OVERLAY);
+ if (ret) {
kfree(intel_plane);
+ goto out;
+ }
+
+ if (!dev->mode_config.rotation_property)
+ dev->mode_config.rotation_property =
+ drm_mode_create_rotation_property(dev,
+ BIT(DRM_ROTATE_0) |
+ BIT(DRM_ROTATE_180));
+
+ if (dev->mode_config.rotation_property)
+ drm_object_attach_property(&intel_plane->base.base,
+ dev->mode_config.rotation_property,
+ intel_plane->rotation);
+ out:
return ret;
}
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 67c6c9a2eb1c..c14341ca3ef9 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -854,6 +854,10 @@ intel_enable_tv(struct intel_encoder *encoder)
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ /* Prevents vblank waits from timing out in intel_tv_detect_type() */
+ intel_wait_for_vblank(encoder->base.dev,
+ to_intel_crtc(encoder->base.crtc)->pipe);
+
I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
}
@@ -1311,6 +1315,7 @@ intel_tv_detect(struct drm_connector *connector, bool force)
{
struct drm_display_mode mode;
struct intel_tv *intel_tv = intel_attached_tv(connector);
+ enum drm_connector_status status;
int type;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n",
@@ -1323,16 +1328,24 @@ intel_tv_detect(struct drm_connector *connector, bool force)
struct intel_load_detect_pipe tmp;
struct drm_modeset_acquire_ctx ctx;
+ drm_modeset_acquire_init(&ctx, 0);
+
if (intel_get_load_detect_pipe(connector, &mode, &tmp, &ctx)) {
type = intel_tv_detect_type(intel_tv, connector);
- intel_release_load_detect_pipe(connector, &tmp, &ctx);
+ intel_release_load_detect_pipe(connector, &tmp);
+ status = type < 0 ?
+ connector_status_disconnected :
+ connector_status_connected;
} else
- return connector_status_unknown;
+ status = connector_status_unknown;
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
} else
return connector->status;
- if (type < 0)
- return connector_status_disconnected;
+ if (status != connector_status_connected)
+ return status;
intel_tv->type = type;
intel_tv_find_better_format(connector);
@@ -1680,5 +1693,5 @@ intel_tv_init(struct drm_device *dev)
drm_object_attach_property(&connector->base,
dev->mode_config.tv_bottom_margin_property,
intel_tv->margin[TV_MARGIN_BOTTOM]);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
}
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index 4f6fef7ac069..918b76163965 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -101,7 +101,7 @@ static void __gen7_gt_force_wake_mt_get(struct drm_i915_private *dev_priv,
{
u32 forcewake_ack;
- if (IS_HASWELL(dev_priv->dev) || IS_GEN8(dev_priv->dev))
+ if (IS_HASWELL(dev_priv->dev) || IS_BROADWELL(dev_priv->dev))
forcewake_ack = FORCEWAKE_ACK_HSW;
else
forcewake_ack = FORCEWAKE_MT_ACK;
@@ -231,8 +231,8 @@ static void __vlv_force_wake_get(struct drm_i915_private *dev_priv,
}
/* WaRsForcewakeWaitTC0:vlv */
- __gen6_gt_wait_for_thread_c0(dev_priv);
-
+ if (!IS_CHERRYVIEW(dev_priv->dev))
+ __gen6_gt_wait_for_thread_c0(dev_priv);
}
static void __vlv_force_wake_put(struct drm_i915_private *dev_priv,
@@ -250,9 +250,10 @@ static void __vlv_force_wake_put(struct drm_i915_private *dev_priv,
__raw_i915_write32(dev_priv, FORCEWAKE_MEDIA_VLV,
_MASKED_BIT_DISABLE(FORCEWAKE_KERNEL));
- /* The below doubles as a POSTING_READ */
- gen6_gt_check_fifodbg(dev_priv);
-
+ /* something from same cacheline, but !FORCEWAKE_VLV */
+ __raw_posting_read(dev_priv, FORCEWAKE_ACK_VLV);
+ if (!IS_CHERRYVIEW(dev_priv->dev))
+ gen6_gt_check_fifodbg(dev_priv);
}
static void vlv_force_wake_get(struct drm_i915_private *dev_priv, int fw_engine)
@@ -315,7 +316,7 @@ static void gen6_force_wake_timer(unsigned long arg)
intel_runtime_pm_put(dev_priv);
}
-static void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore)
+void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore)
{
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long irqflags;
@@ -333,7 +334,7 @@ static void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore)
else if (IS_GEN6(dev) || IS_GEN7(dev))
__gen6_gt_force_wake_reset(dev_priv);
- if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_GEN8(dev))
+ if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev))
__gen7_gt_force_wake_mt_reset(dev_priv);
if (restore) { /* If reset with a user forcewake, try to restore */
@@ -357,16 +358,12 @@ static void intel_uncore_forcewake_reset(struct drm_device *dev, bool restore)
dev_priv->uncore.fifo_count =
__raw_i915_read32(dev_priv, GTFIFOCTL) &
GT_FIFO_FREE_ENTRIES_MASK;
- } else {
- dev_priv->uncore.forcewake_count = 0;
- dev_priv->uncore.fw_rendercount = 0;
- dev_priv->uncore.fw_mediacount = 0;
}
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
}
-void intel_uncore_early_sanitize(struct drm_device *dev)
+void intel_uncore_early_sanitize(struct drm_device *dev, bool restore_forcewake)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -389,7 +386,7 @@ void intel_uncore_early_sanitize(struct drm_device *dev)
__raw_i915_write32(dev_priv, GTFIFODBG,
__raw_i915_read32(dev_priv, GTFIFODBG));
- intel_uncore_forcewake_reset(dev, false);
+ intel_uncore_forcewake_reset(dev, restore_forcewake);
}
void intel_uncore_sanitize(struct drm_device *dev)
@@ -469,16 +466,43 @@ void assert_force_wake_inactive(struct drm_i915_private *dev_priv)
#define NEEDS_FORCE_WAKE(dev_priv, reg) \
((reg) < 0x40000 && (reg) != FORCEWAKE)
-#define FORCEWAKE_VLV_RENDER_RANGE_OFFSET(reg) \
- (((reg) >= 0x2000 && (reg) < 0x4000) ||\
- ((reg) >= 0x5000 && (reg) < 0x8000) ||\
- ((reg) >= 0xB000 && (reg) < 0x12000) ||\
- ((reg) >= 0x2E000 && (reg) < 0x30000))
+#define REG_RANGE(reg, start, end) ((reg) >= (start) && (reg) < (end))
-#define FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(reg)\
- (((reg) >= 0x12000 && (reg) < 0x14000) ||\
- ((reg) >= 0x22000 && (reg) < 0x24000) ||\
- ((reg) >= 0x30000 && (reg) < 0x40000))
+#define FORCEWAKE_VLV_RENDER_RANGE_OFFSET(reg) \
+ (REG_RANGE((reg), 0x2000, 0x4000) || \
+ REG_RANGE((reg), 0x5000, 0x8000) || \
+ REG_RANGE((reg), 0xB000, 0x12000) || \
+ REG_RANGE((reg), 0x2E000, 0x30000))
+
+#define FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(reg) \
+ (REG_RANGE((reg), 0x12000, 0x14000) || \
+ REG_RANGE((reg), 0x22000, 0x24000) || \
+ REG_RANGE((reg), 0x30000, 0x40000))
+
+#define FORCEWAKE_CHV_RENDER_RANGE_OFFSET(reg) \
+ (REG_RANGE((reg), 0x2000, 0x4000) || \
+ REG_RANGE((reg), 0x5000, 0x8000) || \
+ REG_RANGE((reg), 0x8300, 0x8500) || \
+ REG_RANGE((reg), 0xB000, 0xC000) || \
+ REG_RANGE((reg), 0xE000, 0xE800))
+
+#define FORCEWAKE_CHV_MEDIA_RANGE_OFFSET(reg) \
+ (REG_RANGE((reg), 0x8800, 0x8900) || \
+ REG_RANGE((reg), 0xD000, 0xD800) || \
+ REG_RANGE((reg), 0x12000, 0x14000) || \
+ REG_RANGE((reg), 0x1A000, 0x1C000) || \
+ REG_RANGE((reg), 0x1E800, 0x1EA00) || \
+ REG_RANGE((reg), 0x30000, 0x40000))
+
+#define FORCEWAKE_CHV_COMMON_RANGE_OFFSET(reg) \
+ (REG_RANGE((reg), 0x4000, 0x5000) || \
+ REG_RANGE((reg), 0x8000, 0x8300) || \
+ REG_RANGE((reg), 0x8500, 0x8600) || \
+ REG_RANGE((reg), 0x9000, 0xB000) || \
+ REG_RANGE((reg), 0xC000, 0xC800) || \
+ REG_RANGE((reg), 0xF000, 0x10000) || \
+ REG_RANGE((reg), 0x14000, 0x14400) || \
+ REG_RANGE((reg), 0x22000, 0x24000))
static void
ilk_dummy_write(struct drm_i915_private *dev_priv)
@@ -490,20 +514,30 @@ ilk_dummy_write(struct drm_i915_private *dev_priv)
}
static void
-hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg)
+hsw_unclaimed_reg_debug(struct drm_i915_private *dev_priv, u32 reg, bool read,
+ bool before)
{
+ const char *op = read ? "reading" : "writing to";
+ const char *when = before ? "before" : "after";
+
+ if (!i915.mmio_debug)
+ return;
+
if (__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM) {
- DRM_ERROR("Unknown unclaimed register before writing to %x\n",
- reg);
+ WARN(1, "Unclaimed register detected %s %s register 0x%x\n",
+ when, op, reg);
__raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
}
}
static void
-hsw_unclaimed_reg_check(struct drm_i915_private *dev_priv, u32 reg)
+hsw_unclaimed_reg_detect(struct drm_i915_private *dev_priv)
{
+ if (i915.mmio_debug)
+ return;
+
if (__raw_i915_read32(dev_priv, FPGA_DBG) & FPGA_DBG_RM_NOCLAIM) {
- DRM_ERROR("Unclaimed write to %x\n", reg);
+ DRM_ERROR("Unclaimed register detected. Please use the i915.mmio_debug=1 to debug this problem.");
__raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
}
}
@@ -540,6 +574,7 @@ gen5_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
static u##x \
gen6_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
REG_READ_HEADER(x); \
+ hsw_unclaimed_reg_debug(dev_priv, reg, true, true); \
if (dev_priv->uncore.forcewake_count == 0 && \
NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
dev_priv->uncore.funcs.force_wake_get(dev_priv, \
@@ -550,6 +585,7 @@ gen6_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
} else { \
val = __raw_i915_read##x(dev_priv, reg); \
} \
+ hsw_unclaimed_reg_debug(dev_priv, reg, true, false); \
REG_READ_FOOTER; \
}
@@ -573,7 +609,35 @@ vlv_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
REG_READ_FOOTER; \
}
+#define __chv_read(x) \
+static u##x \
+chv_read##x(struct drm_i915_private *dev_priv, off_t reg, bool trace) { \
+ unsigned fwengine = 0; \
+ REG_READ_HEADER(x); \
+ if (FORCEWAKE_CHV_RENDER_RANGE_OFFSET(reg)) { \
+ if (dev_priv->uncore.fw_rendercount == 0) \
+ fwengine = FORCEWAKE_RENDER; \
+ } else if (FORCEWAKE_CHV_MEDIA_RANGE_OFFSET(reg)) { \
+ if (dev_priv->uncore.fw_mediacount == 0) \
+ fwengine = FORCEWAKE_MEDIA; \
+ } else if (FORCEWAKE_CHV_COMMON_RANGE_OFFSET(reg)) { \
+ if (dev_priv->uncore.fw_rendercount == 0) \
+ fwengine |= FORCEWAKE_RENDER; \
+ if (dev_priv->uncore.fw_mediacount == 0) \
+ fwengine |= FORCEWAKE_MEDIA; \
+ } \
+ if (fwengine) \
+ dev_priv->uncore.funcs.force_wake_get(dev_priv, fwengine); \
+ val = __raw_i915_read##x(dev_priv, reg); \
+ if (fwengine) \
+ dev_priv->uncore.funcs.force_wake_put(dev_priv, fwengine); \
+ REG_READ_FOOTER; \
+}
+__chv_read(8)
+__chv_read(16)
+__chv_read(32)
+__chv_read(64)
__vlv_read(8)
__vlv_read(16)
__vlv_read(32)
@@ -591,6 +655,7 @@ __gen4_read(16)
__gen4_read(32)
__gen4_read(64)
+#undef __chv_read
#undef __vlv_read
#undef __gen6_read
#undef __gen5_read
@@ -647,12 +712,13 @@ hsw_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace)
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
__fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
} \
- hsw_unclaimed_reg_clear(dev_priv, reg); \
+ hsw_unclaimed_reg_debug(dev_priv, reg, false, true); \
__raw_i915_write##x(dev_priv, reg, val); \
if (unlikely(__fifo_ret)) { \
gen6_gt_check_fifodbg(dev_priv); \
} \
- hsw_unclaimed_reg_check(dev_priv, reg); \
+ hsw_unclaimed_reg_debug(dev_priv, reg, false, false); \
+ hsw_unclaimed_reg_detect(dev_priv); \
REG_WRITE_FOOTER; \
}
@@ -681,6 +747,7 @@ static bool is_gen8_shadowed(struct drm_i915_private *dev_priv, u32 reg)
static void \
gen8_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace) { \
REG_WRITE_HEADER; \
+ hsw_unclaimed_reg_debug(dev_priv, reg, false, true); \
if (reg < 0x40000 && !is_gen8_shadowed(dev_priv, reg)) { \
if (dev_priv->uncore.forcewake_count == 0) \
dev_priv->uncore.funcs.force_wake_get(dev_priv, \
@@ -692,9 +759,43 @@ gen8_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace
} else { \
__raw_i915_write##x(dev_priv, reg, val); \
} \
+ hsw_unclaimed_reg_debug(dev_priv, reg, false, false); \
+ hsw_unclaimed_reg_detect(dev_priv); \
REG_WRITE_FOOTER; \
}
+#define __chv_write(x) \
+static void \
+chv_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace) { \
+ unsigned fwengine = 0; \
+ bool shadowed = is_gen8_shadowed(dev_priv, reg); \
+ REG_WRITE_HEADER; \
+ if (!shadowed) { \
+ if (FORCEWAKE_CHV_RENDER_RANGE_OFFSET(reg)) { \
+ if (dev_priv->uncore.fw_rendercount == 0) \
+ fwengine = FORCEWAKE_RENDER; \
+ } else if (FORCEWAKE_CHV_MEDIA_RANGE_OFFSET(reg)) { \
+ if (dev_priv->uncore.fw_mediacount == 0) \
+ fwengine = FORCEWAKE_MEDIA; \
+ } else if (FORCEWAKE_CHV_COMMON_RANGE_OFFSET(reg)) { \
+ if (dev_priv->uncore.fw_rendercount == 0) \
+ fwengine |= FORCEWAKE_RENDER; \
+ if (dev_priv->uncore.fw_mediacount == 0) \
+ fwengine |= FORCEWAKE_MEDIA; \
+ } \
+ } \
+ if (fwengine) \
+ dev_priv->uncore.funcs.force_wake_get(dev_priv, fwengine); \
+ __raw_i915_write##x(dev_priv, reg, val); \
+ if (fwengine) \
+ dev_priv->uncore.funcs.force_wake_put(dev_priv, fwengine); \
+ REG_WRITE_FOOTER; \
+}
+
+__chv_write(8)
+__chv_write(16)
+__chv_write(32)
+__chv_write(64)
__gen8_write(8)
__gen8_write(16)
__gen8_write(32)
@@ -716,6 +817,7 @@ __gen4_write(16)
__gen4_write(32)
__gen4_write(64)
+#undef __chv_write
#undef __gen8_write
#undef __hsw_write
#undef __gen6_write
@@ -731,12 +833,12 @@ void intel_uncore_init(struct drm_device *dev)
setup_timer(&dev_priv->uncore.force_wake_timer,
gen6_force_wake_timer, (unsigned long)dev_priv);
- intel_uncore_early_sanitize(dev);
+ intel_uncore_early_sanitize(dev, false);
if (IS_VALLEYVIEW(dev)) {
dev_priv->uncore.funcs.force_wake_get = __vlv_force_wake_get;
dev_priv->uncore.funcs.force_wake_put = __vlv_force_wake_put;
- } else if (IS_HASWELL(dev) || IS_GEN8(dev)) {
+ } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
dev_priv->uncore.funcs.force_wake_get = __gen7_gt_force_wake_mt_get;
dev_priv->uncore.funcs.force_wake_put = __gen7_gt_force_wake_mt_put;
} else if (IS_IVYBRIDGE(dev)) {
@@ -779,14 +881,26 @@ void intel_uncore_init(struct drm_device *dev)
switch (INTEL_INFO(dev)->gen) {
default:
- dev_priv->uncore.funcs.mmio_writeb = gen8_write8;
- dev_priv->uncore.funcs.mmio_writew = gen8_write16;
- dev_priv->uncore.funcs.mmio_writel = gen8_write32;
- dev_priv->uncore.funcs.mmio_writeq = gen8_write64;
- dev_priv->uncore.funcs.mmio_readb = gen6_read8;
- dev_priv->uncore.funcs.mmio_readw = gen6_read16;
- dev_priv->uncore.funcs.mmio_readl = gen6_read32;
- dev_priv->uncore.funcs.mmio_readq = gen6_read64;
+ if (IS_CHERRYVIEW(dev)) {
+ dev_priv->uncore.funcs.mmio_writeb = chv_write8;
+ dev_priv->uncore.funcs.mmio_writew = chv_write16;
+ dev_priv->uncore.funcs.mmio_writel = chv_write32;
+ dev_priv->uncore.funcs.mmio_writeq = chv_write64;
+ dev_priv->uncore.funcs.mmio_readb = chv_read8;
+ dev_priv->uncore.funcs.mmio_readw = chv_read16;
+ dev_priv->uncore.funcs.mmio_readl = chv_read32;
+ dev_priv->uncore.funcs.mmio_readq = chv_read64;
+
+ } else {
+ dev_priv->uncore.funcs.mmio_writeb = gen8_write8;
+ dev_priv->uncore.funcs.mmio_writew = gen8_write16;
+ dev_priv->uncore.funcs.mmio_writel = gen8_write32;
+ dev_priv->uncore.funcs.mmio_writeq = gen8_write64;
+ dev_priv->uncore.funcs.mmio_readb = gen6_read8;
+ dev_priv->uncore.funcs.mmio_readw = gen6_read16;
+ dev_priv->uncore.funcs.mmio_readl = gen6_read32;
+ dev_priv->uncore.funcs.mmio_readq = gen6_read64;
+ }
break;
case 7:
case 6:
@@ -912,7 +1026,7 @@ int i915_get_reset_stats_ioctl(struct drm_device *dev,
if (args->flags || args->pad)
return -EINVAL;
- if (args->ctx_id == DEFAULT_CONTEXT_ID && !capable(CAP_SYS_ADMIN))
+ if (args->ctx_id == DEFAULT_CONTEXT_HANDLE && !capable(CAP_SYS_ADMIN))
return -EPERM;
ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -1053,18 +1167,16 @@ static int gen6_do_reset(struct drm_device *dev)
int intel_gpu_reset(struct drm_device *dev)
{
- switch (INTEL_INFO(dev)->gen) {
- case 8:
- case 7:
- case 6: return gen6_do_reset(dev);
- case 5: return ironlake_do_reset(dev);
- case 4:
- if (IS_G4X(dev))
- return g4x_do_reset(dev);
- else
- return i965_do_reset(dev);
- default: return -ENODEV;
- }
+ if (INTEL_INFO(dev)->gen >= 6)
+ return gen6_do_reset(dev);
+ else if (IS_GEN5(dev))
+ return ironlake_do_reset(dev);
+ else if (IS_G4X(dev))
+ return g4x_do_reset(dev);
+ else if (IS_GEN4(dev))
+ return i965_do_reset(dev);
+ else
+ return -ENODEV;
}
void intel_uncore_check_errors(struct drm_device *dev)
diff --git a/drivers/gpu/drm/mga/mga_dma.c b/drivers/gpu/drm/mga/mga_dma.c
index c3bf059ba720..8cfa9cb74c86 100644
--- a/drivers/gpu/drm/mga/mga_dma.c
+++ b/drivers/gpu/drm/mga/mga_dma.c
@@ -502,31 +502,31 @@ static int mga_do_agp_dma_bootstrap(struct drm_device *dev,
return err;
}
- /* Make drm_addbufs happy by not trying to create a mapping for less
- * than a page.
+ /* Make drm_legacy_addbufs happy by not trying to create a mapping for
+ * less than a page.
*/
if (warp_size < PAGE_SIZE)
warp_size = PAGE_SIZE;
offset = 0;
- err = drm_addmap(dev, offset, warp_size,
- _DRM_AGP, _DRM_READ_ONLY, &dev_priv->warp);
+ err = drm_legacy_addmap(dev, offset, warp_size,
+ _DRM_AGP, _DRM_READ_ONLY, &dev_priv->warp);
if (err) {
DRM_ERROR("Unable to map WARP microcode: %d\n", err);
return err;
}
offset += warp_size;
- err = drm_addmap(dev, offset, dma_bs->primary_size,
- _DRM_AGP, _DRM_READ_ONLY, &dev_priv->primary);
+ err = drm_legacy_addmap(dev, offset, dma_bs->primary_size,
+ _DRM_AGP, _DRM_READ_ONLY, &dev_priv->primary);
if (err) {
DRM_ERROR("Unable to map primary DMA region: %d\n", err);
return err;
}
offset += dma_bs->primary_size;
- err = drm_addmap(dev, offset, secondary_size,
- _DRM_AGP, 0, &dev->agp_buffer_map);
+ err = drm_legacy_addmap(dev, offset, secondary_size,
+ _DRM_AGP, 0, &dev->agp_buffer_map);
if (err) {
DRM_ERROR("Unable to map secondary DMA region: %d\n", err);
return err;
@@ -538,7 +538,7 @@ static int mga_do_agp_dma_bootstrap(struct drm_device *dev,
req.flags = _DRM_AGP_BUFFER;
req.agp_start = offset;
- err = drm_addbufs_agp(dev, &req);
+ err = drm_legacy_addbufs_agp(dev, &req);
if (err) {
DRM_ERROR("Unable to add secondary DMA buffers: %d\n", err);
return err;
@@ -559,16 +559,16 @@ static int mga_do_agp_dma_bootstrap(struct drm_device *dev,
}
offset += secondary_size;
- err = drm_addmap(dev, offset, agp_size - offset,
- _DRM_AGP, 0, &dev_priv->agp_textures);
+ err = drm_legacy_addmap(dev, offset, agp_size - offset,
+ _DRM_AGP, 0, &dev_priv->agp_textures);
if (err) {
DRM_ERROR("Unable to map AGP texture region %d\n", err);
return err;
}
- drm_core_ioremap(dev_priv->warp, dev);
- drm_core_ioremap(dev_priv->primary, dev);
- drm_core_ioremap(dev->agp_buffer_map, dev);
+ drm_legacy_ioremap(dev_priv->warp, dev);
+ drm_legacy_ioremap(dev_priv->primary, dev);
+ drm_legacy_ioremap(dev->agp_buffer_map, dev);
if (!dev_priv->warp->handle ||
!dev_priv->primary->handle || !dev->agp_buffer_map->handle) {
@@ -602,7 +602,7 @@ static int mga_do_agp_dma_bootstrap(struct drm_device *dev,
*
* \todo
* Determine whether the maximum address passed to drm_pci_alloc is correct.
- * The same goes for drm_addbufs_pci.
+ * The same goes for drm_legacy_addbufs_pci.
*
* \sa mga_do_dma_bootstrap, mga_do_agp_dma_bootstrap
*/
@@ -622,15 +622,15 @@ static int mga_do_pci_dma_bootstrap(struct drm_device *dev,
return -EFAULT;
}
- /* Make drm_addbufs happy by not trying to create a mapping for less
- * than a page.
+ /* Make drm_legacy_addbufs happy by not trying to create a mapping for
+ * less than a page.
*/
if (warp_size < PAGE_SIZE)
warp_size = PAGE_SIZE;
/* The proper alignment is 0x100 for this mapping */
- err = drm_addmap(dev, 0, warp_size, _DRM_CONSISTENT,
- _DRM_READ_ONLY, &dev_priv->warp);
+ err = drm_legacy_addmap(dev, 0, warp_size, _DRM_CONSISTENT,
+ _DRM_READ_ONLY, &dev_priv->warp);
if (err != 0) {
DRM_ERROR("Unable to create mapping for WARP microcode: %d\n",
err);
@@ -645,8 +645,8 @@ static int mga_do_pci_dma_bootstrap(struct drm_device *dev,
for (primary_size = dma_bs->primary_size; primary_size != 0;
primary_size >>= 1) {
/* The proper alignment for this mapping is 0x04 */
- err = drm_addmap(dev, 0, primary_size, _DRM_CONSISTENT,
- _DRM_READ_ONLY, &dev_priv->primary);
+ err = drm_legacy_addmap(dev, 0, primary_size, _DRM_CONSISTENT,
+ _DRM_READ_ONLY, &dev_priv->primary);
if (!err)
break;
}
@@ -669,7 +669,7 @@ static int mga_do_pci_dma_bootstrap(struct drm_device *dev,
req.count = bin_count;
req.size = dma_bs->secondary_bin_size;
- err = drm_addbufs_pci(dev, &req);
+ err = drm_legacy_addbufs_pci(dev, &req);
if (!err)
break;
}
@@ -708,15 +708,16 @@ static int mga_do_dma_bootstrap(struct drm_device *dev,
/* The first steps are the same for both PCI and AGP based DMA. Map
* the cards MMIO registers and map a status page.
*/
- err = drm_addmap(dev, dev_priv->mmio_base, dev_priv->mmio_size,
- _DRM_REGISTERS, _DRM_READ_ONLY, &dev_priv->mmio);
+ err = drm_legacy_addmap(dev, dev_priv->mmio_base, dev_priv->mmio_size,
+ _DRM_REGISTERS, _DRM_READ_ONLY,
+ &dev_priv->mmio);
if (err) {
DRM_ERROR("Unable to map MMIO region: %d\n", err);
return err;
}
- err = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM,
- _DRM_READ_ONLY | _DRM_LOCKED | _DRM_KERNEL,
+ err = drm_legacy_addmap(dev, 0, SAREA_MAX, _DRM_SHM,
+ _DRM_READ_ONLY | _DRM_LOCKED | _DRM_KERNEL,
&dev_priv->status);
if (err) {
DRM_ERROR("Unable to map status region: %d\n", err);
@@ -809,7 +810,7 @@ static int mga_do_init_dma(struct drm_device *dev, drm_mga_init_t *init)
dev_priv->texture_offset = init->texture_offset[0];
dev_priv->texture_size = init->texture_size[0];
- dev_priv->sarea = drm_getsarea(dev);
+ dev_priv->sarea = drm_legacy_getsarea(dev);
if (!dev_priv->sarea) {
DRM_ERROR("failed to find sarea!\n");
return -EINVAL;
@@ -820,37 +821,37 @@ static int mga_do_init_dma(struct drm_device *dev, drm_mga_init_t *init)
dev_priv->dma_access = MGA_PAGPXFER;
dev_priv->wagp_enable = MGA_WAGP_ENABLE;
- dev_priv->status = drm_core_findmap(dev, init->status_offset);
+ dev_priv->status = drm_legacy_findmap(dev, init->status_offset);
if (!dev_priv->status) {
DRM_ERROR("failed to find status page!\n");
return -EINVAL;
}
- dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset);
+ dev_priv->mmio = drm_legacy_findmap(dev, init->mmio_offset);
if (!dev_priv->mmio) {
DRM_ERROR("failed to find mmio region!\n");
return -EINVAL;
}
- dev_priv->warp = drm_core_findmap(dev, init->warp_offset);
+ dev_priv->warp = drm_legacy_findmap(dev, init->warp_offset);
if (!dev_priv->warp) {
DRM_ERROR("failed to find warp microcode region!\n");
return -EINVAL;
}
- dev_priv->primary = drm_core_findmap(dev, init->primary_offset);
+ dev_priv->primary = drm_legacy_findmap(dev, init->primary_offset);
if (!dev_priv->primary) {
DRM_ERROR("failed to find primary dma region!\n");
return -EINVAL;
}
dev->agp_buffer_token = init->buffers_offset;
dev->agp_buffer_map =
- drm_core_findmap(dev, init->buffers_offset);
+ drm_legacy_findmap(dev, init->buffers_offset);
if (!dev->agp_buffer_map) {
DRM_ERROR("failed to find dma buffer region!\n");
return -EINVAL;
}
- drm_core_ioremap(dev_priv->warp, dev);
- drm_core_ioremap(dev_priv->primary, dev);
- drm_core_ioremap(dev->agp_buffer_map, dev);
+ drm_legacy_ioremap(dev_priv->warp, dev);
+ drm_legacy_ioremap(dev_priv->primary, dev);
+ drm_legacy_ioremap(dev->agp_buffer_map, dev);
}
dev_priv->sarea_priv =
@@ -936,14 +937,14 @@ static int mga_do_cleanup_dma(struct drm_device *dev, int full_cleanup)
if ((dev_priv->warp != NULL)
&& (dev_priv->warp->type != _DRM_CONSISTENT))
- drm_core_ioremapfree(dev_priv->warp, dev);
+ drm_legacy_ioremapfree(dev_priv->warp, dev);
if ((dev_priv->primary != NULL)
&& (dev_priv->primary->type != _DRM_CONSISTENT))
- drm_core_ioremapfree(dev_priv->primary, dev);
+ drm_legacy_ioremapfree(dev_priv->primary, dev);
if (dev->agp_buffer_map != NULL)
- drm_core_ioremapfree(dev->agp_buffer_map, dev);
+ drm_legacy_ioremapfree(dev->agp_buffer_map, dev);
if (dev_priv->used_new_dma_init) {
#if __OS_HAS_AGP
diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c
index 6b1a87c8aac5..5e2f131a6a72 100644
--- a/drivers/gpu/drm/mga/mga_drv.c
+++ b/drivers/gpu/drm/mga/mga_drv.c
@@ -48,7 +48,7 @@ static const struct file_operations mga_driver_fops = {
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
- .mmap = drm_mmap,
+ .mmap = drm_legacy_mmap,
.poll = drm_poll,
#ifdef CONFIG_COMPAT
.compat_ioctl = mga_compat_ioctl,
@@ -64,6 +64,7 @@ static struct drm_driver driver = {
.load = mga_driver_load,
.unload = mga_driver_unload,
.lastclose = mga_driver_lastclose,
+ .set_busid = drm_pci_set_busid,
.dma_quiescent = mga_driver_dma_quiescent,
.device_is_agp = mga_driver_device_is_agp,
.get_vblank_counter = mga_get_vblank_counter,
diff --git a/drivers/gpu/drm/mga/mga_drv.h b/drivers/gpu/drm/mga/mga_drv.h
index fe453213600a..b4a2014917e5 100644
--- a/drivers/gpu/drm/mga/mga_drv.h
+++ b/drivers/gpu/drm/mga/mga_drv.h
@@ -31,6 +31,8 @@
#ifndef __MGA_DRV_H__
#define __MGA_DRV_H__
+#include <drm/drm_legacy.h>
+
/* General customization:
*/
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index f15ea3c4a90a..97745991544d 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -28,7 +28,7 @@ module_param_named(modeset, mgag200_modeset, int, 0400);
static struct drm_driver driver;
-static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
+static const struct pci_device_id pciidlist[] = {
{ PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A },
{ PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B },
{ PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV },
@@ -91,6 +91,7 @@ static struct drm_driver driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET,
.load = mgag200_driver_load,
.unload = mgag200_driver_unload,
+ .set_busid = drm_pci_set_busid,
.fops = &mgag200_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h
index cf11ee68a6d9..e9eea1d4e7c3 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.h
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.h
@@ -22,6 +22,8 @@
#include <drm/ttm/ttm_memory.h>
#include <drm/ttm/ttm_module.h>
+#include <drm/drm_gem.h>
+
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
@@ -190,8 +192,6 @@ struct mga_device {
resource_size_t rmmio_size;
void __iomem *rmmio;
- drm_local_map_t *framebuffer;
-
struct mga_mc mc;
struct mga_mode_info mode_info;
@@ -224,7 +224,7 @@ struct mgag200_bo {
struct ttm_placement placement;
struct ttm_bo_kmap_obj kmap;
struct drm_gem_object gem;
- u32 placements[3];
+ struct ttm_place placements[3];
int pin_count;
};
#define gem_to_mga_bo(gobj) container_of((gobj), struct mgag200_bo, gem)
@@ -280,7 +280,7 @@ static inline int mgag200_bo_reserve(struct mgag200_bo *bo, bool no_wait)
{
int ret;
- ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+ ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, NULL);
if (ret) {
if (ret != -ERESTARTSYS && ret != -EBUSY)
DRM_ERROR("reserve failed %p\n", bo);
diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
index 13b7dd83faa9..4415af3666ab 100644
--- a/drivers/gpu/drm/mgag200/mgag200_fb.c
+++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
@@ -158,7 +158,8 @@ static int mgag200fb_create_object(struct mga_fbdev *afbdev,
static int mgag200fb_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
- struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper;
+ struct mga_fbdev *mfbdev =
+ container_of(helper, struct mga_fbdev, helper);
struct drm_device *dev = mfbdev->helper.dev;
struct drm_mode_fb_cmd2 mode_cmd;
struct mga_device *mdev = dev->dev_private;
@@ -272,7 +273,7 @@ static int mga_fbdev_destroy(struct drm_device *dev,
return 0;
}
-static struct drm_fb_helper_funcs mga_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs mga_fb_helper_funcs = {
.gamma_set = mga_crtc_fb_gamma_set,
.gamma_get = mga_crtc_fb_gamma_get,
.fb_probe = mgag200fb_create,
@@ -293,9 +294,10 @@ int mgag200_fbdev_init(struct mga_device *mdev)
return -ENOMEM;
mdev->mfbdev = mfbdev;
- mfbdev->helper.funcs = &mga_fb_helper_funcs;
spin_lock_init(&mfbdev->dirty_lock);
+ drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs);
+
ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
mdev->num_crtc, MGAG200FB_CONN_LIMIT);
if (ret)
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index a034ed408252..83485ab81ce8 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -1483,11 +1483,7 @@ static int mga_vga_mode_valid(struct drm_connector *connector,
{
struct drm_device *dev = connector->dev;
struct mga_device *mdev = (struct mga_device*)dev->dev_private;
- struct mga_fbdev *mfbdev = mdev->mfbdev;
- struct drm_fb_helper *fb_helper = &mfbdev->helper;
- struct drm_fb_helper_connector *fb_helper_conn = NULL;
int bpp = 32;
- int i = 0;
if (IS_G200_SE(mdev)) {
if (mdev->unique_rev_id == 0x01) {
@@ -1537,21 +1533,14 @@ static int mga_vga_mode_valid(struct drm_connector *connector,
}
/* Validate the mode input by the user */
- for (i = 0; i < fb_helper->connector_count; i++) {
- if (fb_helper->connector_info[i]->connector == connector) {
- /* Found the helper for this connector */
- fb_helper_conn = fb_helper->connector_info[i];
- if (fb_helper_conn->cmdline_mode.specified) {
- if (fb_helper_conn->cmdline_mode.bpp_specified) {
- bpp = fb_helper_conn->cmdline_mode.bpp;
- }
- }
- }
+ if (connector->cmdline_mode.specified) {
+ if (connector->cmdline_mode.bpp_specified)
+ bpp = connector->cmdline_mode.bpp;
}
if ((mode->hdisplay * mode->vdisplay * (bpp/8)) > mdev->mc.vram_size) {
- if (fb_helper_conn)
- fb_helper_conn->cmdline_mode.specified = false;
+ if (connector->cmdline_mode.specified)
+ connector->cmdline_mode.specified = false;
return MODE_BAD;
}
@@ -1562,19 +1551,9 @@ static struct drm_encoder *mga_connector_best_encoder(struct drm_connector
*connector)
{
int enc_id = connector->encoder_ids[0];
- struct drm_mode_object *obj;
- struct drm_encoder *encoder;
-
/* pick the encoder ids */
- if (enc_id) {
- obj =
- drm_mode_object_find(connector->dev, enc_id,
- DRM_MODE_OBJECT_ENCODER);
- if (!obj)
- return NULL;
- encoder = obj_to_encoder(obj);
- return encoder;
- }
+ if (enc_id)
+ return drm_encoder_find(connector->dev, enc_id);
return NULL;
}
@@ -1621,7 +1600,7 @@ static struct drm_connector *mga_vga_init(struct drm_device *dev)
drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
mga_connector->i2c = mgag200_i2c_create(dev);
if (!mga_connector->i2c)
diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c
index 5a00e90696de..d16964ea0ed4 100644
--- a/drivers/gpu/drm/mgag200/mgag200_ttm.c
+++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c
@@ -293,18 +293,22 @@ void mgag200_mm_fini(struct mga_device *mdev)
void mgag200_ttm_placement(struct mgag200_bo *bo, int domain)
{
u32 c = 0;
- bo->placement.fpfn = 0;
- bo->placement.lpfn = 0;
+ unsigned i;
+
bo->placement.placement = bo->placements;
bo->placement.busy_placement = bo->placements;
if (domain & TTM_PL_FLAG_VRAM)
- bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
+ bo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
if (domain & TTM_PL_FLAG_SYSTEM)
- bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
if (!c)
- bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
bo->placement.num_placement = c;
bo->placement.num_busy_placement = c;
+ for (i = 0; i < c; ++i) {
+ bo->placements[i].fpfn = 0;
+ bo->placements[i].lpfn = 0;
+ }
}
int mgag200_bo_create(struct drm_device *dev, int size, int align,
@@ -335,7 +339,7 @@ int mgag200_bo_create(struct drm_device *dev, int size, int align,
ret = ttm_bo_init(&mdev->ttm.bdev, &mgabo->bo, size,
ttm_bo_type_device, &mgabo->placement,
align >> PAGE_SHIFT, false, NULL, acc_size,
- NULL, mgag200_bo_ttm_destroy);
+ NULL, NULL, mgag200_bo_ttm_destroy);
if (ret)
return ret;
@@ -361,7 +365,7 @@ int mgag200_bo_pin(struct mgag200_bo *bo, u32 pl_flag, u64 *gpu_addr)
mgag200_ttm_placement(bo, pl_flag);
for (i = 0; i < bo->placement.num_placement; i++)
- bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+ bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
if (ret)
return ret;
@@ -384,7 +388,7 @@ int mgag200_bo_unpin(struct mgag200_bo *bo)
return 0;
for (i = 0; i < bo->placement.num_placement ; i++)
- bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
+ bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
if (ret)
return ret;
@@ -408,7 +412,7 @@ int mgag200_bo_push_sysram(struct mgag200_bo *bo)
mgag200_ttm_placement(bo, TTM_PL_FLAG_SYSTEM);
for (i = 0; i < bo->placement.num_placement ; i++)
- bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+ bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false);
if (ret) {
@@ -424,7 +428,7 @@ int mgag200_mmap(struct file *filp, struct vm_area_struct *vma)
struct mga_device *mdev;
if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
- return drm_mmap(filp, vma);
+ return -EINVAL;
file_priv = filp->private_data;
mdev = file_priv->minor->dev->dev_private;
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index f12388967856..9d907c526c94 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -2,9 +2,9 @@
config DRM_MSM
tristate "MSM DRM"
depends on DRM
- depends on MSM_IOMMU
depends on ARCH_QCOM || (ARM && COMPILE_TEST)
select DRM_KMS_HELPER
+ select DRM_PANEL
select SHMEM
select TMPFS
default y
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 93ca49c8df44..6283dcb96af5 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -4,6 +4,7 @@ ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
endif
msm-y := \
+ adreno/adreno_device.o \
adreno/adreno_gpu.o \
adreno/a3xx_gpu.o \
hdmi/hdmi.o \
@@ -18,6 +19,8 @@ msm-y := \
mdp/mdp_kms.o \
mdp/mdp4/mdp4_crtc.o \
mdp/mdp4/mdp4_dtv_encoder.o \
+ mdp/mdp4/mdp4_lcdc_encoder.o \
+ mdp/mdp4/mdp4_lvds_connector.o \
mdp/mdp4/mdp4_irq.o \
mdp/mdp4/mdp4_kms.o \
mdp/mdp4/mdp4_plane.o \
@@ -39,5 +42,6 @@ msm-y := \
msm_ringbuffer.o
msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
+msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o
obj-$(CONFIG_DRM_MSM) += msm.o
diff --git a/drivers/gpu/drm/msm/adreno/a2xx.xml.h b/drivers/gpu/drm/msm/adreno/a2xx.xml.h
index 85d615e7d62f..a3104598c27f 100644
--- a/drivers/gpu/drm/msm/adreno/a2xx.xml.h
+++ b/drivers/gpu/drm/msm/adreno/a2xx.xml.h
@@ -10,13 +10,13 @@ git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2013-11-30 14:47:15)
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32814 bytes, from 2013-11-30 15:07:33)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 8900 bytes, from 2013-10-22 23:57:49)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 10574 bytes, from 2013-11-13 05:44:45)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 53644 bytes, from 2013-11-30 15:07:33)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 8344 bytes, from 2013-11-30 14:49:47)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 9859 bytes, from 2014-06-02 15:21:30)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14960 bytes, from 2014-07-27 17:22:13)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-08-01 12:22:48)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 41068 bytes, from 2014-08-01 12:22:48)
-Copyright (C) 2013 by the following authors:
+Copyright (C) 2013-2014 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
Permission is hereby granted, free of charge, to any person obtaining
@@ -203,6 +203,15 @@ enum a2xx_rb_copy_sample_select {
SAMPLE_0123 = 6,
};
+enum a2xx_rb_blend_opcode {
+ BLEND_DST_PLUS_SRC = 0,
+ BLEND_SRC_MINUS_DST = 1,
+ BLEND_MIN_DST_SRC = 2,
+ BLEND_MAX_DST_SRC = 3,
+ BLEND_DST_MINUS_SRC = 4,
+ BLEND_DST_PLUS_SRC_BIAS = 5,
+};
+
enum adreno_mmu_clnt_beh {
BEH_NEVR = 0,
BEH_TRAN_RNG = 1,
@@ -890,6 +899,39 @@ static inline uint32_t A2XX_SQ_CONTEXT_MISC_PARAM_GEN_POS(uint32_t val)
#define REG_A2XX_VGT_EVENT_INITIATOR 0x000021f9
#define REG_A2XX_VGT_DRAW_INITIATOR 0x000021fc
+#define A2XX_VGT_DRAW_INITIATOR_PRIM_TYPE__MASK 0x0000003f
+#define A2XX_VGT_DRAW_INITIATOR_PRIM_TYPE__SHIFT 0
+static inline uint32_t A2XX_VGT_DRAW_INITIATOR_PRIM_TYPE(enum pc_di_primtype val)
+{
+ return ((val) << A2XX_VGT_DRAW_INITIATOR_PRIM_TYPE__SHIFT) & A2XX_VGT_DRAW_INITIATOR_PRIM_TYPE__MASK;
+}
+#define A2XX_VGT_DRAW_INITIATOR_SOURCE_SELECT__MASK 0x000000c0
+#define A2XX_VGT_DRAW_INITIATOR_SOURCE_SELECT__SHIFT 6
+static inline uint32_t A2XX_VGT_DRAW_INITIATOR_SOURCE_SELECT(enum pc_di_src_sel val)
+{
+ return ((val) << A2XX_VGT_DRAW_INITIATOR_SOURCE_SELECT__SHIFT) & A2XX_VGT_DRAW_INITIATOR_SOURCE_SELECT__MASK;
+}
+#define A2XX_VGT_DRAW_INITIATOR_VIS_CULL__MASK 0x00000600
+#define A2XX_VGT_DRAW_INITIATOR_VIS_CULL__SHIFT 9
+static inline uint32_t A2XX_VGT_DRAW_INITIATOR_VIS_CULL(enum pc_di_vis_cull_mode val)
+{
+ return ((val) << A2XX_VGT_DRAW_INITIATOR_VIS_CULL__SHIFT) & A2XX_VGT_DRAW_INITIATOR_VIS_CULL__MASK;
+}
+#define A2XX_VGT_DRAW_INITIATOR_INDEX_SIZE__MASK 0x00000800
+#define A2XX_VGT_DRAW_INITIATOR_INDEX_SIZE__SHIFT 11
+static inline uint32_t A2XX_VGT_DRAW_INITIATOR_INDEX_SIZE(enum pc_di_index_size val)
+{
+ return ((val) << A2XX_VGT_DRAW_INITIATOR_INDEX_SIZE__SHIFT) & A2XX_VGT_DRAW_INITIATOR_INDEX_SIZE__MASK;
+}
+#define A2XX_VGT_DRAW_INITIATOR_NOT_EOP 0x00001000
+#define A2XX_VGT_DRAW_INITIATOR_SMALL_INDEX 0x00002000
+#define A2XX_VGT_DRAW_INITIATOR_PRE_DRAW_INITIATOR_ENABLE 0x00004000
+#define A2XX_VGT_DRAW_INITIATOR_NUM_INDICES__MASK 0xffff0000
+#define A2XX_VGT_DRAW_INITIATOR_NUM_INDICES__SHIFT 16
+static inline uint32_t A2XX_VGT_DRAW_INITIATOR_NUM_INDICES(uint32_t val)
+{
+ return ((val) << A2XX_VGT_DRAW_INITIATOR_NUM_INDICES__SHIFT) & A2XX_VGT_DRAW_INITIATOR_NUM_INDICES__MASK;
+}
#define REG_A2XX_VGT_IMMED_DATA 0x000021fd
@@ -963,7 +1005,7 @@ static inline uint32_t A2XX_RB_BLEND_CONTROL_COLOR_SRCBLEND(enum adreno_rb_blend
}
#define A2XX_RB_BLEND_CONTROL_COLOR_COMB_FCN__MASK 0x000000e0
#define A2XX_RB_BLEND_CONTROL_COLOR_COMB_FCN__SHIFT 5
-static inline uint32_t A2XX_RB_BLEND_CONTROL_COLOR_COMB_FCN(enum adreno_rb_blend_opcode val)
+static inline uint32_t A2XX_RB_BLEND_CONTROL_COLOR_COMB_FCN(enum a2xx_rb_blend_opcode val)
{
return ((val) << A2XX_RB_BLEND_CONTROL_COLOR_COMB_FCN__SHIFT) & A2XX_RB_BLEND_CONTROL_COLOR_COMB_FCN__MASK;
}
@@ -981,7 +1023,7 @@ static inline uint32_t A2XX_RB_BLEND_CONTROL_ALPHA_SRCBLEND(enum adreno_rb_blend
}
#define A2XX_RB_BLEND_CONTROL_ALPHA_COMB_FCN__MASK 0x00e00000
#define A2XX_RB_BLEND_CONTROL_ALPHA_COMB_FCN__SHIFT 21
-static inline uint32_t A2XX_RB_BLEND_CONTROL_ALPHA_COMB_FCN(enum adreno_rb_blend_opcode val)
+static inline uint32_t A2XX_RB_BLEND_CONTROL_ALPHA_COMB_FCN(enum a2xx_rb_blend_opcode val)
{
return ((val) << A2XX_RB_BLEND_CONTROL_ALPHA_COMB_FCN__SHIFT) & A2XX_RB_BLEND_CONTROL_ALPHA_COMB_FCN__MASK;
}
diff --git a/drivers/gpu/drm/msm/adreno/a3xx.xml.h b/drivers/gpu/drm/msm/adreno/a3xx.xml.h
index a7be56163d23..82d015279b47 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx.xml.h
+++ b/drivers/gpu/drm/msm/adreno/a3xx.xml.h
@@ -10,13 +10,13 @@ git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2013-11-30 14:47:15)
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32814 bytes, from 2013-11-30 15:07:33)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 8900 bytes, from 2013-10-22 23:57:49)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 10574 bytes, from 2013-11-13 05:44:45)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 53644 bytes, from 2013-11-30 15:07:33)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 8344 bytes, from 2013-11-30 14:49:47)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 9859 bytes, from 2014-06-02 15:21:30)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14960 bytes, from 2014-07-27 17:22:13)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-08-01 12:22:48)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 41068 bytes, from 2014-08-01 12:22:48)
-Copyright (C) 2013 by the following authors:
+Copyright (C) 2013-2014 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
Permission is hereby granted, free of charge, to any person obtaining
@@ -41,31 +41,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-enum a3xx_render_mode {
- RB_RENDERING_PASS = 0,
- RB_TILING_PASS = 1,
- RB_RESOLVE_PASS = 2,
-};
-
enum a3xx_tile_mode {
LINEAR = 0,
TILE_32X32 = 2,
};
-enum a3xx_threadmode {
- MULTI = 0,
- SINGLE = 1,
-};
-
-enum a3xx_instrbuffermode {
- BUFFER = 1,
-};
-
-enum a3xx_threadsize {
- TWO_QUADS = 0,
- FOUR_QUADS = 1,
-};
-
enum a3xx_state_block_id {
HLSQ_BLOCK_ID_TP_TEX = 2,
HLSQ_BLOCK_ID_TP_MIPMAP = 3,
@@ -169,6 +149,8 @@ enum a3xx_color_fmt {
RB_R8G8B8A8_UNORM = 8,
RB_Z16_UNORM = 12,
RB_A8_UNORM = 20,
+ RB_R16G16B16A16_FLOAT = 27,
+ RB_R32G32B32A32_FLOAT = 51,
};
enum a3xx_color_swap {
@@ -178,12 +160,6 @@ enum a3xx_color_swap {
XYZW = 3,
};
-enum a3xx_msaa_samples {
- MSAA_ONE = 0,
- MSAA_TWO = 1,
- MSAA_FOUR = 2,
-};
-
enum a3xx_sp_perfcounter_select {
SP_FS_CFLOW_INSTRUCTIONS = 12,
SP_FS_FULL_ALU_INSTRUCTIONS = 14,
@@ -191,21 +167,45 @@ enum a3xx_sp_perfcounter_select {
SP_ALU_ACTIVE_CYCLES = 29,
};
-enum adreno_rb_copy_control_mode {
- RB_COPY_RESOLVE = 1,
- RB_COPY_DEPTH_STENCIL = 5,
+enum a3xx_rop_code {
+ ROP_CLEAR = 0,
+ ROP_NOR = 1,
+ ROP_AND_INVERTED = 2,
+ ROP_COPY_INVERTED = 3,
+ ROP_AND_REVERSE = 4,
+ ROP_INVERT = 5,
+ ROP_XOR = 6,
+ ROP_NAND = 7,
+ ROP_AND = 8,
+ ROP_EQUIV = 9,
+ ROP_NOOP = 10,
+ ROP_OR_INVERTED = 11,
+ ROP_COPY = 12,
+ ROP_OR_REVERSE = 13,
+ ROP_OR = 14,
+ ROP_SET = 15,
+};
+
+enum a3xx_rb_blend_opcode {
+ BLEND_DST_PLUS_SRC = 0,
+ BLEND_SRC_MINUS_DST = 1,
+ BLEND_DST_MINUS_SRC = 2,
+ BLEND_MIN_DST_SRC = 3,
+ BLEND_MAX_DST_SRC = 4,
};
enum a3xx_tex_filter {
A3XX_TEX_NEAREST = 0,
A3XX_TEX_LINEAR = 1,
+ A3XX_TEX_ANISO = 2,
};
enum a3xx_tex_clamp {
A3XX_TEX_REPEAT = 0,
A3XX_TEX_CLAMP_TO_EDGE = 1,
A3XX_TEX_MIRROR_REPEAT = 2,
- A3XX_TEX_CLAMP_NONE = 3,
+ A3XX_TEX_CLAMP_TO_BORDER = 3,
+ A3XX_TEX_MIRROR_CLAMP = 4,
};
enum a3xx_tex_swiz {
@@ -316,6 +316,7 @@ enum a3xx_tex_type {
#define REG_A3XX_RBBM_INT_0_STATUS 0x00000064
#define REG_A3XX_RBBM_PERFCTR_CTL 0x00000080
+#define A3XX_RBBM_PERFCTR_CTL_ENABLE 0x00000001
#define REG_A3XX_RBBM_PERFCTR_LOAD_CMD0 0x00000081
@@ -549,6 +550,10 @@ static inline uint32_t REG_A3XX_CP_PROTECT_REG(uint32_t i0) { return 0x00000460
#define REG_A3XX_CP_AHB_FAULT 0x0000054d
+#define REG_A3XX_SP_GLOBAL_MEM_SIZE 0x00000e22
+
+#define REG_A3XX_SP_GLOBAL_MEM_ADDR 0x00000e23
+
#define REG_A3XX_GRAS_CL_CLIP_CNTL 0x00002040
#define A3XX_GRAS_CL_CLIP_CNTL_IJ_PERSP_CENTER 0x00001000
#define A3XX_GRAS_CL_CLIP_CNTL_CLIP_DISABLE 0x00010000
@@ -556,6 +561,9 @@ static inline uint32_t REG_A3XX_CP_PROTECT_REG(uint32_t i0) { return 0x00000460
#define A3XX_GRAS_CL_CLIP_CNTL_VP_CLIP_CODE_IGNORE 0x00080000
#define A3XX_GRAS_CL_CLIP_CNTL_VP_XFORM_DISABLE 0x00100000
#define A3XX_GRAS_CL_CLIP_CNTL_PERSP_DIVISION_DISABLE 0x00200000
+#define A3XX_GRAS_CL_CLIP_CNTL_ZCOORD 0x00800000
+#define A3XX_GRAS_CL_CLIP_CNTL_WCOORD 0x01000000
+#define A3XX_GRAS_CL_CLIP_CNTL_ZCLIP_DISABLE 0x02000000
#define REG_A3XX_GRAS_CL_GB_CLIP_ADJ 0x00002044
#define A3XX_GRAS_CL_GB_CLIP_ADJ_HORZ__MASK 0x000003ff
@@ -620,15 +628,33 @@ static inline uint32_t A3XX_GRAS_CL_VPORT_ZSCALE(float val)
}
#define REG_A3XX_GRAS_SU_POINT_MINMAX 0x00002068
+#define A3XX_GRAS_SU_POINT_MINMAX_MIN__MASK 0x0000ffff
+#define A3XX_GRAS_SU_POINT_MINMAX_MIN__SHIFT 0
+static inline uint32_t A3XX_GRAS_SU_POINT_MINMAX_MIN(float val)
+{
+ return ((((uint32_t)(val * 8.0))) << A3XX_GRAS_SU_POINT_MINMAX_MIN__SHIFT) & A3XX_GRAS_SU_POINT_MINMAX_MIN__MASK;
+}
+#define A3XX_GRAS_SU_POINT_MINMAX_MAX__MASK 0xffff0000
+#define A3XX_GRAS_SU_POINT_MINMAX_MAX__SHIFT 16
+static inline uint32_t A3XX_GRAS_SU_POINT_MINMAX_MAX(float val)
+{
+ return ((((uint32_t)(val * 8.0))) << A3XX_GRAS_SU_POINT_MINMAX_MAX__SHIFT) & A3XX_GRAS_SU_POINT_MINMAX_MAX__MASK;
+}
#define REG_A3XX_GRAS_SU_POINT_SIZE 0x00002069
+#define A3XX_GRAS_SU_POINT_SIZE__MASK 0xffffffff
+#define A3XX_GRAS_SU_POINT_SIZE__SHIFT 0
+static inline uint32_t A3XX_GRAS_SU_POINT_SIZE(float val)
+{
+ return ((((uint32_t)(val * 8.0))) << A3XX_GRAS_SU_POINT_SIZE__SHIFT) & A3XX_GRAS_SU_POINT_SIZE__MASK;
+}
#define REG_A3XX_GRAS_SU_POLY_OFFSET_SCALE 0x0000206c
#define A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL__MASK 0x00ffffff
#define A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL__SHIFT 0
static inline uint32_t A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL(float val)
{
- return ((((uint32_t)(val * 40.0))) << A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL__SHIFT) & A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL__MASK;
+ return ((((uint32_t)(val * 28.0))) << A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL__SHIFT) & A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL__MASK;
}
#define REG_A3XX_GRAS_SU_POLY_OFFSET_OFFSET 0x0000206d
@@ -636,7 +662,7 @@ static inline uint32_t A3XX_GRAS_SU_POLY_OFFSET_SCALE_VAL(float val)
#define A3XX_GRAS_SU_POLY_OFFSET_OFFSET__SHIFT 0
static inline uint32_t A3XX_GRAS_SU_POLY_OFFSET_OFFSET(float val)
{
- return ((((uint32_t)(val * 44.0))) << A3XX_GRAS_SU_POLY_OFFSET_OFFSET__SHIFT) & A3XX_GRAS_SU_POLY_OFFSET_OFFSET__MASK;
+ return ((((uint32_t)(val * 28.0))) << A3XX_GRAS_SU_POLY_OFFSET_OFFSET__SHIFT) & A3XX_GRAS_SU_POLY_OFFSET_OFFSET__MASK;
}
#define REG_A3XX_GRAS_SU_MODE_CONTROL 0x00002070
@@ -743,6 +769,7 @@ static inline uint32_t A3XX_RB_MODE_CONTROL_RENDER_MODE(enum a3xx_render_mode va
#define A3XX_RB_MODE_CONTROL_PACKER_TIMER_ENABLE 0x00010000
#define REG_A3XX_RB_RENDER_CONTROL 0x000020c1
+#define A3XX_RB_RENDER_CONTROL_FACENESS 0x00000008
#define A3XX_RB_RENDER_CONTROL_BIN_WIDTH__MASK 0x00000ff0
#define A3XX_RB_RENDER_CONTROL_BIN_WIDTH__SHIFT 4
static inline uint32_t A3XX_RB_RENDER_CONTROL_BIN_WIDTH(uint32_t val)
@@ -751,6 +778,10 @@ static inline uint32_t A3XX_RB_RENDER_CONTROL_BIN_WIDTH(uint32_t val)
}
#define A3XX_RB_RENDER_CONTROL_DISABLE_COLOR_PIPE 0x00001000
#define A3XX_RB_RENDER_CONTROL_ENABLE_GMEM 0x00002000
+#define A3XX_RB_RENDER_CONTROL_XCOORD 0x00004000
+#define A3XX_RB_RENDER_CONTROL_YCOORD 0x00008000
+#define A3XX_RB_RENDER_CONTROL_ZCOORD 0x00010000
+#define A3XX_RB_RENDER_CONTROL_WCOORD 0x00020000
#define A3XX_RB_RENDER_CONTROL_ALPHA_TEST 0x00400000
#define A3XX_RB_RENDER_CONTROL_ALPHA_TEST_FUNC__MASK 0x07000000
#define A3XX_RB_RENDER_CONTROL_ALPHA_TEST_FUNC__SHIFT 24
@@ -796,7 +827,7 @@ static inline uint32_t REG_A3XX_RB_MRT_CONTROL(uint32_t i0) { return 0x000020c4
#define A3XX_RB_MRT_CONTROL_BLEND2 0x00000020
#define A3XX_RB_MRT_CONTROL_ROP_CODE__MASK 0x00000f00
#define A3XX_RB_MRT_CONTROL_ROP_CODE__SHIFT 8
-static inline uint32_t A3XX_RB_MRT_CONTROL_ROP_CODE(uint32_t val)
+static inline uint32_t A3XX_RB_MRT_CONTROL_ROP_CODE(enum a3xx_rop_code val)
{
return ((val) << A3XX_RB_MRT_CONTROL_ROP_CODE__SHIFT) & A3XX_RB_MRT_CONTROL_ROP_CODE__MASK;
}
@@ -856,7 +887,7 @@ static inline uint32_t A3XX_RB_MRT_BLEND_CONTROL_RGB_SRC_FACTOR(enum adreno_rb_b
}
#define A3XX_RB_MRT_BLEND_CONTROL_RGB_BLEND_OPCODE__MASK 0x000000e0
#define A3XX_RB_MRT_BLEND_CONTROL_RGB_BLEND_OPCODE__SHIFT 5
-static inline uint32_t A3XX_RB_MRT_BLEND_CONTROL_RGB_BLEND_OPCODE(enum adreno_rb_blend_opcode val)
+static inline uint32_t A3XX_RB_MRT_BLEND_CONTROL_RGB_BLEND_OPCODE(enum a3xx_rb_blend_opcode val)
{
return ((val) << A3XX_RB_MRT_BLEND_CONTROL_RGB_BLEND_OPCODE__SHIFT) & A3XX_RB_MRT_BLEND_CONTROL_RGB_BLEND_OPCODE__MASK;
}
@@ -874,7 +905,7 @@ static inline uint32_t A3XX_RB_MRT_BLEND_CONTROL_ALPHA_SRC_FACTOR(enum adreno_rb
}
#define A3XX_RB_MRT_BLEND_CONTROL_ALPHA_BLEND_OPCODE__MASK 0x00e00000
#define A3XX_RB_MRT_BLEND_CONTROL_ALPHA_BLEND_OPCODE__SHIFT 21
-static inline uint32_t A3XX_RB_MRT_BLEND_CONTROL_ALPHA_BLEND_OPCODE(enum adreno_rb_blend_opcode val)
+static inline uint32_t A3XX_RB_MRT_BLEND_CONTROL_ALPHA_BLEND_OPCODE(enum a3xx_rb_blend_opcode val)
{
return ((val) << A3XX_RB_MRT_BLEND_CONTROL_ALPHA_BLEND_OPCODE__SHIFT) & A3XX_RB_MRT_BLEND_CONTROL_ALPHA_BLEND_OPCODE__MASK;
}
@@ -957,17 +988,24 @@ static inline uint32_t A3XX_RB_COPY_CONTROL_MSAA_RESOLVE(enum a3xx_msaa_samples
{
return ((val) << A3XX_RB_COPY_CONTROL_MSAA_RESOLVE__SHIFT) & A3XX_RB_COPY_CONTROL_MSAA_RESOLVE__MASK;
}
+#define A3XX_RB_COPY_CONTROL_DEPTHCLEAR 0x00000008
#define A3XX_RB_COPY_CONTROL_MODE__MASK 0x00000070
#define A3XX_RB_COPY_CONTROL_MODE__SHIFT 4
static inline uint32_t A3XX_RB_COPY_CONTROL_MODE(enum adreno_rb_copy_control_mode val)
{
return ((val) << A3XX_RB_COPY_CONTROL_MODE__SHIFT) & A3XX_RB_COPY_CONTROL_MODE__MASK;
}
-#define A3XX_RB_COPY_CONTROL_GMEM_BASE__MASK 0xfffffc00
-#define A3XX_RB_COPY_CONTROL_GMEM_BASE__SHIFT 10
+#define A3XX_RB_COPY_CONTROL_FASTCLEAR__MASK 0x00000f00
+#define A3XX_RB_COPY_CONTROL_FASTCLEAR__SHIFT 8
+static inline uint32_t A3XX_RB_COPY_CONTROL_FASTCLEAR(uint32_t val)
+{
+ return ((val) << A3XX_RB_COPY_CONTROL_FASTCLEAR__SHIFT) & A3XX_RB_COPY_CONTROL_FASTCLEAR__MASK;
+}
+#define A3XX_RB_COPY_CONTROL_GMEM_BASE__MASK 0xffffc000
+#define A3XX_RB_COPY_CONTROL_GMEM_BASE__SHIFT 14
static inline uint32_t A3XX_RB_COPY_CONTROL_GMEM_BASE(uint32_t val)
{
- return ((val >> 10) << A3XX_RB_COPY_CONTROL_GMEM_BASE__SHIFT) & A3XX_RB_COPY_CONTROL_GMEM_BASE__MASK;
+ return ((val >> 14) << A3XX_RB_COPY_CONTROL_GMEM_BASE__SHIFT) & A3XX_RB_COPY_CONTROL_GMEM_BASE__MASK;
}
#define REG_A3XX_RB_COPY_DEST_BASE 0x000020ed
@@ -1005,6 +1043,12 @@ static inline uint32_t A3XX_RB_COPY_DEST_INFO_SWAP(enum a3xx_color_swap val)
{
return ((val) << A3XX_RB_COPY_DEST_INFO_SWAP__SHIFT) & A3XX_RB_COPY_DEST_INFO_SWAP__MASK;
}
+#define A3XX_RB_COPY_DEST_INFO_DITHER_MODE__MASK 0x00000c00
+#define A3XX_RB_COPY_DEST_INFO_DITHER_MODE__SHIFT 10
+static inline uint32_t A3XX_RB_COPY_DEST_INFO_DITHER_MODE(enum adreno_rb_dither_mode val)
+{
+ return ((val) << A3XX_RB_COPY_DEST_INFO_DITHER_MODE__SHIFT) & A3XX_RB_COPY_DEST_INFO_DITHER_MODE__MASK;
+}
#define A3XX_RB_COPY_DEST_INFO_COMPONENT_ENABLE__MASK 0x0003c000
#define A3XX_RB_COPY_DEST_INFO_COMPONENT_ENABLE__SHIFT 14
static inline uint32_t A3XX_RB_COPY_DEST_INFO_COMPONENT_ENABLE(uint32_t val)
@@ -1019,6 +1063,7 @@ static inline uint32_t A3XX_RB_COPY_DEST_INFO_ENDIAN(enum adreno_rb_surface_endi
}
#define REG_A3XX_RB_DEPTH_CONTROL 0x00002100
+#define A3XX_RB_DEPTH_CONTROL_FRAG_WRITES_Z 0x00000001
#define A3XX_RB_DEPTH_CONTROL_Z_ENABLE 0x00000002
#define A3XX_RB_DEPTH_CONTROL_Z_WRITE_ENABLE 0x00000004
#define A3XX_RB_DEPTH_CONTROL_EARLY_Z_DISABLE 0x00000008
@@ -1044,7 +1089,7 @@ static inline uint32_t A3XX_RB_DEPTH_INFO_DEPTH_FORMAT(enum adreno_rb_depth_form
#define A3XX_RB_DEPTH_INFO_DEPTH_BASE__SHIFT 11
static inline uint32_t A3XX_RB_DEPTH_INFO_DEPTH_BASE(uint32_t val)
{
- return ((val >> 10) << A3XX_RB_DEPTH_INFO_DEPTH_BASE__SHIFT) & A3XX_RB_DEPTH_INFO_DEPTH_BASE__MASK;
+ return ((val >> 12) << A3XX_RB_DEPTH_INFO_DEPTH_BASE__SHIFT) & A3XX_RB_DEPTH_INFO_DEPTH_BASE__MASK;
}
#define REG_A3XX_RB_DEPTH_PITCH 0x00002103
@@ -1172,6 +1217,8 @@ static inline uint32_t A3XX_RB_WINDOW_OFFSET_Y(uint32_t val)
}
#define REG_A3XX_RB_SAMPLE_COUNT_CONTROL 0x00002110
+#define A3XX_RB_SAMPLE_COUNT_CONTROL_RESET 0x00000001
+#define A3XX_RB_SAMPLE_COUNT_CONTROL_COPY 0x00000002
#define REG_A3XX_RB_SAMPLE_COUNT_ADDR 0x00002111
@@ -1179,7 +1226,23 @@ static inline uint32_t A3XX_RB_WINDOW_OFFSET_Y(uint32_t val)
#define REG_A3XX_RB_Z_CLAMP_MAX 0x00002115
+#define REG_A3XX_VGT_BIN_BASE 0x000021e1
+
+#define REG_A3XX_VGT_BIN_SIZE 0x000021e2
+
#define REG_A3XX_PC_VSTREAM_CONTROL 0x000021e4
+#define A3XX_PC_VSTREAM_CONTROL_SIZE__MASK 0x003f0000
+#define A3XX_PC_VSTREAM_CONTROL_SIZE__SHIFT 16
+static inline uint32_t A3XX_PC_VSTREAM_CONTROL_SIZE(uint32_t val)
+{
+ return ((val) << A3XX_PC_VSTREAM_CONTROL_SIZE__SHIFT) & A3XX_PC_VSTREAM_CONTROL_SIZE__MASK;
+}
+#define A3XX_PC_VSTREAM_CONTROL_N__MASK 0x07c00000
+#define A3XX_PC_VSTREAM_CONTROL_N__SHIFT 22
+static inline uint32_t A3XX_PC_VSTREAM_CONTROL_N(uint32_t val)
+{
+ return ((val) << A3XX_PC_VSTREAM_CONTROL_N__SHIFT) & A3XX_PC_VSTREAM_CONTROL_N__MASK;
+}
#define REG_A3XX_PC_VERTEX_REUSE_BLOCK_CNTL 0x000021ea
@@ -1203,6 +1266,7 @@ static inline uint32_t A3XX_PC_PRIM_VTX_CNTL_POLYMODE_BACK_PTYPE(enum adreno_pa_
return ((val) << A3XX_PC_PRIM_VTX_CNTL_POLYMODE_BACK_PTYPE__SHIFT) & A3XX_PC_PRIM_VTX_CNTL_POLYMODE_BACK_PTYPE__MASK;
}
#define A3XX_PC_PRIM_VTX_CNTL_PROVOKING_VTX_LAST 0x02000000
+#define A3XX_PC_PRIM_VTX_CNTL_PSIZE 0x04000000
#define REG_A3XX_PC_RESTART_INDEX 0x000021ed
@@ -1232,6 +1296,7 @@ static inline uint32_t A3XX_HLSQ_CONTROL_1_REG_VSTHREADSIZE(enum a3xx_threadsize
}
#define A3XX_HLSQ_CONTROL_1_REG_VSSUPERTHREADENABLE 0x00000100
#define A3XX_HLSQ_CONTROL_1_REG_RESERVED1 0x00000200
+#define A3XX_HLSQ_CONTROL_1_REG_ZWCOORD 0x02000000
#define REG_A3XX_HLSQ_CONTROL_2_REG 0x00002202
#define A3XX_HLSQ_CONTROL_2_REG_PRIMALLOCTHRESHOLD__MASK 0xfc000000
@@ -1242,6 +1307,12 @@ static inline uint32_t A3XX_HLSQ_CONTROL_2_REG_PRIMALLOCTHRESHOLD(uint32_t val)
}
#define REG_A3XX_HLSQ_CONTROL_3_REG 0x00002203
+#define A3XX_HLSQ_CONTROL_3_REG_REGID__MASK 0x000000ff
+#define A3XX_HLSQ_CONTROL_3_REG_REGID__SHIFT 0
+static inline uint32_t A3XX_HLSQ_CONTROL_3_REG_REGID(uint32_t val)
+{
+ return ((val) << A3XX_HLSQ_CONTROL_3_REG_REGID__SHIFT) & A3XX_HLSQ_CONTROL_3_REG_REGID__MASK;
+}
#define REG_A3XX_HLSQ_VS_CONTROL_REG 0x00002204
#define A3XX_HLSQ_VS_CONTROL_REG_CONSTLENGTH__MASK 0x00000fff
@@ -1312,10 +1383,36 @@ static inline uint32_t A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_ENDENTRY(uint32_t val)
}
#define REG_A3XX_HLSQ_CL_NDRANGE_0_REG 0x0000220a
+#define A3XX_HLSQ_CL_NDRANGE_0_REG_WORKDIM__MASK 0x00000003
+#define A3XX_HLSQ_CL_NDRANGE_0_REG_WORKDIM__SHIFT 0
+static inline uint32_t A3XX_HLSQ_CL_NDRANGE_0_REG_WORKDIM(uint32_t val)
+{
+ return ((val) << A3XX_HLSQ_CL_NDRANGE_0_REG_WORKDIM__SHIFT) & A3XX_HLSQ_CL_NDRANGE_0_REG_WORKDIM__MASK;
+}
+#define A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE0__MASK 0x00000ffc
+#define A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE0__SHIFT 2
+static inline uint32_t A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE0(uint32_t val)
+{
+ return ((val) << A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE0__SHIFT) & A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE0__MASK;
+}
+#define A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE1__MASK 0x003ff000
+#define A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE1__SHIFT 12
+static inline uint32_t A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE1(uint32_t val)
+{
+ return ((val) << A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE1__SHIFT) & A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE1__MASK;
+}
+#define A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE2__MASK 0xffc00000
+#define A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE2__SHIFT 22
+static inline uint32_t A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE2(uint32_t val)
+{
+ return ((val) << A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE2__SHIFT) & A3XX_HLSQ_CL_NDRANGE_0_REG_LOCALSIZE2__MASK;
+}
+
+static inline uint32_t REG_A3XX_HLSQ_CL_GLOBAL_WORK(uint32_t i0) { return 0x0000220b + 0x2*i0; }
-#define REG_A3XX_HLSQ_CL_NDRANGE_1_REG 0x0000220b
+static inline uint32_t REG_A3XX_HLSQ_CL_GLOBAL_WORK_SIZE(uint32_t i0) { return 0x0000220b + 0x2*i0; }
-#define REG_A3XX_HLSQ_CL_NDRANGE_2_REG 0x0000220c
+static inline uint32_t REG_A3XX_HLSQ_CL_GLOBAL_WORK_OFFSET(uint32_t i0) { return 0x0000220c + 0x2*i0; }
#define REG_A3XX_HLSQ_CL_CONTROL_0_REG 0x00002211
@@ -1323,7 +1420,9 @@ static inline uint32_t A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_ENDENTRY(uint32_t val)
#define REG_A3XX_HLSQ_CL_KERNEL_CONST_REG 0x00002214
-#define REG_A3XX_HLSQ_CL_KERNEL_GROUP_X_REG 0x00002215
+static inline uint32_t REG_A3XX_HLSQ_CL_KERNEL_GROUP(uint32_t i0) { return 0x00002215 + 0x1*i0; }
+
+static inline uint32_t REG_A3XX_HLSQ_CL_KERNEL_GROUP_RATIO(uint32_t i0) { return 0x00002215 + 0x1*i0; }
#define REG_A3XX_HLSQ_CL_KERNEL_GROUP_Y_REG 0x00002216
@@ -1438,6 +1537,12 @@ static inline uint32_t A3XX_VFD_DECODE_INSTR_REGID(uint32_t val)
{
return ((val) << A3XX_VFD_DECODE_INSTR_REGID__SHIFT) & A3XX_VFD_DECODE_INSTR_REGID__MASK;
}
+#define A3XX_VFD_DECODE_INSTR_SWAP__MASK 0x00c00000
+#define A3XX_VFD_DECODE_INSTR_SWAP__SHIFT 22
+static inline uint32_t A3XX_VFD_DECODE_INSTR_SWAP(enum a3xx_color_swap val)
+{
+ return ((val) << A3XX_VFD_DECODE_INSTR_SWAP__SHIFT) & A3XX_VFD_DECODE_INSTR_SWAP__MASK;
+}
#define A3XX_VFD_DECODE_INSTR_SHIFTCNT__MASK 0x1f000000
#define A3XX_VFD_DECODE_INSTR_SHIFTCNT__SHIFT 24
static inline uint32_t A3XX_VFD_DECODE_INSTR_SHIFTCNT(uint32_t val)
@@ -1462,12 +1567,13 @@ static inline uint32_t A3XX_VFD_VS_THREADING_THRESHOLD_REGID_VTXCNT(uint32_t val
}
#define REG_A3XX_VPC_ATTR 0x00002280
-#define A3XX_VPC_ATTR_TOTALATTR__MASK 0x00000fff
+#define A3XX_VPC_ATTR_TOTALATTR__MASK 0x000001ff
#define A3XX_VPC_ATTR_TOTALATTR__SHIFT 0
static inline uint32_t A3XX_VPC_ATTR_TOTALATTR(uint32_t val)
{
return ((val) << A3XX_VPC_ATTR_TOTALATTR__SHIFT) & A3XX_VPC_ATTR_TOTALATTR__MASK;
}
+#define A3XX_VPC_ATTR_PSIZE 0x00000200
#define A3XX_VPC_ATTR_THRDASSIGN__MASK 0x0ffff000
#define A3XX_VPC_ATTR_THRDASSIGN__SHIFT 12
static inline uint32_t A3XX_VPC_ATTR_THRDASSIGN(uint32_t val)
@@ -1522,11 +1628,11 @@ static inline uint32_t A3XX_SP_SP_CTRL_REG_SLEEPMODE(uint32_t val)
{
return ((val) << A3XX_SP_SP_CTRL_REG_SLEEPMODE__SHIFT) & A3XX_SP_SP_CTRL_REG_SLEEPMODE__MASK;
}
-#define A3XX_SP_SP_CTRL_REG_LOMODE__MASK 0x00c00000
-#define A3XX_SP_SP_CTRL_REG_LOMODE__SHIFT 22
-static inline uint32_t A3XX_SP_SP_CTRL_REG_LOMODE(uint32_t val)
+#define A3XX_SP_SP_CTRL_REG_L0MODE__MASK 0x00c00000
+#define A3XX_SP_SP_CTRL_REG_L0MODE__SHIFT 22
+static inline uint32_t A3XX_SP_SP_CTRL_REG_L0MODE(uint32_t val)
{
- return ((val) << A3XX_SP_SP_CTRL_REG_LOMODE__SHIFT) & A3XX_SP_SP_CTRL_REG_LOMODE__MASK;
+ return ((val) << A3XX_SP_SP_CTRL_REG_L0MODE__SHIFT) & A3XX_SP_SP_CTRL_REG_L0MODE__MASK;
}
#define REG_A3XX_SP_VS_CTRL_REG0 0x000022c4
@@ -1569,6 +1675,7 @@ static inline uint32_t A3XX_SP_VS_CTRL_REG0_THREADSIZE(enum a3xx_threadsize val)
}
#define A3XX_SP_VS_CTRL_REG0_SUPERTHREADMODE 0x00200000
#define A3XX_SP_VS_CTRL_REG0_PIXLODENABLE 0x00400000
+#define A3XX_SP_VS_CTRL_REG0_COMPUTEMODE 0x00800000
#define A3XX_SP_VS_CTRL_REG0_LENGTH__MASK 0xff000000
#define A3XX_SP_VS_CTRL_REG0_LENGTH__SHIFT 24
static inline uint32_t A3XX_SP_VS_CTRL_REG0_LENGTH(uint32_t val)
@@ -1589,7 +1696,7 @@ static inline uint32_t A3XX_SP_VS_CTRL_REG1_CONSTFOOTPRINT(uint32_t val)
{
return ((val) << A3XX_SP_VS_CTRL_REG1_CONSTFOOTPRINT__SHIFT) & A3XX_SP_VS_CTRL_REG1_CONSTFOOTPRINT__MASK;
}
-#define A3XX_SP_VS_CTRL_REG1_INITIALOUTSTANDING__MASK 0x3f000000
+#define A3XX_SP_VS_CTRL_REG1_INITIALOUTSTANDING__MASK 0x7f000000
#define A3XX_SP_VS_CTRL_REG1_INITIALOUTSTANDING__SHIFT 24
static inline uint32_t A3XX_SP_VS_CTRL_REG1_INITIALOUTSTANDING(uint32_t val)
{
@@ -1742,6 +1849,7 @@ static inline uint32_t A3XX_SP_FS_CTRL_REG0_THREADSIZE(enum a3xx_threadsize val)
}
#define A3XX_SP_FS_CTRL_REG0_SUPERTHREADMODE 0x00200000
#define A3XX_SP_FS_CTRL_REG0_PIXLODENABLE 0x00400000
+#define A3XX_SP_FS_CTRL_REG0_COMPUTEMODE 0x00800000
#define A3XX_SP_FS_CTRL_REG0_LENGTH__MASK 0xff000000
#define A3XX_SP_FS_CTRL_REG0_LENGTH__SHIFT 24
static inline uint32_t A3XX_SP_FS_CTRL_REG0_LENGTH(uint32_t val)
@@ -1802,6 +1910,13 @@ static inline uint32_t A3XX_SP_FS_OBJ_OFFSET_REG_SHADEROBJOFFSET(uint32_t val)
#define REG_A3XX_SP_FS_FLAT_SHAD_MODE_REG_1 0x000022e9
#define REG_A3XX_SP_FS_OUTPUT_REG 0x000022ec
+#define A3XX_SP_FS_OUTPUT_REG_DEPTH_ENABLE 0x00000080
+#define A3XX_SP_FS_OUTPUT_REG_DEPTH_REGID__MASK 0x0000ff00
+#define A3XX_SP_FS_OUTPUT_REG_DEPTH_REGID__SHIFT 8
+static inline uint32_t A3XX_SP_FS_OUTPUT_REG_DEPTH_REGID(uint32_t val)
+{
+ return ((val) << A3XX_SP_FS_OUTPUT_REG_DEPTH_REGID__SHIFT) & A3XX_SP_FS_OUTPUT_REG_DEPTH_REGID__MASK;
+}
static inline uint32_t REG_A3XX_SP_FS_MRT(uint32_t i0) { return 0x000022f0 + 0x1*i0; }
@@ -1914,6 +2029,42 @@ static inline uint32_t A3XX_TPL1_TP_FS_TEX_OFFSET_BASETABLEPTR(uint32_t val)
#define REG_A3XX_VBIF_OUT_AXI_AOOO 0x0000305f
+#define REG_A3XX_VBIF_PERF_CNT_EN 0x00003070
+#define A3XX_VBIF_PERF_CNT_EN_CNT0 0x00000001
+#define A3XX_VBIF_PERF_CNT_EN_CNT1 0x00000002
+#define A3XX_VBIF_PERF_CNT_EN_PWRCNT0 0x00000004
+#define A3XX_VBIF_PERF_CNT_EN_PWRCNT1 0x00000008
+#define A3XX_VBIF_PERF_CNT_EN_PWRCNT2 0x00000010
+
+#define REG_A3XX_VBIF_PERF_CNT_CLR 0x00003071
+#define A3XX_VBIF_PERF_CNT_CLR_CNT0 0x00000001
+#define A3XX_VBIF_PERF_CNT_CLR_CNT1 0x00000002
+#define A3XX_VBIF_PERF_CNT_CLR_PWRCNT0 0x00000004
+#define A3XX_VBIF_PERF_CNT_CLR_PWRCNT1 0x00000008
+#define A3XX_VBIF_PERF_CNT_CLR_PWRCNT2 0x00000010
+
+#define REG_A3XX_VBIF_PERF_CNT_SEL 0x00003072
+
+#define REG_A3XX_VBIF_PERF_CNT0_LO 0x00003073
+
+#define REG_A3XX_VBIF_PERF_CNT0_HI 0x00003074
+
+#define REG_A3XX_VBIF_PERF_CNT1_LO 0x00003075
+
+#define REG_A3XX_VBIF_PERF_CNT1_HI 0x00003076
+
+#define REG_A3XX_VBIF_PERF_PWR_CNT0_LO 0x00003077
+
+#define REG_A3XX_VBIF_PERF_PWR_CNT0_HI 0x00003078
+
+#define REG_A3XX_VBIF_PERF_PWR_CNT1_LO 0x00003079
+
+#define REG_A3XX_VBIF_PERF_PWR_CNT1_HI 0x0000307a
+
+#define REG_A3XX_VBIF_PERF_PWR_CNT2_LO 0x0000307b
+
+#define REG_A3XX_VBIF_PERF_PWR_CNT2_HI 0x0000307c
+
#define REG_A3XX_VSC_BIN_SIZE 0x00000c01
#define A3XX_VSC_BIN_SIZE_WIDTH__MASK 0x0000001f
#define A3XX_VSC_BIN_SIZE_WIDTH__SHIFT 0
@@ -2080,6 +2231,8 @@ static inline uint32_t A3XX_UCHE_CACHE_INVALIDATE1_REG_OPCODE(enum a3xx_cache_op
}
#define A3XX_UCHE_CACHE_INVALIDATE1_REG_ENTIRE_CACHE 0x80000000
+#define REG_A3XX_UNKNOWN_0EA6 0x00000ea6
+
#define REG_A3XX_SP_PERFCOUNTER0_SELECT 0x00000ec4
#define REG_A3XX_SP_PERFCOUNTER1_SELECT 0x00000ec5
@@ -2117,6 +2270,39 @@ static inline uint32_t A3XX_UCHE_CACHE_INVALIDATE1_REG_OPCODE(enum a3xx_cache_op
#define REG_A3XX_VGT_EVENT_INITIATOR 0x000021f9
#define REG_A3XX_VGT_DRAW_INITIATOR 0x000021fc
+#define A3XX_VGT_DRAW_INITIATOR_PRIM_TYPE__MASK 0x0000003f
+#define A3XX_VGT_DRAW_INITIATOR_PRIM_TYPE__SHIFT 0
+static inline uint32_t A3XX_VGT_DRAW_INITIATOR_PRIM_TYPE(enum pc_di_primtype val)
+{
+ return ((val) << A3XX_VGT_DRAW_INITIATOR_PRIM_TYPE__SHIFT) & A3XX_VGT_DRAW_INITIATOR_PRIM_TYPE__MASK;
+}
+#define A3XX_VGT_DRAW_INITIATOR_SOURCE_SELECT__MASK 0x000000c0
+#define A3XX_VGT_DRAW_INITIATOR_SOURCE_SELECT__SHIFT 6
+static inline uint32_t A3XX_VGT_DRAW_INITIATOR_SOURCE_SELECT(enum pc_di_src_sel val)
+{
+ return ((val) << A3XX_VGT_DRAW_INITIATOR_SOURCE_SELECT__SHIFT) & A3XX_VGT_DRAW_INITIATOR_SOURCE_SELECT__MASK;
+}
+#define A3XX_VGT_DRAW_INITIATOR_VIS_CULL__MASK 0x00000600
+#define A3XX_VGT_DRAW_INITIATOR_VIS_CULL__SHIFT 9
+static inline uint32_t A3XX_VGT_DRAW_INITIATOR_VIS_CULL(enum pc_di_vis_cull_mode val)
+{
+ return ((val) << A3XX_VGT_DRAW_INITIATOR_VIS_CULL__SHIFT) & A3XX_VGT_DRAW_INITIATOR_VIS_CULL__MASK;
+}
+#define A3XX_VGT_DRAW_INITIATOR_INDEX_SIZE__MASK 0x00000800
+#define A3XX_VGT_DRAW_INITIATOR_INDEX_SIZE__SHIFT 11
+static inline uint32_t A3XX_VGT_DRAW_INITIATOR_INDEX_SIZE(enum pc_di_index_size val)
+{
+ return ((val) << A3XX_VGT_DRAW_INITIATOR_INDEX_SIZE__SHIFT) & A3XX_VGT_DRAW_INITIATOR_INDEX_SIZE__MASK;
+}
+#define A3XX_VGT_DRAW_INITIATOR_NOT_EOP 0x00001000
+#define A3XX_VGT_DRAW_INITIATOR_SMALL_INDEX 0x00002000
+#define A3XX_VGT_DRAW_INITIATOR_PRE_DRAW_INITIATOR_ENABLE 0x00004000
+#define A3XX_VGT_DRAW_INITIATOR_NUM_INDICES__MASK 0xffff0000
+#define A3XX_VGT_DRAW_INITIATOR_NUM_INDICES__SHIFT 16
+static inline uint32_t A3XX_VGT_DRAW_INITIATOR_NUM_INDICES(uint32_t val)
+{
+ return ((val) << A3XX_VGT_DRAW_INITIATOR_NUM_INDICES__SHIFT) & A3XX_VGT_DRAW_INITIATOR_NUM_INDICES__MASK;
+}
#define REG_A3XX_VGT_IMMED_DATA 0x000021fd
@@ -2152,6 +2338,12 @@ static inline uint32_t A3XX_TEX_SAMP_0_WRAP_R(enum a3xx_tex_clamp val)
{
return ((val) << A3XX_TEX_SAMP_0_WRAP_R__SHIFT) & A3XX_TEX_SAMP_0_WRAP_R__MASK;
}
+#define A3XX_TEX_SAMP_0_COMPARE_FUNC__MASK 0x00700000
+#define A3XX_TEX_SAMP_0_COMPARE_FUNC__SHIFT 20
+static inline uint32_t A3XX_TEX_SAMP_0_COMPARE_FUNC(enum adreno_compare_func val)
+{
+ return ((val) << A3XX_TEX_SAMP_0_COMPARE_FUNC__SHIFT) & A3XX_TEX_SAMP_0_COMPARE_FUNC__MASK;
+}
#define A3XX_TEX_SAMP_0_UNNORM_COORDS 0x80000000
#define REG_A3XX_TEX_SAMP_1 0x00000001
@@ -2170,6 +2362,7 @@ static inline uint32_t A3XX_TEX_SAMP_1_MIN_LOD(float val)
#define REG_A3XX_TEX_CONST_0 0x00000000
#define A3XX_TEX_CONST_0_TILED 0x00000001
+#define A3XX_TEX_CONST_0_SRGB 0x00000004
#define A3XX_TEX_CONST_0_SWIZ_X__MASK 0x00000070
#define A3XX_TEX_CONST_0_SWIZ_X__SHIFT 4
static inline uint32_t A3XX_TEX_CONST_0_SWIZ_X(enum a3xx_tex_swiz val)
@@ -2206,6 +2399,7 @@ static inline uint32_t A3XX_TEX_CONST_0_FMT(enum a3xx_tex_fmt val)
{
return ((val) << A3XX_TEX_CONST_0_FMT__SHIFT) & A3XX_TEX_CONST_0_FMT__MASK;
}
+#define A3XX_TEX_CONST_0_NOCONVERT 0x20000000
#define A3XX_TEX_CONST_0_TYPE__MASK 0xc0000000
#define A3XX_TEX_CONST_0_TYPE__SHIFT 30
static inline uint32_t A3XX_TEX_CONST_0_TYPE(enum a3xx_tex_type val)
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
index 942e09d898a8..218c5b060398 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
@@ -35,10 +35,8 @@
A3XX_INT0_CP_AHB_ERROR_HALT | \
A3XX_INT0_UCHE_OOB_ACCESS)
+extern bool hang_debug;
-static bool hang_debug = false;
-MODULE_PARM_DESC(hang_debug, "Dump registers when hang is detected (can be slow!)");
-module_param_named(hang_debug, hang_debug, bool, 0600);
static void a3xx_dump(struct msm_gpu *gpu);
static void a3xx_me_init(struct msm_gpu *gpu)
@@ -387,63 +385,26 @@ static const unsigned int a3xx_registers[] = {
0x2750, 0x2756, 0x2760, 0x2760, 0x300c, 0x300e, 0x301c, 0x301d,
0x302a, 0x302a, 0x302c, 0x302d, 0x3030, 0x3031, 0x3034, 0x3036,
0x303c, 0x303c, 0x305e, 0x305f,
+ ~0 /* sentinel */
};
#ifdef CONFIG_DEBUG_FS
static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m)
{
- struct drm_device *dev = gpu->dev;
- int i;
-
- adreno_show(gpu, m);
-
- mutex_lock(&dev->struct_mutex);
-
gpu->funcs->pm_resume(gpu);
-
seq_printf(m, "status: %08x\n",
gpu_read(gpu, REG_A3XX_RBBM_STATUS));
-
- /* dump these out in a form that can be parsed by demsm: */
- seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name);
- for (i = 0; i < ARRAY_SIZE(a3xx_registers); i += 2) {
- uint32_t start = a3xx_registers[i];
- uint32_t end = a3xx_registers[i+1];
- uint32_t addr;
-
- for (addr = start; addr <= end; addr++) {
- uint32_t val = gpu_read(gpu, addr);
- seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);
- }
- }
-
gpu->funcs->pm_suspend(gpu);
-
- mutex_unlock(&dev->struct_mutex);
+ adreno_show(gpu, m);
}
#endif
/* would be nice to not have to duplicate the _show() stuff with printk(): */
static void a3xx_dump(struct msm_gpu *gpu)
{
- int i;
-
- adreno_dump(gpu);
printk("status: %08x\n",
gpu_read(gpu, REG_A3XX_RBBM_STATUS));
-
- /* dump these out in a form that can be parsed by demsm: */
- printk("IO:region %s 00000000 00020000\n", gpu->name);
- for (i = 0; i < ARRAY_SIZE(a3xx_registers); i += 2) {
- uint32_t start = a3xx_registers[i];
- uint32_t end = a3xx_registers[i+1];
- uint32_t addr;
-
- for (addr = start; addr <= end; addr++) {
- uint32_t val = gpu_read(gpu, addr);
- printk("IO:R %08x %08x\n", addr<<2, val);
- }
- }
+ adreno_dump(gpu);
}
static const struct adreno_gpu_funcs funcs = {
@@ -479,7 +440,6 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
struct msm_gpu *gpu;
struct msm_drm_private *priv = dev->dev_private;
struct platform_device *pdev = priv->gpu_pdev;
- struct adreno_platform_config *config;
int ret;
if (!pdev) {
@@ -488,8 +448,6 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
goto fail;
}
- config = pdev->dev.platform_data;
-
a3xx_gpu = kzalloc(sizeof(*a3xx_gpu), GFP_KERNEL);
if (!a3xx_gpu) {
ret = -ENOMEM;
@@ -501,20 +459,12 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
a3xx_gpu->pdev = pdev;
- gpu->fast_rate = config->fast_rate;
- gpu->slow_rate = config->slow_rate;
- gpu->bus_freq = config->bus_freq;
-#ifdef CONFIG_MSM_BUS_SCALING
- gpu->bus_scale_table = config->bus_scale_table;
-#endif
-
- DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u",
- gpu->fast_rate, gpu->slow_rate, gpu->bus_freq);
-
gpu->perfcntrs = perfcntrs;
gpu->num_perfcntrs = ARRAY_SIZE(perfcntrs);
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, config->rev);
+ adreno_gpu->registers = a3xx_registers;
+
+ ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs);
if (ret)
goto fail;
@@ -554,156 +504,3 @@ fail:
return ERR_PTR(ret);
}
-
-/*
- * The a3xx device:
- */
-
-#if defined(CONFIG_MSM_BUS_SCALING) && !defined(CONFIG_OF)
-# include <mach/kgsl.h>
-#endif
-
-static void set_gpu_pdev(struct drm_device *dev,
- struct platform_device *pdev)
-{
- struct msm_drm_private *priv = dev->dev_private;
- priv->gpu_pdev = pdev;
-}
-
-static int a3xx_bind(struct device *dev, struct device *master, void *data)
-{
- static struct adreno_platform_config config = {};
-#ifdef CONFIG_OF
- struct device_node *child, *node = dev->of_node;
- u32 val;
- int ret;
-
- ret = of_property_read_u32(node, "qcom,chipid", &val);
- if (ret) {
- dev_err(dev, "could not find chipid: %d\n", ret);
- return ret;
- }
-
- config.rev = ADRENO_REV((val >> 24) & 0xff,
- (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff);
-
- /* find clock rates: */
- config.fast_rate = 0;
- config.slow_rate = ~0;
- for_each_child_of_node(node, child) {
- if (of_device_is_compatible(child, "qcom,gpu-pwrlevels")) {
- struct device_node *pwrlvl;
- for_each_child_of_node(child, pwrlvl) {
- ret = of_property_read_u32(pwrlvl, "qcom,gpu-freq", &val);
- if (ret) {
- dev_err(dev, "could not find gpu-freq: %d\n", ret);
- return ret;
- }
- config.fast_rate = max(config.fast_rate, val);
- config.slow_rate = min(config.slow_rate, val);
- }
- }
- }
-
- if (!config.fast_rate) {
- dev_err(dev, "could not find clk rates\n");
- return -ENXIO;
- }
-
-#else
- struct kgsl_device_platform_data *pdata = dev->platform_data;
- uint32_t version = socinfo_get_version();
- if (cpu_is_apq8064ab()) {
- config.fast_rate = 450000000;
- config.slow_rate = 27000000;
- config.bus_freq = 4;
- config.rev = ADRENO_REV(3, 2, 1, 0);
- } else if (cpu_is_apq8064()) {
- config.fast_rate = 400000000;
- config.slow_rate = 27000000;
- config.bus_freq = 4;
-
- if (SOCINFO_VERSION_MAJOR(version) == 2)
- config.rev = ADRENO_REV(3, 2, 0, 2);
- else if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
- (SOCINFO_VERSION_MINOR(version) == 1))
- config.rev = ADRENO_REV(3, 2, 0, 1);
- else
- config.rev = ADRENO_REV(3, 2, 0, 0);
-
- } else if (cpu_is_msm8960ab()) {
- config.fast_rate = 400000000;
- config.slow_rate = 320000000;
- config.bus_freq = 4;
-
- if (SOCINFO_VERSION_MINOR(version) == 0)
- config.rev = ADRENO_REV(3, 2, 1, 0);
- else
- config.rev = ADRENO_REV(3, 2, 1, 1);
-
- } else if (cpu_is_msm8930()) {
- config.fast_rate = 400000000;
- config.slow_rate = 27000000;
- config.bus_freq = 3;
-
- if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
- (SOCINFO_VERSION_MINOR(version) == 2))
- config.rev = ADRENO_REV(3, 0, 5, 2);
- else
- config.rev = ADRENO_REV(3, 0, 5, 0);
-
- }
-# ifdef CONFIG_MSM_BUS_SCALING
- config.bus_scale_table = pdata->bus_scale_table;
-# endif
-#endif
- dev->platform_data = &config;
- set_gpu_pdev(dev_get_drvdata(master), to_platform_device(dev));
- return 0;
-}
-
-static void a3xx_unbind(struct device *dev, struct device *master,
- void *data)
-{
- set_gpu_pdev(dev_get_drvdata(master), NULL);
-}
-
-static const struct component_ops a3xx_ops = {
- .bind = a3xx_bind,
- .unbind = a3xx_unbind,
-};
-
-static int a3xx_probe(struct platform_device *pdev)
-{
- return component_add(&pdev->dev, &a3xx_ops);
-}
-
-static int a3xx_remove(struct platform_device *pdev)
-{
- component_del(&pdev->dev, &a3xx_ops);
- return 0;
-}
-
-static const struct of_device_id dt_match[] = {
- { .compatible = "qcom,kgsl-3d0" },
- {}
-};
-
-static struct platform_driver a3xx_driver = {
- .probe = a3xx_probe,
- .remove = a3xx_remove,
- .driver = {
- .name = "kgsl-3d0",
- .of_match_table = dt_match,
- },
-};
-
-void __init a3xx_register(void)
-{
- platform_driver_register(&a3xx_driver);
-}
-
-void __exit a3xx_unregister(void)
-{
- platform_driver_unregister(&a3xx_driver);
-}
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h
index bb9a8ca0507b..85ff66cbddd6 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h
@@ -19,6 +19,11 @@
#define __A3XX_GPU_H__
#include "adreno_gpu.h"
+
+/* arrg, somehow fb.h is getting pulled in: */
+#undef ROP_COPY
+#undef ROP_XOR
+
#include "a3xx.xml.h"
struct a3xx_gpu {
diff --git a/drivers/gpu/drm/msm/adreno/adreno_common.xml.h b/drivers/gpu/drm/msm/adreno/adreno_common.xml.h
index d6e6ce2d1abd..cc341bc62b51 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_common.xml.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_common.xml.h
@@ -10,13 +10,13 @@ git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2013-11-30 14:47:15)
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32814 bytes, from 2013-11-30 15:07:33)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 8900 bytes, from 2013-10-22 23:57:49)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 10574 bytes, from 2013-11-13 05:44:45)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 53644 bytes, from 2013-11-30 15:07:33)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 8344 bytes, from 2013-11-30 14:49:47)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 9859 bytes, from 2014-06-02 15:21:30)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14960 bytes, from 2014-07-27 17:22:13)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-08-01 12:22:48)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 41068 bytes, from 2014-08-01 12:22:48)
-Copyright (C) 2013 by the following authors:
+Copyright (C) 2013-2014 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
Permission is hereby granted, free of charge, to any person obtaining
@@ -87,15 +87,6 @@ enum adreno_rb_blend_factor {
FACTOR_SRC_ALPHA_SATURATE = 16,
};
-enum adreno_rb_blend_opcode {
- BLEND_DST_PLUS_SRC = 0,
- BLEND_SRC_MINUS_DST = 1,
- BLEND_MIN_DST_SRC = 2,
- BLEND_MAX_DST_SRC = 3,
- BLEND_DST_MINUS_SRC = 4,
- BLEND_DST_PLUS_SRC_BIAS = 5,
-};
-
enum adreno_rb_surface_endian {
ENDIAN_NONE = 0,
ENDIAN_8IN16 = 1,
@@ -116,6 +107,39 @@ enum adreno_rb_depth_format {
DEPTHX_24_8 = 1,
};
+enum adreno_rb_copy_control_mode {
+ RB_COPY_RESOLVE = 1,
+ RB_COPY_CLEAR = 2,
+ RB_COPY_DEPTH_STENCIL = 5,
+};
+
+enum a3xx_render_mode {
+ RB_RENDERING_PASS = 0,
+ RB_TILING_PASS = 1,
+ RB_RESOLVE_PASS = 2,
+ RB_COMPUTE_PASS = 3,
+};
+
+enum a3xx_msaa_samples {
+ MSAA_ONE = 0,
+ MSAA_TWO = 1,
+ MSAA_FOUR = 2,
+};
+
+enum a3xx_threadmode {
+ MULTI = 0,
+ SINGLE = 1,
+};
+
+enum a3xx_instrbuffermode {
+ BUFFER = 1,
+};
+
+enum a3xx_threadsize {
+ TWO_QUADS = 0,
+ FOUR_QUADS = 1,
+};
+
#define REG_AXXX_CP_RB_BASE 0x000001c0
#define REG_AXXX_CP_RB_CNTL 0x000001c1
@@ -264,6 +288,8 @@ static inline uint32_t AXXX_SCRATCH_UMSK_SWAP(uint32_t val)
#define REG_AXXX_CP_INT_ACK 0x000001f4
#define REG_AXXX_CP_ME_CNTL 0x000001f6
+#define AXXX_CP_ME_CNTL_BUSY 0x20000000
+#define AXXX_CP_ME_CNTL_HALT 0x10000000
#define REG_AXXX_CP_ME_STATUS 0x000001f7
diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c
new file mode 100644
index 000000000000..7ab85af3a7db
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/adreno_device.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2013-2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.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.
+ *
+ * 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 "adreno_gpu.h"
+
+#if defined(CONFIG_MSM_BUS_SCALING) && !defined(CONFIG_OF)
+# include <mach/kgsl.h>
+#endif
+
+#define ANY_ID 0xff
+
+bool hang_debug = false;
+MODULE_PARM_DESC(hang_debug, "Dump registers when hang is detected (can be slow!)");
+module_param_named(hang_debug, hang_debug, bool, 0600);
+
+struct msm_gpu *a3xx_gpu_init(struct drm_device *dev);
+
+static const struct adreno_info gpulist[] = {
+ {
+ .rev = ADRENO_REV(3, 0, 5, ANY_ID),
+ .revn = 305,
+ .name = "A305",
+ .pm4fw = "a300_pm4.fw",
+ .pfpfw = "a300_pfp.fw",
+ .gmem = SZ_256K,
+ .init = a3xx_gpu_init,
+ }, {
+ .rev = ADRENO_REV(3, 2, ANY_ID, ANY_ID),
+ .revn = 320,
+ .name = "A320",
+ .pm4fw = "a300_pm4.fw",
+ .pfpfw = "a300_pfp.fw",
+ .gmem = SZ_512K,
+ .init = a3xx_gpu_init,
+ }, {
+ .rev = ADRENO_REV(3, 3, 0, ANY_ID),
+ .revn = 330,
+ .name = "A330",
+ .pm4fw = "a330_pm4.fw",
+ .pfpfw = "a330_pfp.fw",
+ .gmem = SZ_1M,
+ .init = a3xx_gpu_init,
+ },
+};
+
+MODULE_FIRMWARE("a300_pm4.fw");
+MODULE_FIRMWARE("a300_pfp.fw");
+MODULE_FIRMWARE("a330_pm4.fw");
+MODULE_FIRMWARE("a330_pfp.fw");
+
+static inline bool _rev_match(uint8_t entry, uint8_t id)
+{
+ return (entry == ANY_ID) || (entry == id);
+}
+
+const struct adreno_info *adreno_info(struct adreno_rev rev)
+{
+ int i;
+
+ /* identify gpu: */
+ for (i = 0; i < ARRAY_SIZE(gpulist); i++) {
+ const struct adreno_info *info = &gpulist[i];
+ if (_rev_match(info->rev.core, rev.core) &&
+ _rev_match(info->rev.major, rev.major) &&
+ _rev_match(info->rev.minor, rev.minor) &&
+ _rev_match(info->rev.patchid, rev.patchid))
+ return info;
+ }
+
+ return NULL;
+}
+
+struct msm_gpu *adreno_load_gpu(struct drm_device *dev)
+{
+ struct msm_drm_private *priv = dev->dev_private;
+ struct platform_device *pdev = priv->gpu_pdev;
+ struct adreno_platform_config *config;
+ struct adreno_rev rev;
+ const struct adreno_info *info;
+ struct msm_gpu *gpu = NULL;
+
+ if (!pdev) {
+ dev_err(dev->dev, "no adreno device\n");
+ return NULL;
+ }
+
+ config = pdev->dev.platform_data;
+ rev = config->rev;
+ info = adreno_info(config->rev);
+
+ if (!info) {
+ dev_warn(dev->dev, "Unknown GPU revision: %u.%u.%u.%u\n",
+ rev.core, rev.major, rev.minor, rev.patchid);
+ return NULL;
+ }
+
+ DBG("Found GPU: %u.%u.%u.%u", rev.core, rev.major,
+ rev.minor, rev.patchid);
+
+ gpu = info->init(dev);
+ if (IS_ERR(gpu)) {
+ dev_warn(dev->dev, "failed to load adreno gpu\n");
+ gpu = NULL;
+ /* not fatal */
+ }
+
+ if (gpu) {
+ int ret;
+ mutex_lock(&dev->struct_mutex);
+ gpu->funcs->pm_resume(gpu);
+ mutex_unlock(&dev->struct_mutex);
+ ret = gpu->funcs->hw_init(gpu);
+ if (ret) {
+ dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
+ gpu->funcs->destroy(gpu);
+ gpu = NULL;
+ } else {
+ /* give inactive pm a chance to kick in: */
+ msm_gpu_retire(gpu);
+ }
+ }
+
+ return gpu;
+}
+
+static void set_gpu_pdev(struct drm_device *dev,
+ struct platform_device *pdev)
+{
+ struct msm_drm_private *priv = dev->dev_private;
+ priv->gpu_pdev = pdev;
+}
+
+static int adreno_bind(struct device *dev, struct device *master, void *data)
+{
+ static struct adreno_platform_config config = {};
+#ifdef CONFIG_OF
+ struct device_node *child, *node = dev->of_node;
+ u32 val;
+ int ret;
+
+ ret = of_property_read_u32(node, "qcom,chipid", &val);
+ if (ret) {
+ dev_err(dev, "could not find chipid: %d\n", ret);
+ return ret;
+ }
+
+ config.rev = ADRENO_REV((val >> 24) & 0xff,
+ (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff);
+
+ /* find clock rates: */
+ config.fast_rate = 0;
+ config.slow_rate = ~0;
+ for_each_child_of_node(node, child) {
+ if (of_device_is_compatible(child, "qcom,gpu-pwrlevels")) {
+ struct device_node *pwrlvl;
+ for_each_child_of_node(child, pwrlvl) {
+ ret = of_property_read_u32(pwrlvl, "qcom,gpu-freq", &val);
+ if (ret) {
+ dev_err(dev, "could not find gpu-freq: %d\n", ret);
+ return ret;
+ }
+ config.fast_rate = max(config.fast_rate, val);
+ config.slow_rate = min(config.slow_rate, val);
+ }
+ }
+ }
+
+ if (!config.fast_rate) {
+ dev_err(dev, "could not find clk rates\n");
+ return -ENXIO;
+ }
+
+#else
+ struct kgsl_device_platform_data *pdata = dev->platform_data;
+ uint32_t version = socinfo_get_version();
+ if (cpu_is_apq8064ab()) {
+ config.fast_rate = 450000000;
+ config.slow_rate = 27000000;
+ config.bus_freq = 4;
+ config.rev = ADRENO_REV(3, 2, 1, 0);
+ } else if (cpu_is_apq8064()) {
+ config.fast_rate = 400000000;
+ config.slow_rate = 27000000;
+ config.bus_freq = 4;
+
+ if (SOCINFO_VERSION_MAJOR(version) == 2)
+ config.rev = ADRENO_REV(3, 2, 0, 2);
+ else if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
+ (SOCINFO_VERSION_MINOR(version) == 1))
+ config.rev = ADRENO_REV(3, 2, 0, 1);
+ else
+ config.rev = ADRENO_REV(3, 2, 0, 0);
+
+ } else if (cpu_is_msm8960ab()) {
+ config.fast_rate = 400000000;
+ config.slow_rate = 320000000;
+ config.bus_freq = 4;
+
+ if (SOCINFO_VERSION_MINOR(version) == 0)
+ config.rev = ADRENO_REV(3, 2, 1, 0);
+ else
+ config.rev = ADRENO_REV(3, 2, 1, 1);
+
+ } else if (cpu_is_msm8930()) {
+ config.fast_rate = 400000000;
+ config.slow_rate = 27000000;
+ config.bus_freq = 3;
+
+ if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
+ (SOCINFO_VERSION_MINOR(version) == 2))
+ config.rev = ADRENO_REV(3, 0, 5, 2);
+ else
+ config.rev = ADRENO_REV(3, 0, 5, 0);
+
+ }
+# ifdef CONFIG_MSM_BUS_SCALING
+ config.bus_scale_table = pdata->bus_scale_table;
+# endif
+#endif
+ dev->platform_data = &config;
+ set_gpu_pdev(dev_get_drvdata(master), to_platform_device(dev));
+ return 0;
+}
+
+static void adreno_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ set_gpu_pdev(dev_get_drvdata(master), NULL);
+}
+
+static const struct component_ops a3xx_ops = {
+ .bind = adreno_bind,
+ .unbind = adreno_unbind,
+};
+
+static int adreno_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &a3xx_ops);
+}
+
+static int adreno_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &a3xx_ops);
+ return 0;
+}
+
+static const struct of_device_id dt_match[] = {
+ { .compatible = "qcom,adreno-3xx" },
+ /* for backwards compat w/ downstream kgsl DT files: */
+ { .compatible = "qcom,kgsl-3d0" },
+ {}
+};
+
+static struct platform_driver adreno_driver = {
+ .probe = adreno_probe,
+ .remove = adreno_remove,
+ .driver = {
+ .name = "adreno",
+ .of_match_table = dt_match,
+ },
+};
+
+void __init adreno_register(void)
+{
+ platform_driver_register(&adreno_driver);
+}
+
+void __exit adreno_unregister(void)
+{
+ platform_driver_unregister(&adreno_driver);
+}
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index 28ca8cd8b09e..6afa29167fee 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -19,46 +19,6 @@
#include "msm_gem.h"
#include "msm_mmu.h"
-struct adreno_info {
- struct adreno_rev rev;
- uint32_t revn;
- const char *name;
- const char *pm4fw, *pfpfw;
- uint32_t gmem;
-};
-
-#define ANY_ID 0xff
-
-static const struct adreno_info gpulist[] = {
- {
- .rev = ADRENO_REV(3, 0, 5, ANY_ID),
- .revn = 305,
- .name = "A305",
- .pm4fw = "a300_pm4.fw",
- .pfpfw = "a300_pfp.fw",
- .gmem = SZ_256K,
- }, {
- .rev = ADRENO_REV(3, 2, ANY_ID, ANY_ID),
- .revn = 320,
- .name = "A320",
- .pm4fw = "a300_pm4.fw",
- .pfpfw = "a300_pfp.fw",
- .gmem = SZ_512K,
- }, {
- .rev = ADRENO_REV(3, 3, 0, ANY_ID),
- .revn = 330,
- .name = "A330",
- .pm4fw = "a330_pm4.fw",
- .pfpfw = "a330_pfp.fw",
- .gmem = SZ_1M,
- },
-};
-
-MODULE_FIRMWARE("a300_pm4.fw");
-MODULE_FIRMWARE("a300_pfp.fw");
-MODULE_FIRMWARE("a330_pm4.fw");
-MODULE_FIRMWARE("a330_pfp.fw");
-
#define RB_SIZE SZ_32K
#define RB_BLKSIZE 16
@@ -91,9 +51,17 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)
int adreno_hw_init(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ int ret;
DBG("%s", gpu->name);
+ ret = msm_gem_get_iova(gpu->rb->bo, gpu->id, &gpu->rb_iova);
+ if (ret) {
+ gpu->rb_iova = 0;
+ dev_err(gpu->dev->dev, "could not map ringbuffer: %d\n", ret);
+ return ret;
+ }
+
/* Setup REG_CP_RB_CNTL: */
gpu_write(gpu, REG_AXXX_CP_RB_CNTL,
/* size is log2(quad-words): */
@@ -244,6 +212,7 @@ void adreno_idle(struct msm_gpu *gpu)
void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ int i;
seq_printf(m, "revision: %d (%d.%d.%d.%d)\n",
adreno_gpu->info->revn, adreno_gpu->rev.core,
@@ -255,6 +224,23 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
seq_printf(m, "rptr: %d\n", adreno_gpu->memptrs->rptr);
seq_printf(m, "wptr: %d\n", adreno_gpu->memptrs->wptr);
seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb));
+
+ gpu->funcs->pm_resume(gpu);
+
+ /* dump these out in a form that can be parsed by demsm: */
+ seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name);
+ for (i = 0; adreno_gpu->registers[i] != ~0; i += 2) {
+ uint32_t start = adreno_gpu->registers[i];
+ uint32_t end = adreno_gpu->registers[i+1];
+ uint32_t addr;
+
+ for (addr = start; addr <= end; addr++) {
+ uint32_t val = gpu_read(gpu, addr);
+ seq_printf(m, "IO:R %08x %08x\n", addr<<2, val);
+ }
+ }
+
+ gpu->funcs->pm_suspend(gpu);
}
#endif
@@ -262,6 +248,7 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m)
void adreno_dump(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ int i;
printk("revision: %d (%d.%d.%d.%d)\n",
adreno_gpu->info->revn, adreno_gpu->rev.core,
@@ -274,6 +261,18 @@ void adreno_dump(struct msm_gpu *gpu)
printk("wptr: %d\n", adreno_gpu->memptrs->wptr);
printk("rb wptr: %d\n", get_wptr(gpu->rb));
+ /* dump these out in a form that can be parsed by demsm: */
+ printk("IO:region %s 00000000 00020000\n", gpu->name);
+ for (i = 0; adreno_gpu->registers[i] != ~0; i += 2) {
+ uint32_t start = adreno_gpu->registers[i];
+ uint32_t end = adreno_gpu->registers[i+1];
+ uint32_t addr;
+
+ for (addr = start; addr <= end; addr++) {
+ uint32_t val = gpu_read(gpu, addr);
+ printk("IO:R %08x %08x\n", addr<<2, val);
+ }
+ }
}
static uint32_t ring_freewords(struct msm_gpu *gpu)
@@ -296,65 +295,51 @@ static const char *iommu_ports[] = {
"gfx3d1_user", "gfx3d1_priv",
};
-static inline bool _rev_match(uint8_t entry, uint8_t id)
-{
- return (entry == ANY_ID) || (entry == id);
-}
-
int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
- struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
- struct adreno_rev rev)
+ struct adreno_gpu *adreno_gpu, const struct adreno_gpu_funcs *funcs)
{
+ struct adreno_platform_config *config = pdev->dev.platform_data;
+ struct msm_gpu *gpu = &adreno_gpu->base;
struct msm_mmu *mmu;
- int i, ret;
-
- /* identify gpu: */
- for (i = 0; i < ARRAY_SIZE(gpulist); i++) {
- const struct adreno_info *info = &gpulist[i];
- if (_rev_match(info->rev.core, rev.core) &&
- _rev_match(info->rev.major, rev.major) &&
- _rev_match(info->rev.minor, rev.minor) &&
- _rev_match(info->rev.patchid, rev.patchid)) {
- gpu->info = info;
- gpu->revn = info->revn;
- break;
- }
- }
-
- if (i == ARRAY_SIZE(gpulist)) {
- dev_err(drm->dev, "Unknown GPU revision: %u.%u.%u.%u\n",
- rev.core, rev.major, rev.minor, rev.patchid);
- return -ENXIO;
- }
+ int ret;
- DBG("Found GPU: %s (%u.%u.%u.%u)", gpu->info->name,
- rev.core, rev.major, rev.minor, rev.patchid);
+ adreno_gpu->funcs = funcs;
+ adreno_gpu->info = adreno_info(config->rev);
+ adreno_gpu->gmem = adreno_gpu->info->gmem;
+ adreno_gpu->revn = adreno_gpu->info->revn;
+ adreno_gpu->rev = config->rev;
+
+ gpu->fast_rate = config->fast_rate;
+ gpu->slow_rate = config->slow_rate;
+ gpu->bus_freq = config->bus_freq;
+#ifdef CONFIG_MSM_BUS_SCALING
+ gpu->bus_scale_table = config->bus_scale_table;
+#endif
- gpu->funcs = funcs;
- gpu->gmem = gpu->info->gmem;
- gpu->rev = rev;
+ DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u",
+ gpu->fast_rate, gpu->slow_rate, gpu->bus_freq);
- ret = request_firmware(&gpu->pm4, gpu->info->pm4fw, drm->dev);
+ ret = request_firmware(&adreno_gpu->pm4, adreno_gpu->info->pm4fw, drm->dev);
if (ret) {
dev_err(drm->dev, "failed to load %s PM4 firmware: %d\n",
- gpu->info->pm4fw, ret);
+ adreno_gpu->info->pm4fw, ret);
return ret;
}
- ret = request_firmware(&gpu->pfp, gpu->info->pfpfw, drm->dev);
+ ret = request_firmware(&adreno_gpu->pfp, adreno_gpu->info->pfpfw, drm->dev);
if (ret) {
dev_err(drm->dev, "failed to load %s PFP firmware: %d\n",
- gpu->info->pfpfw, ret);
+ adreno_gpu->info->pfpfw, ret);
return ret;
}
- ret = msm_gpu_init(drm, pdev, &gpu->base, &funcs->base,
- gpu->info->name, "kgsl_3d0_reg_memory", "kgsl_3d0_irq",
+ ret = msm_gpu_init(drm, pdev, &adreno_gpu->base, &funcs->base,
+ adreno_gpu->info->name, "kgsl_3d0_reg_memory", "kgsl_3d0_irq",
RB_SIZE);
if (ret)
return ret;
- mmu = gpu->base.mmu;
+ mmu = gpu->mmu;
if (mmu) {
ret = mmu->funcs->attach(mmu, iommu_ports,
ARRAY_SIZE(iommu_ports));
@@ -362,23 +347,25 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
return ret;
}
- gpu->memptrs_bo = msm_gem_new(drm, sizeof(*gpu->memptrs),
+ mutex_lock(&drm->struct_mutex);
+ adreno_gpu->memptrs_bo = msm_gem_new(drm, sizeof(*adreno_gpu->memptrs),
MSM_BO_UNCACHED);
- if (IS_ERR(gpu->memptrs_bo)) {
- ret = PTR_ERR(gpu->memptrs_bo);
- gpu->memptrs_bo = NULL;
+ 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;
}
- gpu->memptrs = msm_gem_vaddr_locked(gpu->memptrs_bo);
- if (!gpu->memptrs) {
+ 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_locked(gpu->memptrs_bo, gpu->base.id,
- &gpu->memptrs_iova);
+ ret = msm_gem_get_iova(adreno_gpu->memptrs_bo, gpu->id,
+ &adreno_gpu->memptrs_iova);
if (ret) {
dev_err(drm->dev, "could not map memptrs: %d\n", ret);
return ret;
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index 63c36ce33020..52f051579753 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -39,7 +39,16 @@ struct adreno_gpu_funcs {
struct msm_gpu_funcs base;
};
-struct adreno_info;
+struct adreno_info {
+ struct adreno_rev rev;
+ uint32_t revn;
+ const char *name;
+ const char *pm4fw, *pfpfw;
+ uint32_t gmem;
+ struct msm_gpu *(*init)(struct drm_device *dev);
+};
+
+const struct adreno_info *adreno_info(struct adreno_rev rev);
struct adreno_rbmemptrs {
volatile uint32_t rptr;
@@ -55,6 +64,9 @@ struct adreno_gpu {
uint32_t revn; /* numeric revision name */
const struct adreno_gpu_funcs *funcs;
+ /* interesting register offsets to dump: */
+ const unsigned int *registers;
+
/* firmware: */
const struct firmware *pm4, *pfp;
@@ -131,8 +143,7 @@ void adreno_dump(struct msm_gpu *gpu);
void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords);
int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
- struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
- struct adreno_rev rev);
+ struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs);
void adreno_gpu_cleanup(struct adreno_gpu *gpu);
diff --git a/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h b/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h
index ae992c71703f..6ef43f66c30a 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h
@@ -10,13 +10,13 @@ git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 364 bytes, from 2013-11-30 14:47:15)
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32814 bytes, from 2013-11-30 15:07:33)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 8900 bytes, from 2013-10-22 23:57:49)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 10574 bytes, from 2013-11-13 05:44:45)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 53644 bytes, from 2013-11-30 15:07:33)
-- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 8344 bytes, from 2013-11-30 14:49:47)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2014-06-02 15:21:30)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 9859 bytes, from 2014-06-02 15:21:30)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14960 bytes, from 2014-07-27 17:22:13)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 58020 bytes, from 2014-08-01 12:22:48)
+- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 41068 bytes, from 2014-08-01 12:22:48)
-Copyright (C) 2013 by the following authors:
+Copyright (C) 2013-2014 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
Permission is hereby granted, free of charge, to any person obtaining
@@ -105,6 +105,7 @@ enum pc_di_index_size {
enum pc_di_vis_cull_mode {
IGNORE_VISIBILITY = 0,
+ USE_VISIBILITY = 1,
};
enum adreno_pm4_packet_type {
@@ -162,7 +163,16 @@ enum adreno_pm4_type3_packets {
CP_INDIRECT_BUFFER_PFE = 63,
CP_SET_BIN = 76,
CP_TEST_TWO_MEMS = 113,
+ CP_REG_WR_NO_CTXT = 120,
+ CP_RECORD_PFP_TIMESTAMP = 17,
CP_WAIT_FOR_ME = 19,
+ CP_SET_DRAW_STATE = 67,
+ CP_DRAW_INDX_OFFSET = 56,
+ CP_DRAW_INDIRECT = 40,
+ CP_DRAW_INDX_INDIRECT = 41,
+ CP_DRAW_AUTO = 36,
+ CP_UNKNOWN_1A = 26,
+ CP_WIDE_REG_WRITE = 116,
IN_IB_PREFETCH_END = 23,
IN_SUBBLK_PREFETCH = 31,
IN_INSTR_PREFETCH = 32,
@@ -232,6 +242,211 @@ static inline uint32_t CP_LOAD_STATE_1_EXT_SRC_ADDR(uint32_t val)
return ((val >> 2) << CP_LOAD_STATE_1_EXT_SRC_ADDR__SHIFT) & CP_LOAD_STATE_1_EXT_SRC_ADDR__MASK;
}
+#define REG_CP_DRAW_INDX_0 0x00000000
+#define CP_DRAW_INDX_0_VIZ_QUERY__MASK 0xffffffff
+#define CP_DRAW_INDX_0_VIZ_QUERY__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_0_VIZ_QUERY(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_0_VIZ_QUERY__SHIFT) & CP_DRAW_INDX_0_VIZ_QUERY__MASK;
+}
+
+#define REG_CP_DRAW_INDX_1 0x00000001
+#define CP_DRAW_INDX_1_PRIM_TYPE__MASK 0x0000003f
+#define CP_DRAW_INDX_1_PRIM_TYPE__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_1_PRIM_TYPE(enum pc_di_primtype val)
+{
+ return ((val) << CP_DRAW_INDX_1_PRIM_TYPE__SHIFT) & CP_DRAW_INDX_1_PRIM_TYPE__MASK;
+}
+#define CP_DRAW_INDX_1_SOURCE_SELECT__MASK 0x000000c0
+#define CP_DRAW_INDX_1_SOURCE_SELECT__SHIFT 6
+static inline uint32_t CP_DRAW_INDX_1_SOURCE_SELECT(enum pc_di_src_sel val)
+{
+ return ((val) << CP_DRAW_INDX_1_SOURCE_SELECT__SHIFT) & CP_DRAW_INDX_1_SOURCE_SELECT__MASK;
+}
+#define CP_DRAW_INDX_1_VIS_CULL__MASK 0x00000600
+#define CP_DRAW_INDX_1_VIS_CULL__SHIFT 9
+static inline uint32_t CP_DRAW_INDX_1_VIS_CULL(enum pc_di_vis_cull_mode val)
+{
+ return ((val) << CP_DRAW_INDX_1_VIS_CULL__SHIFT) & CP_DRAW_INDX_1_VIS_CULL__MASK;
+}
+#define CP_DRAW_INDX_1_INDEX_SIZE__MASK 0x00000800
+#define CP_DRAW_INDX_1_INDEX_SIZE__SHIFT 11
+static inline uint32_t CP_DRAW_INDX_1_INDEX_SIZE(enum pc_di_index_size val)
+{
+ return ((val) << CP_DRAW_INDX_1_INDEX_SIZE__SHIFT) & CP_DRAW_INDX_1_INDEX_SIZE__MASK;
+}
+#define CP_DRAW_INDX_1_NOT_EOP 0x00001000
+#define CP_DRAW_INDX_1_SMALL_INDEX 0x00002000
+#define CP_DRAW_INDX_1_PRE_DRAW_INITIATOR_ENABLE 0x00004000
+#define CP_DRAW_INDX_1_NUM_INDICES__MASK 0xffff0000
+#define CP_DRAW_INDX_1_NUM_INDICES__SHIFT 16
+static inline uint32_t CP_DRAW_INDX_1_NUM_INDICES(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_1_NUM_INDICES__SHIFT) & CP_DRAW_INDX_1_NUM_INDICES__MASK;
+}
+
+#define REG_CP_DRAW_INDX_2 0x00000002
+#define CP_DRAW_INDX_2_NUM_INDICES__MASK 0xffffffff
+#define CP_DRAW_INDX_2_NUM_INDICES__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_2_NUM_INDICES(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_2_NUM_INDICES__SHIFT) & CP_DRAW_INDX_2_NUM_INDICES__MASK;
+}
+
+#define REG_CP_DRAW_INDX_2 0x00000002
+#define CP_DRAW_INDX_2_INDX_BASE__MASK 0xffffffff
+#define CP_DRAW_INDX_2_INDX_BASE__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_2_INDX_BASE(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_2_INDX_BASE__SHIFT) & CP_DRAW_INDX_2_INDX_BASE__MASK;
+}
+
+#define REG_CP_DRAW_INDX_2 0x00000002
+#define CP_DRAW_INDX_2_INDX_SIZE__MASK 0xffffffff
+#define CP_DRAW_INDX_2_INDX_SIZE__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_2_INDX_SIZE(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_2_INDX_SIZE__SHIFT) & CP_DRAW_INDX_2_INDX_SIZE__MASK;
+}
+
+#define REG_CP_DRAW_INDX_2_0 0x00000000
+#define CP_DRAW_INDX_2_0_VIZ_QUERY__MASK 0xffffffff
+#define CP_DRAW_INDX_2_0_VIZ_QUERY__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_2_0_VIZ_QUERY(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_2_0_VIZ_QUERY__SHIFT) & CP_DRAW_INDX_2_0_VIZ_QUERY__MASK;
+}
+
+#define REG_CP_DRAW_INDX_2_1 0x00000001
+#define CP_DRAW_INDX_2_1_PRIM_TYPE__MASK 0x0000003f
+#define CP_DRAW_INDX_2_1_PRIM_TYPE__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_2_1_PRIM_TYPE(enum pc_di_primtype val)
+{
+ return ((val) << CP_DRAW_INDX_2_1_PRIM_TYPE__SHIFT) & CP_DRAW_INDX_2_1_PRIM_TYPE__MASK;
+}
+#define CP_DRAW_INDX_2_1_SOURCE_SELECT__MASK 0x000000c0
+#define CP_DRAW_INDX_2_1_SOURCE_SELECT__SHIFT 6
+static inline uint32_t CP_DRAW_INDX_2_1_SOURCE_SELECT(enum pc_di_src_sel val)
+{
+ return ((val) << CP_DRAW_INDX_2_1_SOURCE_SELECT__SHIFT) & CP_DRAW_INDX_2_1_SOURCE_SELECT__MASK;
+}
+#define CP_DRAW_INDX_2_1_VIS_CULL__MASK 0x00000600
+#define CP_DRAW_INDX_2_1_VIS_CULL__SHIFT 9
+static inline uint32_t CP_DRAW_INDX_2_1_VIS_CULL(enum pc_di_vis_cull_mode val)
+{
+ return ((val) << CP_DRAW_INDX_2_1_VIS_CULL__SHIFT) & CP_DRAW_INDX_2_1_VIS_CULL__MASK;
+}
+#define CP_DRAW_INDX_2_1_INDEX_SIZE__MASK 0x00000800
+#define CP_DRAW_INDX_2_1_INDEX_SIZE__SHIFT 11
+static inline uint32_t CP_DRAW_INDX_2_1_INDEX_SIZE(enum pc_di_index_size val)
+{
+ return ((val) << CP_DRAW_INDX_2_1_INDEX_SIZE__SHIFT) & CP_DRAW_INDX_2_1_INDEX_SIZE__MASK;
+}
+#define CP_DRAW_INDX_2_1_NOT_EOP 0x00001000
+#define CP_DRAW_INDX_2_1_SMALL_INDEX 0x00002000
+#define CP_DRAW_INDX_2_1_PRE_DRAW_INITIATOR_ENABLE 0x00004000
+#define CP_DRAW_INDX_2_1_NUM_INDICES__MASK 0xffff0000
+#define CP_DRAW_INDX_2_1_NUM_INDICES__SHIFT 16
+static inline uint32_t CP_DRAW_INDX_2_1_NUM_INDICES(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_2_1_NUM_INDICES__SHIFT) & CP_DRAW_INDX_2_1_NUM_INDICES__MASK;
+}
+
+#define REG_CP_DRAW_INDX_2_2 0x00000002
+#define CP_DRAW_INDX_2_2_NUM_INDICES__MASK 0xffffffff
+#define CP_DRAW_INDX_2_2_NUM_INDICES__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_2_2_NUM_INDICES(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_2_2_NUM_INDICES__SHIFT) & CP_DRAW_INDX_2_2_NUM_INDICES__MASK;
+}
+
+#define REG_CP_DRAW_INDX_OFFSET_0 0x00000000
+#define CP_DRAW_INDX_OFFSET_0_PRIM_TYPE__MASK 0x0000003f
+#define CP_DRAW_INDX_OFFSET_0_PRIM_TYPE__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_OFFSET_0_PRIM_TYPE(enum pc_di_primtype val)
+{
+ return ((val) << CP_DRAW_INDX_OFFSET_0_PRIM_TYPE__SHIFT) & CP_DRAW_INDX_OFFSET_0_PRIM_TYPE__MASK;
+}
+#define CP_DRAW_INDX_OFFSET_0_SOURCE_SELECT__MASK 0x000000c0
+#define CP_DRAW_INDX_OFFSET_0_SOURCE_SELECT__SHIFT 6
+static inline uint32_t CP_DRAW_INDX_OFFSET_0_SOURCE_SELECT(enum pc_di_src_sel val)
+{
+ return ((val) << CP_DRAW_INDX_OFFSET_0_SOURCE_SELECT__SHIFT) & CP_DRAW_INDX_OFFSET_0_SOURCE_SELECT__MASK;
+}
+#define CP_DRAW_INDX_OFFSET_0_VIS_CULL__MASK 0x00000700
+#define CP_DRAW_INDX_OFFSET_0_VIS_CULL__SHIFT 8
+static inline uint32_t CP_DRAW_INDX_OFFSET_0_VIS_CULL(enum pc_di_vis_cull_mode val)
+{
+ return ((val) << CP_DRAW_INDX_OFFSET_0_VIS_CULL__SHIFT) & CP_DRAW_INDX_OFFSET_0_VIS_CULL__MASK;
+}
+#define CP_DRAW_INDX_OFFSET_0_INDEX_SIZE__MASK 0x00000800
+#define CP_DRAW_INDX_OFFSET_0_INDEX_SIZE__SHIFT 11
+static inline uint32_t CP_DRAW_INDX_OFFSET_0_INDEX_SIZE(enum pc_di_index_size val)
+{
+ return ((val) << CP_DRAW_INDX_OFFSET_0_INDEX_SIZE__SHIFT) & CP_DRAW_INDX_OFFSET_0_INDEX_SIZE__MASK;
+}
+#define CP_DRAW_INDX_OFFSET_0_NOT_EOP 0x00001000
+#define CP_DRAW_INDX_OFFSET_0_SMALL_INDEX 0x00002000
+#define CP_DRAW_INDX_OFFSET_0_PRE_DRAW_INITIATOR_ENABLE 0x00004000
+#define CP_DRAW_INDX_OFFSET_0_NUM_INDICES__MASK 0xffff0000
+#define CP_DRAW_INDX_OFFSET_0_NUM_INDICES__SHIFT 16
+static inline uint32_t CP_DRAW_INDX_OFFSET_0_NUM_INDICES(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_OFFSET_0_NUM_INDICES__SHIFT) & CP_DRAW_INDX_OFFSET_0_NUM_INDICES__MASK;
+}
+
+#define REG_CP_DRAW_INDX_OFFSET_1 0x00000001
+
+#define REG_CP_DRAW_INDX_OFFSET_2 0x00000002
+#define CP_DRAW_INDX_OFFSET_2_NUM_INDICES__MASK 0xffffffff
+#define CP_DRAW_INDX_OFFSET_2_NUM_INDICES__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_OFFSET_2_NUM_INDICES(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_OFFSET_2_NUM_INDICES__SHIFT) & CP_DRAW_INDX_OFFSET_2_NUM_INDICES__MASK;
+}
+
+#define REG_CP_DRAW_INDX_OFFSET_2 0x00000002
+#define CP_DRAW_INDX_OFFSET_2_INDX_BASE__MASK 0xffffffff
+#define CP_DRAW_INDX_OFFSET_2_INDX_BASE__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_OFFSET_2_INDX_BASE(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_OFFSET_2_INDX_BASE__SHIFT) & CP_DRAW_INDX_OFFSET_2_INDX_BASE__MASK;
+}
+
+#define REG_CP_DRAW_INDX_OFFSET_2 0x00000002
+#define CP_DRAW_INDX_OFFSET_2_INDX_SIZE__MASK 0xffffffff
+#define CP_DRAW_INDX_OFFSET_2_INDX_SIZE__SHIFT 0
+static inline uint32_t CP_DRAW_INDX_OFFSET_2_INDX_SIZE(uint32_t val)
+{
+ return ((val) << CP_DRAW_INDX_OFFSET_2_INDX_SIZE__SHIFT) & CP_DRAW_INDX_OFFSET_2_INDX_SIZE__MASK;
+}
+
+#define REG_CP_SET_DRAW_STATE_0 0x00000000
+#define CP_SET_DRAW_STATE_0_COUNT__MASK 0x0000ffff
+#define CP_SET_DRAW_STATE_0_COUNT__SHIFT 0
+static inline uint32_t CP_SET_DRAW_STATE_0_COUNT(uint32_t val)
+{
+ return ((val) << CP_SET_DRAW_STATE_0_COUNT__SHIFT) & CP_SET_DRAW_STATE_0_COUNT__MASK;
+}
+#define CP_SET_DRAW_STATE_0_DIRTY 0x00010000
+#define CP_SET_DRAW_STATE_0_DISABLE 0x00020000
+#define CP_SET_DRAW_STATE_0_DISABLE_ALL_GROUPS 0x00040000
+#define CP_SET_DRAW_STATE_0_LOAD_IMMED 0x00080000
+#define CP_SET_DRAW_STATE_0_GROUP_ID__MASK 0x1f000000
+#define CP_SET_DRAW_STATE_0_GROUP_ID__SHIFT 24
+static inline uint32_t CP_SET_DRAW_STATE_0_GROUP_ID(uint32_t val)
+{
+ return ((val) << CP_SET_DRAW_STATE_0_GROUP_ID__SHIFT) & CP_SET_DRAW_STATE_0_GROUP_ID__MASK;
+}
+
+#define REG_CP_SET_DRAW_STATE_1 0x00000001
+#define CP_SET_DRAW_STATE_1_ADDR__MASK 0xffffffff
+#define CP_SET_DRAW_STATE_1_ADDR__SHIFT 0
+static inline uint32_t CP_SET_DRAW_STATE_1_ADDR(uint32_t val)
+{
+ return ((val) << CP_SET_DRAW_STATE_1_ADDR__SHIFT) & CP_SET_DRAW_STATE_1_ADDR__MASK;
+}
+
#define REG_CP_SET_BIN_0 0x00000000
#define REG_CP_SET_BIN_1 0x00000001
@@ -262,5 +477,21 @@ static inline uint32_t CP_SET_BIN_2_Y2(uint32_t val)
return ((val) << CP_SET_BIN_2_Y2__SHIFT) & CP_SET_BIN_2_Y2__MASK;
}
+#define REG_CP_SET_BIN_DATA_0 0x00000000
+#define CP_SET_BIN_DATA_0_BIN_DATA_ADDR__MASK 0xffffffff
+#define CP_SET_BIN_DATA_0_BIN_DATA_ADDR__SHIFT 0
+static inline uint32_t CP_SET_BIN_DATA_0_BIN_DATA_ADDR(uint32_t val)
+{
+ return ((val) << CP_SET_BIN_DATA_0_BIN_DATA_ADDR__SHIFT) & CP_SET_BIN_DATA_0_BIN_DATA_ADDR__MASK;
+}
+
+#define REG_CP_SET_BIN_DATA_1 0x00000001
+#define CP_SET_BIN_DATA_1_BIN_SIZE_ADDRESS__MASK 0xffffffff
+#define CP_SET_BIN_DATA_1_BIN_SIZE_ADDRESS__SHIFT 0
+static inline uint32_t CP_SET_BIN_DATA_1_BIN_SIZE_ADDRESS(uint32_t val)
+{
+ return ((val) << CP_SET_BIN_DATA_1_BIN_SIZE_ADDRESS__SHIFT) & CP_SET_BIN_DATA_1_BIN_SIZE_ADDRESS__MASK;
+}
+
#endif /* ADRENO_PM4_XML */
diff --git a/drivers/gpu/drm/msm/dsi/dsi.xml.h b/drivers/gpu/drm/msm/dsi/dsi.xml.h
index 87be647e3825..e965898dfda6 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.xml.h
+++ b/drivers/gpu/drm/msm/dsi/dsi.xml.h
@@ -10,14 +10,14 @@ git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35)
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2013-12-03 20:59:13)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
-- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
+- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53)
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 20932 bytes, from 2013-12-01 15:13:04)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30)
Copyright (C) 2013 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
diff --git a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h
index 747a6ef4211f..f2bdda957205 100644
--- a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h
+++ b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h
@@ -10,16 +10,16 @@ git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35)
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2013-12-03 20:59:13)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
-- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
+- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53)
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 20932 bytes, from 2013-12-01 15:13:04)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30)
-Copyright (C) 2013 by the following authors:
+Copyright (C) 2013-2014 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
Permission is hereby granted, free of charge, to any person obtaining
@@ -112,5 +112,11 @@ static inline uint32_t MMSS_CC_CLK_NS_VAL(uint32_t val)
return ((val) << MMSS_CC_CLK_NS_VAL__SHIFT) & MMSS_CC_CLK_NS_VAL__MASK;
}
+#define REG_MMSS_CC_DSI2_PIXEL_CC 0x00000094
+
+#define REG_MMSS_CC_DSI2_PIXEL_NS 0x000000e4
+
+#define REG_MMSS_CC_DSI2_PIXEL_CC2 0x00000264
+
#endif /* MMSS_CC_XML */
diff --git a/drivers/gpu/drm/msm/dsi/sfpb.xml.h b/drivers/gpu/drm/msm/dsi/sfpb.xml.h
index 48e03acf19bf..e5b071ffd865 100644
--- a/drivers/gpu/drm/msm/dsi/sfpb.xml.h
+++ b/drivers/gpu/drm/msm/dsi/sfpb.xml.h
@@ -10,14 +10,14 @@ git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35)
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2013-12-03 20:59:13)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
-- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
+- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53)
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 20932 bytes, from 2013-12-01 15:13:04)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30)
Copyright (C) 2013 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index 7f7aadef8a82..9d00dcba6959 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -123,7 +123,8 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
for (i = 0; i < config->hpd_reg_cnt; i++) {
struct regulator *reg;
- reg = devm_regulator_get(&pdev->dev, config->hpd_reg_names[i]);
+ reg = devm_regulator_get(&pdev->dev,
+ config->hpd_reg_names[i]);
if (IS_ERR(reg)) {
ret = PTR_ERR(reg);
dev_err(dev->dev, "failed to get hpd regulator: %s (%d)\n",
@@ -138,7 +139,8 @@ struct hdmi *hdmi_init(struct drm_device *dev, struct drm_encoder *encoder)
for (i = 0; i < config->pwr_reg_cnt; i++) {
struct regulator *reg;
- reg = devm_regulator_get(&pdev->dev, config->pwr_reg_names[i]);
+ reg = devm_regulator_get(&pdev->dev,
+ config->pwr_reg_names[i]);
if (IS_ERR(reg)) {
ret = PTR_ERR(reg);
dev_err(dev->dev, "failed to get pwr regulator: %s (%d)\n",
@@ -256,47 +258,68 @@ static void set_hdmi_pdev(struct drm_device *dev,
priv->hdmi_pdev = pdev;
}
-static int hdmi_bind(struct device *dev, struct device *master, void *data)
-{
- static struct hdmi_platform_config config = {};
#ifdef CONFIG_OF
- struct device_node *of_node = dev->of_node;
-
- int get_gpio(const char *name)
- {
- int gpio = of_get_named_gpio(of_node, name, 0);
+static int get_gpio(struct device *dev, struct device_node *of_node, const char *name)
+{
+ int gpio = of_get_named_gpio(of_node, name, 0);
+ if (gpio < 0) {
+ char name2[32];
+ snprintf(name2, sizeof(name2), "%s-gpio", name);
+ gpio = of_get_named_gpio(of_node, name2, 0);
if (gpio < 0) {
dev_err(dev, "failed to get gpio: %s (%d)\n",
name, gpio);
gpio = -1;
}
- return gpio;
}
+ return gpio;
+}
+#endif
+
+static int hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+ static struct hdmi_platform_config config = {};
+#ifdef CONFIG_OF
+ struct device_node *of_node = dev->of_node;
- /* TODO actually use DT.. */
- static const char *hpd_reg_names[] = {"hpd-gdsc", "hpd-5v"};
- static const char *pwr_reg_names[] = {"core-vdda", "core-vcc"};
- static const char *hpd_clk_names[] = {"iface_clk", "core_clk", "mdp_core_clk"};
- static unsigned long hpd_clk_freq[] = {0, 19200000, 0};
- static const char *pwr_clk_names[] = {"extp_clk", "alt_iface_clk"};
+ if (of_device_is_compatible(of_node, "qcom,hdmi-tx-8074")) {
+ static const char *hpd_reg_names[] = {"hpd-gdsc", "hpd-5v"};
+ static const char *pwr_reg_names[] = {"core-vdda", "core-vcc"};
+ static const char *hpd_clk_names[] = {"iface_clk", "core_clk", "mdp_core_clk"};
+ static unsigned long hpd_clk_freq[] = {0, 19200000, 0};
+ static const char *pwr_clk_names[] = {"extp_clk", "alt_iface_clk"};
+ config.phy_init = hdmi_phy_8x74_init;
+ config.hpd_reg_names = hpd_reg_names;
+ config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names);
+ config.pwr_reg_names = pwr_reg_names;
+ config.pwr_reg_cnt = ARRAY_SIZE(pwr_reg_names);
+ config.hpd_clk_names = hpd_clk_names;
+ config.hpd_freq = hpd_clk_freq;
+ config.hpd_clk_cnt = ARRAY_SIZE(hpd_clk_names);
+ config.pwr_clk_names = pwr_clk_names;
+ config.pwr_clk_cnt = ARRAY_SIZE(pwr_clk_names);
+ config.shared_irq = true;
+ } else if (of_device_is_compatible(of_node, "qcom,hdmi-tx-8960")) {
+ static const char *hpd_clk_names[] = {"core_clk", "master_iface_clk", "slave_iface_clk"};
+ static const char *hpd_reg_names[] = {"core-vdda", "hdmi-mux"};
+ config.phy_init = hdmi_phy_8960_init;
+ config.hpd_reg_names = hpd_reg_names;
+ config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names);
+ config.hpd_clk_names = hpd_clk_names;
+ config.hpd_clk_cnt = ARRAY_SIZE(hpd_clk_names);
+ } else if (of_device_is_compatible(of_node, "qcom,hdmi-tx-8660")) {
+ config.phy_init = hdmi_phy_8x60_init;
+ } else {
+ dev_err(dev, "unknown phy: %s\n", of_node->name);
+ }
- config.phy_init = hdmi_phy_8x74_init;
config.mmio_name = "core_physical";
- config.hpd_reg_names = hpd_reg_names;
- config.hpd_reg_cnt = ARRAY_SIZE(hpd_reg_names);
- config.pwr_reg_names = pwr_reg_names;
- config.pwr_reg_cnt = ARRAY_SIZE(pwr_reg_names);
- config.hpd_clk_names = hpd_clk_names;
- config.hpd_freq = hpd_clk_freq;
- config.hpd_clk_cnt = ARRAY_SIZE(hpd_clk_names);
- config.pwr_clk_names = pwr_clk_names;
- config.pwr_clk_cnt = ARRAY_SIZE(pwr_clk_names);
- config.ddc_clk_gpio = get_gpio("qcom,hdmi-tx-ddc-clk");
- config.ddc_data_gpio = get_gpio("qcom,hdmi-tx-ddc-data");
- config.hpd_gpio = get_gpio("qcom,hdmi-tx-hpd");
- config.mux_en_gpio = get_gpio("qcom,hdmi-tx-mux-en");
- config.mux_sel_gpio = get_gpio("qcom,hdmi-tx-mux-sel");
- config.shared_irq = true;
+ config.ddc_clk_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-clk");
+ config.ddc_data_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-data");
+ config.hpd_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-hpd");
+ config.mux_en_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-en");
+ config.mux_sel_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-sel");
+ config.mux_lpm_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-lpm");
#else
static const char *hpd_clk_names[] = {
@@ -373,7 +396,9 @@ static int hdmi_dev_remove(struct platform_device *pdev)
}
static const struct of_device_id dt_match[] = {
- { .compatible = "qcom,hdmi-tx" },
+ { .compatible = "qcom,hdmi-tx-8074" },
+ { .compatible = "qcom,hdmi-tx-8960" },
+ { .compatible = "qcom,hdmi-tx-8660" },
{}
};
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h
index 9d7723c6528a..b981995410b5 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.h
@@ -96,6 +96,7 @@ struct hdmi_platform_config {
/* gpio's: */
int ddc_clk_gpio, ddc_data_gpio, hpd_gpio, mux_en_gpio, mux_sel_gpio;
+ int mux_lpm_gpio;
/* older devices had their own irq, mdp5+ it is shared w/ mdp: */
bool shared_irq;
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h
index e2636582cfd7..76fd0cfc6558 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h
@@ -10,16 +10,16 @@ git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35)
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2013-12-03 20:59:13)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
-- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
+- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53)
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 20932 bytes, from 2013-12-01 15:13:04)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30)
-Copyright (C) 2013 by the following authors:
+Copyright (C) 2013-2014 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
Permission is hereby granted, free of charge, to any person obtaining
@@ -148,9 +148,9 @@ static inline uint32_t REG_HDMI_GENERIC0(uint32_t i0) { return 0x00000088 + 0x4*
static inline uint32_t REG_HDMI_GENERIC1(uint32_t i0) { return 0x000000a8 + 0x4*i0; }
-static inline uint32_t REG_HDMI_ACR(uint32_t i0) { return 0x000000c4 + 0x8*i0; }
+static inline uint32_t REG_HDMI_ACR(enum hdmi_acr_cts i0) { return 0x000000c4 + 0x8*i0; }
-static inline uint32_t REG_HDMI_ACR_0(uint32_t i0) { return 0x000000c4 + 0x8*i0; }
+static inline uint32_t REG_HDMI_ACR_0(enum hdmi_acr_cts i0) { return 0x000000c4 + 0x8*i0; }
#define HDMI_ACR_0_CTS__MASK 0xfffff000
#define HDMI_ACR_0_CTS__SHIFT 12
static inline uint32_t HDMI_ACR_0_CTS(uint32_t val)
@@ -158,7 +158,7 @@ static inline uint32_t HDMI_ACR_0_CTS(uint32_t val)
return ((val) << HDMI_ACR_0_CTS__SHIFT) & HDMI_ACR_0_CTS__MASK;
}
-static inline uint32_t REG_HDMI_ACR_1(uint32_t i0) { return 0x000000c8 + 0x8*i0; }
+static inline uint32_t REG_HDMI_ACR_1(enum hdmi_acr_cts i0) { return 0x000000c8 + 0x8*i0; }
#define HDMI_ACR_1_N__MASK 0xffffffff
#define HDMI_ACR_1_N__SHIFT 0
static inline uint32_t HDMI_ACR_1_N(uint32_t val)
@@ -552,6 +552,103 @@ static inline uint32_t HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(uint32_t val)
#define REG_HDMI_8960_PHY_REG11 0x0000042c
#define REG_HDMI_8960_PHY_REG12 0x00000430
+#define HDMI_8960_PHY_REG12_SW_RESET 0x00000020
+#define HDMI_8960_PHY_REG12_PWRDN_B 0x00000080
+
+#define REG_HDMI_8960_PHY_REG_BIST_CFG 0x00000434
+
+#define REG_HDMI_8960_PHY_DEBUG_BUS_SEL 0x00000438
+
+#define REG_HDMI_8960_PHY_REG_MISC0 0x0000043c
+
+#define REG_HDMI_8960_PHY_REG13 0x00000440
+
+#define REG_HDMI_8960_PHY_REG14 0x00000444
+
+#define REG_HDMI_8960_PHY_REG15 0x00000448
+
+#define REG_HDMI_8960_PHY_PLL_REFCLK_CFG 0x00000500
+
+#define REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG 0x00000504
+
+#define REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 0x00000508
+
+#define REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 0x0000050c
+
+#define REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG 0x00000510
+
+#define REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG 0x00000514
+
+#define REG_HDMI_8960_PHY_PLL_PWRDN_B 0x00000518
+#define HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL 0x00000002
+#define HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B 0x00000008
+
+#define REG_HDMI_8960_PHY_PLL_SDM_CFG0 0x0000051c
+
+#define REG_HDMI_8960_PHY_PLL_SDM_CFG1 0x00000520
+
+#define REG_HDMI_8960_PHY_PLL_SDM_CFG2 0x00000524
+
+#define REG_HDMI_8960_PHY_PLL_SDM_CFG3 0x00000528
+
+#define REG_HDMI_8960_PHY_PLL_SDM_CFG4 0x0000052c
+
+#define REG_HDMI_8960_PHY_PLL_SSC_CFG0 0x00000530
+
+#define REG_HDMI_8960_PHY_PLL_SSC_CFG1 0x00000534
+
+#define REG_HDMI_8960_PHY_PLL_SSC_CFG2 0x00000538
+
+#define REG_HDMI_8960_PHY_PLL_SSC_CFG3 0x0000053c
+
+#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 0x00000540
+
+#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 0x00000544
+
+#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 0x00000548
+
+#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 0x0000054c
+
+#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 0x00000550
+
+#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 0x00000554
+
+#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 0x00000558
+
+#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 0x0000055c
+
+#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 0x00000560
+
+#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 0x00000564
+
+#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 0x00000568
+
+#define REG_HDMI_8960_PHY_PLL_DEBUG_SEL 0x0000056c
+
+#define REG_HDMI_8960_PHY_PLL_MISC0 0x00000570
+
+#define REG_HDMI_8960_PHY_PLL_MISC1 0x00000574
+
+#define REG_HDMI_8960_PHY_PLL_MISC2 0x00000578
+
+#define REG_HDMI_8960_PHY_PLL_MISC3 0x0000057c
+
+#define REG_HDMI_8960_PHY_PLL_MISC4 0x00000580
+
+#define REG_HDMI_8960_PHY_PLL_MISC5 0x00000584
+
+#define REG_HDMI_8960_PHY_PLL_MISC6 0x00000588
+
+#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS0 0x0000058c
+
+#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS1 0x00000590
+
+#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS2 0x00000594
+
+#define REG_HDMI_8960_PHY_PLL_STATUS0 0x00000598
+#define HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK 0x00000001
+
+#define REG_HDMI_8960_PHY_PLL_STATUS1 0x0000059c
#define REG_HDMI_8x74_ANA_CFG0 0x00000000
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
index 28f7e3ec6c28..4aca2a3c667c 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
@@ -63,7 +63,7 @@ static int gpio_config(struct hdmi *hdmi, bool on)
ret = gpio_request(config->mux_en_gpio, "HDMI_MUX_EN");
if (ret) {
dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
- "HDMI_MUX_SEL", config->mux_en_gpio, ret);
+ "HDMI_MUX_EN", config->mux_en_gpio, ret);
goto error4;
}
gpio_set_value_cansleep(config->mux_en_gpio, 1);
@@ -78,6 +78,19 @@ static int gpio_config(struct hdmi *hdmi, bool on)
}
gpio_set_value_cansleep(config->mux_sel_gpio, 0);
}
+
+ if (config->mux_lpm_gpio != -1) {
+ ret = gpio_request(config->mux_lpm_gpio,
+ "HDMI_MUX_LPM");
+ if (ret) {
+ dev_err(dev->dev,
+ "'%s'(%d) gpio_request failed: %d\n",
+ "HDMI_MUX_LPM",
+ config->mux_lpm_gpio, ret);
+ goto error6;
+ }
+ gpio_set_value_cansleep(config->mux_lpm_gpio, 1);
+ }
DBG("gpio on");
} else {
gpio_free(config->ddc_clk_gpio);
@@ -93,11 +106,19 @@ static int gpio_config(struct hdmi *hdmi, bool on)
gpio_set_value_cansleep(config->mux_sel_gpio, 1);
gpio_free(config->mux_sel_gpio);
}
+
+ if (config->mux_lpm_gpio != -1) {
+ gpio_set_value_cansleep(config->mux_lpm_gpio, 0);
+ gpio_free(config->mux_lpm_gpio);
+ }
DBG("gpio off");
}
return 0;
+error6:
+ if (config->mux_sel_gpio != -1)
+ gpio_free(config->mux_sel_gpio);
error5:
if (config->mux_en_gpio != -1)
gpio_free(config->mux_en_gpio);
@@ -306,7 +327,7 @@ static void hdmi_connector_destroy(struct drm_connector *connector)
hdp_disable(hdmi_connector);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
hdmi_unreference(hdmi_connector->hdmi);
@@ -416,7 +437,7 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)
connector->interlace_allowed = 1;
connector->doublescan_allowed = 0;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
ret = hpd_enable(hdmi_connector);
if (ret) {
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c
index e5b7ed5b8f01..f408b69486a8 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c
@@ -15,14 +15,377 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#ifdef CONFIG_COMMON_CLK
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#endif
+
#include "hdmi.h"
struct hdmi_phy_8960 {
struct hdmi_phy base;
struct hdmi *hdmi;
+#ifdef CONFIG_COMMON_CLK
+ struct clk_hw pll_hw;
+ struct clk *pll;
+ unsigned long pixclk;
+#endif
};
#define to_hdmi_phy_8960(x) container_of(x, struct hdmi_phy_8960, base)
+#ifdef CONFIG_COMMON_CLK
+#define clk_to_phy(x) container_of(x, struct hdmi_phy_8960, pll_hw)
+
+/*
+ * HDMI PLL:
+ *
+ * To get the parent clock setup properly, we need to plug in hdmi pll
+ * configuration into common-clock-framework.
+ */
+
+struct pll_rate {
+ unsigned long rate;
+ struct {
+ uint32_t val;
+ uint32_t reg;
+ } conf[32];
+};
+
+/* NOTE: keep sorted highest freq to lowest: */
+static const struct pll_rate freqtbl[] = {
+ /* 1080p60/1080p50 case */
+ { 148500000, {
+ { 0x02, REG_HDMI_8960_PHY_PLL_REFCLK_CFG },
+ { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
+ { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+ { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+ { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG },
+ { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
+ { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B },
+ { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0 },
+ { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1 },
+ { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2 },
+ { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 },
+ { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3 },
+ { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 },
+ { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 },
+ { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 },
+ { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 },
+ { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 },
+ { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 },
+ { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 },
+ { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 },
+ { 0, 0 } }
+ },
+ { 108000000, {
+ { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG },
+ { 0x21, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+ { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+ { 0x1c, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 },
+ { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 },
+ { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 },
+ { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 },
+ { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG0 },
+ { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG1 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG3 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 },
+ { 0, 0 } }
+ },
+ /* 720p60/720p50/1080i60/1080i50/1080p24/1080p30/1080p25 */
+ { 74250000, {
+ { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B },
+ { 0x12, REG_HDMI_8960_PHY_PLL_REFCLK_CFG },
+ { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+ { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+ { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0 },
+ { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 },
+ { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 },
+ { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 },
+ { 0, 0 } }
+ },
+ { 65000000, {
+ { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG },
+ { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+ { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+ { 0x8a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 },
+ { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 },
+ { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 },
+ { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 },
+ { 0x0b, REG_HDMI_8960_PHY_PLL_SDM_CFG0 },
+ { 0x4b, REG_HDMI_8960_PHY_PLL_SDM_CFG1 },
+ { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG2 },
+ { 0x09, REG_HDMI_8960_PHY_PLL_SDM_CFG3 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 },
+ { 0, 0 } }
+ },
+ /* 480p60/480i60 */
+ { 27030000, {
+ { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B },
+ { 0x38, REG_HDMI_8960_PHY_PLL_REFCLK_CFG },
+ { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
+ { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+ { 0xff, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG0 },
+ { 0x4e, REG_HDMI_8960_PHY_PLL_SDM_CFG1 },
+ { 0xd7, REG_HDMI_8960_PHY_PLL_SDM_CFG2 },
+ { 0x03, REG_HDMI_8960_PHY_PLL_SDM_CFG3 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 },
+ { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 },
+ { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 },
+ { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 },
+ { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 },
+ { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 },
+ { 0, 0 } }
+ },
+ /* 576p50/576i50 */
+ { 27000000, {
+ { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG },
+ { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
+ { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+ { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+ { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG },
+ { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
+ { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B },
+ { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG0 },
+ { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1 },
+ { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2 },
+ { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 },
+ { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3 },
+ { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 },
+ { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 },
+ { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 },
+ { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 },
+ { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 },
+ { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 },
+ { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 },
+ { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 },
+ { 0, 0 } }
+ },
+ /* 640x480p60 */
+ { 25200000, {
+ { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG },
+ { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
+ { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
+ { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
+ { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG },
+ { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
+ { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B },
+ { 0x77, REG_HDMI_8960_PHY_PLL_SDM_CFG0 },
+ { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2 },
+ { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 },
+ { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 },
+ { 0x20, REG_HDMI_8960_PHY_PLL_SSC_CFG3 },
+ { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 },
+ { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 },
+ { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 },
+ { 0xf4, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 },
+ { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 },
+ { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 },
+ { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 },
+ { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 },
+ { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 },
+ { 0, 0 } }
+ },
+};
+
+static int hdmi_pll_enable(struct clk_hw *hw)
+{
+ struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw);
+ struct hdmi *hdmi = phy_8960->hdmi;
+ int timeout_count, pll_lock_retry = 10;
+ unsigned int val;
+
+ DBG("");
+
+ /* Assert PLL S/W reset */
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0, 0x10);
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1, 0x1a);
+
+ /* Wait for a short time before de-asserting
+ * to allow the hardware to complete its job.
+ * This much of delay should be fine for hardware
+ * to assert and de-assert.
+ */
+ udelay(10);
+
+ /* De-assert PLL S/W reset */
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
+
+ val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12);
+ val |= HDMI_8960_PHY_REG12_SW_RESET;
+ /* Assert PHY S/W reset */
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
+ val &= ~HDMI_8960_PHY_REG12_SW_RESET;
+ /* Wait for a short time before de-asserting
+ to allow the hardware to complete its job.
+ This much of delay should be fine for hardware
+ to assert and de-assert. */
+ udelay(10);
+ /* De-assert PHY S/W reset */
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x3f);
+
+ val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12);
+ val |= HDMI_8960_PHY_REG12_PWRDN_B;
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
+ /* Wait 10 us for enabling global power for PHY */
+ mb();
+ udelay(10);
+
+ val = hdmi_read(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B);
+ val |= HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B;
+ val &= ~HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL;
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x80);
+
+ timeout_count = 1000;
+ while (--pll_lock_retry > 0) {
+
+ /* are we there yet? */
+ val = hdmi_read(hdmi, REG_HDMI_8960_PHY_PLL_STATUS0);
+ if (val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK)
+ break;
+
+ udelay(1);
+
+ if (--timeout_count > 0)
+ continue;
+
+ /*
+ * PLL has still not locked.
+ * Do a software reset and try again
+ * Assert PLL S/W reset first
+ */
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
+ udelay(10);
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
+
+ /*
+ * Wait for a short duration for the PLL calibration
+ * before checking if the PLL gets locked
+ */
+ udelay(350);
+
+ timeout_count = 1000;
+ }
+
+ return 0;
+}
+
+static void hdmi_pll_disable(struct clk_hw *hw)
+{
+ struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw);
+ struct hdmi *hdmi = phy_8960->hdmi;
+ unsigned int val;
+
+ DBG("");
+
+ val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12);
+ val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
+
+ val = hdmi_read(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B);
+ val |= HDMI_8960_PHY_REG12_SW_RESET;
+ val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
+ /* Make sure HDMI PHY/PLL are powered down */
+ mb();
+}
+
+static const struct pll_rate *find_rate(unsigned long rate)
+{
+ int i;
+ for (i = 1; i < ARRAY_SIZE(freqtbl); i++)
+ if (rate > freqtbl[i].rate)
+ return &freqtbl[i-1];
+ return &freqtbl[i-1];
+}
+
+static unsigned long hdmi_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw);
+ return phy_8960->pixclk;
+}
+
+static long hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ const struct pll_rate *pll_rate = find_rate(rate);
+ return pll_rate->rate;
+}
+
+static int hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw);
+ struct hdmi *hdmi = phy_8960->hdmi;
+ const struct pll_rate *pll_rate = find_rate(rate);
+ int i;
+
+ DBG("rate=%lu", rate);
+
+ for (i = 0; pll_rate->conf[i].reg; i++)
+ hdmi_write(hdmi, pll_rate->conf[i].reg, pll_rate->conf[i].val);
+
+ phy_8960->pixclk = rate;
+
+ return 0;
+}
+
+
+static const struct clk_ops hdmi_pll_ops = {
+ .enable = hdmi_pll_enable,
+ .disable = hdmi_pll_disable,
+ .recalc_rate = hdmi_pll_recalc_rate,
+ .round_rate = hdmi_pll_round_rate,
+ .set_rate = hdmi_pll_set_rate,
+};
+
+static const char *hdmi_pll_parents[] = {
+ "pxo",
+};
+
+static struct clk_init_data pll_init = {
+ .name = "hdmi_pll",
+ .ops = &hdmi_pll_ops,
+ .parent_names = hdmi_pll_parents,
+ .num_parents = ARRAY_SIZE(hdmi_pll_parents),
+};
+#endif
+
+/*
+ * HDMI Phy:
+ */
+
static void hdmi_phy_8960_destroy(struct hdmi_phy *phy)
{
struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
@@ -86,6 +449,9 @@ static void hdmi_phy_8960_powerup(struct hdmi_phy *phy,
struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
struct hdmi *hdmi = phy_8960->hdmi;
+ DBG("pixclock: %lu", pixclock);
+
+ hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x00);
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG0, 0x1b);
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG1, 0xf2);
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG4, 0x00);
@@ -104,6 +470,8 @@ static void hdmi_phy_8960_powerdown(struct hdmi_phy *phy)
struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
struct hdmi *hdmi = phy_8960->hdmi;
+ DBG("");
+
hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x7f);
}
@@ -119,6 +487,14 @@ struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi)
struct hdmi_phy_8960 *phy_8960;
struct hdmi_phy *phy = NULL;
int ret;
+#ifdef CONFIG_COMMON_CLK
+ int i;
+
+ /* sanity check: */
+ for (i = 0; i < (ARRAY_SIZE(freqtbl) - 1); i++)
+ if (WARN_ON(freqtbl[i].rate < freqtbl[i+1].rate))
+ return ERR_PTR(-EINVAL);
+#endif
phy_8960 = kzalloc(sizeof(*phy_8960), GFP_KERNEL);
if (!phy_8960) {
@@ -132,6 +508,16 @@ struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi)
phy_8960->hdmi = hdmi;
+#ifdef CONFIG_COMMON_CLK
+ phy_8960->pll_hw.init = &pll_init;
+ phy_8960->pll = devm_clk_register(hdmi->dev->dev, &phy_8960->pll_hw);
+ if (IS_ERR(phy_8960->pll)) {
+ ret = PTR_ERR(phy_8960->pll);
+ phy_8960->pll = NULL;
+ goto fail;
+ }
+#endif
+
return phy;
fail:
diff --git a/drivers/gpu/drm/msm/hdmi/qfprom.xml.h b/drivers/gpu/drm/msm/hdmi/qfprom.xml.h
index d591567173c4..d53c29327df9 100644
--- a/drivers/gpu/drm/msm/hdmi/qfprom.xml.h
+++ b/drivers/gpu/drm/msm/hdmi/qfprom.xml.h
@@ -10,14 +10,14 @@ git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35)
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2013-12-03 20:59:13)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
-- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
+- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53)
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 20932 bytes, from 2013-12-01 15:13:04)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30)
Copyright (C) 2013 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h
index 416a26e1e58d..03c0bd9cd5b9 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h
@@ -10,16 +10,16 @@ git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 647 bytes, from 2013-11-30 14:45:35)
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2013-12-03 20:59:13)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20457 bytes, from 2014-08-01 12:22:48)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2014-07-17 15:34:33)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-07-17 15:34:33)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
-- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
+- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-08-01 12:23:53)
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 20932 bytes, from 2013-12-01 15:13:04)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-07-17 15:33:30)
-Copyright (C) 2013 by the following authors:
+Copyright (C) 2013-2014 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
Permission is hereby granted, free of charge, to any person obtaining
@@ -871,6 +871,101 @@ static inline uint32_t MDP4_LCDC_UNDERFLOW_CLR_COLOR(uint32_t val)
#define MDP4_LCDC_CTRL_POLARITY_VSYNC_LOW 0x00000002
#define MDP4_LCDC_CTRL_POLARITY_DATA_EN_LOW 0x00000004
+#define REG_MDP4_LCDC_LVDS_INTF_CTL 0x000c2000
+#define MDP4_LCDC_LVDS_INTF_CTL_MODE_SEL 0x00000004
+#define MDP4_LCDC_LVDS_INTF_CTL_RGB_OUT 0x00000008
+#define MDP4_LCDC_LVDS_INTF_CTL_CH_SWAP 0x00000010
+#define MDP4_LCDC_LVDS_INTF_CTL_CH1_RES_BIT 0x00000020
+#define MDP4_LCDC_LVDS_INTF_CTL_CH2_RES_BIT 0x00000040
+#define MDP4_LCDC_LVDS_INTF_CTL_ENABLE 0x00000080
+#define MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN 0x00000100
+#define MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN 0x00000200
+#define MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN 0x00000400
+#define MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN 0x00000800
+#define MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN 0x00001000
+#define MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN 0x00002000
+#define MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN 0x00004000
+#define MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE3_EN 0x00008000
+#define MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN 0x00010000
+#define MDP4_LCDC_LVDS_INTF_CTL_CH2_CLK_LANE_EN 0x00020000
+
+static inline uint32_t REG_MDP4_LCDC_LVDS_MUX_CTL(uint32_t i0) { return 0x000c2014 + 0x8*i0; }
+
+static inline uint32_t REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(uint32_t i0) { return 0x000c2014 + 0x8*i0; }
+#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0__MASK 0x000000ff
+#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0__SHIFT 0
+static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(uint32_t val)
+{
+ return ((val) << MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0__MASK;
+}
+#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1__MASK 0x0000ff00
+#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1__SHIFT 8
+static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(uint32_t val)
+{
+ return ((val) << MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1__MASK;
+}
+#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2__MASK 0x00ff0000
+#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2__SHIFT 16
+static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(uint32_t val)
+{
+ return ((val) << MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2__MASK;
+}
+#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3__MASK 0xff000000
+#define MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3__SHIFT 24
+static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(uint32_t val)
+{
+ return ((val) << MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3__MASK;
+}
+
+static inline uint32_t REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(uint32_t i0) { return 0x000c2018 + 0x8*i0; }
+#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4__MASK 0x000000ff
+#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4__SHIFT 0
+static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(uint32_t val)
+{
+ return ((val) << MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4__MASK;
+}
+#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5__MASK 0x0000ff00
+#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5__SHIFT 8
+static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(uint32_t val)
+{
+ return ((val) << MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5__MASK;
+}
+#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6__MASK 0x00ff0000
+#define MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6__SHIFT 16
+static inline uint32_t MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(uint32_t val)
+{
+ return ((val) << MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6__SHIFT) & MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6__MASK;
+}
+
+#define REG_MDP4_LCDC_LVDS_PHY_RESET 0x000c2034
+
+#define REG_MDP4_LVDS_PHY_PLL_CTRL_0 0x000c3000
+
+#define REG_MDP4_LVDS_PHY_PLL_CTRL_1 0x000c3004
+
+#define REG_MDP4_LVDS_PHY_PLL_CTRL_2 0x000c3008
+
+#define REG_MDP4_LVDS_PHY_PLL_CTRL_3 0x000c300c
+
+#define REG_MDP4_LVDS_PHY_PLL_CTRL_5 0x000c3014
+
+#define REG_MDP4_LVDS_PHY_PLL_CTRL_6 0x000c3018
+
+#define REG_MDP4_LVDS_PHY_PLL_CTRL_7 0x000c301c
+
+#define REG_MDP4_LVDS_PHY_PLL_CTRL_8 0x000c3020
+
+#define REG_MDP4_LVDS_PHY_PLL_CTRL_9 0x000c3024
+
+#define REG_MDP4_LVDS_PHY_PLL_LOCKED 0x000c3080
+
+#define REG_MDP4_LVDS_PHY_CFG2 0x000c3108
+
+#define REG_MDP4_LVDS_PHY_CFG0 0x000c3100
+#define MDP4_LVDS_PHY_CFG0_SERIALIZATION_ENBLE 0x00000010
+#define MDP4_LVDS_PHY_CFG0_CHANNEL0 0x00000040
+#define MDP4_LVDS_PHY_CFG0_CHANNEL1 0x00000080
+
#define REG_MDP4_DTV 0x000d0000
#define REG_MDP4_DTV_ENABLE 0x000d0000
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index 74cebb51e8c2..7d00f7fb5773 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -273,14 +273,17 @@ static void blend_setup(struct drm_crtc *crtc)
};
bool alpha[4]= { false, false, false, false };
+ /* Don't rely on value read back from hw, but instead use our
+ * own shadowed value. Possibly disable/reenable looses the
+ * previous value and goes back to power-on default?
+ */
+ mixer_cfg = mdp4_kms->mixer_cfg;
+
mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW0(ovlp), 0);
mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_LOW1(ovlp), 0);
mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH0(ovlp), 0);
mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH1(ovlp), 0);
- /* TODO single register for all CRTCs, so this won't work properly
- * when multiple CRTCs are active..
- */
for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) {
struct drm_plane *plane = mdp4_crtc->planes[i];
if (plane) {
@@ -291,7 +294,8 @@ static void blend_setup(struct drm_crtc *crtc)
to_mdp_format(msm_framebuffer_format(plane->fb));
alpha[idx-1] = format->alpha_enable;
}
- mixer_cfg |= mixercfg(mdp4_crtc->mixer, pipe_id, stages[idx]);
+ mixer_cfg = mixercfg(mixer_cfg, mdp4_crtc->mixer,
+ pipe_id, stages[idx]);
}
}
@@ -320,6 +324,7 @@ static void blend_setup(struct drm_crtc *crtc)
mdp4_write(mdp4_kms, REG_MDP4_OVLP_STAGE_TRANSP_HIGH1(ovlp, i), 0);
}
+ mdp4_kms->mixer_cfg = mixer_cfg;
mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, mixer_cfg);
}
@@ -397,6 +402,7 @@ static void mdp4_crtc_prepare(struct drm_crtc *crtc)
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
DBG("%s", mdp4_crtc->name);
/* make sure we hold a ref to mdp clks while setting up mode: */
+ drm_crtc_vblank_get(crtc);
mdp4_enable(get_kms(crtc));
mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
}
@@ -407,6 +413,7 @@ static void mdp4_crtc_commit(struct drm_crtc *crtc)
crtc_flush(crtc);
/* drop the ref to mdp clk's that we got in prepare: */
mdp4_disable(get_kms(crtc));
+ drm_crtc_vblank_put(crtc);
}
static int mdp4_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
@@ -670,7 +677,7 @@ void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config)
}
/* set interface for routing crtc->encoder: */
-void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf)
+void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf, int mixer)
{
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
struct mdp4_kms *mdp4_kms = get_kms(crtc);
@@ -696,15 +703,13 @@ void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf)
if (intf == INTF_DSI_VIDEO) {
intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_CMD;
intf_sel |= MDP4_DISP_INTF_SEL_DSI_VIDEO;
- mdp4_crtc->mixer = 0;
} else if (intf == INTF_DSI_CMD) {
intf_sel &= ~MDP4_DISP_INTF_SEL_DSI_VIDEO;
intf_sel |= MDP4_DISP_INTF_SEL_DSI_CMD;
- mdp4_crtc->mixer = 0;
- } else if (intf == INTF_LCDC_DTV){
- mdp4_crtc->mixer = 1;
}
+ mdp4_crtc->mixer = mixer;
+
blend_setup(crtc);
DBG("%s: intf_sel=%08x", mdp4_crtc->name, intf_sel);
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c
index 067ed03b35fe..c3878420180b 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_dtv_encoder.c
@@ -233,7 +233,7 @@ static void mdp4_dtv_encoder_commit(struct drm_encoder *encoder)
MDP4_DMA_CONFIG_G_BPC(BPC8) |
MDP4_DMA_CONFIG_B_BPC(BPC8) |
MDP4_DMA_CONFIG_PACK(0x21));
- mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV);
+ mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV, 1);
mdp4_dtv_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
}
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
index 0bb4faa17523..79d804e61cc4 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
@@ -106,6 +106,7 @@ static int mdp4_hw_init(struct msm_kms *kms)
if (mdp4_kms->rev >= 2)
mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG_UPDATE_METHOD, 1);
+ mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, 0);
/* disable CSC matrix / YUV by default: */
mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG1), 0);
@@ -147,7 +148,7 @@ static void mdp4_destroy(struct msm_kms *kms)
if (mdp4_kms->blank_cursor_iova)
msm_gem_put_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id);
if (mdp4_kms->blank_cursor_bo)
- drm_gem_object_unreference(mdp4_kms->blank_cursor_bo);
+ drm_gem_object_unreference_unlocked(mdp4_kms->blank_cursor_bo);
kfree(mdp4_kms);
}
@@ -176,6 +177,8 @@ int mdp4_disable(struct mdp4_kms *mdp4_kms)
if (mdp4_kms->pclk)
clk_disable_unprepare(mdp4_kms->pclk);
clk_disable_unprepare(mdp4_kms->lut_clk);
+ if (mdp4_kms->axi_clk)
+ clk_disable_unprepare(mdp4_kms->axi_clk);
return 0;
}
@@ -188,10 +191,34 @@ int mdp4_enable(struct mdp4_kms *mdp4_kms)
if (mdp4_kms->pclk)
clk_prepare_enable(mdp4_kms->pclk);
clk_prepare_enable(mdp4_kms->lut_clk);
+ if (mdp4_kms->axi_clk)
+ clk_prepare_enable(mdp4_kms->axi_clk);
return 0;
}
+#ifdef CONFIG_OF
+static struct drm_panel *detect_panel(struct drm_device *dev, const char *name)
+{
+ struct device_node *n;
+ struct drm_panel *panel = NULL;
+
+ n = of_parse_phandle(dev->dev->of_node, name, 0);
+ if (n) {
+ panel = of_drm_find_panel(n);
+ if (!panel)
+ panel = ERR_PTR(-EPROBE_DEFER);
+ }
+
+ return panel;
+}
+#else
+static struct drm_panel *detect_panel(struct drm_device *dev, const char *name)
+{
+ // ??? maybe use a module param to specify which panel is attached?
+}
+#endif
+
static int modeset_init(struct mdp4_kms *mdp4_kms)
{
struct drm_device *dev = mdp4_kms->dev;
@@ -199,14 +226,11 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ struct drm_panel *panel;
struct hdmi *hdmi;
int ret;
- /*
- * NOTE: this is a bit simplistic until we add support
- * for more than just RGB1->DMA_E->DTV->HDMI
- */
-
/* construct non-private planes: */
plane = mdp4_plane_init(dev, VG1, false);
if (IS_ERR(plane)) {
@@ -224,7 +248,57 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
}
priv->planes[priv->num_planes++] = plane;
- /* the CRTCs get constructed with a private plane: */
+ /*
+ * Setup the LCDC/LVDS path: RGB2 -> DMA_P -> LCDC -> LVDS:
+ */
+
+ panel = detect_panel(dev, "qcom,lvds-panel");
+ if (IS_ERR(panel)) {
+ ret = PTR_ERR(panel);
+ dev_err(dev->dev, "failed to detect LVDS panel: %d\n", ret);
+ goto fail;
+ }
+
+ plane = mdp4_plane_init(dev, RGB2, true);
+ if (IS_ERR(plane)) {
+ dev_err(dev->dev, "failed to construct plane for RGB2\n");
+ ret = PTR_ERR(plane);
+ goto fail;
+ }
+
+ crtc = mdp4_crtc_init(dev, plane, priv->num_crtcs, 0, DMA_P);
+ if (IS_ERR(crtc)) {
+ dev_err(dev->dev, "failed to construct crtc for DMA_P\n");
+ ret = PTR_ERR(crtc);
+ goto fail;
+ }
+
+ encoder = mdp4_lcdc_encoder_init(dev, panel);
+ if (IS_ERR(encoder)) {
+ dev_err(dev->dev, "failed to construct LCDC encoder\n");
+ ret = PTR_ERR(encoder);
+ goto fail;
+ }
+
+ /* LCDC can be hooked to DMA_P: */
+ encoder->possible_crtcs = 1 << priv->num_crtcs;
+
+ priv->crtcs[priv->num_crtcs++] = crtc;
+ priv->encoders[priv->num_encoders++] = encoder;
+
+ connector = mdp4_lvds_connector_init(dev, panel, encoder);
+ if (IS_ERR(connector)) {
+ ret = PTR_ERR(connector);
+ dev_err(dev->dev, "failed to initialize LVDS connector: %d\n", ret);
+ goto fail;
+ }
+
+ priv->connectors[priv->num_connectors++] = connector;
+
+ /*
+ * Setup DTV/HDMI path: RGB1 -> DMA_E -> DTV -> HDMI:
+ */
+
plane = mdp4_plane_init(dev, RGB1, true);
if (IS_ERR(plane)) {
dev_err(dev->dev, "failed to construct plane for RGB1\n");
@@ -238,7 +312,6 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
ret = PTR_ERR(crtc);
goto fail;
}
- priv->crtcs[priv->num_crtcs++] = crtc;
encoder = mdp4_dtv_encoder_init(dev);
if (IS_ERR(encoder)) {
@@ -246,7 +319,11 @@ static int modeset_init(struct mdp4_kms *mdp4_kms)
ret = PTR_ERR(encoder);
goto fail;
}
- encoder->possible_crtcs = 0x1; /* DTV can be hooked to DMA_E */
+
+ /* DTV can be hooked to DMA_E: */
+ encoder->possible_crtcs = 1 << priv->num_crtcs;
+
+ priv->crtcs[priv->num_crtcs++] = crtc;
priv->encoders[priv->num_encoders++] = encoder;
hdmi = hdmi_init(dev, encoder);
@@ -294,15 +371,17 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
goto fail;
}
- mdp4_kms->dsi_pll_vdda = devm_regulator_get(&pdev->dev, "dsi_pll_vdda");
+ mdp4_kms->dsi_pll_vdda =
+ devm_regulator_get_optional(&pdev->dev, "dsi_pll_vdda");
if (IS_ERR(mdp4_kms->dsi_pll_vdda))
mdp4_kms->dsi_pll_vdda = NULL;
- mdp4_kms->dsi_pll_vddio = devm_regulator_get(&pdev->dev, "dsi_pll_vddio");
+ mdp4_kms->dsi_pll_vddio =
+ devm_regulator_get_optional(&pdev->dev, "dsi_pll_vddio");
if (IS_ERR(mdp4_kms->dsi_pll_vddio))
mdp4_kms->dsi_pll_vddio = NULL;
- mdp4_kms->vdd = devm_regulator_get(&pdev->dev, "vdd");
+ mdp4_kms->vdd = devm_regulator_get_exclusive(&pdev->dev, "vdd");
if (IS_ERR(mdp4_kms->vdd))
mdp4_kms->vdd = NULL;
@@ -333,6 +412,13 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
goto fail;
}
+ mdp4_kms->axi_clk = devm_clk_get(&pdev->dev, "mdp_axi_clk");
+ if (IS_ERR(mdp4_kms->axi_clk)) {
+ dev_err(dev->dev, "failed to get axi_clk\n");
+ ret = PTR_ERR(mdp4_kms->axi_clk);
+ goto fail;
+ }
+
clk_set_rate(mdp4_kms->clk, config->max_clk);
clk_set_rate(mdp4_kms->lut_clk, config->max_clk);
@@ -348,7 +434,7 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
mdelay(16);
if (config->iommu) {
- mmu = msm_iommu_new(dev, config->iommu);
+ mmu = msm_iommu_new(&pdev->dev, config->iommu);
if (IS_ERR(mmu)) {
ret = PTR_ERR(mmu);
goto fail;
@@ -406,6 +492,8 @@ static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev)
static struct mdp4_platform_config config = {};
#ifdef CONFIG_OF
/* TODO */
+ config.max_clk = 266667000;
+ config.iommu = iommu_domain_alloc(&platform_bus_type);
#else
if (cpu_is_apq8064())
config.max_clk = 266667000;
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
index 715520c54cde..9ff6e7ccfe90 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
@@ -23,6 +23,8 @@
#include "mdp/mdp_kms.h"
#include "mdp4.xml.h"
+#include "drm_panel.h"
+
struct mdp4_kms {
struct mdp_kms base;
@@ -30,6 +32,13 @@ struct mdp4_kms {
int rev;
+ /* Shadow value for MDP4_LAYERMIXER_IN_CFG.. since setup for all
+ * crtcs/encoders is in one shared register, we need to update it
+ * via read/modify/write. But to avoid getting confused by power-
+ * on-default values after resume, use this shadow value instead:
+ */
+ uint32_t mixer_cfg;
+
/* mapper-id used to request GEM buffer mapped for scanout: */
int id;
@@ -42,6 +51,7 @@ struct mdp4_kms {
struct clk *clk;
struct clk *pclk;
struct clk *lut_clk;
+ struct clk *axi_clk;
struct mdp_irq error_handler;
@@ -73,7 +83,7 @@ static inline uint32_t pipe2flush(enum mdp4_pipe pipe)
case VG1: return MDP4_OVERLAY_FLUSH_VG1;
case VG2: return MDP4_OVERLAY_FLUSH_VG2;
case RGB1: return MDP4_OVERLAY_FLUSH_RGB1;
- case RGB2: return MDP4_OVERLAY_FLUSH_RGB1;
+ case RGB2: return MDP4_OVERLAY_FLUSH_RGB2;
default: return 0;
}
}
@@ -107,38 +117,50 @@ static inline uint32_t dma2err(enum mdp4_dma dma)
}
}
-static inline uint32_t mixercfg(int mixer, enum mdp4_pipe pipe,
- enum mdp_mixer_stage_id stage)
+static inline uint32_t mixercfg(uint32_t mixer_cfg, int mixer,
+ enum mdp4_pipe pipe, enum mdp_mixer_stage_id stage)
{
- uint32_t mixer_cfg = 0;
-
switch (pipe) {
case VG1:
- mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE0(stage) |
+ mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE0__MASK |
+ MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1);
+ mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE0(stage) |
COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE0_MIXER1);
break;
case VG2:
- mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE1(stage) |
+ mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE1__MASK |
+ MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1);
+ mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE1(stage) |
COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE1_MIXER1);
break;
case RGB1:
- mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE2(stage) |
+ mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE2__MASK |
+ MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1);
+ mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE2(stage) |
COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE2_MIXER1);
break;
case RGB2:
- mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE3(stage) |
+ mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE3__MASK |
+ MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1);
+ mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE3(stage) |
COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE3_MIXER1);
break;
case RGB3:
- mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE4(stage) |
+ mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE4__MASK |
+ MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1);
+ mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE4(stage) |
COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE4_MIXER1);
break;
case VG3:
- mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE5(stage) |
+ mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE5__MASK |
+ MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1);
+ mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE5(stage) |
COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE5_MIXER1);
break;
case VG4:
- mixer_cfg = MDP4_LAYERMIXER_IN_CFG_PIPE6(stage) |
+ mixer_cfg &= ~(MDP4_LAYERMIXER_IN_CFG_PIPE6__MASK |
+ MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1);
+ mixer_cfg |= MDP4_LAYERMIXER_IN_CFG_PIPE6(stage) |
COND(mixer == 1, MDP4_LAYERMIXER_IN_CFG_PIPE6_MIXER1);
break;
default:
@@ -187,7 +209,7 @@ struct drm_plane *mdp4_plane_init(struct drm_device *dev,
uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc);
void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config);
-void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf);
+void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf, int mixer);
void mdp4_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane);
void mdp4_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane);
struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
@@ -197,6 +219,22 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
long mdp4_dtv_round_pixclk(struct drm_encoder *encoder, unsigned long rate);
struct drm_encoder *mdp4_dtv_encoder_init(struct drm_device *dev);
+long mdp4_lcdc_round_pixclk(struct drm_encoder *encoder, unsigned long rate);
+struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev,
+ struct drm_panel *panel);
+
+struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev,
+ struct drm_panel *panel, struct drm_encoder *encoder);
+
+#ifdef CONFIG_COMMON_CLK
+struct clk *mpd4_lvds_pll_init(struct drm_device *dev);
+#else
+static inline struct clk *mpd4_lvds_pll_init(struct drm_device *dev)
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif
+
#ifdef CONFIG_MSM_BUS_SCALING
static inline int match_dev_name(struct device *dev, void *data)
{
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c
new file mode 100644
index 000000000000..41f6436754fc
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ * Author: Vinay Simha <vinaysimha@inforcecomputing.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.
+ *
+ * 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 "mdp4_kms.h"
+
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+struct mdp4_lcdc_encoder {
+ struct drm_encoder base;
+ struct drm_panel *panel;
+ struct clk *lcdc_clk;
+ unsigned long int pixclock;
+ struct regulator *regs[3];
+ bool enabled;
+ uint32_t bsc;
+};
+#define to_mdp4_lcdc_encoder(x) container_of(x, struct mdp4_lcdc_encoder, base)
+
+static struct mdp4_kms *get_kms(struct drm_encoder *encoder)
+{
+ struct msm_drm_private *priv = encoder->dev->dev_private;
+ return to_mdp4_kms(to_mdp_kms(priv->kms));
+}
+
+#ifdef CONFIG_MSM_BUS_SCALING
+#include <mach/board.h>
+static void bs_init(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder)
+{
+ struct drm_device *dev = mdp4_lcdc_encoder->base.dev;
+ struct lcdc_platform_data *lcdc_pdata = mdp4_find_pdata("lvds.0");
+
+ if (!lcdc_pdata) {
+ dev_err(dev->dev, "could not find lvds pdata\n");
+ return;
+ }
+
+ if (lcdc_pdata->bus_scale_table) {
+ mdp4_lcdc_encoder->bsc = msm_bus_scale_register_client(
+ lcdc_pdata->bus_scale_table);
+ DBG("lvds : bus scale client: %08x", mdp4_lcdc_encoder->bsc);
+ }
+}
+
+static void bs_fini(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder)
+{
+ if (mdp4_lcdc_encoder->bsc) {
+ msm_bus_scale_unregister_client(mdp4_lcdc_encoder->bsc);
+ mdp4_lcdc_encoder->bsc = 0;
+ }
+}
+
+static void bs_set(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder, int idx)
+{
+ if (mdp4_lcdc_encoder->bsc) {
+ DBG("set bus scaling: %d", idx);
+ msm_bus_scale_client_update_request(mdp4_lcdc_encoder->bsc, idx);
+ }
+}
+#else
+static void bs_init(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) {}
+static void bs_fini(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder) {}
+static void bs_set(struct mdp4_lcdc_encoder *mdp4_lcdc_encoder, int idx) {}
+#endif
+
+static void mdp4_lcdc_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
+ to_mdp4_lcdc_encoder(encoder);
+ bs_fini(mdp4_lcdc_encoder);
+ drm_encoder_cleanup(encoder);
+ kfree(mdp4_lcdc_encoder);
+}
+
+static const struct drm_encoder_funcs mdp4_lcdc_encoder_funcs = {
+ .destroy = mdp4_lcdc_encoder_destroy,
+};
+
+/* this should probably be a helper: */
+struct drm_connector *get_connector(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_connector *connector;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ if (connector->encoder == encoder)
+ return connector;
+
+ return NULL;
+}
+
+static void setup_phy(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct drm_connector *connector = get_connector(encoder);
+ struct mdp4_kms *mdp4_kms = get_kms(encoder);
+ uint32_t lvds_intf = 0, lvds_phy_cfg0 = 0;
+ int bpp, nchan, swap;
+
+ if (!connector)
+ return;
+
+ bpp = 3 * connector->display_info.bpc;
+
+ if (!bpp)
+ bpp = 18;
+
+ /* TODO, these should come from panel somehow: */
+ nchan = 1;
+ swap = 0;
+
+ switch (bpp) {
+ case 24:
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0),
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x08) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x05) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x04) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x03));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0),
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x02) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x01) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x00));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1),
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x11) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x10) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0d) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0c));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1),
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0b) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0a) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x09));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2),
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x15));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2),
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x14) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x13) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x12));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(3),
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1b) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x17) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x16) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0f));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(3),
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0e) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x07) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x06));
+ if (nchan == 2) {
+ lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE3_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
+ } else {
+ lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE3_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
+ }
+ break;
+
+ case 18:
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(0),
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x0a) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x07) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x06) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x05));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(0),
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x04) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x03) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x02));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(1),
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x13) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x12) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x0f) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x0e));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(1),
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x0d) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x0c) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x0b));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_3_TO_0(2),
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT0(0x1a) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT1(0x19) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT2(0x18) |
+ MDP4_LCDC_LVDS_MUX_CTL_3_TO_0_BIT3(0x17));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_MUX_CTL_6_TO_4(2),
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT4(0x16) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT5(0x15) |
+ MDP4_LCDC_LVDS_MUX_CTL_6_TO_4_BIT6(0x14));
+ if (nchan == 2) {
+ lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE2_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE1_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH2_DATA_LANE0_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
+ } else {
+ lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE2_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE1_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_DATA_LANE0_EN;
+ }
+ lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_RGB_OUT;
+ break;
+
+ default:
+ dev_err(dev->dev, "unknown bpp: %d\n", bpp);
+ return;
+ }
+
+ switch (nchan) {
+ case 1:
+ lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0;
+ lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_MODE_SEL;
+ break;
+ case 2:
+ lvds_phy_cfg0 = MDP4_LVDS_PHY_CFG0_CHANNEL0 |
+ MDP4_LVDS_PHY_CFG0_CHANNEL1;
+ lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH2_CLK_LANE_EN |
+ MDP4_LCDC_LVDS_INTF_CTL_CH1_CLK_LANE_EN;
+ break;
+ default:
+ dev_err(dev->dev, "unknown # of channels: %d\n", nchan);
+ return;
+ }
+
+ if (swap)
+ lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_CH_SWAP;
+
+ lvds_intf |= MDP4_LCDC_LVDS_INTF_CTL_ENABLE;
+
+ mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0);
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_INTF_CTL, lvds_intf);
+ mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG2, 0x30);
+
+ mb();
+ udelay(1);
+ lvds_phy_cfg0 |= MDP4_LVDS_PHY_CFG0_SERIALIZATION_ENBLE;
+ mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, lvds_phy_cfg0);
+}
+
+static void mdp4_lcdc_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
+ to_mdp4_lcdc_encoder(encoder);
+ struct mdp4_kms *mdp4_kms = get_kms(encoder);
+ struct drm_panel *panel = mdp4_lcdc_encoder->panel;
+ bool enabled = (mode == DRM_MODE_DPMS_ON);
+ int i, ret;
+
+ DBG("mode=%d", mode);
+
+ if (enabled == mdp4_lcdc_encoder->enabled)
+ return;
+
+ if (enabled) {
+ unsigned long pc = mdp4_lcdc_encoder->pixclock;
+ int ret;
+
+ bs_set(mdp4_lcdc_encoder, 1);
+
+ for (i = 0; i < ARRAY_SIZE(mdp4_lcdc_encoder->regs); i++) {
+ ret = regulator_enable(mdp4_lcdc_encoder->regs[i]);
+ if (ret)
+ dev_err(dev->dev, "failed to enable regulator: %d\n", ret);
+ }
+
+ DBG("setting lcdc_clk=%lu", pc);
+ ret = clk_set_rate(mdp4_lcdc_encoder->lcdc_clk, pc);
+ if (ret)
+ dev_err(dev->dev, "failed to configure lcdc_clk: %d\n", ret);
+ ret = clk_prepare_enable(mdp4_lcdc_encoder->lcdc_clk);
+ if (ret)
+ dev_err(dev->dev, "failed to enable lcdc_clk: %d\n", ret);
+
+ if (panel)
+ drm_panel_enable(panel);
+
+ setup_phy(encoder);
+
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 1);
+ } else {
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0);
+
+ if (panel)
+ drm_panel_disable(panel);
+
+ /*
+ * Wait for a vsync so we know the ENABLE=0 latched before
+ * the (connector) source of the vsync's gets disabled,
+ * otherwise we end up in a funny state if we re-enable
+ * before the disable latches, which results that some of
+ * the settings changes for the new modeset (like new
+ * scanout buffer) don't latch properly..
+ */
+ mdp_irq_wait(&mdp4_kms->base, MDP4_IRQ_PRIMARY_VSYNC);
+
+ clk_disable_unprepare(mdp4_lcdc_encoder->lcdc_clk);
+
+ for (i = 0; i < ARRAY_SIZE(mdp4_lcdc_encoder->regs); i++) {
+ ret = regulator_disable(mdp4_lcdc_encoder->regs[i]);
+ if (ret)
+ dev_err(dev->dev, "failed to disable regulator: %d\n", ret);
+ }
+
+ bs_set(mdp4_lcdc_encoder, 0);
+ }
+
+ mdp4_lcdc_encoder->enabled = enabled;
+}
+
+static bool mdp4_lcdc_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void mdp4_lcdc_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
+ to_mdp4_lcdc_encoder(encoder);
+ struct mdp4_kms *mdp4_kms = get_kms(encoder);
+ uint32_t lcdc_hsync_skew, vsync_period, vsync_len, ctrl_pol;
+ uint32_t display_v_start, display_v_end;
+ uint32_t hsync_start_x, hsync_end_x;
+
+ mode = adjusted_mode;
+
+ DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+ mode->base.id, mode->name,
+ mode->vrefresh, mode->clock,
+ mode->hdisplay, mode->hsync_start,
+ mode->hsync_end, mode->htotal,
+ mode->vdisplay, mode->vsync_start,
+ mode->vsync_end, mode->vtotal,
+ mode->type, mode->flags);
+
+ mdp4_lcdc_encoder->pixclock = mode->clock * 1000;
+
+ DBG("pixclock=%lu", mdp4_lcdc_encoder->pixclock);
+
+ ctrl_pol = 0;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ ctrl_pol |= MDP4_LCDC_CTRL_POLARITY_HSYNC_LOW;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ ctrl_pol |= MDP4_LCDC_CTRL_POLARITY_VSYNC_LOW;
+ /* probably need to get DATA_EN polarity from panel.. */
+
+ lcdc_hsync_skew = 0; /* get this from panel? */
+
+ hsync_start_x = (mode->htotal - mode->hsync_start);
+ hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1;
+
+ vsync_period = mode->vtotal * mode->htotal;
+ vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal;
+ display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + lcdc_hsync_skew;
+ display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + lcdc_hsync_skew - 1;
+
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_HSYNC_CTRL,
+ MDP4_LCDC_HSYNC_CTRL_PULSEW(mode->hsync_end - mode->hsync_start) |
+ MDP4_LCDC_HSYNC_CTRL_PERIOD(mode->htotal));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_VSYNC_PERIOD, vsync_period);
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_VSYNC_LEN, vsync_len);
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_HCTRL,
+ MDP4_LCDC_DISPLAY_HCTRL_START(hsync_start_x) |
+ MDP4_LCDC_DISPLAY_HCTRL_END(hsync_end_x));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_VSTART, display_v_start);
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_DISPLAY_VEND, display_v_end);
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_BORDER_CLR, 0);
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_UNDERFLOW_CLR,
+ MDP4_LCDC_UNDERFLOW_CLR_ENABLE_RECOVERY |
+ MDP4_LCDC_UNDERFLOW_CLR_COLOR(0xff));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_HSYNC_SKEW, lcdc_hsync_skew);
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_CTRL_POLARITY, ctrl_pol);
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_HCTL,
+ MDP4_LCDC_ACTIVE_HCTL_START(0) |
+ MDP4_LCDC_ACTIVE_HCTL_END(0));
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_VSTART, 0);
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_ACTIVE_VEND, 0);
+}
+
+static void mdp4_lcdc_encoder_prepare(struct drm_encoder *encoder)
+{
+ mdp4_lcdc_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static void mdp4_lcdc_encoder_commit(struct drm_encoder *encoder)
+{
+ /* TODO: hard-coded for 18bpp: */
+ mdp4_crtc_set_config(encoder->crtc,
+ MDP4_DMA_CONFIG_R_BPC(BPC6) |
+ MDP4_DMA_CONFIG_G_BPC(BPC6) |
+ MDP4_DMA_CONFIG_B_BPC(BPC6) |
+ MDP4_DMA_CONFIG_PACK_ALIGN_MSB |
+ MDP4_DMA_CONFIG_PACK(0x21) |
+ MDP4_DMA_CONFIG_DEFLKR_EN |
+ MDP4_DMA_CONFIG_DITHER_EN);
+ mdp4_crtc_set_intf(encoder->crtc, INTF_LCDC_DTV, 0);
+ mdp4_lcdc_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static const struct drm_encoder_helper_funcs mdp4_lcdc_encoder_helper_funcs = {
+ .dpms = mdp4_lcdc_encoder_dpms,
+ .mode_fixup = mdp4_lcdc_encoder_mode_fixup,
+ .mode_set = mdp4_lcdc_encoder_mode_set,
+ .prepare = mdp4_lcdc_encoder_prepare,
+ .commit = mdp4_lcdc_encoder_commit,
+};
+
+long mdp4_lcdc_round_pixclk(struct drm_encoder *encoder, unsigned long rate)
+{
+ struct mdp4_lcdc_encoder *mdp4_lcdc_encoder =
+ to_mdp4_lcdc_encoder(encoder);
+ return clk_round_rate(mdp4_lcdc_encoder->lcdc_clk, rate);
+}
+
+/* initialize encoder */
+struct drm_encoder *mdp4_lcdc_encoder_init(struct drm_device *dev,
+ struct drm_panel *panel)
+{
+ struct drm_encoder *encoder = NULL;
+ struct mdp4_lcdc_encoder *mdp4_lcdc_encoder;
+ struct regulator *reg;
+ int ret;
+
+ mdp4_lcdc_encoder = kzalloc(sizeof(*mdp4_lcdc_encoder), GFP_KERNEL);
+ if (!mdp4_lcdc_encoder) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ mdp4_lcdc_encoder->panel = panel;
+
+ encoder = &mdp4_lcdc_encoder->base;
+
+ drm_encoder_init(dev, encoder, &mdp4_lcdc_encoder_funcs,
+ DRM_MODE_ENCODER_LVDS);
+ drm_encoder_helper_add(encoder, &mdp4_lcdc_encoder_helper_funcs);
+
+ /* TODO: do we need different pll in other cases? */
+ mdp4_lcdc_encoder->lcdc_clk = mpd4_lvds_pll_init(dev);
+ if (IS_ERR(mdp4_lcdc_encoder->lcdc_clk)) {
+ dev_err(dev->dev, "failed to get lvds_clk\n");
+ ret = PTR_ERR(mdp4_lcdc_encoder->lcdc_clk);
+ goto fail;
+ }
+
+ /* TODO: different regulators in other cases? */
+ reg = devm_regulator_get(dev->dev, "lvds-vccs-3p3v");
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+ dev_err(dev->dev, "failed to get lvds-vccs-3p3v: %d\n", ret);
+ goto fail;
+ }
+ mdp4_lcdc_encoder->regs[0] = reg;
+
+ reg = devm_regulator_get(dev->dev, "lvds-pll-vdda");
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+ dev_err(dev->dev, "failed to get lvds-pll-vdda: %d\n", ret);
+ goto fail;
+ }
+ mdp4_lcdc_encoder->regs[1] = reg;
+
+ reg = devm_regulator_get(dev->dev, "lvds-vdda");
+ if (IS_ERR(reg)) {
+ ret = PTR_ERR(reg);
+ dev_err(dev->dev, "failed to get lvds-vdda: %d\n", ret);
+ goto fail;
+ }
+ mdp4_lcdc_encoder->regs[2] = reg;
+
+ bs_init(mdp4_lcdc_encoder);
+
+ return encoder;
+
+fail:
+ if (encoder)
+ mdp4_lcdc_encoder_destroy(encoder);
+
+ return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
new file mode 100644
index 000000000000..310034688c15
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ * Author: Vinay Simha <vinaysimha@inforcecomputing.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.
+ *
+ * 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/gpio.h>
+
+#include "mdp4_kms.h"
+
+struct mdp4_lvds_connector {
+ struct drm_connector base;
+ struct drm_encoder *encoder;
+ struct drm_panel *panel;
+};
+#define to_mdp4_lvds_connector(x) container_of(x, struct mdp4_lvds_connector, base)
+
+static enum drm_connector_status mdp4_lvds_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ struct mdp4_lvds_connector *mdp4_lvds_connector =
+ to_mdp4_lvds_connector(connector);
+
+ return mdp4_lvds_connector->panel ?
+ connector_status_connected :
+ connector_status_disconnected;
+}
+
+static void mdp4_lvds_connector_destroy(struct drm_connector *connector)
+{
+ struct mdp4_lvds_connector *mdp4_lvds_connector =
+ to_mdp4_lvds_connector(connector);
+ struct drm_panel *panel = mdp4_lvds_connector->panel;
+
+ if (panel)
+ drm_panel_detach(panel);
+
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+
+ kfree(mdp4_lvds_connector);
+}
+
+static int mdp4_lvds_connector_get_modes(struct drm_connector *connector)
+{
+ struct mdp4_lvds_connector *mdp4_lvds_connector =
+ to_mdp4_lvds_connector(connector);
+ struct drm_panel *panel = mdp4_lvds_connector->panel;
+ int ret = 0;
+
+ if (panel)
+ ret = panel->funcs->get_modes(panel);
+
+ return ret;
+}
+
+static int mdp4_lvds_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct mdp4_lvds_connector *mdp4_lvds_connector =
+ to_mdp4_lvds_connector(connector);
+ struct drm_encoder *encoder = mdp4_lvds_connector->encoder;
+ long actual, requested;
+
+ requested = 1000 * mode->clock;
+ actual = mdp4_lcdc_round_pixclk(encoder, requested);
+
+ DBG("requested=%ld, actual=%ld", requested, actual);
+
+ if (actual != requested)
+ return MODE_CLOCK_RANGE;
+
+ return MODE_OK;
+}
+
+static struct drm_encoder *
+mdp4_lvds_connector_best_encoder(struct drm_connector *connector)
+{
+ struct mdp4_lvds_connector *mdp4_lvds_connector =
+ to_mdp4_lvds_connector(connector);
+ return mdp4_lvds_connector->encoder;
+}
+
+static const struct drm_connector_funcs mdp4_lvds_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = mdp4_lvds_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = mdp4_lvds_connector_destroy,
+};
+
+static const struct drm_connector_helper_funcs mdp4_lvds_connector_helper_funcs = {
+ .get_modes = mdp4_lvds_connector_get_modes,
+ .mode_valid = mdp4_lvds_connector_mode_valid,
+ .best_encoder = mdp4_lvds_connector_best_encoder,
+};
+
+/* initialize connector */
+struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev,
+ struct drm_panel *panel, struct drm_encoder *encoder)
+{
+ struct drm_connector *connector = NULL;
+ struct mdp4_lvds_connector *mdp4_lvds_connector;
+ int ret;
+
+ mdp4_lvds_connector = kzalloc(sizeof(*mdp4_lvds_connector), GFP_KERNEL);
+ if (!mdp4_lvds_connector) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ mdp4_lvds_connector->encoder = encoder;
+ mdp4_lvds_connector->panel = panel;
+
+ connector = &mdp4_lvds_connector->base;
+
+ drm_connector_init(dev, connector, &mdp4_lvds_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+ drm_connector_helper_add(connector, &mdp4_lvds_connector_helper_funcs);
+
+ connector->polled = 0;
+
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+
+ drm_connector_register(connector);
+
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ if (panel)
+ drm_panel_attach(panel, connector);
+
+ return connector;
+
+fail:
+ if (connector)
+ mdp4_lvds_connector_destroy(connector);
+
+ return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c
new file mode 100644
index 000000000000..ce4245971673
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_pll.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.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.
+ *
+ * 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/clk.h>
+#include <linux/clk-provider.h>
+
+#include "mdp4_kms.h"
+
+struct mdp4_lvds_pll {
+ struct clk_hw pll_hw;
+ struct drm_device *dev;
+ unsigned long pixclk;
+};
+#define to_mdp4_lvds_pll(x) container_of(x, struct mdp4_lvds_pll, pll_hw)
+
+static struct mdp4_kms *get_kms(struct mdp4_lvds_pll *lvds_pll)
+{
+ struct msm_drm_private *priv = lvds_pll->dev->dev_private;
+ return to_mdp4_kms(to_mdp_kms(priv->kms));
+}
+
+struct pll_rate {
+ unsigned long rate;
+ struct {
+ uint32_t val;
+ uint32_t reg;
+ } conf[32];
+};
+
+/* NOTE: keep sorted highest freq to lowest: */
+static const struct pll_rate freqtbl[] = {
+ { 72000000, {
+ { 0x8f, REG_MDP4_LVDS_PHY_PLL_CTRL_1 },
+ { 0x30, REG_MDP4_LVDS_PHY_PLL_CTRL_2 },
+ { 0xc6, REG_MDP4_LVDS_PHY_PLL_CTRL_3 },
+ { 0x10, REG_MDP4_LVDS_PHY_PLL_CTRL_5 },
+ { 0x07, REG_MDP4_LVDS_PHY_PLL_CTRL_6 },
+ { 0x62, REG_MDP4_LVDS_PHY_PLL_CTRL_7 },
+ { 0x41, REG_MDP4_LVDS_PHY_PLL_CTRL_8 },
+ { 0x0d, REG_MDP4_LVDS_PHY_PLL_CTRL_9 },
+ { 0, 0 } }
+ },
+};
+
+static const struct pll_rate *find_rate(unsigned long rate)
+{
+ int i;
+ for (i = 1; i < ARRAY_SIZE(freqtbl); i++)
+ if (rate > freqtbl[i].rate)
+ return &freqtbl[i-1];
+ return &freqtbl[i-1];
+}
+
+static int mpd4_lvds_pll_enable(struct clk_hw *hw)
+{
+ struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
+ struct mdp4_kms *mdp4_kms = get_kms(lvds_pll);
+ const struct pll_rate *pll_rate = find_rate(lvds_pll->pixclk);
+ int i;
+
+ DBG("pixclk=%lu (%lu)", lvds_pll->pixclk, pll_rate->rate);
+
+ if (WARN_ON(!pll_rate))
+ return -EINVAL;
+
+ mdp4_write(mdp4_kms, REG_MDP4_LCDC_LVDS_PHY_RESET, 0x33);
+
+ for (i = 0; pll_rate->conf[i].reg; i++)
+ mdp4_write(mdp4_kms, pll_rate->conf[i].reg, pll_rate->conf[i].val);
+
+ mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_CTRL_0, 0x01);
+
+ /* Wait until LVDS PLL is locked and ready */
+ while (!mdp4_read(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_LOCKED))
+ cpu_relax();
+
+ return 0;
+}
+
+static void mpd4_lvds_pll_disable(struct clk_hw *hw)
+{
+ struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
+ struct mdp4_kms *mdp4_kms = get_kms(lvds_pll);
+
+ DBG("");
+
+ mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_CFG0, 0x0);
+ mdp4_write(mdp4_kms, REG_MDP4_LVDS_PHY_PLL_CTRL_0, 0x0);
+}
+
+static unsigned long mpd4_lvds_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
+ return lvds_pll->pixclk;
+}
+
+static long mpd4_lvds_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ const struct pll_rate *pll_rate = find_rate(rate);
+ return pll_rate->rate;
+}
+
+static int mpd4_lvds_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct mdp4_lvds_pll *lvds_pll = to_mdp4_lvds_pll(hw);
+ lvds_pll->pixclk = rate;
+ return 0;
+}
+
+
+static const struct clk_ops mpd4_lvds_pll_ops = {
+ .enable = mpd4_lvds_pll_enable,
+ .disable = mpd4_lvds_pll_disable,
+ .recalc_rate = mpd4_lvds_pll_recalc_rate,
+ .round_rate = mpd4_lvds_pll_round_rate,
+ .set_rate = mpd4_lvds_pll_set_rate,
+};
+
+static const char *mpd4_lvds_pll_parents[] = {
+ "pxo",
+};
+
+static struct clk_init_data pll_init = {
+ .name = "mpd4_lvds_pll",
+ .ops = &mpd4_lvds_pll_ops,
+ .parent_names = mpd4_lvds_pll_parents,
+ .num_parents = ARRAY_SIZE(mpd4_lvds_pll_parents),
+};
+
+struct clk *mpd4_lvds_pll_init(struct drm_device *dev)
+{
+ struct mdp4_lvds_pll *lvds_pll;
+ struct clk *clk;
+ int ret;
+
+ lvds_pll = devm_kzalloc(dev->dev, sizeof(*lvds_pll), GFP_KERNEL);
+ if (!lvds_pll) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ lvds_pll->dev = dev;
+
+ lvds_pll->pll_hw.init = &pll_init;
+ clk = devm_clk_register(dev->dev, &lvds_pll->pll_hw);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto fail;
+ }
+
+ return clk;
+
+fail:
+ return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h
index 0aa51517f826..67f4f896ba8c 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h
@@ -12,14 +12,14 @@ The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31)
- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2013-12-03 20:59:13)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-06-25 12:55:02)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 20932 bytes, from 2013-12-01 15:13:04)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-06-25 12:53:44)
-Copyright (C) 2013 by the following authors:
+Copyright (C) 2013-2014 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
Permission is hereby granted, free of charge, to any person obtaining
@@ -68,6 +68,8 @@ enum mdp5_pipe {
SSPP_RGB2 = 5,
SSPP_DMA0 = 6,
SSPP_DMA1 = 7,
+ SSPP_VIG3 = 8,
+ SSPP_RGB3 = 9,
};
enum mdp5_ctl_mode {
@@ -126,7 +128,11 @@ enum mdp5_client_id {
CID_RGB0 = 16,
CID_RGB1 = 17,
CID_RGB2 = 18,
- CID_MAX = 19,
+ CID_VIG3_Y = 19,
+ CID_VIG3_CR = 20,
+ CID_VIG3_CB = 21,
+ CID_RGB3 = 22,
+ CID_MAX = 23,
};
enum mdp5_igc_type {
@@ -299,11 +305,34 @@ static inline uint32_t MDP5_IGC_LUT_REG_VAL(uint32_t val)
#define MDP5_IGC_LUT_REG_DISABLE_PIPE_1 0x20000000
#define MDP5_IGC_LUT_REG_DISABLE_PIPE_2 0x40000000
-static inline uint32_t REG_MDP5_CTL(uint32_t i0) { return 0x00000600 + 0x100*i0; }
+static inline uint32_t __offset_CTL(uint32_t idx)
+{
+ switch (idx) {
+ case 0: return (mdp5_cfg->ctl.base[0]);
+ case 1: return (mdp5_cfg->ctl.base[1]);
+ case 2: return (mdp5_cfg->ctl.base[2]);
+ case 3: return (mdp5_cfg->ctl.base[3]);
+ case 4: return (mdp5_cfg->ctl.base[4]);
+ default: return INVALID_IDX(idx);
+ }
+}
+static inline uint32_t REG_MDP5_CTL(uint32_t i0) { return 0x00000000 + __offset_CTL(i0); }
-static inline uint32_t REG_MDP5_CTL_LAYER(uint32_t i0, uint32_t i1) { return 0x00000600 + 0x100*i0 + 0x4*i1; }
+static inline uint32_t __offset_LAYER(uint32_t idx)
+{
+ switch (idx) {
+ case 0: return 0x00000000;
+ case 1: return 0x00000004;
+ case 2: return 0x00000008;
+ case 3: return 0x0000000c;
+ case 4: return 0x00000010;
+ case 5: return 0x00000024;
+ default: return INVALID_IDX(idx);
+ }
+}
+static inline uint32_t REG_MDP5_CTL_LAYER(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_CTL(i0) + __offset_LAYER(i1); }
-static inline uint32_t REG_MDP5_CTL_LAYER_REG(uint32_t i0, uint32_t i1) { return 0x00000600 + 0x100*i0 + 0x4*i1; }
+static inline uint32_t REG_MDP5_CTL_LAYER_REG(uint32_t i0, uint32_t i1) { return 0x00000000 + __offset_CTL(i0) + __offset_LAYER(i1); }
#define MDP5_CTL_LAYER_REG_VIG0__MASK 0x00000007
#define MDP5_CTL_LAYER_REG_VIG0__SHIFT 0
static inline uint32_t MDP5_CTL_LAYER_REG_VIG0(enum mdp_mixer_stage_id val)
@@ -354,8 +383,20 @@ static inline uint32_t MDP5_CTL_LAYER_REG_DMA1(enum mdp_mixer_stage_id val)
}
#define MDP5_CTL_LAYER_REG_BORDER_COLOR 0x01000000
#define MDP5_CTL_LAYER_REG_CURSOR_OUT 0x02000000
+#define MDP5_CTL_LAYER_REG_VIG3__MASK 0x1c000000
+#define MDP5_CTL_LAYER_REG_VIG3__SHIFT 26
+static inline uint32_t MDP5_CTL_LAYER_REG_VIG3(enum mdp_mixer_stage_id val)
+{
+ return ((val) << MDP5_CTL_LAYER_REG_VIG3__SHIFT) & MDP5_CTL_LAYER_REG_VIG3__MASK;
+}
+#define MDP5_CTL_LAYER_REG_RGB3__MASK 0xe0000000
+#define MDP5_CTL_LAYER_REG_RGB3__SHIFT 29
+static inline uint32_t MDP5_CTL_LAYER_REG_RGB3(enum mdp_mixer_stage_id val)
+{
+ return ((val) << MDP5_CTL_LAYER_REG_RGB3__SHIFT) & MDP5_CTL_LAYER_REG_RGB3__MASK;
+}
-static inline uint32_t REG_MDP5_CTL_OP(uint32_t i0) { return 0x00000614 + 0x100*i0; }
+static inline uint32_t REG_MDP5_CTL_OP(uint32_t i0) { return 0x00000014 + __offset_CTL(i0); }
#define MDP5_CTL_OP_MODE__MASK 0x0000000f
#define MDP5_CTL_OP_MODE__SHIFT 0
static inline uint32_t MDP5_CTL_OP_MODE(enum mdp5_ctl_mode val)
@@ -377,7 +418,7 @@ static inline uint32_t MDP5_CTL_OP_PACK_3D(enum mdp5_pack_3d val)
return ((val) << MDP5_CTL_OP_PACK_3D__SHIFT) & MDP5_CTL_OP_PACK_3D__MASK;
}
-static inline uint32_t REG_MDP5_CTL_FLUSH(uint32_t i0) { return 0x00000618 + 0x100*i0; }
+static inline uint32_t REG_MDP5_CTL_FLUSH(uint32_t i0) { return 0x00000018 + __offset_CTL(i0); }
#define MDP5_CTL_FLUSH_VIG0 0x00000001
#define MDP5_CTL_FLUSH_VIG1 0x00000002
#define MDP5_CTL_FLUSH_VIG2 0x00000004
@@ -387,26 +428,48 @@ static inline uint32_t REG_MDP5_CTL_FLUSH(uint32_t i0) { return 0x00000618 + 0x1
#define MDP5_CTL_FLUSH_LM0 0x00000040
#define MDP5_CTL_FLUSH_LM1 0x00000080
#define MDP5_CTL_FLUSH_LM2 0x00000100
+#define MDP5_CTL_FLUSH_LM3 0x00000200
+#define MDP5_CTL_FLUSH_LM4 0x00000400
#define MDP5_CTL_FLUSH_DMA0 0x00000800
#define MDP5_CTL_FLUSH_DMA1 0x00001000
#define MDP5_CTL_FLUSH_DSPP0 0x00002000
#define MDP5_CTL_FLUSH_DSPP1 0x00004000
#define MDP5_CTL_FLUSH_DSPP2 0x00008000
#define MDP5_CTL_FLUSH_CTL 0x00020000
+#define MDP5_CTL_FLUSH_VIG3 0x00040000
+#define MDP5_CTL_FLUSH_RGB3 0x00080000
+#define MDP5_CTL_FLUSH_LM5 0x00100000
+#define MDP5_CTL_FLUSH_DSPP3 0x00200000
-static inline uint32_t REG_MDP5_CTL_START(uint32_t i0) { return 0x0000061c + 0x100*i0; }
+static inline uint32_t REG_MDP5_CTL_START(uint32_t i0) { return 0x0000001c + __offset_CTL(i0); }
-static inline uint32_t REG_MDP5_CTL_PACK_3D(uint32_t i0) { return 0x00000620 + 0x100*i0; }
+static inline uint32_t REG_MDP5_CTL_PACK_3D(uint32_t i0) { return 0x00000020 + __offset_CTL(i0); }
-static inline uint32_t REG_MDP5_PIPE(enum mdp5_pipe i0) { return 0x00001200 + 0x400*i0; }
+static inline uint32_t __offset_PIPE(enum mdp5_pipe idx)
+{
+ switch (idx) {
+ case SSPP_VIG0: return (mdp5_cfg->pipe_vig.base[0]);
+ case SSPP_VIG1: return (mdp5_cfg->pipe_vig.base[1]);
+ case SSPP_VIG2: return (mdp5_cfg->pipe_vig.base[2]);
+ case SSPP_RGB0: return (mdp5_cfg->pipe_rgb.base[0]);
+ case SSPP_RGB1: return (mdp5_cfg->pipe_rgb.base[1]);
+ case SSPP_RGB2: return (mdp5_cfg->pipe_rgb.base[2]);
+ case SSPP_DMA0: return (mdp5_cfg->pipe_dma.base[0]);
+ case SSPP_DMA1: return (mdp5_cfg->pipe_dma.base[1]);
+ case SSPP_VIG3: return (mdp5_cfg->pipe_vig.base[3]);
+ case SSPP_RGB3: return (mdp5_cfg->pipe_rgb.base[3]);
+ default: return INVALID_IDX(idx);
+ }
+}
+static inline uint32_t REG_MDP5_PIPE(enum mdp5_pipe i0) { return 0x00000000 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_HIST_CTL_BASE(enum mdp5_pipe i0) { return 0x000014c4 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_HIST_CTL_BASE(enum mdp5_pipe i0) { return 0x000002c4 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_HIST_LUT_BASE(enum mdp5_pipe i0) { return 0x000014f0 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_HIST_LUT_BASE(enum mdp5_pipe i0) { return 0x000002f0 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_HIST_LUT_SWAP(enum mdp5_pipe i0) { return 0x00001500 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_HIST_LUT_SWAP(enum mdp5_pipe i0) { return 0x00000300 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_SRC_SIZE(enum mdp5_pipe i0) { return 0x00001200 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC_SIZE(enum mdp5_pipe i0) { return 0x00000000 + __offset_PIPE(i0); }
#define MDP5_PIPE_SRC_SIZE_HEIGHT__MASK 0xffff0000
#define MDP5_PIPE_SRC_SIZE_HEIGHT__SHIFT 16
static inline uint32_t MDP5_PIPE_SRC_SIZE_HEIGHT(uint32_t val)
@@ -420,7 +483,7 @@ static inline uint32_t MDP5_PIPE_SRC_SIZE_WIDTH(uint32_t val)
return ((val) << MDP5_PIPE_SRC_SIZE_WIDTH__SHIFT) & MDP5_PIPE_SRC_SIZE_WIDTH__MASK;
}
-static inline uint32_t REG_MDP5_PIPE_SRC_IMG_SIZE(enum mdp5_pipe i0) { return 0x00001204 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC_IMG_SIZE(enum mdp5_pipe i0) { return 0x00000004 + __offset_PIPE(i0); }
#define MDP5_PIPE_SRC_IMG_SIZE_HEIGHT__MASK 0xffff0000
#define MDP5_PIPE_SRC_IMG_SIZE_HEIGHT__SHIFT 16
static inline uint32_t MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(uint32_t val)
@@ -434,7 +497,7 @@ static inline uint32_t MDP5_PIPE_SRC_IMG_SIZE_WIDTH(uint32_t val)
return ((val) << MDP5_PIPE_SRC_IMG_SIZE_WIDTH__SHIFT) & MDP5_PIPE_SRC_IMG_SIZE_WIDTH__MASK;
}
-static inline uint32_t REG_MDP5_PIPE_SRC_XY(enum mdp5_pipe i0) { return 0x00001208 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC_XY(enum mdp5_pipe i0) { return 0x00000008 + __offset_PIPE(i0); }
#define MDP5_PIPE_SRC_XY_Y__MASK 0xffff0000
#define MDP5_PIPE_SRC_XY_Y__SHIFT 16
static inline uint32_t MDP5_PIPE_SRC_XY_Y(uint32_t val)
@@ -448,7 +511,7 @@ static inline uint32_t MDP5_PIPE_SRC_XY_X(uint32_t val)
return ((val) << MDP5_PIPE_SRC_XY_X__SHIFT) & MDP5_PIPE_SRC_XY_X__MASK;
}
-static inline uint32_t REG_MDP5_PIPE_OUT_SIZE(enum mdp5_pipe i0) { return 0x0000120c + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_OUT_SIZE(enum mdp5_pipe i0) { return 0x0000000c + __offset_PIPE(i0); }
#define MDP5_PIPE_OUT_SIZE_HEIGHT__MASK 0xffff0000
#define MDP5_PIPE_OUT_SIZE_HEIGHT__SHIFT 16
static inline uint32_t MDP5_PIPE_OUT_SIZE_HEIGHT(uint32_t val)
@@ -462,7 +525,7 @@ static inline uint32_t MDP5_PIPE_OUT_SIZE_WIDTH(uint32_t val)
return ((val) << MDP5_PIPE_OUT_SIZE_WIDTH__SHIFT) & MDP5_PIPE_OUT_SIZE_WIDTH__MASK;
}
-static inline uint32_t REG_MDP5_PIPE_OUT_XY(enum mdp5_pipe i0) { return 0x00001210 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_OUT_XY(enum mdp5_pipe i0) { return 0x00000010 + __offset_PIPE(i0); }
#define MDP5_PIPE_OUT_XY_Y__MASK 0xffff0000
#define MDP5_PIPE_OUT_XY_Y__SHIFT 16
static inline uint32_t MDP5_PIPE_OUT_XY_Y(uint32_t val)
@@ -476,15 +539,15 @@ static inline uint32_t MDP5_PIPE_OUT_XY_X(uint32_t val)
return ((val) << MDP5_PIPE_OUT_XY_X__SHIFT) & MDP5_PIPE_OUT_XY_X__MASK;
}
-static inline uint32_t REG_MDP5_PIPE_SRC0_ADDR(enum mdp5_pipe i0) { return 0x00001214 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC0_ADDR(enum mdp5_pipe i0) { return 0x00000014 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_SRC1_ADDR(enum mdp5_pipe i0) { return 0x00001218 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC1_ADDR(enum mdp5_pipe i0) { return 0x00000018 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_SRC2_ADDR(enum mdp5_pipe i0) { return 0x0000121c + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC2_ADDR(enum mdp5_pipe i0) { return 0x0000001c + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_SRC3_ADDR(enum mdp5_pipe i0) { return 0x00001220 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC3_ADDR(enum mdp5_pipe i0) { return 0x00000020 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_SRC_STRIDE_A(enum mdp5_pipe i0) { return 0x00001224 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC_STRIDE_A(enum mdp5_pipe i0) { return 0x00000024 + __offset_PIPE(i0); }
#define MDP5_PIPE_SRC_STRIDE_A_P0__MASK 0x0000ffff
#define MDP5_PIPE_SRC_STRIDE_A_P0__SHIFT 0
static inline uint32_t MDP5_PIPE_SRC_STRIDE_A_P0(uint32_t val)
@@ -498,7 +561,7 @@ static inline uint32_t MDP5_PIPE_SRC_STRIDE_A_P1(uint32_t val)
return ((val) << MDP5_PIPE_SRC_STRIDE_A_P1__SHIFT) & MDP5_PIPE_SRC_STRIDE_A_P1__MASK;
}
-static inline uint32_t REG_MDP5_PIPE_SRC_STRIDE_B(enum mdp5_pipe i0) { return 0x00001228 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC_STRIDE_B(enum mdp5_pipe i0) { return 0x00000028 + __offset_PIPE(i0); }
#define MDP5_PIPE_SRC_STRIDE_B_P2__MASK 0x0000ffff
#define MDP5_PIPE_SRC_STRIDE_B_P2__SHIFT 0
static inline uint32_t MDP5_PIPE_SRC_STRIDE_B_P2(uint32_t val)
@@ -512,9 +575,9 @@ static inline uint32_t MDP5_PIPE_SRC_STRIDE_B_P3(uint32_t val)
return ((val) << MDP5_PIPE_SRC_STRIDE_B_P3__SHIFT) & MDP5_PIPE_SRC_STRIDE_B_P3__MASK;
}
-static inline uint32_t REG_MDP5_PIPE_STILE_FRAME_SIZE(enum mdp5_pipe i0) { return 0x0000122c + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_STILE_FRAME_SIZE(enum mdp5_pipe i0) { return 0x0000002c + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_SRC_FORMAT(enum mdp5_pipe i0) { return 0x00001230 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC_FORMAT(enum mdp5_pipe i0) { return 0x00000030 + __offset_PIPE(i0); }
#define MDP5_PIPE_SRC_FORMAT_G_BPC__MASK 0x00000003
#define MDP5_PIPE_SRC_FORMAT_G_BPC__SHIFT 0
static inline uint32_t MDP5_PIPE_SRC_FORMAT_G_BPC(enum mdp_bpc val)
@@ -568,7 +631,7 @@ static inline uint32_t MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(enum mdp5_chroma_samp_ty
return ((val) << MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP__SHIFT) & MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP__MASK;
}
-static inline uint32_t REG_MDP5_PIPE_SRC_UNPACK(enum mdp5_pipe i0) { return 0x00001234 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC_UNPACK(enum mdp5_pipe i0) { return 0x00000034 + __offset_PIPE(i0); }
#define MDP5_PIPE_SRC_UNPACK_ELEM0__MASK 0x000000ff
#define MDP5_PIPE_SRC_UNPACK_ELEM0__SHIFT 0
static inline uint32_t MDP5_PIPE_SRC_UNPACK_ELEM0(uint32_t val)
@@ -594,7 +657,7 @@ static inline uint32_t MDP5_PIPE_SRC_UNPACK_ELEM3(uint32_t val)
return ((val) << MDP5_PIPE_SRC_UNPACK_ELEM3__SHIFT) & MDP5_PIPE_SRC_UNPACK_ELEM3__MASK;
}
-static inline uint32_t REG_MDP5_PIPE_SRC_OP_MODE(enum mdp5_pipe i0) { return 0x00001238 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC_OP_MODE(enum mdp5_pipe i0) { return 0x00000038 + __offset_PIPE(i0); }
#define MDP5_PIPE_SRC_OP_MODE_BWC_EN 0x00000001
#define MDP5_PIPE_SRC_OP_MODE_BWC__MASK 0x00000006
#define MDP5_PIPE_SRC_OP_MODE_BWC__SHIFT 1
@@ -610,29 +673,29 @@ static inline uint32_t MDP5_PIPE_SRC_OP_MODE_BWC(enum mdp5_pipe_bwc val)
#define MDP5_PIPE_SRC_OP_MODE_DEINTERLACE 0x00400000
#define MDP5_PIPE_SRC_OP_MODE_DEINTERLACE_ODD 0x00800000
-static inline uint32_t REG_MDP5_PIPE_SRC_CONSTANT_COLOR(enum mdp5_pipe i0) { return 0x0000123c + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC_CONSTANT_COLOR(enum mdp5_pipe i0) { return 0x0000003c + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_FETCH_CONFIG(enum mdp5_pipe i0) { return 0x00001248 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_FETCH_CONFIG(enum mdp5_pipe i0) { return 0x00000048 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_VC1_RANGE(enum mdp5_pipe i0) { return 0x0000124c + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_VC1_RANGE(enum mdp5_pipe i0) { return 0x0000004c + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_REQPRIO_FIFO_WM_0(enum mdp5_pipe i0) { return 0x00001250 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_REQPRIO_FIFO_WM_0(enum mdp5_pipe i0) { return 0x00000050 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_REQPRIO_FIFO_WM_1(enum mdp5_pipe i0) { return 0x00001254 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_REQPRIO_FIFO_WM_1(enum mdp5_pipe i0) { return 0x00000054 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_REQPRIO_FIFO_WM_2(enum mdp5_pipe i0) { return 0x00001258 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_REQPRIO_FIFO_WM_2(enum mdp5_pipe i0) { return 0x00000058 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(enum mdp5_pipe i0) { return 0x00001270 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(enum mdp5_pipe i0) { return 0x00000070 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC0_ADDR(enum mdp5_pipe i0) { return 0x000012a4 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC0_ADDR(enum mdp5_pipe i0) { return 0x000000a4 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC1_ADDR(enum mdp5_pipe i0) { return 0x000012a8 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC1_ADDR(enum mdp5_pipe i0) { return 0x000000a8 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC2_ADDR(enum mdp5_pipe i0) { return 0x000012ac + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC2_ADDR(enum mdp5_pipe i0) { return 0x000000ac + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC3_ADDR(enum mdp5_pipe i0) { return 0x000012b0 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_CURRENT_SRC3_ADDR(enum mdp5_pipe i0) { return 0x000000b0 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_DECIMATION(enum mdp5_pipe i0) { return 0x000012b4 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_DECIMATION(enum mdp5_pipe i0) { return 0x000000b4 + __offset_PIPE(i0); }
#define MDP5_PIPE_DECIMATION_VERT__MASK 0x000000ff
#define MDP5_PIPE_DECIMATION_VERT__SHIFT 0
static inline uint32_t MDP5_PIPE_DECIMATION_VERT(uint32_t val)
@@ -646,7 +709,7 @@ static inline uint32_t MDP5_PIPE_DECIMATION_HORZ(uint32_t val)
return ((val) << MDP5_PIPE_DECIMATION_HORZ__SHIFT) & MDP5_PIPE_DECIMATION_HORZ__MASK;
}
-static inline uint32_t REG_MDP5_PIPE_SCALE_CONFIG(enum mdp5_pipe i0) { return 0x00001404 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SCALE_CONFIG(enum mdp5_pipe i0) { return 0x00000204 + __offset_PIPE(i0); }
#define MDP5_PIPE_SCALE_CONFIG_SCALEX_EN 0x00000001
#define MDP5_PIPE_SCALE_CONFIG_SCALEY_EN 0x00000002
#define MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER__MASK 0x00000300
@@ -686,23 +749,34 @@ static inline uint32_t MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(enum mdp5_scale_
return ((val) << MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER__SHIFT) & MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER__MASK;
}
-static inline uint32_t REG_MDP5_PIPE_SCALE_PHASE_STEP_X(enum mdp5_pipe i0) { return 0x00001410 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SCALE_PHASE_STEP_X(enum mdp5_pipe i0) { return 0x00000210 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(enum mdp5_pipe i0) { return 0x00001414 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(enum mdp5_pipe i0) { return 0x00000214 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_SCALE_INIT_PHASE_X(enum mdp5_pipe i0) { return 0x00001420 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SCALE_INIT_PHASE_X(enum mdp5_pipe i0) { return 0x00000220 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_PIPE_SCALE_INIT_PHASE_Y(enum mdp5_pipe i0) { return 0x00001424 + 0x400*i0; }
+static inline uint32_t REG_MDP5_PIPE_SCALE_INIT_PHASE_Y(enum mdp5_pipe i0) { return 0x00000224 + __offset_PIPE(i0); }
-static inline uint32_t REG_MDP5_LM(uint32_t i0) { return 0x00003200 + 0x400*i0; }
+static inline uint32_t __offset_LM(uint32_t idx)
+{
+ switch (idx) {
+ case 0: return (mdp5_cfg->lm.base[0]);
+ case 1: return (mdp5_cfg->lm.base[1]);
+ case 2: return (mdp5_cfg->lm.base[2]);
+ case 3: return (mdp5_cfg->lm.base[3]);
+ case 4: return (mdp5_cfg->lm.base[4]);
+ default: return INVALID_IDX(idx);
+ }
+}
+static inline uint32_t REG_MDP5_LM(uint32_t i0) { return 0x00000000 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_BLEND_COLOR_OUT(uint32_t i0) { return 0x00003200 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_BLEND_COLOR_OUT(uint32_t i0) { return 0x00000000 + __offset_LM(i0); }
#define MDP5_LM_BLEND_COLOR_OUT_STAGE0_FG_ALPHA 0x00000002
#define MDP5_LM_BLEND_COLOR_OUT_STAGE1_FG_ALPHA 0x00000004
#define MDP5_LM_BLEND_COLOR_OUT_STAGE2_FG_ALPHA 0x00000008
#define MDP5_LM_BLEND_COLOR_OUT_STAGE3_FG_ALPHA 0x00000010
-static inline uint32_t REG_MDP5_LM_OUT_SIZE(uint32_t i0) { return 0x00003204 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_OUT_SIZE(uint32_t i0) { return 0x00000004 + __offset_LM(i0); }
#define MDP5_LM_OUT_SIZE_HEIGHT__MASK 0xffff0000
#define MDP5_LM_OUT_SIZE_HEIGHT__SHIFT 16
static inline uint32_t MDP5_LM_OUT_SIZE_HEIGHT(uint32_t val)
@@ -716,13 +790,13 @@ static inline uint32_t MDP5_LM_OUT_SIZE_WIDTH(uint32_t val)
return ((val) << MDP5_LM_OUT_SIZE_WIDTH__SHIFT) & MDP5_LM_OUT_SIZE_WIDTH__MASK;
}
-static inline uint32_t REG_MDP5_LM_BORDER_COLOR_0(uint32_t i0) { return 0x00003208 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_BORDER_COLOR_0(uint32_t i0) { return 0x00000008 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_BORDER_COLOR_1(uint32_t i0) { return 0x00003210 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_BORDER_COLOR_1(uint32_t i0) { return 0x00000010 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_BLEND(uint32_t i0, uint32_t i1) { return 0x00003220 + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND(uint32_t i0, uint32_t i1) { return 0x00000020 + __offset_LM(i0) + 0x30*i1; }
-static inline uint32_t REG_MDP5_LM_BLEND_OP_MODE(uint32_t i0, uint32_t i1) { return 0x00003220 + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND_OP_MODE(uint32_t i0, uint32_t i1) { return 0x00000020 + __offset_LM(i0) + 0x30*i1; }
#define MDP5_LM_BLEND_OP_MODE_FG_ALPHA__MASK 0x00000003
#define MDP5_LM_BLEND_OP_MODE_FG_ALPHA__SHIFT 0
static inline uint32_t MDP5_LM_BLEND_OP_MODE_FG_ALPHA(enum mdp_alpha_type val)
@@ -744,57 +818,67 @@ static inline uint32_t MDP5_LM_BLEND_OP_MODE_BG_ALPHA(enum mdp_alpha_type val)
#define MDP5_LM_BLEND_OP_MODE_BG_INV_MOD_ALPHA 0x00001000
#define MDP5_LM_BLEND_OP_MODE_BG_TRANSP_EN 0x00002000
-static inline uint32_t REG_MDP5_LM_BLEND_FG_ALPHA(uint32_t i0, uint32_t i1) { return 0x00003224 + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND_FG_ALPHA(uint32_t i0, uint32_t i1) { return 0x00000024 + __offset_LM(i0) + 0x30*i1; }
-static inline uint32_t REG_MDP5_LM_BLEND_BG_ALPHA(uint32_t i0, uint32_t i1) { return 0x00003228 + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND_BG_ALPHA(uint32_t i0, uint32_t i1) { return 0x00000028 + __offset_LM(i0) + 0x30*i1; }
-static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_LOW0(uint32_t i0, uint32_t i1) { return 0x0000322c + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_LOW0(uint32_t i0, uint32_t i1) { return 0x0000002c + __offset_LM(i0) + 0x30*i1; }
-static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_LOW1(uint32_t i0, uint32_t i1) { return 0x00003230 + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_LOW1(uint32_t i0, uint32_t i1) { return 0x00000030 + __offset_LM(i0) + 0x30*i1; }
-static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_HIGH0(uint32_t i0, uint32_t i1) { return 0x00003234 + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_HIGH0(uint32_t i0, uint32_t i1) { return 0x00000034 + __offset_LM(i0) + 0x30*i1; }
-static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_HIGH1(uint32_t i0, uint32_t i1) { return 0x00003238 + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND_FG_TRANSP_HIGH1(uint32_t i0, uint32_t i1) { return 0x00000038 + __offset_LM(i0) + 0x30*i1; }
-static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_LOW0(uint32_t i0, uint32_t i1) { return 0x0000323c + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_LOW0(uint32_t i0, uint32_t i1) { return 0x0000003c + __offset_LM(i0) + 0x30*i1; }
-static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_LOW1(uint32_t i0, uint32_t i1) { return 0x00003240 + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_LOW1(uint32_t i0, uint32_t i1) { return 0x00000040 + __offset_LM(i0) + 0x30*i1; }
-static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_HIGH0(uint32_t i0, uint32_t i1) { return 0x00003244 + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_HIGH0(uint32_t i0, uint32_t i1) { return 0x00000044 + __offset_LM(i0) + 0x30*i1; }
-static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_HIGH1(uint32_t i0, uint32_t i1) { return 0x00003248 + 0x400*i0 + 0x30*i1; }
+static inline uint32_t REG_MDP5_LM_BLEND_BG_TRANSP_HIGH1(uint32_t i0, uint32_t i1) { return 0x00000048 + __offset_LM(i0) + 0x30*i1; }
-static inline uint32_t REG_MDP5_LM_CURSOR_IMG_SIZE(uint32_t i0) { return 0x000032e0 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_IMG_SIZE(uint32_t i0) { return 0x000000e0 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_SIZE(uint32_t i0) { return 0x000032e4 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_SIZE(uint32_t i0) { return 0x000000e4 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_XY(uint32_t i0) { return 0x000032e8 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_XY(uint32_t i0) { return 0x000000e8 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_STRIDE(uint32_t i0) { return 0x000032dc + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_STRIDE(uint32_t i0) { return 0x000000dc + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_FORMAT(uint32_t i0) { return 0x000032ec + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_FORMAT(uint32_t i0) { return 0x000000ec + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_BASE_ADDR(uint32_t i0) { return 0x000032f0 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_BASE_ADDR(uint32_t i0) { return 0x000000f0 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_START_XY(uint32_t i0) { return 0x000032f4 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_START_XY(uint32_t i0) { return 0x000000f4 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_CONFIG(uint32_t i0) { return 0x000032f8 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_CONFIG(uint32_t i0) { return 0x000000f8 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_PARAM(uint32_t i0) { return 0x000032fc + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_PARAM(uint32_t i0) { return 0x000000fc + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_LOW0(uint32_t i0) { return 0x00003300 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_LOW0(uint32_t i0) { return 0x00000100 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_LOW1(uint32_t i0) { return 0x00003304 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_LOW1(uint32_t i0) { return 0x00000104 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_HIGH0(uint32_t i0) { return 0x00003308 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_HIGH0(uint32_t i0) { return 0x00000108 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_HIGH1(uint32_t i0) { return 0x0000330c + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_CURSOR_BLEND_TRANSP_HIGH1(uint32_t i0) { return 0x0000010c + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_LM_GC_LUT_BASE(uint32_t i0) { return 0x00003310 + 0x400*i0; }
+static inline uint32_t REG_MDP5_LM_GC_LUT_BASE(uint32_t i0) { return 0x00000110 + __offset_LM(i0); }
-static inline uint32_t REG_MDP5_DSPP(uint32_t i0) { return 0x00004600 + 0x400*i0; }
+static inline uint32_t __offset_DSPP(uint32_t idx)
+{
+ switch (idx) {
+ case 0: return (mdp5_cfg->dspp.base[0]);
+ case 1: return (mdp5_cfg->dspp.base[1]);
+ case 2: return (mdp5_cfg->dspp.base[2]);
+ case 3: return (mdp5_cfg->dspp.base[3]);
+ default: return INVALID_IDX(idx);
+ }
+}
+static inline uint32_t REG_MDP5_DSPP(uint32_t i0) { return 0x00000000 + __offset_DSPP(i0); }
-static inline uint32_t REG_MDP5_DSPP_OP_MODE(uint32_t i0) { return 0x00004600 + 0x400*i0; }
+static inline uint32_t REG_MDP5_DSPP_OP_MODE(uint32_t i0) { return 0x00000000 + __offset_DSPP(i0); }
#define MDP5_DSPP_OP_MODE_IGC_LUT_EN 0x00000001
#define MDP5_DSPP_OP_MODE_IGC_TBL_IDX__MASK 0x0000000e
#define MDP5_DSPP_OP_MODE_IGC_TBL_IDX__SHIFT 1
@@ -811,29 +895,40 @@ static inline uint32_t MDP5_DSPP_OP_MODE_IGC_TBL_IDX(uint32_t val)
#define MDP5_DSPP_OP_MODE_GAMUT_EN 0x00800000
#define MDP5_DSPP_OP_MODE_GAMUT_ORDER 0x01000000
-static inline uint32_t REG_MDP5_DSPP_PCC_BASE(uint32_t i0) { return 0x00004630 + 0x400*i0; }
+static inline uint32_t REG_MDP5_DSPP_PCC_BASE(uint32_t i0) { return 0x00000030 + __offset_DSPP(i0); }
-static inline uint32_t REG_MDP5_DSPP_DITHER_DEPTH(uint32_t i0) { return 0x00004750 + 0x400*i0; }
+static inline uint32_t REG_MDP5_DSPP_DITHER_DEPTH(uint32_t i0) { return 0x00000150 + __offset_DSPP(i0); }
-static inline uint32_t REG_MDP5_DSPP_HIST_CTL_BASE(uint32_t i0) { return 0x00004810 + 0x400*i0; }
+static inline uint32_t REG_MDP5_DSPP_HIST_CTL_BASE(uint32_t i0) { return 0x00000210 + __offset_DSPP(i0); }
-static inline uint32_t REG_MDP5_DSPP_HIST_LUT_BASE(uint32_t i0) { return 0x00004830 + 0x400*i0; }
+static inline uint32_t REG_MDP5_DSPP_HIST_LUT_BASE(uint32_t i0) { return 0x00000230 + __offset_DSPP(i0); }
-static inline uint32_t REG_MDP5_DSPP_HIST_LUT_SWAP(uint32_t i0) { return 0x00004834 + 0x400*i0; }
+static inline uint32_t REG_MDP5_DSPP_HIST_LUT_SWAP(uint32_t i0) { return 0x00000234 + __offset_DSPP(i0); }
-static inline uint32_t REG_MDP5_DSPP_PA_BASE(uint32_t i0) { return 0x00004838 + 0x400*i0; }
+static inline uint32_t REG_MDP5_DSPP_PA_BASE(uint32_t i0) { return 0x00000238 + __offset_DSPP(i0); }
-static inline uint32_t REG_MDP5_DSPP_GAMUT_BASE(uint32_t i0) { return 0x000048dc + 0x400*i0; }
+static inline uint32_t REG_MDP5_DSPP_GAMUT_BASE(uint32_t i0) { return 0x000002dc + __offset_DSPP(i0); }
-static inline uint32_t REG_MDP5_DSPP_GC_BASE(uint32_t i0) { return 0x000048b0 + 0x400*i0; }
+static inline uint32_t REG_MDP5_DSPP_GC_BASE(uint32_t i0) { return 0x000002b0 + __offset_DSPP(i0); }
-static inline uint32_t REG_MDP5_INTF(uint32_t i0) { return 0x00012500 + 0x200*i0; }
+static inline uint32_t __offset_INTF(uint32_t idx)
+{
+ switch (idx) {
+ case 0: return (mdp5_cfg->intf.base[0]);
+ case 1: return (mdp5_cfg->intf.base[1]);
+ case 2: return (mdp5_cfg->intf.base[2]);
+ case 3: return (mdp5_cfg->intf.base[3]);
+ case 4: return (mdp5_cfg->intf.base[4]);
+ default: return INVALID_IDX(idx);
+ }
+}
+static inline uint32_t REG_MDP5_INTF(uint32_t i0) { return 0x00000000 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_TIMING_ENGINE_EN(uint32_t i0) { return 0x00012500 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TIMING_ENGINE_EN(uint32_t i0) { return 0x00000000 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_CONFIG(uint32_t i0) { return 0x00012504 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_CONFIG(uint32_t i0) { return 0x00000004 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_HSYNC_CTL(uint32_t i0) { return 0x00012508 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_HSYNC_CTL(uint32_t i0) { return 0x00000008 + __offset_INTF(i0); }
#define MDP5_INTF_HSYNC_CTL_PULSEW__MASK 0x0000ffff
#define MDP5_INTF_HSYNC_CTL_PULSEW__SHIFT 0
static inline uint32_t MDP5_INTF_HSYNC_CTL_PULSEW(uint32_t val)
@@ -847,23 +942,23 @@ static inline uint32_t MDP5_INTF_HSYNC_CTL_PERIOD(uint32_t val)
return ((val) << MDP5_INTF_HSYNC_CTL_PERIOD__SHIFT) & MDP5_INTF_HSYNC_CTL_PERIOD__MASK;
}
-static inline uint32_t REG_MDP5_INTF_VSYNC_PERIOD_F0(uint32_t i0) { return 0x0001250c + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_VSYNC_PERIOD_F0(uint32_t i0) { return 0x0000000c + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_VSYNC_PERIOD_F1(uint32_t i0) { return 0x00012510 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_VSYNC_PERIOD_F1(uint32_t i0) { return 0x00000010 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_VSYNC_LEN_F0(uint32_t i0) { return 0x00012514 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_VSYNC_LEN_F0(uint32_t i0) { return 0x00000014 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_VSYNC_LEN_F1(uint32_t i0) { return 0x00012518 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_VSYNC_LEN_F1(uint32_t i0) { return 0x00000018 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_DISPLAY_VSTART_F0(uint32_t i0) { return 0x0001251c + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_DISPLAY_VSTART_F0(uint32_t i0) { return 0x0000001c + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_DISPLAY_VSTART_F1(uint32_t i0) { return 0x00012520 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_DISPLAY_VSTART_F1(uint32_t i0) { return 0x00000020 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_DISPLAY_VEND_F0(uint32_t i0) { return 0x00012524 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_DISPLAY_VEND_F0(uint32_t i0) { return 0x00000024 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_DISPLAY_VEND_F1(uint32_t i0) { return 0x00012528 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_DISPLAY_VEND_F1(uint32_t i0) { return 0x00000028 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_ACTIVE_VSTART_F0(uint32_t i0) { return 0x0001252c + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_ACTIVE_VSTART_F0(uint32_t i0) { return 0x0000002c + __offset_INTF(i0); }
#define MDP5_INTF_ACTIVE_VSTART_F0_VAL__MASK 0x7fffffff
#define MDP5_INTF_ACTIVE_VSTART_F0_VAL__SHIFT 0
static inline uint32_t MDP5_INTF_ACTIVE_VSTART_F0_VAL(uint32_t val)
@@ -872,7 +967,7 @@ static inline uint32_t MDP5_INTF_ACTIVE_VSTART_F0_VAL(uint32_t val)
}
#define MDP5_INTF_ACTIVE_VSTART_F0_ACTIVE_V_ENABLE 0x80000000
-static inline uint32_t REG_MDP5_INTF_ACTIVE_VSTART_F1(uint32_t i0) { return 0x00012530 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_ACTIVE_VSTART_F1(uint32_t i0) { return 0x00000030 + __offset_INTF(i0); }
#define MDP5_INTF_ACTIVE_VSTART_F1_VAL__MASK 0x7fffffff
#define MDP5_INTF_ACTIVE_VSTART_F1_VAL__SHIFT 0
static inline uint32_t MDP5_INTF_ACTIVE_VSTART_F1_VAL(uint32_t val)
@@ -880,11 +975,11 @@ static inline uint32_t MDP5_INTF_ACTIVE_VSTART_F1_VAL(uint32_t val)
return ((val) << MDP5_INTF_ACTIVE_VSTART_F1_VAL__SHIFT) & MDP5_INTF_ACTIVE_VSTART_F1_VAL__MASK;
}
-static inline uint32_t REG_MDP5_INTF_ACTIVE_VEND_F0(uint32_t i0) { return 0x00012534 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_ACTIVE_VEND_F0(uint32_t i0) { return 0x00000034 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_ACTIVE_VEND_F1(uint32_t i0) { return 0x00012538 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_ACTIVE_VEND_F1(uint32_t i0) { return 0x00000038 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_DISPLAY_HCTL(uint32_t i0) { return 0x0001253c + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_DISPLAY_HCTL(uint32_t i0) { return 0x0000003c + __offset_INTF(i0); }
#define MDP5_INTF_DISPLAY_HCTL_START__MASK 0x0000ffff
#define MDP5_INTF_DISPLAY_HCTL_START__SHIFT 0
static inline uint32_t MDP5_INTF_DISPLAY_HCTL_START(uint32_t val)
@@ -898,7 +993,7 @@ static inline uint32_t MDP5_INTF_DISPLAY_HCTL_END(uint32_t val)
return ((val) << MDP5_INTF_DISPLAY_HCTL_END__SHIFT) & MDP5_INTF_DISPLAY_HCTL_END__MASK;
}
-static inline uint32_t REG_MDP5_INTF_ACTIVE_HCTL(uint32_t i0) { return 0x00012540 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_ACTIVE_HCTL(uint32_t i0) { return 0x00000040 + __offset_INTF(i0); }
#define MDP5_INTF_ACTIVE_HCTL_START__MASK 0x00007fff
#define MDP5_INTF_ACTIVE_HCTL_START__SHIFT 0
static inline uint32_t MDP5_INTF_ACTIVE_HCTL_START(uint32_t val)
@@ -913,124 +1008,132 @@ static inline uint32_t MDP5_INTF_ACTIVE_HCTL_END(uint32_t val)
}
#define MDP5_INTF_ACTIVE_HCTL_ACTIVE_H_ENABLE 0x80000000
-static inline uint32_t REG_MDP5_INTF_BORDER_COLOR(uint32_t i0) { return 0x00012544 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_BORDER_COLOR(uint32_t i0) { return 0x00000044 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_UNDERFLOW_COLOR(uint32_t i0) { return 0x00012548 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_UNDERFLOW_COLOR(uint32_t i0) { return 0x00000048 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_HSYNC_SKEW(uint32_t i0) { return 0x0001254c + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_HSYNC_SKEW(uint32_t i0) { return 0x0000004c + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_POLARITY_CTL(uint32_t i0) { return 0x00012550 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_POLARITY_CTL(uint32_t i0) { return 0x00000050 + __offset_INTF(i0); }
#define MDP5_INTF_POLARITY_CTL_HSYNC_LOW 0x00000001
#define MDP5_INTF_POLARITY_CTL_VSYNC_LOW 0x00000002
#define MDP5_INTF_POLARITY_CTL_DATA_EN_LOW 0x00000004
-static inline uint32_t REG_MDP5_INTF_TEST_CTL(uint32_t i0) { return 0x00012554 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TEST_CTL(uint32_t i0) { return 0x00000054 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_TP_COLOR0(uint32_t i0) { return 0x00012558 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TP_COLOR0(uint32_t i0) { return 0x00000058 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_TP_COLOR1(uint32_t i0) { return 0x0001255c + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TP_COLOR1(uint32_t i0) { return 0x0000005c + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_DSI_CMD_MODE_TRIGGER_EN(uint32_t i0) { return 0x00012584 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_DSI_CMD_MODE_TRIGGER_EN(uint32_t i0) { return 0x00000084 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_PANEL_FORMAT(uint32_t i0) { return 0x00012590 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_PANEL_FORMAT(uint32_t i0) { return 0x00000090 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_FRAME_LINE_COUNT_EN(uint32_t i0) { return 0x000125a8 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_FRAME_LINE_COUNT_EN(uint32_t i0) { return 0x000000a8 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_FRAME_COUNT(uint32_t i0) { return 0x000125ac + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_FRAME_COUNT(uint32_t i0) { return 0x000000ac + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_LINE_COUNT(uint32_t i0) { return 0x000125b0 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_LINE_COUNT(uint32_t i0) { return 0x000000b0 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_DEFLICKER_CONFIG(uint32_t i0) { return 0x000125f0 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_DEFLICKER_CONFIG(uint32_t i0) { return 0x000000f0 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_DEFLICKER_STRNG_COEFF(uint32_t i0) { return 0x000125f4 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_DEFLICKER_STRNG_COEFF(uint32_t i0) { return 0x000000f4 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_DEFLICKER_WEAK_COEFF(uint32_t i0) { return 0x000125f8 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_DEFLICKER_WEAK_COEFF(uint32_t i0) { return 0x000000f8 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_TPG_ENABLE(uint32_t i0) { return 0x00012600 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TPG_ENABLE(uint32_t i0) { return 0x00000100 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_TPG_MAIN_CONTROL(uint32_t i0) { return 0x00012604 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TPG_MAIN_CONTROL(uint32_t i0) { return 0x00000104 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_TPG_VIDEO_CONFIG(uint32_t i0) { return 0x00012608 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TPG_VIDEO_CONFIG(uint32_t i0) { return 0x00000108 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_TPG_COMPONENT_LIMITS(uint32_t i0) { return 0x0001260c + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TPG_COMPONENT_LIMITS(uint32_t i0) { return 0x0000010c + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_TPG_RECTANGLE(uint32_t i0) { return 0x00012610 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TPG_RECTANGLE(uint32_t i0) { return 0x00000110 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_TPG_INITIAL_VALUE(uint32_t i0) { return 0x00012614 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TPG_INITIAL_VALUE(uint32_t i0) { return 0x00000114 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_TPG_BLK_WHITE_PATTERN_FRAME(uint32_t i0) { return 0x00012618 + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TPG_BLK_WHITE_PATTERN_FRAME(uint32_t i0) { return 0x00000118 + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_INTF_TPG_RGB_MAPPING(uint32_t i0) { return 0x0001261c + 0x200*i0; }
+static inline uint32_t REG_MDP5_INTF_TPG_RGB_MAPPING(uint32_t i0) { return 0x0000011c + __offset_INTF(i0); }
-static inline uint32_t REG_MDP5_AD(uint32_t i0) { return 0x00013100 + 0x200*i0; }
+static inline uint32_t __offset_AD(uint32_t idx)
+{
+ switch (idx) {
+ case 0: return (mdp5_cfg->ad.base[0]);
+ case 1: return (mdp5_cfg->ad.base[1]);
+ default: return INVALID_IDX(idx);
+ }
+}
+static inline uint32_t REG_MDP5_AD(uint32_t i0) { return 0x00000000 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_BYPASS(uint32_t i0) { return 0x00013100 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_BYPASS(uint32_t i0) { return 0x00000000 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_CTRL_0(uint32_t i0) { return 0x00013104 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_CTRL_0(uint32_t i0) { return 0x00000004 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_CTRL_1(uint32_t i0) { return 0x00013108 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_CTRL_1(uint32_t i0) { return 0x00000008 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_FRAME_SIZE(uint32_t i0) { return 0x0001310c + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_FRAME_SIZE(uint32_t i0) { return 0x0000000c + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_CON_CTRL_0(uint32_t i0) { return 0x00013110 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_CON_CTRL_0(uint32_t i0) { return 0x00000010 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_CON_CTRL_1(uint32_t i0) { return 0x00013114 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_CON_CTRL_1(uint32_t i0) { return 0x00000014 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_STR_MAN(uint32_t i0) { return 0x00013118 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_STR_MAN(uint32_t i0) { return 0x00000018 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_VAR(uint32_t i0) { return 0x0001311c + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_VAR(uint32_t i0) { return 0x0000001c + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_DITH(uint32_t i0) { return 0x00013120 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_DITH(uint32_t i0) { return 0x00000020 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_DITH_CTRL(uint32_t i0) { return 0x00013124 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_DITH_CTRL(uint32_t i0) { return 0x00000024 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_AMP_LIM(uint32_t i0) { return 0x00013128 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_AMP_LIM(uint32_t i0) { return 0x00000028 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_SLOPE(uint32_t i0) { return 0x0001312c + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_SLOPE(uint32_t i0) { return 0x0000002c + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_BW_LVL(uint32_t i0) { return 0x00013130 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_BW_LVL(uint32_t i0) { return 0x00000030 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_LOGO_POS(uint32_t i0) { return 0x00013134 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_LOGO_POS(uint32_t i0) { return 0x00000034 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_LUT_FI(uint32_t i0) { return 0x00013138 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_LUT_FI(uint32_t i0) { return 0x00000038 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_LUT_CC(uint32_t i0) { return 0x0001317c + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_LUT_CC(uint32_t i0) { return 0x0000007c + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_STR_LIM(uint32_t i0) { return 0x000131c8 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_STR_LIM(uint32_t i0) { return 0x000000c8 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_CALIB_AB(uint32_t i0) { return 0x000131cc + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_CALIB_AB(uint32_t i0) { return 0x000000cc + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_CALIB_CD(uint32_t i0) { return 0x000131d0 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_CALIB_CD(uint32_t i0) { return 0x000000d0 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_MODE_SEL(uint32_t i0) { return 0x000131d4 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_MODE_SEL(uint32_t i0) { return 0x000000d4 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_TFILT_CTRL(uint32_t i0) { return 0x000131d8 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_TFILT_CTRL(uint32_t i0) { return 0x000000d8 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_BL_MINMAX(uint32_t i0) { return 0x000131dc + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_BL_MINMAX(uint32_t i0) { return 0x000000dc + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_BL(uint32_t i0) { return 0x000131e0 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_BL(uint32_t i0) { return 0x000000e0 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_BL_MAX(uint32_t i0) { return 0x000131e8 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_BL_MAX(uint32_t i0) { return 0x000000e8 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_AL(uint32_t i0) { return 0x000131ec + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_AL(uint32_t i0) { return 0x000000ec + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_AL_MIN(uint32_t i0) { return 0x000131f0 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_AL_MIN(uint32_t i0) { return 0x000000f0 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_AL_FILT(uint32_t i0) { return 0x000131f4 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_AL_FILT(uint32_t i0) { return 0x000000f4 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_CFG_BUF(uint32_t i0) { return 0x000131f8 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_CFG_BUF(uint32_t i0) { return 0x000000f8 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_LUT_AL(uint32_t i0) { return 0x00013200 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_LUT_AL(uint32_t i0) { return 0x00000100 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_TARG_STR(uint32_t i0) { return 0x00013244 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_TARG_STR(uint32_t i0) { return 0x00000144 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_START_CALC(uint32_t i0) { return 0x00013248 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_START_CALC(uint32_t i0) { return 0x00000148 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_STR_OUT(uint32_t i0) { return 0x0001324c + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_STR_OUT(uint32_t i0) { return 0x0000014c + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_BL_OUT(uint32_t i0) { return 0x00013254 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_BL_OUT(uint32_t i0) { return 0x00000154 + __offset_AD(i0); }
-static inline uint32_t REG_MDP5_AD_CALC_DONE(uint32_t i0) { return 0x00013258 + 0x200*i0; }
+static inline uint32_t REG_MDP5_AD_CALC_DONE(uint32_t i0) { return 0x00000158 + __offset_AD(i0); }
#endif /* MDP5_XML */
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index 71510ee26e96..31a2c6331a1d 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -26,14 +26,98 @@ static const char *iommu_ports[] = {
static struct mdp5_platform_config *mdp5_get_config(struct platform_device *dev);
-static int mdp5_hw_init(struct msm_kms *kms)
+const struct mdp5_config *mdp5_cfg;
+
+static const struct mdp5_config msm8x74_config = {
+ .name = "msm8x74",
+ .ctl = {
+ .count = 5,
+ .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 },
+ },
+ .pipe_vig = {
+ .count = 3,
+ .base = { 0x01200, 0x01600, 0x01a00 },
+ },
+ .pipe_rgb = {
+ .count = 3,
+ .base = { 0x01e00, 0x02200, 0x02600 },
+ },
+ .pipe_dma = {
+ .count = 2,
+ .base = { 0x02a00, 0x02e00 },
+ },
+ .lm = {
+ .count = 5,
+ .base = { 0x03200, 0x03600, 0x03a00, 0x03e00, 0x04200 },
+ },
+ .dspp = {
+ .count = 3,
+ .base = { 0x04600, 0x04a00, 0x04e00 },
+ },
+ .ad = {
+ .count = 2,
+ .base = { 0x13100, 0x13300 }, /* NOTE: no ad in v1.0 */
+ },
+ .intf = {
+ .count = 4,
+ .base = { 0x12500, 0x12700, 0x12900, 0x12b00 },
+ },
+};
+
+static const struct mdp5_config apq8084_config = {
+ .name = "apq8084",
+ .ctl = {
+ .count = 5,
+ .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 },
+ },
+ .pipe_vig = {
+ .count = 4,
+ .base = { 0x01200, 0x01600, 0x01a00, 0x01e00 },
+ },
+ .pipe_rgb = {
+ .count = 4,
+ .base = { 0x02200, 0x02600, 0x02a00, 0x02e00 },
+ },
+ .pipe_dma = {
+ .count = 2,
+ .base = { 0x03200, 0x03600 },
+ },
+ .lm = {
+ .count = 6,
+ .base = { 0x03a00, 0x03e00, 0x04200, 0x04600, 0x04a00, 0x04e00 },
+ },
+ .dspp = {
+ .count = 4,
+ .base = { 0x05200, 0x05600, 0x05a00, 0x05e00 },
+
+ },
+ .ad = {
+ .count = 3,
+ .base = { 0x13500, 0x13700, 0x13900 },
+ },
+ .intf = {
+ .count = 5,
+ .base = { 0x12500, 0x12700, 0x12900, 0x12b00, 0x12d00 },
+ },
+};
+
+struct mdp5_config_entry {
+ int revision;
+ const struct mdp5_config *config;
+};
+
+static const struct mdp5_config_entry mdp5_configs[] = {
+ { .revision = 0, .config = &msm8x74_config },
+ { .revision = 2, .config = &msm8x74_config },
+ { .revision = 3, .config = &apq8084_config },
+};
+
+static int mdp5_select_hw_cfg(struct msm_kms *kms)
{
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
struct drm_device *dev = mdp5_kms->dev;
uint32_t version, major, minor;
- int ret = 0;
-
- pm_runtime_get_sync(dev->dev);
+ int i, ret = 0;
mdp5_enable(mdp5_kms);
version = mdp5_read(mdp5_kms, REG_MDP5_MDP_VERSION);
@@ -44,8 +128,8 @@ static int mdp5_hw_init(struct msm_kms *kms)
DBG("found MDP5 version v%d.%d", major, minor);
- if ((major != 1) || ((minor != 0) && (minor != 2))) {
- dev_err(dev->dev, "unexpected MDP version: v%d.%d\n",
+ if (major != 1) {
+ dev_err(dev->dev, "unexpected MDP major version: v%d.%d\n",
major, minor);
ret = -ENXIO;
goto out;
@@ -53,6 +137,35 @@ static int mdp5_hw_init(struct msm_kms *kms)
mdp5_kms->rev = minor;
+ /* only after mdp5_cfg global pointer's init can we access the hw */
+ for (i = 0; i < ARRAY_SIZE(mdp5_configs); i++) {
+ if (mdp5_configs[i].revision != minor)
+ continue;
+ mdp5_kms->hw_cfg = mdp5_cfg = mdp5_configs[i].config;
+ break;
+ }
+ if (unlikely(!mdp5_kms->hw_cfg)) {
+ dev_err(dev->dev, "unexpected MDP minor revision: v%d.%d\n",
+ major, minor);
+ ret = -ENXIO;
+ goto out;
+ }
+
+ DBG("MDP5: %s config selected", mdp5_kms->hw_cfg->name);
+
+ return 0;
+out:
+ return ret;
+}
+
+static int mdp5_hw_init(struct msm_kms *kms)
+{
+ struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
+ struct drm_device *dev = mdp5_kms->dev;
+ int i;
+
+ pm_runtime_get_sync(dev->dev);
+
/* Magic unknown register writes:
*
* W VBIF:0x004 00000001 (mdss_mdp.c:839)
@@ -78,15 +191,13 @@ static int mdp5_hw_init(struct msm_kms *kms)
*/
mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, 0);
- mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(0), 0);
- mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(1), 0);
- mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(2), 0);
- mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(3), 0);
-out:
+ for (i = 0; i < mdp5_kms->hw_cfg->ctl.count; i++)
+ mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(i), 0);
+
pm_runtime_put_sync(dev->dev);
- return ret;
+ return 0;
}
static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate,
@@ -161,7 +272,7 @@ int mdp5_enable(struct mdp5_kms *mdp5_kms)
static int modeset_init(struct mdp5_kms *mdp5_kms)
{
static const enum mdp5_pipe crtcs[] = {
- SSPP_RGB0, SSPP_RGB1, SSPP_RGB2,
+ SSPP_RGB0, SSPP_RGB1, SSPP_RGB2, SSPP_RGB3,
};
struct drm_device *dev = mdp5_kms->dev;
struct msm_drm_private *priv = dev->dev_private;
@@ -169,7 +280,7 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
int i, ret;
/* construct CRTCs: */
- for (i = 0; i < ARRAY_SIZE(crtcs); i++) {
+ for (i = 0; i < mdp5_kms->hw_cfg->pipe_rgb.count; i++) {
struct drm_plane *plane;
struct drm_crtc *crtc;
@@ -246,7 +357,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
struct mdp5_kms *mdp5_kms;
struct msm_kms *kms = NULL;
struct msm_mmu *mmu;
- int ret;
+ int i, ret;
mdp5_kms = kzalloc(sizeof(*mdp5_kms), GFP_KERNEL);
if (!mdp5_kms) {
@@ -307,20 +418,22 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
ret = clk_set_rate(mdp5_kms->src_clk, config->max_clk);
+ ret = mdp5_select_hw_cfg(kms);
+ if (ret)
+ goto fail;
+
/* make sure things are off before attaching iommu (bootloader could
* have left things on, in which case we'll start getting faults if
* we don't disable):
*/
mdp5_enable(mdp5_kms);
- mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(0), 0);
- mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(1), 0);
- mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(2), 0);
- mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(3), 0);
+ for (i = 0; i < mdp5_kms->hw_cfg->intf.count; i++)
+ mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0);
mdp5_disable(mdp5_kms);
mdelay(16);
if (config->iommu) {
- mmu = msm_iommu_new(dev, config->iommu);
+ mmu = msm_iommu_new(&pdev->dev, config->iommu);
if (IS_ERR(mmu)) {
ret = PTR_ERR(mmu);
dev_err(dev->dev, "failed to init iommu: %d\n", ret);
@@ -368,5 +481,11 @@ static struct mdp5_platform_config *mdp5_get_config(struct platform_device *dev)
#ifdef CONFIG_OF
/* TODO */
#endif
+ config.iommu = iommu_domain_alloc(&platform_bus_type);
+ /* TODO hard-coded in downstream mdss, but should it be? */
+ config.max_clk = 200000000;
+ /* TODO get from DT: */
+ config.smp_blk_cnt = 22;
+
return &config;
}
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
index 6e981b692d1d..5bf340dd0f00 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
@@ -21,6 +21,24 @@
#include "msm_drv.h"
#include "msm_kms.h"
#include "mdp/mdp_kms.h"
+/* dynamic offsets used by mdp5.xml.h (initialized in mdp5_kms.c) */
+#define MDP5_MAX_BASES 8
+struct mdp5_sub_block {
+ int count;
+ uint32_t base[MDP5_MAX_BASES];
+};
+struct mdp5_config {
+ char *name;
+ struct mdp5_sub_block ctl;
+ struct mdp5_sub_block pipe_vig;
+ struct mdp5_sub_block pipe_rgb;
+ struct mdp5_sub_block pipe_dma;
+ struct mdp5_sub_block lm;
+ struct mdp5_sub_block dspp;
+ struct mdp5_sub_block ad;
+ struct mdp5_sub_block intf;
+};
+extern const struct mdp5_config *mdp5_cfg;
#include "mdp5.xml.h"
#include "mdp5_smp.h"
@@ -30,6 +48,7 @@ struct mdp5_kms {
struct drm_device *dev;
int rev;
+ const struct mdp5_config *hw_cfg;
/* mapper-id used to request GEM buffer mapped for scanout: */
int id;
@@ -82,6 +101,7 @@ static inline const char *pipe2name(enum mdp5_pipe pipe)
NAME(VIG0), NAME(VIG1), NAME(VIG2),
NAME(RGB0), NAME(RGB1), NAME(RGB2),
NAME(DMA0), NAME(DMA1),
+ NAME(VIG3), NAME(RGB3),
#undef NAME
};
return names[pipe];
@@ -98,6 +118,8 @@ static inline uint32_t pipe2flush(enum mdp5_pipe pipe)
case SSPP_RGB2: return MDP5_CTL_FLUSH_RGB2;
case SSPP_DMA0: return MDP5_CTL_FLUSH_DMA0;
case SSPP_DMA1: return MDP5_CTL_FLUSH_DMA1;
+ case SSPP_VIG3: return MDP5_CTL_FLUSH_VIG3;
+ case SSPP_RGB3: return MDP5_CTL_FLUSH_RGB3;
default: return 0;
}
}
@@ -108,6 +130,7 @@ static inline int pipe2nclients(enum mdp5_pipe pipe)
case SSPP_RGB0:
case SSPP_RGB1:
case SSPP_RGB2:
+ case SSPP_RGB3:
return 1;
default:
return 3;
@@ -126,6 +149,8 @@ static inline enum mdp5_client_id pipe2client(enum mdp5_pipe pipe, int plane)
case SSPP_RGB2: return CID_RGB2;
case SSPP_DMA0: return CID_DMA0_Y + plane;
case SSPP_DMA1: return CID_DMA1_Y + plane;
+ case SSPP_VIG3: return CID_VIG3_Y + plane;
+ case SSPP_RGB3: return CID_RGB3;
default: return CID_UNUSED;
}
}
diff --git a/drivers/gpu/drm/msm/mdp/mdp_common.xml.h b/drivers/gpu/drm/msm/mdp/mdp_common.xml.h
index a9629b85b983..64c1afd6030a 100644
--- a/drivers/gpu/drm/msm/mdp/mdp_common.xml.h
+++ b/drivers/gpu/drm/msm/mdp/mdp_common.xml.h
@@ -12,12 +12,12 @@ The rules-ng-ng source files this header was generated from are:
- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 17996 bytes, from 2013-12-01 19:10:31)
- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 1615 bytes, from 2013-11-30 15:00:52)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2013-12-03 20:59:13)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 22517 bytes, from 2014-06-25 12:55:02)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1544 bytes, from 2013-08-16 19:17:05)
- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 20932 bytes, from 2013-12-01 15:13:04)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 23613 bytes, from 2014-06-25 12:53:44)
Copyright (C) 2013 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 9a5d87db5c23..b67ef5985125 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -52,7 +52,7 @@ module_param(reglog, bool, 0600);
#define reglog 0
#endif
-static char *vram;
+static char *vram = "16m";
MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU");
module_param(vram, charp, 0);
@@ -181,7 +181,6 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
struct msm_kms *kms;
int ret;
-
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(dev->dev, "failed to allocate private data\n");
@@ -281,7 +280,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
dev->mode_config.max_height = 2048;
dev->mode_config.funcs = &mode_config_funcs;
- ret = drm_vblank_init(dev, 1);
+ ret = drm_vblank_init(dev, priv->num_crtcs);
if (ret < 0) {
dev_err(dev->dev, "failed to initialize vblank\n");
goto fail;
@@ -314,38 +313,15 @@ fail:
static void load_gpu(struct drm_device *dev)
{
+ static DEFINE_MUTEX(init_lock);
struct msm_drm_private *priv = dev->dev_private;
- struct msm_gpu *gpu;
-
- if (priv->gpu)
- return;
-
- mutex_lock(&dev->struct_mutex);
- gpu = a3xx_gpu_init(dev);
- if (IS_ERR(gpu)) {
- dev_warn(dev->dev, "failed to load a3xx gpu\n");
- gpu = NULL;
- /* not fatal */
- }
-
- if (gpu) {
- int ret;
- gpu->funcs->pm_resume(gpu);
- ret = gpu->funcs->hw_init(gpu);
- if (ret) {
- dev_err(dev->dev, "gpu hw init failed: %d\n", ret);
- gpu->funcs->destroy(gpu);
- gpu = NULL;
- } else {
- /* give inactive pm a chance to kick in: */
- msm_gpu_retire(gpu);
- }
- }
+ mutex_lock(&init_lock);
- priv->gpu = gpu;
+ if (!priv->gpu)
+ priv->gpu = adreno_load_gpu(dev);
- mutex_unlock(&dev->struct_mutex);
+ mutex_unlock(&init_lock);
}
static int msm_open(struct drm_device *dev, struct drm_file *file)
@@ -833,6 +809,7 @@ static struct drm_driver msm_driver = {
.open = msm_open,
.preclose = msm_preclose,
.lastclose = msm_lastclose,
+ .set_busid = drm_platform_set_busid,
.irq_handler = msm_irq,
.irq_preinstall = msm_irq_preinstall,
.irq_postinstall = msm_irq_postinstall,
@@ -906,25 +883,22 @@ static int compare_of(struct device *dev, void *data)
return dev->of_node == data;
}
-static int msm_drm_add_components(struct device *master, struct master *m)
+static int add_components(struct device *dev, struct component_match **matchptr,
+ const char *name)
{
- struct device_node *np = master->of_node;
+ struct device_node *np = dev->of_node;
unsigned i;
- int ret;
for (i = 0; ; i++) {
struct device_node *node;
- node = of_parse_phandle(np, "connectors", i);
+ node = of_parse_phandle(np, name, i);
if (!node)
break;
- ret = component_master_add_child(m, compare_of, node);
- of_node_put(node);
-
- if (ret)
- return ret;
+ component_match_add(dev, matchptr, compare_of, node);
}
+
return 0;
}
#else
@@ -932,9 +906,34 @@ static int compare_dev(struct device *dev, void *data)
{
return dev == data;
}
+#endif
+
+static int msm_drm_bind(struct device *dev)
+{
+ return drm_platform_init(&msm_driver, to_platform_device(dev));
+}
+
+static void msm_drm_unbind(struct device *dev)
+{
+ drm_put_dev(platform_get_drvdata(to_platform_device(dev)));
+}
+
+static const struct component_master_ops msm_drm_ops = {
+ .bind = msm_drm_bind,
+ .unbind = msm_drm_unbind,
+};
-static int msm_drm_add_components(struct device *master, struct master *m)
+/*
+ * Platform driver:
+ */
+
+static int msm_pdev_probe(struct platform_device *pdev)
{
+ struct component_match *match = NULL;
+#ifdef CONFIG_OF
+ add_components(&pdev->dev, &match, "connectors");
+ add_components(&pdev->dev, &match, "gpus");
+#else
/* For non-DT case, it kinda sucks. We don't actually have a way
* to know whether or not we are waiting for certain devices (or if
* they are simply not present). But for non-DT we only need to
@@ -949,50 +948,20 @@ static int msm_drm_add_components(struct device *master, struct master *m)
for (i = 0; i < ARRAY_SIZE(devnames); i++) {
struct device *dev;
- int ret;
dev = bus_find_device_by_name(&platform_bus_type,
NULL, devnames[i]);
if (!dev) {
- dev_info(master, "still waiting for %s\n", devnames[i]);
+ dev_info(&pdev->dev, "still waiting for %s\n", devnames[i]);
return -EPROBE_DEFER;
}
- ret = component_master_add_child(m, compare_dev, dev);
- if (ret) {
- DBG("could not add child: %d", ret);
- return ret;
- }
+ component_match_add(&pdev->dev, &match, compare_dev, dev);
}
-
- return 0;
-}
#endif
-static int msm_drm_bind(struct device *dev)
-{
- return drm_platform_init(&msm_driver, to_platform_device(dev));
-}
-
-static void msm_drm_unbind(struct device *dev)
-{
- drm_put_dev(platform_get_drvdata(to_platform_device(dev)));
-}
-
-static const struct component_master_ops msm_drm_ops = {
- .add_components = msm_drm_add_components,
- .bind = msm_drm_bind,
- .unbind = msm_drm_unbind,
-};
-
-/*
- * Platform driver:
- */
-
-static int msm_pdev_probe(struct platform_device *pdev)
-{
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
- return component_master_add(&pdev->dev, &msm_drm_ops);
+ return component_master_add_with_match(&pdev->dev, &msm_drm_ops, match);
}
static int msm_pdev_remove(struct platform_device *pdev)
@@ -1008,7 +977,8 @@ static const struct platform_device_id msm_id[] = {
};
static const struct of_device_id dt_match[] = {
- { .compatible = "qcom,mdss_mdp" },
+ { .compatible = "qcom,mdp" }, /* mdp4 */
+ { .compatible = "qcom,mdss_mdp" }, /* mdp5 */
{}
};
MODULE_DEVICE_TABLE(of, dt_match);
@@ -1029,7 +999,7 @@ static int __init msm_drm_register(void)
{
DBG("init");
hdmi_register();
- a3xx_register();
+ adreno_register();
return platform_driver_register(&msm_platform_driver);
}
@@ -1038,7 +1008,7 @@ static void __exit msm_drm_unregister(void)
DBG("fini");
platform_driver_unregister(&msm_platform_driver);
hdmi_unregister();
- a3xx_unregister();
+ adreno_unregister();
}
module_init(msm_drm_register);
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 8a2c5fd0893e..67f9d0a2332c 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -51,6 +51,7 @@ static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/msm_drm.h>
+#include <drm/drm_gem.h>
struct msm_kms;
struct msm_gpu;
@@ -170,7 +171,7 @@ 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);
struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
- size_t size, struct sg_table *sg);
+ 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);
diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
index 5107fc4826bc..ab5bfd2d0ebf 100644
--- a/drivers/gpu/drm/msm/msm_fbdev.c
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -19,6 +19,11 @@
#include "drm_crtc.h"
#include "drm_fb_helper.h"
+#include "msm_gem.h"
+
+extern int msm_gem_mmap_obj(struct drm_gem_object *obj,
+ struct vm_area_struct *vma);
+static int msm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma);
/*
* fbdev funcs, to implement legacy fbdev interface on top of drm driver
@@ -43,6 +48,7 @@ static struct fb_ops msm_fb_ops = {
.fb_fillrect = sys_fillrect,
.fb_copyarea = sys_copyarea,
.fb_imageblit = sys_imageblit,
+ .fb_mmap = msm_fbdev_mmap,
.fb_check_var = drm_fb_helper_check_var,
.fb_set_par = drm_fb_helper_set_par,
@@ -51,6 +57,31 @@ static struct fb_ops msm_fb_ops = {
.fb_setcmap = drm_fb_helper_setcmap,
};
+static int msm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct drm_fb_helper *helper = (struct drm_fb_helper *)info->par;
+ struct msm_fbdev *fbdev = to_msm_fbdev(helper);
+ struct drm_gem_object *drm_obj = fbdev->bo;
+ struct drm_device *dev = helper->dev;
+ int ret = 0;
+
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
+ mutex_lock(&dev->struct_mutex);
+
+ ret = drm_gem_mmap_obj(drm_obj, drm_obj->size, vma);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ if (ret) {
+ pr_err("%s:drm_gem_mmap_obj fail\n", __func__);
+ return ret;
+ }
+
+ return msm_gem_mmap_obj(drm_obj, vma);
+}
+
static int msm_fbdev_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
@@ -104,8 +135,16 @@ static int msm_fbdev_create(struct drm_fb_helper *helper,
mutex_lock(&dev->struct_mutex);
- /* TODO implement our own fb_mmap so we don't need this: */
- msm_gem_get_iova_locked(fbdev->bo, 0, &paddr);
+ /*
+ * NOTE: if we can be guaranteed to be able to map buffer
+ * in panic (ie. lock-safe, etc) we could avoid pinning the
+ * buffer now:
+ */
+ ret = msm_gem_get_iova_locked(fbdev->bo, 0, &paddr);
+ if (ret) {
+ dev_err(dev->dev, "failed to get buffer obj iova: %d\n", ret);
+ goto fail_unlock;
+ }
fbi = framebuffer_alloc(0, dev->dev);
if (!fbi) {
@@ -177,7 +216,7 @@ static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc,
DBG("fbdev: get gamma");
}
-static struct drm_fb_helper_funcs msm_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs msm_fb_helper_funcs = {
.gamma_set = msm_crtc_fb_gamma_set,
.gamma_get = msm_crtc_fb_gamma_get,
.fb_probe = msm_fbdev_create,
@@ -189,7 +228,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
struct msm_drm_private *priv = dev->dev_private;
struct msm_fbdev *fbdev = NULL;
struct drm_fb_helper *helper;
- int ret = 0;
+ int ret;
fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
if (!fbdev)
@@ -197,7 +236,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
helper = &fbdev->base;
- helper->funcs = &msm_fb_helper_funcs;
+ drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs);
ret = drm_fb_helper_init(dev, helper,
priv->num_crtcs, priv->num_connectors);
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 690d7e7b6d1e..4b1b82adabde 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -73,7 +73,7 @@ static struct page **get_pages(struct drm_gem_object *obj)
int npages = obj->size >> PAGE_SHIFT;
if (iommu_present(&platform_bus_type))
- p = drm_gem_get_pages(obj, 0);
+ p = drm_gem_get_pages(obj);
else
p = get_pages_vram(obj, npages);
@@ -278,24 +278,23 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, int id,
uint32_t *iova)
{
struct msm_gem_object *msm_obj = to_msm_bo(obj);
- struct drm_device *dev = obj->dev;
int ret = 0;
if (!msm_obj->domain[id].iova) {
struct msm_drm_private *priv = obj->dev->dev_private;
- struct msm_mmu *mmu = priv->mmus[id];
struct page **pages = get_pages(obj);
- if (!mmu) {
- dev_err(dev->dev, "null MMU pointer\n");
- return -EINVAL;
- }
-
if (IS_ERR(pages))
return PTR_ERR(pages);
if (iommu_present(&platform_bus_type)) {
- uint32_t offset = (uint32_t)mmap_offset(obj);
+ struct msm_mmu *mmu = priv->mmus[id];
+ uint32_t offset;
+
+ if (WARN_ON(!mmu))
+ return -EINVAL;
+
+ offset = (uint32_t)mmap_offset(obj);
ret = mmu->funcs->map(mmu, offset, msm_obj->sgt,
obj->size, IOMMU_READ | IOMMU_WRITE);
msm_obj->domain[id].iova = offset;
diff --git a/drivers/gpu/drm/msm/msm_gem_prime.c b/drivers/gpu/drm/msm/msm_gem_prime.c
index d48f9fc5129b..ad772fe36115 100644
--- a/drivers/gpu/drm/msm/msm_gem_prime.c
+++ b/drivers/gpu/drm/msm/msm_gem_prime.c
@@ -18,6 +18,7 @@
#include "msm_drv.h"
#include "msm_gem.h"
+#include <linux/dma-buf.h>
struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj)
{
@@ -37,9 +38,9 @@ void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
}
struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
- size_t size, struct sg_table *sg)
+ struct dma_buf_attachment *attach, struct sg_table *sg)
{
- return msm_gem_import(dev, size, sg);
+ return msm_gem_import(dev, attach->dmabuf->size, sg);
}
int msm_gem_prime_pin(struct drm_gem_object *obj)
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index c6322197db8c..4a0dce587745 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -606,14 +606,17 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
iommu = iommu_domain_alloc(&platform_bus_type);
if (iommu) {
dev_info(drm->dev, "%s: using IOMMU\n", name);
- gpu->mmu = msm_iommu_new(drm, iommu);
+ gpu->mmu = msm_iommu_new(&pdev->dev, iommu);
} else {
dev_info(drm->dev, "%s: no IOMMU, fallback to VRAM carveout!\n", name);
}
gpu->id = msm_register_mmu(drm, gpu->mmu);
+
/* Create ringbuffer: */
+ mutex_lock(&drm->struct_mutex);
gpu->rb = msm_ringbuffer_new(gpu, ringsz);
+ mutex_unlock(&drm->struct_mutex);
if (IS_ERR(gpu->rb)) {
ret = PTR_ERR(gpu->rb);
gpu->rb = NULL;
@@ -621,13 +624,6 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
goto fail;
}
- ret = msm_gem_get_iova_locked(gpu->rb->bo, gpu->id, &gpu->rb_iova);
- if (ret) {
- gpu->rb_iova = 0;
- dev_err(drm->dev, "could not map ringbuffer: %d\n", ret);
- goto fail;
- }
-
bs_init(gpu);
return 0;
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index 9b579b792840..fd1e4b4a6d40 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -166,8 +166,8 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
const char *name, const char *ioname, const char *irqname, int ringsz);
void msm_gpu_cleanup(struct msm_gpu *gpu);
-struct msm_gpu *a3xx_gpu_init(struct drm_device *dev);
-void __init a3xx_register(void);
-void __exit a3xx_unregister(void);
+struct msm_gpu *adreno_load_gpu(struct drm_device *dev);
+void __init adreno_register(void);
+void __exit adreno_unregister(void);
#endif /* __MSM_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c
index 4b2ad9181edf..7acdaa5688b7 100644
--- a/drivers/gpu/drm/msm/msm_iommu.c
+++ b/drivers/gpu/drm/msm/msm_iommu.c
@@ -27,45 +27,20 @@ struct msm_iommu {
static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev,
unsigned long iova, int flags, void *arg)
{
- DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
- return -ENOSYS;
+ pr_warn_ratelimited("*** fault: iova=%08lx, flags=%d\n", iova, flags);
+ return 0;
}
static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt)
{
- struct drm_device *dev = mmu->dev;
struct msm_iommu *iommu = to_msm_iommu(mmu);
- int i, ret;
-
- for (i = 0; i < cnt; i++) {
- struct device *msm_iommu_get_ctx(const char *ctx_name);
- struct device *ctx = msm_iommu_get_ctx(names[i]);
- if (IS_ERR_OR_NULL(ctx)) {
- dev_warn(dev->dev, "couldn't get %s context", names[i]);
- continue;
- }
- ret = iommu_attach_device(iommu->domain, ctx);
- if (ret) {
- dev_warn(dev->dev, "could not attach iommu to %s", names[i]);
- return ret;
- }
- }
-
- return 0;
+ return iommu_attach_device(iommu->domain, mmu->dev);
}
static void msm_iommu_detach(struct msm_mmu *mmu, const char **names, int cnt)
{
struct msm_iommu *iommu = to_msm_iommu(mmu);
- int i;
-
- for (i = 0; i < cnt; i++) {
- struct device *msm_iommu_get_ctx(const char *ctx_name);
- struct device *ctx = msm_iommu_get_ctx(names[i]);
- if (IS_ERR_OR_NULL(ctx))
- continue;
- iommu_detach_device(iommu->domain, ctx);
- }
+ iommu_detach_device(iommu->domain, mmu->dev);
}
static int msm_iommu_map(struct msm_mmu *mmu, uint32_t iova,
@@ -149,7 +124,7 @@ static const struct msm_mmu_funcs funcs = {
.destroy = msm_iommu_destroy,
};
-struct msm_mmu *msm_iommu_new(struct drm_device *dev, struct iommu_domain *domain)
+struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain)
{
struct msm_iommu *iommu;
diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h
index 21da6d154f71..7cd88d9dc155 100644
--- a/drivers/gpu/drm/msm/msm_mmu.h
+++ b/drivers/gpu/drm/msm/msm_mmu.h
@@ -32,17 +32,17 @@ struct msm_mmu_funcs {
struct msm_mmu {
const struct msm_mmu_funcs *funcs;
- struct drm_device *dev;
+ struct device *dev;
};
-static inline void msm_mmu_init(struct msm_mmu *mmu, struct drm_device *dev,
+static inline void msm_mmu_init(struct msm_mmu *mmu, struct device *dev,
const struct msm_mmu_funcs *funcs)
{
mmu->dev = dev;
mmu->funcs = funcs;
}
-struct msm_mmu *msm_iommu_new(struct drm_device *dev, struct iommu_domain *domain);
-struct msm_mmu *msm_gpummu_new(struct drm_device *dev, struct msm_gpu *gpu);
+struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain);
+struct msm_mmu *msm_gpummu_new(struct device *dev, struct msm_gpu *gpu);
#endif /* __MSM_MMU_H__ */
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
index 637c29a33127..40afc69a3778 100644
--- a/drivers/gpu/drm/nouveau/Kconfig
+++ b/drivers/gpu/drm/nouveau/Kconfig
@@ -1,5 +1,5 @@
config DRM_NOUVEAU
- tristate "Nouveau (nVidia) cards"
+ tristate "Nouveau (NVIDIA) cards"
depends on DRM && PCI
select FW_LOADER
select DRM_KMS_HELPER
@@ -23,7 +23,15 @@ config DRM_NOUVEAU
select THERMAL if ACPI && X86
select ACPI_VIDEO if ACPI && X86
help
- Choose this option for open-source nVidia support.
+ Choose this option for open-source NVIDIA support.
+
+config NOUVEAU_PLATFORM_DRIVER
+ tristate "Nouveau (NVIDIA) SoC GPUs"
+ depends on DRM_NOUVEAU && ARCH_TEGRA
+ default y
+ help
+ Support for Nouveau platform driver, used for SoC GPUs as found
+ on NVIDIA Tegra K1.
config NOUVEAU_DEBUG
int "Maximum debug level"
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 8b307e143632..12c24c8abf7f 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -14,8 +14,10 @@ nouveau-y += core/core/enum.o
nouveau-y += core/core/event.o
nouveau-y += core/core/gpuobj.o
nouveau-y += core/core/handle.o
+nouveau-y += core/core/ioctl.o
nouveau-y += core/core/mm.o
nouveau-y += core/core/namedb.o
+nouveau-y += core/core/notify.o
nouveau-y += core/core/object.o
nouveau-y += core/core/option.o
nouveau-y += core/core/parent.o
@@ -26,6 +28,7 @@ nouveau-y += core/core/subdev.o
nouveau-y += core/subdev/bar/base.o
nouveau-y += core/subdev/bar/nv50.o
nouveau-y += core/subdev/bar/nvc0.o
+nouveau-y += core/subdev/bar/gk20a.o
nouveau-y += core/subdev/bios/base.o
nouveau-y += core/subdev/bios/bit.o
nouveau-y += core/subdev/bios/boost.o
@@ -35,6 +38,7 @@ nouveau-y += core/subdev/bios/dcb.o
nouveau-y += core/subdev/bios/disp.o
nouveau-y += core/subdev/bios/dp.o
nouveau-y += core/subdev/bios/extdev.o
+nouveau-y += core/subdev/bios/fan.o
nouveau-y += core/subdev/bios/gpio.o
nouveau-y += core/subdev/bios/i2c.o
nouveau-y += core/subdev/bios/init.o
@@ -48,6 +52,8 @@ nouveau-y += core/subdev/bios/therm.o
nouveau-y += core/subdev/bios/vmap.o
nouveau-y += core/subdev/bios/volt.o
nouveau-y += core/subdev/bios/xpio.o
+nouveau-y += core/subdev/bios/M0205.o
+nouveau-y += core/subdev/bios/M0209.o
nouveau-y += core/subdev/bios/P0260.o
nouveau-y += core/subdev/bus/hwsq.o
nouveau-y += core/subdev/bus/nv04.o
@@ -64,6 +70,7 @@ nouveau-y += core/subdev/clock/nva3.o
nouveau-y += core/subdev/clock/nvaa.o
nouveau-y += core/subdev/clock/nvc0.o
nouveau-y += core/subdev/clock/nve0.o
+nouveau-y += core/subdev/clock/gk20a.o
nouveau-y += core/subdev/clock/pllnv04.o
nouveau-y += core/subdev/clock/pllnva3.o
nouveau-y += core/subdev/devinit/base.o
@@ -120,12 +127,17 @@ nouveau-y += core/subdev/fb/ramnvc0.o
nouveau-y += core/subdev/fb/ramnve0.o
nouveau-y += core/subdev/fb/ramgk20a.o
nouveau-y += core/subdev/fb/ramgm107.o
+nouveau-y += core/subdev/fb/sddr2.o
nouveau-y += core/subdev/fb/sddr3.o
nouveau-y += core/subdev/fb/gddr5.o
+nouveau-y += core/subdev/fuse/base.o
+nouveau-y += core/subdev/fuse/g80.o
+nouveau-y += core/subdev/fuse/gf100.o
+nouveau-y += core/subdev/fuse/gm107.o
nouveau-y += core/subdev/gpio/base.o
nouveau-y += core/subdev/gpio/nv10.o
nouveau-y += core/subdev/gpio/nv50.o
-nouveau-y += core/subdev/gpio/nv92.o
+nouveau-y += core/subdev/gpio/nv94.o
nouveau-y += core/subdev/gpio/nvd0.o
nouveau-y += core/subdev/gpio/nve0.o
nouveau-y += core/subdev/i2c/base.o
@@ -149,8 +161,10 @@ nouveau-y += core/subdev/instmem/base.o
nouveau-y += core/subdev/instmem/nv04.o
nouveau-y += core/subdev/instmem/nv40.o
nouveau-y += core/subdev/instmem/nv50.o
-nouveau-y += core/subdev/ltcg/gf100.o
-nouveau-y += core/subdev/ltcg/gm107.o
+nouveau-y += core/subdev/ltc/base.o
+nouveau-y += core/subdev/ltc/gf100.o
+nouveau-y += core/subdev/ltc/gk104.o
+nouveau-y += core/subdev/ltc/gm107.o
nouveau-y += core/subdev/mc/base.o
nouveau-y += core/subdev/mc/nv04.o
nouveau-y += core/subdev/mc/nv40.o
@@ -161,6 +175,7 @@ nouveau-y += core/subdev/mc/nv94.o
nouveau-y += core/subdev/mc/nv98.o
nouveau-y += core/subdev/mc/nvc0.o
nouveau-y += core/subdev/mc/nvc3.o
+nouveau-y += core/subdev/mc/gk20a.o
nouveau-y += core/subdev/mxm/base.o
nouveau-y += core/subdev/mxm/mxms.o
nouveau-y += core/subdev/mxm/nv50.o
@@ -169,6 +184,7 @@ nouveau-y += core/subdev/pwr/memx.o
nouveau-y += core/subdev/pwr/nva3.o
nouveau-y += core/subdev/pwr/nvc0.o
nouveau-y += core/subdev/pwr/nvd0.o
+nouveau-y += core/subdev/pwr/gk104.o
nouveau-y += core/subdev/pwr/nv108.o
nouveau-y += core/subdev/therm/base.o
nouveau-y += core/subdev/therm/fan.o
@@ -182,6 +198,7 @@ nouveau-y += core/subdev/therm/nv50.o
nouveau-y += core/subdev/therm/nv84.o
nouveau-y += core/subdev/therm/nva3.o
nouveau-y += core/subdev/therm/nvd0.o
+nouveau-y += core/subdev/therm/gm107.o
nouveau-y += core/subdev/timer/base.o
nouveau-y += core/subdev/timer/nv04.o
nouveau-y += core/subdev/timer/gk20a.o
@@ -211,6 +228,7 @@ nouveau-y += core/engine/copy/nvc0.o
nouveau-y += core/engine/copy/nve0.o
nouveau-y += core/engine/crypt/nv84.o
nouveau-y += core/engine/crypt/nv98.o
+nouveau-y += core/engine/device/acpi.o
nouveau-y += core/engine/device/base.o
nouveau-y += core/engine/device/ctrl.o
nouveau-y += core/engine/device/nv04.o
@@ -243,6 +261,7 @@ nouveau-y += core/engine/disp/hdanvd0.o
nouveau-y += core/engine/disp/hdminv84.o
nouveau-y += core/engine/disp/hdminva3.o
nouveau-y += core/engine/disp/hdminvd0.o
+nouveau-y += core/engine/disp/hdminve0.o
nouveau-y += core/engine/disp/piornv50.o
nouveau-y += core/engine/disp/sornv50.o
nouveau-y += core/engine/disp/sornv94.o
@@ -270,6 +289,7 @@ nouveau-y += core/engine/graph/ctxnvd9.o
nouveau-y += core/engine/graph/ctxnve4.o
nouveau-y += core/engine/graph/ctxgk20a.o
nouveau-y += core/engine/graph/ctxnvf0.o
+nouveau-y += core/engine/graph/ctxgk110b.o
nouveau-y += core/engine/graph/ctxnv108.o
nouveau-y += core/engine/graph/ctxgm107.o
nouveau-y += core/engine/graph/nv04.o
@@ -291,6 +311,7 @@ nouveau-y += core/engine/graph/nvd9.o
nouveau-y += core/engine/graph/nve4.o
nouveau-y += core/engine/graph/gk20a.o
nouveau-y += core/engine/graph/nvf0.o
+nouveau-y += core/engine/graph/gk110b.o
nouveau-y += core/engine/graph/nv108.o
nouveau-y += core/engine/graph/gm107.o
nouveau-y += core/engine/mpeg/nv31.o
@@ -318,11 +339,18 @@ nouveau-y += core/engine/vp/nv98.o
nouveau-y += core/engine/vp/nvc0.o
nouveau-y += core/engine/vp/nve0.o
+# nvif
+nouveau-y += nvif/object.o
+nouveau-y += nvif/client.o
+nouveau-y += nvif/device.o
+nouveau-y += nvif/notify.o
+
# drm/core
nouveau-y += nouveau_drm.o nouveau_chan.o nouveau_dma.o nouveau_fence.o
nouveau-y += nouveau_vga.o nouveau_agp.o
nouveau-y += nouveau_ttm.o nouveau_sgdma.o nouveau_bo.o nouveau_gem.o
nouveau-y += nouveau_prime.o nouveau_abi16.o
+nouveau-y += nouveau_nvif.o nouveau_usif.o
nouveau-y += nv04_fence.o nv10_fence.o nv17_fence.o
nouveau-y += nv50_fence.o nv84_fence.o nvc0_fence.o
@@ -349,3 +377,6 @@ nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
nouveau-$(CONFIG_DEBUG_FS) += nouveau_debugfs.o
obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o
+
+# platform driver
+obj-$(CONFIG_NOUVEAU_PLATFORM_DRIVER) += nouveau_platform.o
diff --git a/drivers/gpu/drm/nouveau/core/core/client.c b/drivers/gpu/drm/nouveau/core/core/client.c
index 9079c0ac58e6..e962433294c3 100644
--- a/drivers/gpu/drm/nouveau/core/core/client.c
+++ b/drivers/gpu/drm/nouveau/core/core/client.c
@@ -26,13 +26,168 @@
#include <core/client.h>
#include <core/handle.h>
#include <core/option.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
+
+#include <nvif/unpack.h>
+#include <nvif/event.h>
#include <engine/device.h>
+struct nvkm_client_notify {
+ struct nouveau_client *client;
+ struct nvkm_notify n;
+ u8 version;
+ u8 size;
+ union {
+ struct nvif_notify_rep_v0 v0;
+ } rep;
+};
+
+static int
+nvkm_client_notify(struct nvkm_notify *n)
+{
+ struct nvkm_client_notify *notify = container_of(n, typeof(*notify), n);
+ struct nouveau_client *client = notify->client;
+ return client->ntfy(&notify->rep, notify->size, n->data, n->size);
+}
+
+int
+nvkm_client_notify_put(struct nouveau_client *client, int index)
+{
+ if (index < ARRAY_SIZE(client->notify)) {
+ if (client->notify[index]) {
+ nvkm_notify_put(&client->notify[index]->n);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+int
+nvkm_client_notify_get(struct nouveau_client *client, int index)
+{
+ if (index < ARRAY_SIZE(client->notify)) {
+ if (client->notify[index]) {
+ nvkm_notify_get(&client->notify[index]->n);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+int
+nvkm_client_notify_del(struct nouveau_client *client, int index)
+{
+ if (index < ARRAY_SIZE(client->notify)) {
+ if (client->notify[index]) {
+ nvkm_notify_fini(&client->notify[index]->n);
+ kfree(client->notify[index]);
+ client->notify[index] = NULL;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+int
+nvkm_client_notify_new(struct nouveau_object *object,
+ struct nvkm_event *event, void *data, u32 size)
+{
+ struct nouveau_client *client = nouveau_client(object);
+ struct nvkm_client_notify *notify;
+ union {
+ struct nvif_notify_req_v0 v0;
+ } *req = data;
+ u8 index, reply;
+ int ret;
+
+ for (index = 0; index < ARRAY_SIZE(client->notify); index++) {
+ if (!client->notify[index])
+ break;
+ }
+
+ if (index == ARRAY_SIZE(client->notify))
+ return -ENOSPC;
+
+ notify = kzalloc(sizeof(*notify), GFP_KERNEL);
+ if (!notify)
+ return -ENOMEM;
+
+ nv_ioctl(client, "notify new size %d\n", size);
+ if (nvif_unpack(req->v0, 0, 0, true)) {
+ nv_ioctl(client, "notify new vers %d reply %d route %02x "
+ "token %llx\n", req->v0.version,
+ req->v0.reply, req->v0.route, req->v0.token);
+ notify->version = req->v0.version;
+ notify->size = sizeof(notify->rep.v0);
+ notify->rep.v0.version = req->v0.version;
+ notify->rep.v0.route = req->v0.route;
+ notify->rep.v0.token = req->v0.token;
+ reply = req->v0.reply;
+ }
+
+ if (ret == 0) {
+ ret = nvkm_notify_init(object, event, nvkm_client_notify,
+ false, data, size, reply, &notify->n);
+ if (ret == 0) {
+ client->notify[index] = notify;
+ notify->client = client;
+ return index;
+ }
+ }
+
+ kfree(notify);
+ return ret;
+}
+
+static int
+nouveau_client_devlist(struct nouveau_object *object, void *data, u32 size)
+{
+ union {
+ struct nv_client_devlist_v0 v0;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "client devlist size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ nv_ioctl(object, "client devlist vers %d count %d\n",
+ args->v0.version, args->v0.count);
+ if (size == sizeof(args->v0.device[0]) * args->v0.count) {
+ ret = nouveau_device_list(args->v0.device,
+ args->v0.count);
+ if (ret >= 0) {
+ args->v0.count = ret;
+ ret = 0;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+static int
+nouveau_client_mthd(struct nouveau_object *object, u32 mthd,
+ void *data, u32 size)
+{
+ switch (mthd) {
+ case NV_CLIENT_DEVLIST:
+ return nouveau_client_devlist(object, data, size);
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
static void
nouveau_client_dtor(struct nouveau_object *object)
{
struct nouveau_client *client = (void *)object;
+ int i;
+ for (i = 0; i < ARRAY_SIZE(client->notify); i++)
+ nvkm_client_notify_del(client, i);
nouveau_object_ref(NULL, &client->device);
nouveau_handle_destroy(client->root);
nouveau_namedb_destroy(&client->base);
@@ -42,6 +197,7 @@ static struct nouveau_oclass
nouveau_client_oclass = {
.ofuncs = &(struct nouveau_ofuncs) {
.dtor = nouveau_client_dtor,
+ .mthd = nouveau_client_mthd,
},
};
@@ -93,9 +249,12 @@ int
nouveau_client_fini(struct nouveau_client *client, bool suspend)
{
const char *name[2] = { "fini", "suspend" };
- int ret;
-
+ int ret, i;
nv_debug(client, "%s running\n", name[suspend]);
+ nv_debug(client, "%s notify\n", name[suspend]);
+ for (i = 0; i < ARRAY_SIZE(client->notify); i++)
+ nvkm_client_notify_put(client, i);
+ nv_debug(client, "%s object\n", name[suspend]);
ret = nouveau_handle_fini(client->root, suspend);
nv_debug(client, "%s completed with %d\n", name[suspend], ret);
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c
index ae81d3b5d8b7..ff2b434b3db4 100644
--- a/drivers/gpu/drm/nouveau/core/core/event.c
+++ b/drivers/gpu/drm/nouveau/core/core/event.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Red Hat Inc.
+ * Copyright 2013-2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -20,177 +20,81 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
-#include <core/os.h>
+#include <core/object.h>
#include <core/event.h>
void
-nouveau_event_put(struct nouveau_eventh *handler)
+nvkm_event_put(struct nvkm_event *event, u32 types, int index)
{
- struct nouveau_event *event = handler->event;
- unsigned long flags;
- u32 m, t;
-
- if (!__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags))
- return;
-
- spin_lock_irqsave(&event->refs_lock, flags);
- for (m = handler->types; t = __ffs(m), m; m &= ~(1 << t)) {
- if (!--event->refs[handler->index * event->types_nr + t]) {
- if (event->disable)
- event->disable(event, 1 << t, handler->index);
+ BUG_ON(!spin_is_locked(&event->refs_lock));
+ while (types) {
+ int type = __ffs(types); types &= ~(1 << type);
+ if (--event->refs[index * event->types_nr + type] == 0) {
+ if (event->func->fini)
+ event->func->fini(event, 1 << type, index);
}
-
}
- spin_unlock_irqrestore(&event->refs_lock, flags);
}
void
-nouveau_event_get(struct nouveau_eventh *handler)
+nvkm_event_get(struct nvkm_event *event, u32 types, int index)
{
- struct nouveau_event *event = handler->event;
- unsigned long flags;
- u32 m, t;
-
- if (__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags))
- return;
-
- spin_lock_irqsave(&event->refs_lock, flags);
- for (m = handler->types; t = __ffs(m), m; m &= ~(1 << t)) {
- if (!event->refs[handler->index * event->types_nr + t]++) {
- if (event->enable)
- event->enable(event, 1 << t, handler->index);
+ BUG_ON(!spin_is_locked(&event->refs_lock));
+ while (types) {
+ int type = __ffs(types); types &= ~(1 << type);
+ if (++event->refs[index * event->types_nr + type] == 1) {
+ if (event->func->init)
+ event->func->init(event, 1 << type, index);
}
-
}
- spin_unlock_irqrestore(&event->refs_lock, flags);
-}
-
-static void
-nouveau_event_fini(struct nouveau_eventh *handler)
-{
- struct nouveau_event *event = handler->event;
- unsigned long flags;
- nouveau_event_put(handler);
- spin_lock_irqsave(&event->list_lock, flags);
- list_del(&handler->head);
- spin_unlock_irqrestore(&event->list_lock, flags);
-}
-
-static int
-nouveau_event_init(struct nouveau_event *event, u32 types, int index,
- int (*func)(void *, u32, int), void *priv,
- struct nouveau_eventh *handler)
-{
- unsigned long flags;
-
- if (types & ~((1 << event->types_nr) - 1))
- return -EINVAL;
- if (index >= event->index_nr)
- return -EINVAL;
-
- handler->event = event;
- handler->flags = 0;
- handler->types = types;
- handler->index = index;
- handler->func = func;
- handler->priv = priv;
-
- spin_lock_irqsave(&event->list_lock, flags);
- list_add_tail(&handler->head, &event->list[index]);
- spin_unlock_irqrestore(&event->list_lock, flags);
- return 0;
-}
-
-int
-nouveau_event_new(struct nouveau_event *event, u32 types, int index,
- int (*func)(void *, u32, int), void *priv,
- struct nouveau_eventh **phandler)
-{
- struct nouveau_eventh *handler;
- int ret = -ENOMEM;
-
- if (event->check) {
- ret = event->check(event, types, index);
- if (ret)
- return ret;
- }
-
- handler = *phandler = kmalloc(sizeof(*handler), GFP_KERNEL);
- if (handler) {
- ret = nouveau_event_init(event, types, index, func, priv, handler);
- if (ret)
- kfree(handler);
- }
-
- return ret;
-}
-
-void
-nouveau_event_ref(struct nouveau_eventh *handler, struct nouveau_eventh **ref)
-{
- BUG_ON(handler != NULL);
- if (*ref) {
- nouveau_event_fini(*ref);
- kfree(*ref);
- }
- *ref = handler;
}
void
-nouveau_event_trigger(struct nouveau_event *event, u32 types, int index)
+nvkm_event_send(struct nvkm_event *event, u32 types, int index,
+ void *data, u32 size)
{
- struct nouveau_eventh *handler;
+ struct nvkm_notify *notify;
unsigned long flags;
- if (WARN_ON(index >= event->index_nr))
+ if (!event->refs || WARN_ON(index >= event->index_nr))
return;
spin_lock_irqsave(&event->list_lock, flags);
- list_for_each_entry(handler, &event->list[index], head) {
- if (!test_bit(NVKM_EVENT_ENABLE, &handler->flags))
- continue;
- if (!(handler->types & types))
- continue;
- if (handler->func(handler->priv, handler->types & types, index)
- != NVKM_EVENT_DROP)
- continue;
- nouveau_event_put(handler);
+ list_for_each_entry(notify, &event->list, head) {
+ if (notify->index == index && (notify->types & types)) {
+ if (event->func->send) {
+ event->func->send(data, size, notify);
+ continue;
+ }
+ nvkm_notify_send(notify, data, size);
+ }
}
spin_unlock_irqrestore(&event->list_lock, flags);
}
void
-nouveau_event_destroy(struct nouveau_event **pevent)
+nvkm_event_fini(struct nvkm_event *event)
{
- struct nouveau_event *event = *pevent;
- if (event) {
- kfree(event);
- *pevent = NULL;
+ if (event->refs) {
+ kfree(event->refs);
+ event->refs = NULL;
}
}
int
-nouveau_event_create(int types_nr, int index_nr, struct nouveau_event **pevent)
+nvkm_event_init(const struct nvkm_event_func *func, int types_nr, int index_nr,
+ struct nvkm_event *event)
{
- struct nouveau_event *event;
- int i;
-
- event = *pevent = kzalloc(sizeof(*event) + (index_nr * types_nr) *
- sizeof(event->refs[0]), GFP_KERNEL);
- if (!event)
- return -ENOMEM;
-
- event->list = kmalloc(sizeof(*event->list) * index_nr, GFP_KERNEL);
- if (!event->list) {
- kfree(event);
+ event->refs = kzalloc(sizeof(*event->refs) * index_nr * types_nr,
+ GFP_KERNEL);
+ if (!event->refs)
return -ENOMEM;
- }
- spin_lock_init(&event->list_lock);
- spin_lock_init(&event->refs_lock);
- for (i = 0; i < index_nr; i++)
- INIT_LIST_HEAD(&event->list[i]);
+ event->func = func;
event->types_nr = types_nr;
event->index_nr = index_nr;
+ spin_lock_init(&event->refs_lock);
+ spin_lock_init(&event->list_lock);
+ INIT_LIST_HEAD(&event->list);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/core/gpuobj.c b/drivers/gpu/drm/nouveau/core/core/gpuobj.c
index 560b2214cf1c..daee87702502 100644
--- a/drivers/gpu/drm/nouveau/core/core/gpuobj.c
+++ b/drivers/gpu/drm/nouveau/core/core/gpuobj.c
@@ -115,7 +115,7 @@ nouveau_gpuobj_create_(struct nouveau_object *parent,
gpuobj->size = size;
if (heap) {
- ret = nouveau_mm_head(heap, 1, size, size,
+ ret = nouveau_mm_head(heap, 0, 1, size, size,
max(align, (u32)1), &gpuobj->node);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/core/handle.c b/drivers/gpu/drm/nouveau/core/core/handle.c
index 264c2b338ac3..a490b805d7e3 100644
--- a/drivers/gpu/drm/nouveau/core/core/handle.c
+++ b/drivers/gpu/drm/nouveau/core/core/handle.c
@@ -146,9 +146,7 @@ nouveau_handle_create(struct nouveau_object *parent, u32 _parent, u32 _handle,
}
hprintk(handle, TRACE, "created\n");
-
*phandle = handle;
-
return 0;
}
@@ -224,3 +222,116 @@ nouveau_handle_put(struct nouveau_handle *handle)
if (handle)
nouveau_namedb_put(handle);
}
+
+int
+nouveau_handle_new(struct nouveau_object *client, u32 _parent, u32 _handle,
+ u16 _oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct nouveau_object *parent = NULL;
+ struct nouveau_object *engctx = NULL;
+ struct nouveau_object *object = NULL;
+ struct nouveau_object *engine;
+ struct nouveau_oclass *oclass;
+ struct nouveau_handle *handle;
+ int ret;
+
+ /* lookup parent object and ensure it *is* a parent */
+ parent = nouveau_handle_ref(client, _parent);
+ if (!parent) {
+ nv_error(client, "parent 0x%08x not found\n", _parent);
+ return -ENOENT;
+ }
+
+ if (!nv_iclass(parent, NV_PARENT_CLASS)) {
+ nv_error(parent, "cannot have children\n");
+ ret = -EINVAL;
+ goto fail_class;
+ }
+
+ /* check that parent supports the requested subclass */
+ ret = nouveau_parent_sclass(parent, _oclass, &engine, &oclass);
+ if (ret) {
+ nv_debug(parent, "illegal class 0x%04x\n", _oclass);
+ goto fail_class;
+ }
+
+ /* make sure engine init has been completed *before* any objects
+ * it controls are created - the constructors may depend on
+ * state calculated at init (ie. default context construction)
+ */
+ if (engine) {
+ ret = nouveau_object_inc(engine);
+ if (ret)
+ goto fail_class;
+ }
+
+ /* if engine requires it, create a context object to insert
+ * between the parent and its children (eg. PGRAPH context)
+ */
+ if (engine && nv_engine(engine)->cclass) {
+ ret = nouveau_object_ctor(parent, engine,
+ nv_engine(engine)->cclass,
+ data, size, &engctx);
+ if (ret)
+ goto fail_engctx;
+ } else {
+ nouveau_object_ref(parent, &engctx);
+ }
+
+ /* finally, create new object and bind it to its handle */
+ ret = nouveau_object_ctor(engctx, engine, oclass, data, size, &object);
+ *pobject = object;
+ if (ret)
+ goto fail_ctor;
+
+ ret = nouveau_object_inc(object);
+ if (ret)
+ goto fail_init;
+
+ ret = nouveau_handle_create(parent, _parent, _handle, object, &handle);
+ if (ret)
+ goto fail_handle;
+
+ ret = nouveau_handle_init(handle);
+ if (ret)
+ nouveau_handle_destroy(handle);
+
+fail_handle:
+ nouveau_object_dec(object, false);
+fail_init:
+ nouveau_object_ref(NULL, &object);
+fail_ctor:
+ nouveau_object_ref(NULL, &engctx);
+fail_engctx:
+ if (engine)
+ nouveau_object_dec(engine, false);
+fail_class:
+ nouveau_object_ref(NULL, &parent);
+ return ret;
+}
+
+int
+nouveau_handle_del(struct nouveau_object *client, u32 _parent, u32 _handle)
+{
+ struct nouveau_object *parent = NULL;
+ struct nouveau_object *namedb = NULL;
+ struct nouveau_handle *handle = NULL;
+
+ parent = nouveau_handle_ref(client, _parent);
+ if (!parent)
+ return -ENOENT;
+
+ namedb = nv_pclass(parent, NV_NAMEDB_CLASS);
+ if (namedb) {
+ handle = nouveau_namedb_get(nv_namedb(namedb), _handle);
+ if (handle) {
+ nouveau_namedb_put(handle);
+ nouveau_handle_fini(handle, false);
+ nouveau_handle_destroy(handle);
+ }
+ }
+
+ nouveau_object_ref(NULL, &parent);
+ return handle ? 0 : -EINVAL;
+}
diff --git a/drivers/gpu/drm/nouveau/core/core/ioctl.c b/drivers/gpu/drm/nouveau/core/core/ioctl.c
new file mode 100644
index 000000000000..692aa92dd850
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/core/ioctl.c
@@ -0,0 +1,530 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include <core/object.h>
+#include <core/parent.h>
+#include <core/handle.h>
+#include <core/namedb.h>
+#include <core/client.h>
+#include <core/device.h>
+#include <core/ioctl.h>
+#include <core/event.h>
+
+#include <nvif/unpack.h>
+#include <nvif/ioctl.h>
+
+static int
+nvkm_ioctl_nop(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_object *object = handle->object;
+ union {
+ struct nvif_ioctl_nop none;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "nop size %d\n", size);
+ if (nvif_unvers(args->none)) {
+ nv_ioctl(object, "nop\n");
+ }
+
+ return ret;
+}
+
+static int
+nvkm_ioctl_sclass(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_object *object = handle->object;
+ union {
+ struct nvif_ioctl_sclass_v0 v0;
+ } *args = data;
+ int ret;
+
+ if (!nv_iclass(object, NV_PARENT_CLASS)) {
+ nv_debug(object, "cannot have children (sclass)\n");
+ return -ENODEV;
+ }
+
+ nv_ioctl(object, "sclass size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ nv_ioctl(object, "sclass vers %d count %d\n",
+ args->v0.version, args->v0.count);
+ if (size == args->v0.count * sizeof(args->v0.oclass[0])) {
+ ret = nouveau_parent_lclass(object, args->v0.oclass,
+ args->v0.count);
+ if (ret >= 0) {
+ args->v0.count = ret;
+ ret = 0;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+static int
+nvkm_ioctl_new(struct nouveau_handle *parent, void *data, u32 size)
+{
+ union {
+ struct nvif_ioctl_new_v0 v0;
+ } *args = data;
+ struct nouveau_client *client = nouveau_client(parent->object);
+ struct nouveau_object *engctx = NULL;
+ struct nouveau_object *object = NULL;
+ struct nouveau_object *engine;
+ struct nouveau_oclass *oclass;
+ struct nouveau_handle *handle;
+ u32 _handle, _oclass;
+ int ret;
+
+ nv_ioctl(client, "new size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ _handle = args->v0.handle;
+ _oclass = args->v0.oclass;
+ } else
+ return ret;
+
+ nv_ioctl(client, "new vers %d handle %08x class %08x "
+ "route %02x token %llx\n",
+ args->v0.version, _handle, _oclass,
+ args->v0.route, args->v0.token);
+
+ if (!nv_iclass(parent->object, NV_PARENT_CLASS)) {
+ nv_debug(parent->object, "cannot have children (ctor)\n");
+ ret = -ENODEV;
+ goto fail_class;
+ }
+
+ /* check that parent supports the requested subclass */
+ ret = nouveau_parent_sclass(parent->object, _oclass, &engine, &oclass);
+ if (ret) {
+ nv_debug(parent->object, "illegal class 0x%04x\n", _oclass);
+ goto fail_class;
+ }
+
+ /* make sure engine init has been completed *before* any objects
+ * it controls are created - the constructors may depend on
+ * state calculated at init (ie. default context construction)
+ */
+ if (engine) {
+ ret = nouveau_object_inc(engine);
+ if (ret)
+ goto fail_class;
+ }
+
+ /* if engine requires it, create a context object to insert
+ * between the parent and its children (eg. PGRAPH context)
+ */
+ if (engine && nv_engine(engine)->cclass) {
+ ret = nouveau_object_ctor(parent->object, engine,
+ nv_engine(engine)->cclass,
+ data, size, &engctx);
+ if (ret)
+ goto fail_engctx;
+ } else {
+ nouveau_object_ref(parent->object, &engctx);
+ }
+
+ /* finally, create new object and bind it to its handle */
+ ret = nouveau_object_ctor(engctx, engine, oclass, data, size, &object);
+ client->data = object;
+ if (ret)
+ goto fail_ctor;
+
+ ret = nouveau_object_inc(object);
+ if (ret)
+ goto fail_init;
+
+ ret = nouveau_handle_create(parent->object, parent->name,
+ _handle, object, &handle);
+ if (ret)
+ goto fail_handle;
+
+ ret = nouveau_handle_init(handle);
+ handle->route = args->v0.route;
+ handle->token = args->v0.token;
+ if (ret)
+ nouveau_handle_destroy(handle);
+
+fail_handle:
+ nouveau_object_dec(object, false);
+fail_init:
+ nouveau_object_ref(NULL, &object);
+fail_ctor:
+ nouveau_object_ref(NULL, &engctx);
+fail_engctx:
+ if (engine)
+ nouveau_object_dec(engine, false);
+fail_class:
+ return ret;
+}
+
+static int
+nvkm_ioctl_del(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_object *object = handle->object;
+ union {
+ struct nvif_ioctl_del none;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "delete size %d\n", size);
+ if (nvif_unvers(args->none)) {
+ nv_ioctl(object, "delete\n");
+ nouveau_handle_fini(handle, false);
+ nouveau_handle_destroy(handle);
+ }
+
+ return ret;
+}
+
+static int
+nvkm_ioctl_mthd(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_object *object = handle->object;
+ struct nouveau_ofuncs *ofuncs = object->oclass->ofuncs;
+ union {
+ struct nvif_ioctl_mthd_v0 v0;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "mthd size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ nv_ioctl(object, "mthd vers %d mthd %02x\n",
+ args->v0.version, args->v0.method);
+ if (ret = -ENODEV, ofuncs->mthd)
+ ret = ofuncs->mthd(object, args->v0.method, data, size);
+ }
+
+ return ret;
+}
+
+
+static int
+nvkm_ioctl_rd(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_object *object = handle->object;
+ struct nouveau_ofuncs *ofuncs = object->oclass->ofuncs;
+ union {
+ struct nvif_ioctl_rd_v0 v0;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "rd size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "rd vers %d size %d addr %016llx\n",
+ args->v0.version, args->v0.size, args->v0.addr);
+ switch (args->v0.size) {
+ case 1:
+ if (ret = -ENODEV, ofuncs->rd08) {
+ args->v0.data = nv_ro08(object, args->v0.addr);
+ ret = 0;
+ }
+ break;
+ case 2:
+ if (ret = -ENODEV, ofuncs->rd16) {
+ args->v0.data = nv_ro16(object, args->v0.addr);
+ ret = 0;
+ }
+ break;
+ case 4:
+ if (ret = -ENODEV, ofuncs->rd32) {
+ args->v0.data = nv_ro32(object, args->v0.addr);
+ ret = 0;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int
+nvkm_ioctl_wr(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_object *object = handle->object;
+ struct nouveau_ofuncs *ofuncs = object->oclass->ofuncs;
+ union {
+ struct nvif_ioctl_wr_v0 v0;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "wr size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "wr vers %d size %d addr %016llx data %08x\n",
+ args->v0.version, args->v0.size, args->v0.addr,
+ args->v0.data);
+ switch (args->v0.size) {
+ case 1:
+ if (ret = -ENODEV, ofuncs->wr08) {
+ nv_wo08(object, args->v0.addr, args->v0.data);
+ ret = 0;
+ }
+ break;
+ case 2:
+ if (ret = -ENODEV, ofuncs->wr16) {
+ nv_wo16(object, args->v0.addr, args->v0.data);
+ ret = 0;
+ }
+ break;
+ case 4:
+ if (ret = -ENODEV, ofuncs->wr32) {
+ nv_wo32(object, args->v0.addr, args->v0.data);
+ ret = 0;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int
+nvkm_ioctl_map(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_object *object = handle->object;
+ struct nouveau_ofuncs *ofuncs = object->oclass->ofuncs;
+ union {
+ struct nvif_ioctl_map_v0 v0;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "map size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "map vers %d\n", args->v0.version);
+ if (ret = -ENODEV, ofuncs->map) {
+ ret = ofuncs->map(object, &args->v0.handle,
+ &args->v0.length);
+ }
+ }
+
+ return ret;
+}
+
+static int
+nvkm_ioctl_unmap(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_object *object = handle->object;
+ union {
+ struct nvif_ioctl_unmap none;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "unmap size %d\n", size);
+ if (nvif_unvers(args->none)) {
+ nv_ioctl(object, "unmap\n");
+ }
+
+ return ret;
+}
+
+static int
+nvkm_ioctl_ntfy_new(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_object *object = handle->object;
+ struct nouveau_ofuncs *ofuncs = object->oclass->ofuncs;
+ union {
+ struct nvif_ioctl_ntfy_new_v0 v0;
+ } *args = data;
+ struct nvkm_event *event;
+ int ret;
+
+ nv_ioctl(object, "ntfy new size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ nv_ioctl(object, "ntfy new vers %d event %02x\n",
+ args->v0.version, args->v0.event);
+ if (ret = -ENODEV, ofuncs->ntfy)
+ ret = ofuncs->ntfy(object, args->v0.event, &event);
+ if (ret == 0) {
+ ret = nvkm_client_notify_new(object, event, data, size);
+ if (ret >= 0) {
+ args->v0.index = ret;
+ ret = 0;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int
+nvkm_ioctl_ntfy_del(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_client *client = nouveau_client(handle->object);
+ struct nouveau_object *object = handle->object;
+ union {
+ struct nvif_ioctl_ntfy_del_v0 v0;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "ntfy del size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "ntfy del vers %d index %d\n",
+ args->v0.version, args->v0.index);
+ ret = nvkm_client_notify_del(client, args->v0.index);
+ }
+
+ return ret;
+}
+
+static int
+nvkm_ioctl_ntfy_get(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_client *client = nouveau_client(handle->object);
+ struct nouveau_object *object = handle->object;
+ union {
+ struct nvif_ioctl_ntfy_get_v0 v0;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "ntfy get size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "ntfy get vers %d index %d\n",
+ args->v0.version, args->v0.index);
+ ret = nvkm_client_notify_get(client, args->v0.index);
+ }
+
+ return ret;
+}
+
+static int
+nvkm_ioctl_ntfy_put(struct nouveau_handle *handle, void *data, u32 size)
+{
+ struct nouveau_client *client = nouveau_client(handle->object);
+ struct nouveau_object *object = handle->object;
+ union {
+ struct nvif_ioctl_ntfy_put_v0 v0;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "ntfy put size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "ntfy put vers %d index %d\n",
+ args->v0.version, args->v0.index);
+ ret = nvkm_client_notify_put(client, args->v0.index);
+ }
+
+ return ret;
+}
+
+static struct {
+ int version;
+ int (*func)(struct nouveau_handle *, void *, u32);
+}
+nvkm_ioctl_v0[] = {
+ { 0x00, nvkm_ioctl_nop },
+ { 0x00, nvkm_ioctl_sclass },
+ { 0x00, nvkm_ioctl_new },
+ { 0x00, nvkm_ioctl_del },
+ { 0x00, nvkm_ioctl_mthd },
+ { 0x00, nvkm_ioctl_rd },
+ { 0x00, nvkm_ioctl_wr },
+ { 0x00, nvkm_ioctl_map },
+ { 0x00, nvkm_ioctl_unmap },
+ { 0x00, nvkm_ioctl_ntfy_new },
+ { 0x00, nvkm_ioctl_ntfy_del },
+ { 0x00, nvkm_ioctl_ntfy_get },
+ { 0x00, nvkm_ioctl_ntfy_put },
+};
+
+static int
+nvkm_ioctl_path(struct nouveau_handle *parent, u32 type, u32 nr,
+ u32 *path, void *data, u32 size,
+ u8 owner, u8 *route, u64 *token)
+{
+ struct nouveau_handle *handle = parent;
+ struct nouveau_namedb *namedb;
+ struct nouveau_object *object;
+ int ret;
+
+ while ((object = parent->object), nr--) {
+ nv_ioctl(object, "path 0x%08x\n", path[nr]);
+ if (!nv_iclass(object, NV_PARENT_CLASS)) {
+ nv_debug(object, "cannot have children (path)\n");
+ return -EINVAL;
+ }
+
+ if (!(namedb = (void *)nv_pclass(object, NV_NAMEDB_CLASS)) ||
+ !(handle = nouveau_namedb_get(namedb, path[nr]))) {
+ nv_debug(object, "handle 0x%08x not found\n", path[nr]);
+ return -ENOENT;
+ }
+ nouveau_namedb_put(handle);
+ parent = handle;
+ }
+
+ if (owner != NVIF_IOCTL_V0_OWNER_ANY &&
+ owner != handle->route) {
+ nv_ioctl(object, "object route != owner\n");
+ return -EACCES;
+ }
+ *route = handle->route;
+ *token = handle->token;
+
+ if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) {
+ if (nvkm_ioctl_v0[type].version == 0) {
+ ret = nvkm_ioctl_v0[type].func(handle, data, size);
+ }
+ }
+
+ return ret;
+}
+
+int
+nvkm_ioctl(struct nouveau_client *client, bool supervisor,
+ void *data, u32 size, void **hack)
+{
+ union {
+ struct nvif_ioctl_v0 v0;
+ } *args = data;
+ int ret;
+
+ client->super = supervisor;
+ nv_ioctl(client, "size %d\n", size);
+
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ nv_ioctl(client, "vers %d type %02x path %d owner %02x\n",
+ args->v0.version, args->v0.type, args->v0.path_nr,
+ args->v0.owner);
+ ret = nvkm_ioctl_path(client->root, args->v0.type,
+ args->v0.path_nr, args->v0.path,
+ data, size, args->v0.owner,
+ &args->v0.route, &args->v0.token);
+ }
+
+ nv_ioctl(client, "return %d\n", ret);
+ if (hack) {
+ *hack = client->data;
+ client->data = NULL;
+ }
+ client->super = false;
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/core/core/mm.c b/drivers/gpu/drm/nouveau/core/core/mm.c
index 7a4e0891c5f8..b4f5db66d5b5 100644
--- a/drivers/gpu/drm/nouveau/core/core/mm.c
+++ b/drivers/gpu/drm/nouveau/core/core/mm.c
@@ -28,6 +28,24 @@
#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
+static void
+nouveau_mm_dump(struct nouveau_mm *mm, const char *header)
+{
+ struct nouveau_mm_node *node;
+
+ printk(KERN_ERR "nouveau: %s\n", header);
+ printk(KERN_ERR "nouveau: node list:\n");
+ list_for_each_entry(node, &mm->nodes, nl_entry) {
+ printk(KERN_ERR "nouveau: \t%08x %08x %d\n",
+ node->offset, node->length, node->type);
+ }
+ printk(KERN_ERR "nouveau: free list:\n");
+ list_for_each_entry(node, &mm->free, fl_entry) {
+ printk(KERN_ERR "nouveau: \t%08x %08x %d\n",
+ node->offset, node->length, node->type);
+ }
+}
+
void
nouveau_mm_free(struct nouveau_mm *mm, struct nouveau_mm_node **pthis)
{
@@ -37,29 +55,29 @@ nouveau_mm_free(struct nouveau_mm *mm, struct nouveau_mm_node **pthis)
struct nouveau_mm_node *prev = node(this, prev);
struct nouveau_mm_node *next = node(this, next);
- if (prev && prev->type == 0) {
+ if (prev && prev->type == NVKM_MM_TYPE_NONE) {
prev->length += this->length;
list_del(&this->nl_entry);
kfree(this); this = prev;
}
- if (next && next->type == 0) {
+ if (next && next->type == NVKM_MM_TYPE_NONE) {
next->offset = this->offset;
next->length += this->length;
- if (this->type == 0)
+ if (this->type == NVKM_MM_TYPE_NONE)
list_del(&this->fl_entry);
list_del(&this->nl_entry);
kfree(this); this = NULL;
}
- if (this && this->type != 0) {
+ if (this && this->type != NVKM_MM_TYPE_NONE) {
list_for_each_entry(prev, &mm->free, fl_entry) {
if (this->offset < prev->offset)
break;
}
list_add_tail(&this->fl_entry, &prev->fl_entry);
- this->type = 0;
+ this->type = NVKM_MM_TYPE_NONE;
}
}
@@ -80,27 +98,32 @@ region_head(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
b->offset = a->offset;
b->length = size;
+ b->heap = a->heap;
b->type = a->type;
a->offset += size;
a->length -= size;
list_add_tail(&b->nl_entry, &a->nl_entry);
- if (b->type == 0)
+ if (b->type == NVKM_MM_TYPE_NONE)
list_add_tail(&b->fl_entry, &a->fl_entry);
return b;
}
int
-nouveau_mm_head(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,
- u32 align, struct nouveau_mm_node **pnode)
+nouveau_mm_head(struct nouveau_mm *mm, u8 heap, u8 type, u32 size_max,
+ u32 size_min, u32 align, struct nouveau_mm_node **pnode)
{
struct nouveau_mm_node *prev, *this, *next;
u32 mask = align - 1;
u32 splitoff;
u32 s, e;
- BUG_ON(!type);
+ BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE);
list_for_each_entry(this, &mm->free, fl_entry) {
+ if (unlikely(heap != NVKM_MM_HEAP_ANY)) {
+ if (this->heap != heap)
+ continue;
+ }
e = this->offset + this->length;
s = this->offset;
@@ -149,27 +172,32 @@ region_tail(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
a->length -= size;
b->offset = a->offset + a->length;
b->length = size;
+ b->heap = a->heap;
b->type = a->type;
list_add(&b->nl_entry, &a->nl_entry);
- if (b->type == 0)
+ if (b->type == NVKM_MM_TYPE_NONE)
list_add(&b->fl_entry, &a->fl_entry);
return b;
}
int
-nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,
- u32 align, struct nouveau_mm_node **pnode)
+nouveau_mm_tail(struct nouveau_mm *mm, u8 heap, u8 type, u32 size_max,
+ u32 size_min, u32 align, struct nouveau_mm_node **pnode)
{
struct nouveau_mm_node *prev, *this, *next;
u32 mask = align - 1;
- BUG_ON(!type);
+ BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE);
list_for_each_entry_reverse(this, &mm->free, fl_entry) {
u32 e = this->offset + this->length;
u32 s = this->offset;
u32 c = 0, a;
+ if (unlikely(heap != NVKM_MM_HEAP_ANY)) {
+ if (this->heap != heap)
+ continue;
+ }
prev = node(this, prev);
if (prev && prev->type != type)
@@ -209,9 +237,23 @@ nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,
int
nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
{
- struct nouveau_mm_node *node;
+ struct nouveau_mm_node *node, *prev;
+ u32 next;
- if (block) {
+ if (nouveau_mm_initialised(mm)) {
+ prev = list_last_entry(&mm->nodes, typeof(*node), nl_entry);
+ next = prev->offset + prev->length;
+ if (next != offset) {
+ BUG_ON(next > offset);
+ if (!(node = kzalloc(sizeof(*node), GFP_KERNEL)))
+ return -ENOMEM;
+ node->type = NVKM_MM_TYPE_HOLE;
+ node->offset = next;
+ node->length = offset - next;
+ list_add_tail(&node->nl_entry, &mm->nodes);
+ }
+ BUG_ON(block != mm->block_size);
+ } else {
INIT_LIST_HEAD(&mm->nodes);
INIT_LIST_HEAD(&mm->free);
mm->block_size = block;
@@ -230,25 +272,32 @@ nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
list_add_tail(&node->nl_entry, &mm->nodes);
list_add_tail(&node->fl_entry, &mm->free);
- mm->heap_nodes++;
+ node->heap = ++mm->heap_nodes;
return 0;
}
int
nouveau_mm_fini(struct nouveau_mm *mm)
{
- if (nouveau_mm_initialised(mm)) {
- struct nouveau_mm_node *node, *heap =
- list_first_entry(&mm->nodes, typeof(*heap), nl_entry);
- int nodes = 0;
+ struct nouveau_mm_node *node, *temp;
+ int nodes = 0;
- list_for_each_entry(node, &mm->nodes, nl_entry) {
- if (WARN_ON(nodes++ == mm->heap_nodes))
+ if (!nouveau_mm_initialised(mm))
+ return 0;
+
+ list_for_each_entry(node, &mm->nodes, nl_entry) {
+ if (node->type != NVKM_MM_TYPE_HOLE) {
+ if (++nodes > mm->heap_nodes) {
+ nouveau_mm_dump(mm, "mm not clean!");
return -EBUSY;
+ }
}
-
- kfree(heap);
}
+ list_for_each_entry_safe(node, temp, &mm->nodes, nl_entry) {
+ list_del(&node->nl_entry);
+ kfree(node);
+ }
+ mm->heap_nodes = 0;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/core/notify.c b/drivers/gpu/drm/nouveau/core/core/notify.c
new file mode 100644
index 000000000000..d1bcde55e9d7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/core/notify.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include <core/client.h>
+#include <core/event.h>
+#include <core/notify.h>
+
+#include <nvif/unpack.h>
+#include <nvif/event.h>
+
+static inline void
+nvkm_notify_put_locked(struct nvkm_notify *notify)
+{
+ if (notify->block++ == 0)
+ nvkm_event_put(notify->event, notify->types, notify->index);
+}
+
+void
+nvkm_notify_put(struct nvkm_notify *notify)
+{
+ struct nvkm_event *event = notify->event;
+ unsigned long flags;
+ if (likely(event) &&
+ test_and_clear_bit(NVKM_NOTIFY_USER, &notify->flags)) {
+ spin_lock_irqsave(&event->refs_lock, flags);
+ nvkm_notify_put_locked(notify);
+ spin_unlock_irqrestore(&event->refs_lock, flags);
+ if (test_bit(NVKM_NOTIFY_WORK, &notify->flags))
+ flush_work(&notify->work);
+ }
+}
+
+static inline void
+nvkm_notify_get_locked(struct nvkm_notify *notify)
+{
+ if (--notify->block == 0)
+ nvkm_event_get(notify->event, notify->types, notify->index);
+}
+
+void
+nvkm_notify_get(struct nvkm_notify *notify)
+{
+ struct nvkm_event *event = notify->event;
+ unsigned long flags;
+ if (likely(event) &&
+ !test_and_set_bit(NVKM_NOTIFY_USER, &notify->flags)) {
+ spin_lock_irqsave(&event->refs_lock, flags);
+ nvkm_notify_get_locked(notify);
+ spin_unlock_irqrestore(&event->refs_lock, flags);
+ }
+}
+
+static inline void
+nvkm_notify_func(struct nvkm_notify *notify)
+{
+ struct nvkm_event *event = notify->event;
+ int ret = notify->func(notify);
+ unsigned long flags;
+ if ((ret == NVKM_NOTIFY_KEEP) ||
+ !test_and_clear_bit(NVKM_NOTIFY_USER, &notify->flags)) {
+ spin_lock_irqsave(&event->refs_lock, flags);
+ nvkm_notify_get_locked(notify);
+ spin_unlock_irqrestore(&event->refs_lock, flags);
+ }
+}
+
+static void
+nvkm_notify_work(struct work_struct *work)
+{
+ struct nvkm_notify *notify = container_of(work, typeof(*notify), work);
+ nvkm_notify_func(notify);
+}
+
+void
+nvkm_notify_send(struct nvkm_notify *notify, void *data, u32 size)
+{
+ struct nvkm_event *event = notify->event;
+ unsigned long flags;
+
+ BUG_ON(!spin_is_locked(&event->list_lock));
+ BUG_ON(size != notify->size);
+
+ spin_lock_irqsave(&event->refs_lock, flags);
+ if (notify->block) {
+ spin_unlock_irqrestore(&event->refs_lock, flags);
+ return;
+ }
+ nvkm_notify_put_locked(notify);
+ spin_unlock_irqrestore(&event->refs_lock, flags);
+
+ if (test_bit(NVKM_NOTIFY_WORK, &notify->flags)) {
+ memcpy((void *)notify->data, data, size);
+ schedule_work(&notify->work);
+ } else {
+ notify->data = data;
+ nvkm_notify_func(notify);
+ notify->data = NULL;
+ }
+}
+
+void
+nvkm_notify_fini(struct nvkm_notify *notify)
+{
+ unsigned long flags;
+ if (notify->event) {
+ nvkm_notify_put(notify);
+ spin_lock_irqsave(&notify->event->list_lock, flags);
+ list_del(&notify->head);
+ spin_unlock_irqrestore(&notify->event->list_lock, flags);
+ kfree((void *)notify->data);
+ notify->event = NULL;
+ }
+}
+
+int
+nvkm_notify_init(struct nouveau_object *object, struct nvkm_event *event,
+ int (*func)(struct nvkm_notify *), bool work,
+ void *data, u32 size, u32 reply,
+ struct nvkm_notify *notify)
+{
+ unsigned long flags;
+ int ret = -ENODEV;
+ if ((notify->event = event), event->refs) {
+ ret = event->func->ctor(object, data, size, notify);
+ if (ret == 0 && (ret = -EINVAL, notify->size == reply)) {
+ notify->flags = 0;
+ notify->block = 1;
+ notify->func = func;
+ notify->data = NULL;
+ if (ret = 0, work) {
+ INIT_WORK(&notify->work, nvkm_notify_work);
+ set_bit(NVKM_NOTIFY_WORK, &notify->flags);
+ notify->data = kmalloc(reply, GFP_KERNEL);
+ if (!notify->data)
+ ret = -ENOMEM;
+ }
+ }
+ if (ret == 0) {
+ spin_lock_irqsave(&event->list_lock, flags);
+ list_add_tail(&notify->head, &event->list);
+ spin_unlock_irqrestore(&event->list_lock, flags);
+ }
+ }
+ if (ret)
+ notify->event = NULL;
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/core/core/object.c b/drivers/gpu/drm/nouveau/core/core/object.c
index 124538555904..b08630577c82 100644
--- a/drivers/gpu/drm/nouveau/core/core/object.c
+++ b/drivers/gpu/drm/nouveau/core/core/object.c
@@ -23,9 +23,6 @@
*/
#include <core/object.h>
-#include <core/parent.h>
-#include <core/namedb.h>
-#include <core/handle.h>
#include <core/engine.h>
#ifdef NOUVEAU_OBJECT_MAGIC
@@ -61,21 +58,15 @@ nouveau_object_create_(struct nouveau_object *parent,
return 0;
}
-static int
+int
_nouveau_object_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
- struct nouveau_object *object;
- int ret;
-
- ret = nouveau_object_create(parent, engine, oclass, 0, &object);
- *pobject = nv_object(object);
- if (ret)
- return ret;
-
- return 0;
+ if (size != 0)
+ return -ENOSYS;
+ return nouveau_object_create(parent, engine, oclass, 0, pobject);
}
void
@@ -91,42 +82,24 @@ nouveau_object_destroy(struct nouveau_object *object)
kfree(object);
}
-static void
-_nouveau_object_dtor(struct nouveau_object *object)
-{
- nouveau_object_destroy(object);
-}
-
int
nouveau_object_init(struct nouveau_object *object)
{
return 0;
}
-static int
-_nouveau_object_init(struct nouveau_object *object)
-{
- return nouveau_object_init(object);
-}
-
int
nouveau_object_fini(struct nouveau_object *object, bool suspend)
{
return 0;
}
-static int
-_nouveau_object_fini(struct nouveau_object *object, bool suspend)
-{
- return nouveau_object_fini(object, suspend);
-}
-
struct nouveau_ofuncs
nouveau_object_ofuncs = {
.ctor = _nouveau_object_ctor,
- .dtor = _nouveau_object_dtor,
- .init = _nouveau_object_init,
- .fini = _nouveau_object_fini,
+ .dtor = nouveau_object_destroy,
+ .init = nouveau_object_init,
+ .fini = nouveau_object_fini,
};
int
@@ -189,119 +162,6 @@ nouveau_object_ref(struct nouveau_object *obj, struct nouveau_object **ref)
}
int
-nouveau_object_new(struct nouveau_object *client, u32 _parent, u32 _handle,
- u16 _oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nouveau_object *parent = NULL;
- struct nouveau_object *engctx = NULL;
- struct nouveau_object *object = NULL;
- struct nouveau_object *engine;
- struct nouveau_oclass *oclass;
- struct nouveau_handle *handle;
- int ret;
-
- /* lookup parent object and ensure it *is* a parent */
- parent = nouveau_handle_ref(client, _parent);
- if (!parent) {
- nv_error(client, "parent 0x%08x not found\n", _parent);
- return -ENOENT;
- }
-
- if (!nv_iclass(parent, NV_PARENT_CLASS)) {
- nv_error(parent, "cannot have children\n");
- ret = -EINVAL;
- goto fail_class;
- }
-
- /* check that parent supports the requested subclass */
- ret = nouveau_parent_sclass(parent, _oclass, &engine, &oclass);
- if (ret) {
- nv_debug(parent, "illegal class 0x%04x\n", _oclass);
- goto fail_class;
- }
-
- /* make sure engine init has been completed *before* any objects
- * it controls are created - the constructors may depend on
- * state calculated at init (ie. default context construction)
- */
- if (engine) {
- ret = nouveau_object_inc(engine);
- if (ret)
- goto fail_class;
- }
-
- /* if engine requires it, create a context object to insert
- * between the parent and its children (eg. PGRAPH context)
- */
- if (engine && nv_engine(engine)->cclass) {
- ret = nouveau_object_ctor(parent, engine,
- nv_engine(engine)->cclass,
- data, size, &engctx);
- if (ret)
- goto fail_engctx;
- } else {
- nouveau_object_ref(parent, &engctx);
- }
-
- /* finally, create new object and bind it to its handle */
- ret = nouveau_object_ctor(engctx, engine, oclass, data, size, &object);
- *pobject = object;
- if (ret)
- goto fail_ctor;
-
- ret = nouveau_object_inc(object);
- if (ret)
- goto fail_init;
-
- ret = nouveau_handle_create(parent, _parent, _handle, object, &handle);
- if (ret)
- goto fail_handle;
-
- ret = nouveau_handle_init(handle);
- if (ret)
- nouveau_handle_destroy(handle);
-
-fail_handle:
- nouveau_object_dec(object, false);
-fail_init:
- nouveau_object_ref(NULL, &object);
-fail_ctor:
- nouveau_object_ref(NULL, &engctx);
-fail_engctx:
- if (engine)
- nouveau_object_dec(engine, false);
-fail_class:
- nouveau_object_ref(NULL, &parent);
- return ret;
-}
-
-int
-nouveau_object_del(struct nouveau_object *client, u32 _parent, u32 _handle)
-{
- struct nouveau_object *parent = NULL;
- struct nouveau_object *namedb = NULL;
- struct nouveau_handle *handle = NULL;
-
- parent = nouveau_handle_ref(client, _parent);
- if (!parent)
- return -ENOENT;
-
- namedb = nv_pclass(parent, NV_NAMEDB_CLASS);
- if (namedb) {
- handle = nouveau_namedb_get(nv_namedb(namedb), _handle);
- if (handle) {
- nouveau_namedb_put(handle);
- nouveau_handle_fini(handle, false);
- nouveau_handle_destroy(handle);
- }
- }
-
- nouveau_object_ref(NULL, &parent);
- return handle ? 0 : -EINVAL;
-}
-
-int
nouveau_object_inc(struct nouveau_object *object)
{
int ref = atomic_add_return(1, &object->usecount);
diff --git a/drivers/gpu/drm/nouveau/core/core/parent.c b/drivers/gpu/drm/nouveau/core/core/parent.c
index dee5d1235e9b..30a2911878f8 100644
--- a/drivers/gpu/drm/nouveau/core/core/parent.c
+++ b/drivers/gpu/drm/nouveau/core/core/parent.c
@@ -75,6 +75,39 @@ nouveau_parent_sclass(struct nouveau_object *parent, u16 handle,
}
int
+nouveau_parent_lclass(struct nouveau_object *parent, u32 *lclass, int size)
+{
+ struct nouveau_sclass *sclass;
+ struct nouveau_engine *engine;
+ struct nouveau_oclass *oclass;
+ int nr = -1, i;
+ u64 mask;
+
+ sclass = nv_parent(parent)->sclass;
+ while (sclass) {
+ if (++nr < size)
+ lclass[nr] = sclass->oclass->handle & 0xffff;
+ sclass = sclass->sclass;
+ }
+
+ mask = nv_parent(parent)->engine;
+ while (i = __ffs64(mask), mask) {
+ engine = nouveau_engine(parent, i);
+ if (engine && (oclass = engine->sclass)) {
+ while (oclass->ofuncs) {
+ if (++nr < size)
+ lclass[nr] = oclass->handle & 0xffff;
+ oclass++;
+ }
+ }
+
+ mask &= ~(1ULL << i);
+ }
+
+ return nr + 1;
+}
+
+int
nouveau_parent_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, u32 pclass,
diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c
index f31527733e00..abb410ef09ea 100644
--- a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c
@@ -30,7 +30,6 @@
#include <subdev/vm.h>
#include <core/client.h>
-#include <core/class.h>
#include <core/enum.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c
index ac3291f781f6..9261694d0d35 100644
--- a/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c
@@ -26,9 +26,7 @@
#include <engine/fifo.h>
#include <engine/copy.h>
-#include <core/class.h>
#include <core/enum.h>
-#include <core/class.h>
#include <core/enum.h>
#include "fuc/nvc0.fuc.h"
diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c b/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c
index 748a61eb3c6f..c7194b354605 100644
--- a/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c
@@ -24,7 +24,6 @@
#include <core/os.h>
#include <core/enum.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <engine/copy.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c b/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c
index 2551dafbec73..ea5c42f31791 100644
--- a/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c
@@ -25,7 +25,6 @@
#include <core/client.h>
#include <core/os.h>
#include <core/enum.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <core/gpuobj.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c
index c7082377ec76..5571c09534cb 100644
--- a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c
+++ b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c
@@ -25,7 +25,6 @@
#include <core/client.h>
#include <core/os.h>
#include <core/enum.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <subdev/timer.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/acpi.c b/drivers/gpu/drm/nouveau/core/engine/device/acpi.c
new file mode 100644
index 000000000000..4dbf0ba89e5c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/device/acpi.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "acpi.h"
+
+#ifdef CONFIG_ACPI
+static int
+nvkm_acpi_ntfy(struct notifier_block *nb, unsigned long val, void *data)
+{
+ struct nouveau_device *device =
+ container_of(nb, typeof(*device), acpi.nb);
+ struct acpi_bus_event *info = data;
+
+ if (!strcmp(info->device_class, "ac_adapter"))
+ nvkm_event_send(&device->event, 1, 0, NULL, 0);
+
+ return NOTIFY_DONE;
+}
+#endif
+
+int
+nvkm_acpi_fini(struct nouveau_device *device, bool suspend)
+{
+#ifdef CONFIG_ACPI
+ unregister_acpi_notifier(&device->acpi.nb);
+#endif
+ return 0;
+}
+
+int
+nvkm_acpi_init(struct nouveau_device *device)
+{
+#ifdef CONFIG_ACPI
+ device->acpi.nb.notifier_call = nvkm_acpi_ntfy;
+ register_acpi_notifier(&device->acpi.nb);
+#endif
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/acpi.h b/drivers/gpu/drm/nouveau/core/engine/device/acpi.h
new file mode 100644
index 000000000000..cc49f4f568cd
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/device/acpi.h
@@ -0,0 +1,9 @@
+#ifndef __NVKM_DEVICE_ACPI_H__
+#define __NVKM_DEVICE_ACPI_H__
+
+#include <engine/device.h>
+
+int nvkm_acpi_init(struct nouveau_device *);
+int nvkm_acpi_fini(struct nouveau_device *, bool);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/base.c b/drivers/gpu/drm/nouveau/core/engine/device/base.c
index 18c8c7245b73..0ef5a5713182 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/base.c
@@ -26,10 +26,14 @@
#include <core/device.h>
#include <core/client.h>
#include <core/option.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
-#include <core/class.h>
+#include <subdev/fb.h>
+#include <subdev/instmem.h>
#include "priv.h"
+#include "acpi.h"
static DEFINE_MUTEX(nv_devices_mutex);
static LIST_HEAD(nv_devices);
@@ -49,74 +53,258 @@ nouveau_device_find(u64 name)
return match;
}
+int
+nouveau_device_list(u64 *name, int size)
+{
+ struct nouveau_device *device;
+ int nr = 0;
+ mutex_lock(&nv_devices_mutex);
+ list_for_each_entry(device, &nv_devices, head) {
+ if (nr++ < size)
+ name[nr - 1] = device->handle;
+ }
+ mutex_unlock(&nv_devices_mutex);
+ return nr;
+}
+
/******************************************************************************
* nouveau_devobj (0x0080): class implementation
*****************************************************************************/
+
struct nouveau_devobj {
struct nouveau_parent base;
struct nouveau_object *subdev[NVDEV_SUBDEV_NR];
};
+static int
+nouveau_devobj_info(struct nouveau_object *object, void *data, u32 size)
+{
+ struct nouveau_device *device = nv_device(object);
+ struct nouveau_fb *pfb = nouveau_fb(device);
+ struct nouveau_instmem *imem = nouveau_instmem(device);
+ union {
+ struct nv_device_info_v0 v0;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "device info size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "device info vers %d\n", args->v0.version);
+ } else
+ return ret;
+
+ switch (device->chipset) {
+ case 0x01a:
+ case 0x01f:
+ case 0x04c:
+ case 0x04e:
+ case 0x063:
+ case 0x067:
+ case 0x068:
+ case 0x0aa:
+ case 0x0ac:
+ case 0x0af:
+ args->v0.platform = NV_DEVICE_INFO_V0_IGP;
+ break;
+ default:
+ if (device->pdev) {
+ if (pci_find_capability(device->pdev, PCI_CAP_ID_AGP))
+ args->v0.platform = NV_DEVICE_INFO_V0_AGP;
+ else
+ if (pci_is_pcie(device->pdev))
+ args->v0.platform = NV_DEVICE_INFO_V0_PCIE;
+ else
+ args->v0.platform = NV_DEVICE_INFO_V0_PCI;
+ } else {
+ args->v0.platform = NV_DEVICE_INFO_V0_SOC;
+ }
+ break;
+ }
+
+ switch (device->card_type) {
+ case NV_04: args->v0.family = NV_DEVICE_INFO_V0_TNT; break;
+ case NV_10:
+ case NV_11: args->v0.family = NV_DEVICE_INFO_V0_CELSIUS; break;
+ case NV_20: args->v0.family = NV_DEVICE_INFO_V0_KELVIN; break;
+ case NV_30: args->v0.family = NV_DEVICE_INFO_V0_RANKINE; break;
+ case NV_40: args->v0.family = NV_DEVICE_INFO_V0_CURIE; break;
+ case NV_50: args->v0.family = NV_DEVICE_INFO_V0_TESLA; break;
+ case NV_C0: args->v0.family = NV_DEVICE_INFO_V0_FERMI; break;
+ case NV_E0: args->v0.family = NV_DEVICE_INFO_V0_KEPLER; break;
+ case GM100: args->v0.family = NV_DEVICE_INFO_V0_MAXWELL; break;
+ default:
+ args->v0.family = 0;
+ break;
+ }
+
+ args->v0.chipset = device->chipset;
+ args->v0.revision = device->chipset >= 0x10 ? nv_rd32(device, 0) : 0x00;
+ if (pfb) args->v0.ram_size = args->v0.ram_user = pfb->ram->size;
+ else args->v0.ram_size = args->v0.ram_user = 0;
+ if (imem) args->v0.ram_user = args->v0.ram_user - imem->reserved;
+ return 0;
+}
+
+static int
+nouveau_devobj_mthd(struct nouveau_object *object, u32 mthd,
+ void *data, u32 size)
+{
+ switch (mthd) {
+ case NV_DEVICE_V0_INFO:
+ return nouveau_devobj_info(object, data, size);
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static u8
+nouveau_devobj_rd08(struct nouveau_object *object, u64 addr)
+{
+ return nv_rd08(object->engine, addr);
+}
+
+static u16
+nouveau_devobj_rd16(struct nouveau_object *object, u64 addr)
+{
+ return nv_rd16(object->engine, addr);
+}
+
+static u32
+nouveau_devobj_rd32(struct nouveau_object *object, u64 addr)
+{
+ return nv_rd32(object->engine, addr);
+}
+
+static void
+nouveau_devobj_wr08(struct nouveau_object *object, u64 addr, u8 data)
+{
+ nv_wr08(object->engine, addr, data);
+}
+
+static void
+nouveau_devobj_wr16(struct nouveau_object *object, u64 addr, u16 data)
+{
+ nv_wr16(object->engine, addr, data);
+}
+
+static void
+nouveau_devobj_wr32(struct nouveau_object *object, u64 addr, u32 data)
+{
+ nv_wr32(object->engine, addr, data);
+}
+
+static int
+nouveau_devobj_map(struct nouveau_object *object, u64 *addr, u32 *size)
+{
+ struct nouveau_device *device = nv_device(object);
+ *addr = nv_device_resource_start(device, 0);
+ *size = nv_device_resource_len(device, 0);
+ return 0;
+}
+
static const u64 disable_map[] = {
- [NVDEV_SUBDEV_VBIOS] = NV_DEVICE_DISABLE_VBIOS,
- [NVDEV_SUBDEV_DEVINIT] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_GPIO] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_I2C] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_CLOCK] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_MXM] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_MC] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_BUS] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_TIMER] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_FB] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_LTCG] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_IBUS] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_INSTMEM] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_VM] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_BAR] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_VOLT] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_THERM] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_SUBDEV_PWR] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_ENGINE_DMAOBJ] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_ENGINE_PERFMON] = NV_DEVICE_DISABLE_CORE,
- [NVDEV_ENGINE_FIFO] = NV_DEVICE_DISABLE_FIFO,
- [NVDEV_ENGINE_SW] = NV_DEVICE_DISABLE_FIFO,
- [NVDEV_ENGINE_GR] = NV_DEVICE_DISABLE_GRAPH,
- [NVDEV_ENGINE_MPEG] = NV_DEVICE_DISABLE_MPEG,
- [NVDEV_ENGINE_ME] = NV_DEVICE_DISABLE_ME,
- [NVDEV_ENGINE_VP] = NV_DEVICE_DISABLE_VP,
- [NVDEV_ENGINE_CRYPT] = NV_DEVICE_DISABLE_CRYPT,
- [NVDEV_ENGINE_BSP] = NV_DEVICE_DISABLE_BSP,
- [NVDEV_ENGINE_PPP] = NV_DEVICE_DISABLE_PPP,
- [NVDEV_ENGINE_COPY0] = NV_DEVICE_DISABLE_COPY0,
- [NVDEV_ENGINE_COPY1] = NV_DEVICE_DISABLE_COPY1,
- [NVDEV_ENGINE_VIC] = NV_DEVICE_DISABLE_VIC,
- [NVDEV_ENGINE_VENC] = NV_DEVICE_DISABLE_VENC,
- [NVDEV_ENGINE_DISP] = NV_DEVICE_DISABLE_DISP,
+ [NVDEV_SUBDEV_VBIOS] = NV_DEVICE_V0_DISABLE_VBIOS,
+ [NVDEV_SUBDEV_DEVINIT] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_GPIO] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_I2C] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_CLOCK] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_MXM] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_MC] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_BUS] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_TIMER] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_FB] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_LTC] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_IBUS] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_INSTMEM] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_VM] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_BAR] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_VOLT] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_THERM] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_SUBDEV_PWR] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_ENGINE_DMAOBJ] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_ENGINE_PERFMON] = NV_DEVICE_V0_DISABLE_CORE,
+ [NVDEV_ENGINE_FIFO] = NV_DEVICE_V0_DISABLE_FIFO,
+ [NVDEV_ENGINE_SW] = NV_DEVICE_V0_DISABLE_FIFO,
+ [NVDEV_ENGINE_GR] = NV_DEVICE_V0_DISABLE_GRAPH,
+ [NVDEV_ENGINE_MPEG] = NV_DEVICE_V0_DISABLE_MPEG,
+ [NVDEV_ENGINE_ME] = NV_DEVICE_V0_DISABLE_ME,
+ [NVDEV_ENGINE_VP] = NV_DEVICE_V0_DISABLE_VP,
+ [NVDEV_ENGINE_CRYPT] = NV_DEVICE_V0_DISABLE_CRYPT,
+ [NVDEV_ENGINE_BSP] = NV_DEVICE_V0_DISABLE_BSP,
+ [NVDEV_ENGINE_PPP] = NV_DEVICE_V0_DISABLE_PPP,
+ [NVDEV_ENGINE_COPY0] = NV_DEVICE_V0_DISABLE_COPY0,
+ [NVDEV_ENGINE_COPY1] = NV_DEVICE_V0_DISABLE_COPY1,
+ [NVDEV_ENGINE_VIC] = NV_DEVICE_V0_DISABLE_VIC,
+ [NVDEV_ENGINE_VENC] = NV_DEVICE_V0_DISABLE_VENC,
+ [NVDEV_ENGINE_DISP] = NV_DEVICE_V0_DISABLE_DISP,
[NVDEV_SUBDEV_NR] = 0,
};
+static void
+nouveau_devobj_dtor(struct nouveau_object *object)
+{
+ struct nouveau_devobj *devobj = (void *)object;
+ int i;
+
+ for (i = NVDEV_SUBDEV_NR - 1; i >= 0; i--)
+ nouveau_object_ref(NULL, &devobj->subdev[i]);
+
+ nouveau_parent_destroy(&devobj->base);
+}
+
+static struct nouveau_oclass
+nouveau_devobj_oclass_super = {
+ .handle = NV_DEVICE,
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .dtor = nouveau_devobj_dtor,
+ .init = _nouveau_parent_init,
+ .fini = _nouveau_parent_fini,
+ .mthd = nouveau_devobj_mthd,
+ .map = nouveau_devobj_map,
+ .rd08 = nouveau_devobj_rd08,
+ .rd16 = nouveau_devobj_rd16,
+ .rd32 = nouveau_devobj_rd32,
+ .wr08 = nouveau_devobj_wr08,
+ .wr16 = nouveau_devobj_wr16,
+ .wr32 = nouveau_devobj_wr32,
+ }
+};
+
static int
nouveau_devobj_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ union {
+ struct nv_device_v0 v0;
+ } *args = data;
struct nouveau_client *client = nv_client(parent);
struct nouveau_device *device;
struct nouveau_devobj *devobj;
- struct nv_device_class *args = data;
u32 boot0, strap;
u64 disable, mmio_base, mmio_size;
void __iomem *map;
int ret, i, c;
- if (size < sizeof(struct nv_device_class))
- return -EINVAL;
+ nv_ioctl(parent, "create device size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create device v%d device %016llx "
+ "disable %016llx debug0 %016llx\n",
+ args->v0.version, args->v0.device,
+ args->v0.disable, args->v0.debug0);
+ } else
+ return ret;
+
+ /* give priviledged clients register access */
+ if (client->super)
+ oclass = &nouveau_devobj_oclass_super;
/* find the device subdev that matches what the client requested */
device = nv_device(client->device);
- if (args->device != ~0) {
- device = nouveau_device_find(args->device);
+ if (args->v0.device != ~0) {
+ device = nouveau_device_find(args->v0.device);
if (!device)
return -ENODEV;
}
@@ -135,14 +323,14 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
mmio_size = nv_device_resource_len(device, 0);
/* translate api disable mask into internal mapping */
- disable = args->debug0;
+ disable = args->v0.debug0;
for (i = 0; i < NVDEV_SUBDEV_NR; i++) {
- if (args->disable & disable_map[i])
+ if (args->v0.disable & disable_map[i])
disable |= (1ULL << i);
}
/* identify the chipset, and determine classes of subdev/engines */
- if (!(args->disable & NV_DEVICE_DISABLE_IDENTIFY) &&
+ if (!(args->v0.disable & NV_DEVICE_V0_DISABLE_IDENTIFY) &&
!device->card_type) {
map = ioremap(mmio_base, 0x102000);
if (map == NULL)
@@ -180,8 +368,8 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
case 0x080:
case 0x090:
case 0x0a0: device->card_type = NV_50; break;
- case 0x0c0: device->card_type = NV_C0; break;
- case 0x0d0: device->card_type = NV_D0; break;
+ case 0x0c0:
+ case 0x0d0: device->card_type = NV_C0; break;
case 0x0e0:
case 0x0f0:
case 0x100: device->card_type = NV_E0; break;
@@ -206,8 +394,7 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
case NV_30: ret = nv30_identify(device); break;
case NV_40: ret = nv40_identify(device); break;
case NV_50: ret = nv50_identify(device); break;
- case NV_C0:
- case NV_D0: ret = nvc0_identify(device); break;
+ case NV_C0: ret = nvc0_identify(device); break;
case NV_E0: ret = nve0_identify(device); break;
case GM100: ret = gm100_identify(device); break;
default:
@@ -242,7 +429,7 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
nv_debug(device, "crystal freq: %dKHz\n", device->crystal);
}
- if (!(args->disable & NV_DEVICE_DISABLE_MMIO) &&
+ if (!(args->v0.disable & NV_DEVICE_V0_DISABLE_MMIO) &&
!nv_subdev(device)->mmio) {
nv_subdev(device)->mmio = ioremap(mmio_base, mmio_size);
if (!nv_subdev(device)->mmio) {
@@ -298,71 +485,19 @@ nouveau_devobj_ctor(struct nouveau_object *parent,
return 0;
}
-static void
-nouveau_devobj_dtor(struct nouveau_object *object)
-{
- struct nouveau_devobj *devobj = (void *)object;
- int i;
-
- for (i = NVDEV_SUBDEV_NR - 1; i >= 0; i--)
- nouveau_object_ref(NULL, &devobj->subdev[i]);
-
- nouveau_parent_destroy(&devobj->base);
-}
-
-static u8
-nouveau_devobj_rd08(struct nouveau_object *object, u64 addr)
-{
- return nv_rd08(object->engine, addr);
-}
-
-static u16
-nouveau_devobj_rd16(struct nouveau_object *object, u64 addr)
-{
- return nv_rd16(object->engine, addr);
-}
-
-static u32
-nouveau_devobj_rd32(struct nouveau_object *object, u64 addr)
-{
- return nv_rd32(object->engine, addr);
-}
-
-static void
-nouveau_devobj_wr08(struct nouveau_object *object, u64 addr, u8 data)
-{
- nv_wr08(object->engine, addr, data);
-}
-
-static void
-nouveau_devobj_wr16(struct nouveau_object *object, u64 addr, u16 data)
-{
- nv_wr16(object->engine, addr, data);
-}
-
-static void
-nouveau_devobj_wr32(struct nouveau_object *object, u64 addr, u32 data)
-{
- nv_wr32(object->engine, addr, data);
-}
-
static struct nouveau_ofuncs
nouveau_devobj_ofuncs = {
.ctor = nouveau_devobj_ctor,
.dtor = nouveau_devobj_dtor,
.init = _nouveau_parent_init,
.fini = _nouveau_parent_fini,
- .rd08 = nouveau_devobj_rd08,
- .rd16 = nouveau_devobj_rd16,
- .rd32 = nouveau_devobj_rd32,
- .wr08 = nouveau_devobj_wr08,
- .wr16 = nouveau_devobj_wr16,
- .wr32 = nouveau_devobj_wr32,
+ .mthd = nouveau_devobj_mthd,
};
/******************************************************************************
* nouveau_device: engine functions
*****************************************************************************/
+
static struct nouveau_oclass
nouveau_device_sclass[] = {
{ 0x0080, &nouveau_devobj_ofuncs },
@@ -370,6 +505,24 @@ nouveau_device_sclass[] = {
};
static int
+nouveau_device_event_ctor(struct nouveau_object *object, void *data, u32 size,
+ struct nvkm_notify *notify)
+{
+ if (!WARN_ON(size != 0)) {
+ notify->size = 0;
+ notify->types = 1;
+ notify->index = 0;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static const struct nvkm_event_func
+nouveau_device_event_func = {
+ .ctor = nouveau_device_event_ctor,
+};
+
+static int
nouveau_device_fini(struct nouveau_object *object, bool suspend)
{
struct nouveau_device *device = (void *)object;
@@ -386,7 +539,7 @@ nouveau_device_fini(struct nouveau_object *object, bool suspend)
}
}
- ret = 0;
+ ret = nvkm_acpi_fini(device, suspend);
fail:
for (; ret && i < NVDEV_SUBDEV_NR; i++) {
if ((subdev = device->subdev[i])) {
@@ -407,7 +560,11 @@ nouveau_device_init(struct nouveau_object *object)
{
struct nouveau_device *device = (void *)object;
struct nouveau_object *subdev;
- int ret, i;
+ int ret, i = 0;
+
+ ret = nvkm_acpi_init(device);
+ if (ret)
+ goto fail;
for (i = 0; i < NVDEV_SUBDEV_NR; i++) {
if ((subdev = device->subdev[i])) {
@@ -430,6 +587,8 @@ fail:
}
}
+ if (ret)
+ nvkm_acpi_fini(device, false);
return ret;
}
@@ -438,6 +597,8 @@ nouveau_device_dtor(struct nouveau_object *object)
{
struct nouveau_device *device = (void *)object;
+ nvkm_event_fini(&device->event);
+
mutex_lock(&nv_devices_mutex);
list_del(&device->head);
mutex_unlock(&nv_devices_mutex);
@@ -478,31 +639,6 @@ nv_device_resource_len(struct nouveau_device *device, unsigned int bar)
}
}
-dma_addr_t
-nv_device_map_page(struct nouveau_device *device, struct page *page)
-{
- dma_addr_t ret;
-
- if (nv_device_is_pci(device)) {
- ret = pci_map_page(device->pdev, page, 0, PAGE_SIZE,
- PCI_DMA_BIDIRECTIONAL);
- if (pci_dma_mapping_error(device->pdev, ret))
- ret = 0;
- } else {
- ret = page_to_phys(page);
- }
-
- return ret;
-}
-
-void
-nv_device_unmap_page(struct nouveau_device *device, dma_addr_t addr)
-{
- if (nv_device_is_pci(device))
- pci_unmap_page(device->pdev, addr, PAGE_SIZE,
- PCI_DMA_BIDIRECTIONAL);
-}
-
int
nv_device_get_irq(struct nouveau_device *device, bool stall)
{
@@ -560,6 +696,9 @@ nouveau_device_create_(void *dev, enum nv_bus_type type, u64 name,
nv_subdev(device)->debug = nouveau_dbgopt(device->dbgopt, "DEVICE");
nv_engine(device)->sclass = nouveau_device_sclass;
list_add(&device->head, &nv_devices);
+
+ ret = nvkm_event_init(&nouveau_device_event_func, 1, 1,
+ &device->event);
done:
mutex_unlock(&nv_devices_mutex);
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/ctrl.c b/drivers/gpu/drm/nouveau/core/engine/device/ctrl.c
index 4b69bf56ed01..e34101a3490e 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/ctrl.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/ctrl.c
@@ -22,55 +22,82 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
+#include <core/client.h>
#include <core/object.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
+#include <nvif/ioctl.h>
#include <subdev/clock.h>
#include "priv.h"
static int
-nouveau_control_mthd_pstate_info(struct nouveau_object *object, u32 mthd,
- void *data, u32 size)
+nouveau_control_mthd_pstate_info(struct nouveau_object *object,
+ void *data, u32 size)
{
+ union {
+ struct nvif_control_pstate_info_v0 v0;
+ } *args = data;
struct nouveau_clock *clk = nouveau_clock(object);
- struct nv_control_pstate_info *args = data;
+ int ret;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(object, "control pstate info size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "control pstate info vers %d\n",
+ args->v0.version);
+ } else
+ return ret;
if (clk) {
- args->count = clk->state_nr;
- args->ustate = clk->ustate;
- args->pstate = clk->pstate;
+ args->v0.count = clk->state_nr;
+ args->v0.ustate_ac = clk->ustate_ac;
+ args->v0.ustate_dc = clk->ustate_dc;
+ args->v0.pwrsrc = clk->pwrsrc;
+ args->v0.pstate = clk->pstate;
} else {
- args->count = 0;
- args->ustate = NV_CONTROL_PSTATE_INFO_USTATE_DISABLE;
- args->pstate = NV_CONTROL_PSTATE_INFO_PSTATE_UNKNOWN;
+ args->v0.count = 0;
+ args->v0.ustate_ac = NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE;
+ args->v0.ustate_dc = NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE;
+ args->v0.pwrsrc = -ENOSYS;
+ args->v0.pstate = NVIF_CONTROL_PSTATE_INFO_V0_PSTATE_UNKNOWN;
}
return 0;
}
static int
-nouveau_control_mthd_pstate_attr(struct nouveau_object *object, u32 mthd,
- void *data, u32 size)
+nouveau_control_mthd_pstate_attr(struct nouveau_object *object,
+ void *data, u32 size)
{
+ union {
+ struct nvif_control_pstate_attr_v0 v0;
+ } *args = data;
struct nouveau_clock *clk = nouveau_clock(object);
- struct nv_control_pstate_attr *args = data;
struct nouveau_clocks *domain;
struct nouveau_pstate *pstate;
struct nouveau_cstate *cstate;
int i = 0, j = -1;
u32 lo, hi;
-
- if ((size < sizeof(*args)) || !clk ||
- (args->state >= 0 && args->state >= clk->state_nr))
- return -EINVAL;
+ int ret;
+
+ nv_ioctl(object, "control pstate attr size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "control pstate attr vers %d state %d "
+ "index %d\n",
+ args->v0.version, args->v0.state, args->v0.index);
+ if (!clk)
+ return -ENODEV;
+ if (args->v0.state < NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT)
+ return -EINVAL;
+ if (args->v0.state >= clk->state_nr)
+ return -EINVAL;
+ } else
+ return ret;
domain = clk->domains;
while (domain->name != nv_clk_src_max) {
- if (domain->mname && ++j == args->index)
+ if (domain->mname && ++j == args->v0.index)
break;
domain++;
}
@@ -78,9 +105,9 @@ nouveau_control_mthd_pstate_attr(struct nouveau_object *object, u32 mthd,
if (domain->name == nv_clk_src_max)
return -EINVAL;
- if (args->state != NV_CONTROL_PSTATE_ATTR_STATE_CURRENT) {
+ if (args->v0.state != NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) {
list_for_each_entry(pstate, &clk->states, head) {
- if (i++ == args->state)
+ if (i++ == args->v0.state)
break;
}
@@ -91,21 +118,21 @@ nouveau_control_mthd_pstate_attr(struct nouveau_object *object, u32 mthd,
hi = max(hi, cstate->domain[domain->name]);
}
- args->state = pstate->pstate;
+ args->v0.state = pstate->pstate;
} else {
lo = max(clk->read(clk, domain->name), 0);
hi = lo;
}
- snprintf(args->name, sizeof(args->name), "%s", domain->mname);
- snprintf(args->unit, sizeof(args->unit), "MHz");
- args->min = lo / domain->mdiv;
- args->max = hi / domain->mdiv;
+ snprintf(args->v0.name, sizeof(args->v0.name), "%s", domain->mname);
+ snprintf(args->v0.unit, sizeof(args->v0.unit), "MHz");
+ args->v0.min = lo / domain->mdiv;
+ args->v0.max = hi / domain->mdiv;
- args->index = 0;
+ args->v0.index = 0;
while ((++domain)->name != nv_clk_src_max) {
if (domain->mname) {
- args->index = ++j;
+ args->v0.index = ++j;
break;
}
}
@@ -114,31 +141,65 @@ nouveau_control_mthd_pstate_attr(struct nouveau_object *object, u32 mthd,
}
static int
-nouveau_control_mthd_pstate_user(struct nouveau_object *object, u32 mthd,
- void *data, u32 size)
+nouveau_control_mthd_pstate_user(struct nouveau_object *object,
+ void *data, u32 size)
{
+ union {
+ struct nvif_control_pstate_user_v0 v0;
+ } *args = data;
struct nouveau_clock *clk = nouveau_clock(object);
- struct nv_control_pstate_user *args = data;
+ int ret;
+
+ nv_ioctl(object, "control pstate user size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "control pstate user vers %d ustate %d "
+ "pwrsrc %d\n", args->v0.version,
+ args->v0.ustate, args->v0.pwrsrc);
+ if (!clk)
+ return -ENODEV;
+ } else
+ return ret;
+
+ if (args->v0.pwrsrc >= 0) {
+ ret |= nouveau_clock_ustate(clk, args->v0.ustate, args->v0.pwrsrc);
+ } else {
+ ret |= nouveau_clock_ustate(clk, args->v0.ustate, 0);
+ ret |= nouveau_clock_ustate(clk, args->v0.ustate, 1);
+ }
- if (size < sizeof(*args) || !clk)
- return -EINVAL;
+ return ret;
+}
- return nouveau_clock_ustate(clk, args->state);
+static int
+nouveau_control_mthd(struct nouveau_object *object, u32 mthd,
+ void *data, u32 size)
+{
+ switch (mthd) {
+ case NVIF_CONTROL_PSTATE_INFO:
+ return nouveau_control_mthd_pstate_info(object, data, size);
+ case NVIF_CONTROL_PSTATE_ATTR:
+ return nouveau_control_mthd_pstate_attr(object, data, size);
+ case NVIF_CONTROL_PSTATE_USER:
+ return nouveau_control_mthd_pstate_user(object, data, size);
+ default:
+ break;
+ }
+ return -EINVAL;
}
+static struct nouveau_ofuncs
+nouveau_control_ofuncs = {
+ .ctor = _nouveau_object_ctor,
+ .dtor = nouveau_object_destroy,
+ .init = nouveau_object_init,
+ .fini = nouveau_object_fini,
+ .mthd = nouveau_control_mthd,
+};
+
struct nouveau_oclass
nouveau_control_oclass[] = {
- { .handle = NV_CONTROL_CLASS,
- .ofuncs = &nouveau_object_ofuncs,
- .omthds = (struct nouveau_omthds[]) {
- { NV_CONTROL_PSTATE_INFO,
- NV_CONTROL_PSTATE_INFO, nouveau_control_mthd_pstate_info },
- { NV_CONTROL_PSTATE_ATTR,
- NV_CONTROL_PSTATE_ATTR, nouveau_control_mthd_pstate_attr },
- { NV_CONTROL_PSTATE_USER,
- NV_CONTROL_PSTATE_USER, nouveau_control_mthd_pstate_user },
- {},
- },
+ { .handle = NVIF_IOCTL_NEW_V0_CONTROL,
+ .ofuncs = &nouveau_control_ofuncs
},
{}
};
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/gm100.c b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c
index a520029e25d9..6295668e29a5 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/gm100.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c
@@ -26,6 +26,7 @@
#include <subdev/bus.h>
#include <subdev/gpio.h>
#include <subdev/i2c.h>
+#include <subdev/fuse.h>
#include <subdev/clock.h>
#include <subdev/therm.h>
#include <subdev/mxm.h>
@@ -33,7 +34,7 @@
#include <subdev/mc.h>
#include <subdev/timer.h>
#include <subdev/fb.h>
-#include <subdev/ltcg.h>
+#include <subdev/ltc.h>
#include <subdev/ibus.h>
#include <subdev/instmem.h>
#include <subdev/vm.h>
@@ -62,26 +63,26 @@ gm100_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gm107_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
-#if 0
- device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
-#endif
+ device->oclass[NVDEV_SUBDEV_THERM ] = &gm107_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = gm107_devinit_oclass;
- device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass;
+ device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass;
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = gm107_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gm107_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gm107_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nv108_pwr_oclass;
+
#if 0
- device->oclass[NVDEV_SUBDEV_PWR ] = &nv108_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
#endif
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv108_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = gm107_graph_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv04.c b/drivers/gpu/drm/nouveau/core/engine/device/nv04.c
index 40b29d0214cb..573b55f5c2f9 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv04.c
@@ -56,7 +56,7 @@ nv04_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv04_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv04_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv04_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv04_graph_oclass;
@@ -74,7 +74,7 @@ nv04_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv04_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv04_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv04_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv04_graph_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv10.c b/drivers/gpu/drm/nouveau/core/engine/device/nv10.c
index 5f7c25ff523d..183a85a6204e 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv10.c
@@ -58,7 +58,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
device->oclass[NVDEV_ENGINE_DISP ] = nv04_disp_oclass;
break;
@@ -75,7 +75,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
@@ -94,7 +94,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
@@ -113,7 +113,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv1a_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
@@ -132,7 +132,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv10_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
@@ -151,7 +151,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
@@ -170,7 +170,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv1a_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
@@ -189,7 +189,7 @@ nv10_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv10_graph_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv20.c b/drivers/gpu/drm/nouveau/core/engine/device/nv20.c
index 75fed11bba0a..aa564c68a920 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv20.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv20.c
@@ -59,7 +59,7 @@ nv20_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv20_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv20_graph_oclass;
@@ -78,7 +78,7 @@ nv20_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv25_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv25_graph_oclass;
@@ -97,7 +97,7 @@ nv20_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv25_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv25_graph_oclass;
@@ -116,7 +116,7 @@ nv20_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv25_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv2a_graph_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv30.c b/drivers/gpu/drm/nouveau/core/engine/device/nv30.c
index 36919d7db7cc..11bd31da82ab 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv30.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv30.c
@@ -59,7 +59,7 @@ nv30_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv30_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv30_graph_oclass;
@@ -78,7 +78,7 @@ nv30_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv35_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv35_graph_oclass;
@@ -97,7 +97,7 @@ nv30_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv30_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv30_graph_oclass;
@@ -117,7 +117,7 @@ nv30_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv36_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv35_graph_oclass;
@@ -137,7 +137,7 @@ nv30_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_FB ] = nv10_fb_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv04_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv17_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv34_graph_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv40.c b/drivers/gpu/drm/nouveau/core/engine/device/nv40.c
index 1130a62be2c7..e96c223cb797 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv40.c
@@ -65,7 +65,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -88,7 +88,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -111,7 +111,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -134,7 +134,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -157,7 +157,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -180,7 +180,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -203,7 +203,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -226,7 +226,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -249,7 +249,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -272,7 +272,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -295,7 +295,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -318,7 +318,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -341,7 +341,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -364,7 +364,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -387,7 +387,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
@@ -410,7 +410,7 @@ nv40_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv40_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv04_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv40_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv10_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv40_graph_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c
index ef0b0bde1a91..96f568d1321b 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c
@@ -26,6 +26,7 @@
#include <subdev/bus.h>
#include <subdev/gpio.h>
#include <subdev/i2c.h>
+#include <subdev/fuse.h>
#include <subdev/clock.h>
#include <subdev/therm.h>
#include <subdev/mxm.h>
@@ -62,6 +63,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv50_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv50_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -74,7 +76,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv50_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -87,6 +89,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -99,7 +102,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -115,6 +118,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -127,7 +131,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -141,8 +145,9 @@ nv50_identify(struct nouveau_device *device)
case 0x92:
device->cname = "G92";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv50_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -155,7 +160,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -169,8 +174,9 @@ nv50_identify(struct nouveau_device *device)
case 0x94:
device->cname = "G94";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -183,7 +189,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -197,8 +203,9 @@ nv50_identify(struct nouveau_device *device)
case 0x96:
device->cname = "G96";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -211,7 +218,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -225,8 +232,9 @@ nv50_identify(struct nouveau_device *device)
case 0x98:
device->cname = "G98";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -239,7 +247,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -253,8 +261,9 @@ nv50_identify(struct nouveau_device *device)
case 0xa0:
device->cname = "G200";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nv84_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -267,7 +276,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -281,8 +290,9 @@ nv50_identify(struct nouveau_device *device)
case 0xaa:
device->cname = "MCP77/MCP78";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -295,7 +305,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -309,8 +319,9 @@ nv50_identify(struct nouveau_device *device)
case 0xac:
device->cname = "MCP79/MCP7A";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = nvaa_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv84_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -323,7 +334,7 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -337,8 +348,9 @@ nv50_identify(struct nouveau_device *device)
case 0xa3:
device->cname = "GT215";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -350,9 +362,9 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nva3_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nva3_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -367,8 +379,9 @@ nv50_identify(struct nouveau_device *device)
case 0xa5:
device->cname = "GT216";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -380,9 +393,9 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nva3_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nva3_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -396,8 +409,9 @@ nv50_identify(struct nouveau_device *device)
case 0xa8:
device->cname = "GT218";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -409,9 +423,9 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nva3_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nva3_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
@@ -425,8 +439,9 @@ nv50_identify(struct nouveau_device *device)
case 0xaf:
device->cname = "MCP89";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &g80_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -438,9 +453,9 @@ nv50_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nv50_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nv50_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nva3_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nva3_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv50_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nv50_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv84_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nv50_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
index 8d55ed633b19..cd05677ad4b7 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
@@ -26,6 +26,7 @@
#include <subdev/bus.h>
#include <subdev/gpio.h>
#include <subdev/i2c.h>
+#include <subdev/fuse.h>
#include <subdev/clock.h>
#include <subdev/therm.h>
#include <subdev/mxm.h>
@@ -33,7 +34,7 @@
#include <subdev/mc.h>
#include <subdev/timer.h>
#include <subdev/fb.h>
-#include <subdev/ltcg.h>
+#include <subdev/ltc.h>
#include <subdev/ibus.h>
#include <subdev/instmem.h>
#include <subdev/vm.h>
@@ -60,8 +61,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xc0:
device->cname = "GF100";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -70,14 +72,14 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvc0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nvc0_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvc0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nvc0_graph_oclass;
@@ -92,8 +94,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xc4:
device->cname = "GF104";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -102,14 +105,14 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvc0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nvc0_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvc0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nvc4_graph_oclass;
@@ -124,8 +127,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xc3:
device->cname = "GF106";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -134,14 +138,14 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvc0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nvc0_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvc0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nvc4_graph_oclass;
@@ -155,8 +159,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xce:
device->cname = "GF114";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -165,14 +170,14 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvc0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nvc0_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvc0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nvc4_graph_oclass;
@@ -187,8 +192,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xcf:
device->cname = "GF116";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -197,14 +203,14 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvc0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nvc0_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvc0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nvc4_graph_oclass;
@@ -219,8 +225,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xc1:
device->cname = "GF108";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -229,14 +236,14 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvc0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nvc0_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvc0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nvc1_graph_oclass;
@@ -250,8 +257,9 @@ nvc0_identify(struct nouveau_device *device)
case 0xc8:
device->cname = "GF110";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv94_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -260,14 +268,14 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvc0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nvc0_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvc0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nvc8_graph_oclass;
@@ -284,6 +292,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -292,14 +301,14 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvd0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nvd0_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nvd9_graph_oclass;
@@ -315,6 +324,7 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nvd0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = gf117_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -323,12 +333,12 @@ nvc0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nvc0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gf100_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nvc0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nvc0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nvd7_graph_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c
index 2d1e97d4264f..b1b2e484ecfa 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c
@@ -26,6 +26,7 @@
#include <subdev/bus.h>
#include <subdev/gpio.h>
#include <subdev/i2c.h>
+#include <subdev/fuse.h>
#include <subdev/clock.h>
#include <subdev/therm.h>
#include <subdev/mxm.h>
@@ -33,7 +34,7 @@
#include <subdev/mc.h>
#include <subdev/timer.h>
#include <subdev/fb.h>
-#include <subdev/ltcg.h>
+#include <subdev/ltc.h>
#include <subdev/ibus.h>
#include <subdev/instmem.h>
#include <subdev/vm.h>
@@ -62,6 +63,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -70,14 +72,14 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvd0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = gk104_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nve0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nve4_graph_oclass;
@@ -95,6 +97,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -103,14 +106,14 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvd0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nvd0_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nve0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nve4_graph_oclass;
@@ -128,6 +131,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -136,14 +140,14 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvd0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = gk104_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nve0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nve4_graph_oclass;
@@ -158,15 +162,18 @@ nve0_identify(struct nouveau_device *device)
break;
case 0xea:
device->cname = "GK20A";
- device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass;
+ device->oclass[NVDEV_SUBDEV_CLOCK ] = &gk20a_clock_oclass;
+ device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass;
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = gk20a_fb_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &gk20a_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
- device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+ device->oclass[NVDEV_SUBDEV_BAR ] = &gk20a_bar_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = gk20a_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = gk20a_graph_oclass;
@@ -178,6 +185,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -186,14 +194,14 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvd0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nvd0_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nve0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nvf0_graph_oclass;
@@ -211,6 +219,7 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -219,17 +228,17 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nvd0_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nvd0_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nve0_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
- device->oclass[NVDEV_ENGINE_GR ] = nvf0_graph_oclass;
+ device->oclass[NVDEV_ENGINE_GR ] = gk110b_graph_oclass;
device->oclass[NVDEV_ENGINE_DISP ] = nvf0_disp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass;
@@ -244,22 +253,23 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass;
device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nvc0_devinit_oclass;
- device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass;
+ device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass;
device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass;
device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass;
- device->oclass[NVDEV_SUBDEV_LTCG ] = gf100_ltcg_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass;
device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass;
device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass;
device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass;
- device->oclass[NVDEV_SUBDEV_PWR ] = &nv108_pwr_oclass;
+ device->oclass[NVDEV_SUBDEV_PWR ] = nv108_pwr_oclass;
device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
- device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass;
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass;
device->oclass[NVDEV_ENGINE_FIFO ] = nv108_fifo_oclass;
device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
device->oclass[NVDEV_ENGINE_GR ] = nv108_graph_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/base.c b/drivers/gpu/drm/nouveau/core/engine/disp/base.c
index 9c38c5e40500..64b84667f3a5 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/base.c
@@ -22,23 +22,95 @@
* Authors: Ben Skeggs
*/
+#include <core/os.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
+#include <nvif/event.h>
+
#include "priv.h"
#include "outp.h"
#include "conn.h"
+int
+nouveau_disp_vblank_ctor(struct nouveau_object *object, void *data, u32 size,
+ struct nvkm_notify *notify)
+{
+ struct nouveau_disp *disp =
+ container_of(notify->event, typeof(*disp), vblank);
+ union {
+ struct nvif_notify_head_req_v0 v0;
+ } *req = data;
+ int ret;
+
+ if (nvif_unpack(req->v0, 0, 0, false)) {
+ notify->size = sizeof(struct nvif_notify_head_rep_v0);
+ if (ret = -ENXIO, req->v0.head <= disp->vblank.index_nr) {
+ notify->types = 1;
+ notify->index = req->v0.head;
+ return 0;
+ }
+ }
+
+ return ret;
+}
+
+void
+nouveau_disp_vblank(struct nouveau_disp *disp, int head)
+{
+ struct nvif_notify_head_rep_v0 rep = {};
+ nvkm_event_send(&disp->vblank, 1, head, &rep, sizeof(rep));
+}
+
static int
-nouveau_disp_hpd_check(struct nouveau_event *event, u32 types, int index)
+nouveau_disp_hpd_ctor(struct nouveau_object *object, void *data, u32 size,
+ struct nvkm_notify *notify)
{
- struct nouveau_disp *disp = event->priv;
+ struct nouveau_disp *disp =
+ container_of(notify->event, typeof(*disp), hpd);
+ union {
+ struct nvif_notify_conn_req_v0 v0;
+ } *req = data;
struct nvkm_output *outp;
- list_for_each_entry(outp, &disp->outp, head) {
- if (outp->conn->index == index) {
- if (outp->conn->hpd.event)
- return 0;
- break;
+ int ret;
+
+ if (nvif_unpack(req->v0, 0, 0, false)) {
+ notify->size = sizeof(struct nvif_notify_conn_rep_v0);
+ list_for_each_entry(outp, &disp->outp, head) {
+ if (ret = -ENXIO, outp->conn->index == req->v0.conn) {
+ if (ret = -ENODEV, outp->conn->hpd.event) {
+ notify->types = req->v0.mask;
+ notify->index = req->v0.conn;
+ ret = 0;
+ }
+ break;
+ }
}
}
- return -ENOSYS;
+
+ return ret;
+}
+
+static const struct nvkm_event_func
+nouveau_disp_hpd_func = {
+ .ctor = nouveau_disp_hpd_ctor
+};
+
+int
+nouveau_disp_ntfy(struct nouveau_object *object, u32 type,
+ struct nvkm_event **event)
+{
+ struct nouveau_disp *disp = (void *)object->engine;
+ switch (type) {
+ case NV04_DISP_NTFY_VBLANK:
+ *event = &disp->vblank;
+ return 0;
+ case NV04_DISP_NTFY_CONN:
+ *event = &disp->hpd;
+ return 0;
+ default:
+ break;
+ }
+ return -EINVAL;
}
int
@@ -97,7 +169,8 @@ _nouveau_disp_dtor(struct nouveau_object *object)
struct nouveau_disp *disp = (void *)object;
struct nvkm_output *outp, *outt;
- nouveau_event_destroy(&disp->vblank);
+ nvkm_event_fini(&disp->vblank);
+ nvkm_event_fini(&disp->hpd);
if (disp->outp.next) {
list_for_each_entry_safe(outp, outt, &disp->outp, head) {
@@ -157,14 +230,11 @@ nouveau_disp_create_(struct nouveau_object *parent,
hpd = max(hpd, (u8)(dcbE.connector + 1));
}
- ret = nouveau_event_create(3, hpd, &disp->hpd);
+ ret = nvkm_event_init(&nouveau_disp_hpd_func, 3, hpd, &disp->hpd);
if (ret)
return ret;
- disp->hpd->priv = disp;
- disp->hpd->check = nouveau_disp_hpd_check;
-
- ret = nouveau_event_create(1, heads, &disp->vblank);
+ ret = nvkm_event_init(impl->vblank, 1, heads, &disp->vblank);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/conn.c b/drivers/gpu/drm/nouveau/core/engine/disp/conn.c
index 4ffbc70ecf5a..1496b567dd4a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/conn.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/conn.c
@@ -22,39 +22,41 @@
* Authors: Ben Skeggs
*/
+#include <core/os.h>
+#include <nvif/event.h>
+
#include <subdev/gpio.h>
#include "conn.h"
#include "outp.h"
-static void
-nvkm_connector_hpd_work(struct work_struct *w)
+static int
+nvkm_connector_hpd(struct nvkm_notify *notify)
{
- struct nvkm_connector *conn = container_of(w, typeof(*conn), hpd.work);
+ struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd);
struct nouveau_disp *disp = nouveau_disp(conn);
struct nouveau_gpio *gpio = nouveau_gpio(conn);
- u32 send = NVKM_HPD_UNPLUG;
- if (gpio->get(gpio, 0, DCB_GPIO_UNUSED, conn->hpd.event->index))
- send = NVKM_HPD_PLUG;
- nouveau_event_trigger(disp->hpd, send, conn->index);
- nouveau_event_get(conn->hpd.event);
-}
+ const struct nvkm_gpio_ntfy_rep *line = notify->data;
+ struct nvif_notify_conn_rep_v0 rep;
+ int index = conn->index;
-static int
-nvkm_connector_hpd(void *data, u32 type, int index)
-{
- struct nvkm_connector *conn = data;
- DBG("HPD: %d\n", type);
- schedule_work(&conn->hpd.work);
- return NVKM_EVENT_DROP;
+ DBG("HPD: %d\n", line->mask);
+
+ if (!gpio->get(gpio, 0, DCB_GPIO_UNUSED, conn->hpd.index))
+ rep.mask = NVIF_NOTIFY_CONN_V0_UNPLUG;
+ else
+ rep.mask = NVIF_NOTIFY_CONN_V0_PLUG;
+ rep.version = 0;
+
+ nvkm_event_send(&disp->hpd, rep.mask, index, &rep, sizeof(rep));
+ return NVKM_NOTIFY_KEEP;
}
int
_nvkm_connector_fini(struct nouveau_object *object, bool suspend)
{
struct nvkm_connector *conn = (void *)object;
- if (conn->hpd.event)
- nouveau_event_put(conn->hpd.event);
+ nvkm_notify_put(&conn->hpd);
return nouveau_object_fini(&conn->base, suspend);
}
@@ -63,10 +65,8 @@ _nvkm_connector_init(struct nouveau_object *object)
{
struct nvkm_connector *conn = (void *)object;
int ret = nouveau_object_init(&conn->base);
- if (ret == 0) {
- if (conn->hpd.event)
- nouveau_event_get(conn->hpd.event);
- }
+ if (ret == 0)
+ nvkm_notify_get(&conn->hpd);
return ret;
}
@@ -74,7 +74,7 @@ void
_nvkm_connector_dtor(struct nouveau_object *object)
{
struct nvkm_connector *conn = (void *)object;
- nouveau_event_ref(NULL, &conn->hpd.event);
+ nvkm_notify_fini(&conn->hpd);
nouveau_object_destroy(&conn->base);
}
@@ -116,19 +116,24 @@ nvkm_connector_create_(struct nouveau_object *parent,
if ((info->hpd = ffs(info->hpd))) {
if (--info->hpd >= ARRAY_SIZE(hpd)) {
ERR("hpd %02x unknown\n", info->hpd);
- goto done;
+ return 0;
}
info->hpd = hpd[info->hpd];
ret = gpio->find(gpio, 0, info->hpd, DCB_GPIO_UNUSED, &func);
if (ret) {
ERR("func %02x lookup failed, %d\n", info->hpd, ret);
- goto done;
+ return 0;
}
- ret = nouveau_event_new(gpio->events, NVKM_GPIO_TOGGLED,
- func.line, nvkm_connector_hpd,
- conn, &conn->hpd.event);
+ ret = nvkm_notify_init(NULL, &gpio->event, nvkm_connector_hpd,
+ true, &(struct nvkm_gpio_ntfy_req) {
+ .mask = NVKM_GPIO_TOGGLED,
+ .line = func.line,
+ },
+ sizeof(struct nvkm_gpio_ntfy_req),
+ sizeof(struct nvkm_gpio_ntfy_rep),
+ &conn->hpd);
if (ret) {
ERR("func %02x failed, %d\n", info->hpd, ret);
} else {
@@ -136,8 +141,6 @@ nvkm_connector_create_(struct nouveau_object *parent,
}
}
-done:
- INIT_WORK(&conn->hpd.work, nvkm_connector_hpd_work);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/conn.h b/drivers/gpu/drm/nouveau/core/engine/disp/conn.h
index 035ebeacbb1c..55e5f5c82c14 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/conn.h
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/conn.h
@@ -10,10 +10,7 @@ struct nvkm_connector {
struct nvbios_connE info;
int index;
- struct {
- struct nouveau_eventh *event;
- struct work_struct work;
- } hpd;
+ struct nvkm_notify hpd;
};
#define nvkm_connector_create(p,e,c,b,i,d) \
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c
index a66b27c0fcab..b36addff06a9 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c
@@ -22,8 +22,9 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
@@ -32,13 +33,28 @@
#include "nv50.h"
int
-nv50_dac_power(struct nv50_disp_priv *priv, int or, u32 data)
+nv50_dac_power(NV50_DISP_MTHD_V1)
{
- const u32 stat = (data & NV50_DISP_DAC_PWR_HSYNC) |
- (data & NV50_DISP_DAC_PWR_VSYNC) |
- (data & NV50_DISP_DAC_PWR_DATA) |
- (data & NV50_DISP_DAC_PWR_STATE);
- const u32 doff = (or * 0x800);
+ const u32 doff = outp->or * 0x800;
+ union {
+ struct nv50_disp_dac_pwr_v0 v0;
+ } *args = data;
+ u32 stat;
+ int ret;
+
+ nv_ioctl(object, "disp dac pwr size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp dac pwr vers %d state %d data %d "
+ "vsync %d hsync %d\n",
+ args->v0.version, args->v0.state, args->v0.data,
+ args->v0.vsync, args->v0.hsync);
+ stat = 0x00000040 * !args->v0.state;
+ stat |= 0x00000010 * !args->v0.data;
+ stat |= 0x00000004 * !args->v0.vsync;
+ stat |= 0x00000001 * !args->v0.hsync;
+ } else
+ return ret;
+
nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000);
nv_mask(priv, 0x61a004 + doff, 0xc000007f, 0x80000000 | stat);
nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000);
@@ -46,9 +62,24 @@ nv50_dac_power(struct nv50_disp_priv *priv, int or, u32 data)
}
int
-nv50_dac_sense(struct nv50_disp_priv *priv, int or, u32 loadval)
+nv50_dac_sense(NV50_DISP_MTHD_V1)
{
- const u32 doff = (or * 0x800);
+ union {
+ struct nv50_disp_dac_load_v0 v0;
+ } *args = data;
+ const u32 doff = outp->or * 0x800;
+ u32 loadval;
+ int ret;
+
+ nv_ioctl(object, "disp dac load size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp dac load vers %d data %08x\n",
+ args->v0.version, args->v0.data);
+ if (args->v0.data & 0xfff00000)
+ return -EINVAL;
+ loadval = args->v0.data;
+ } else
+ return ret;
nv_mask(priv, 0x61a004 + doff, 0x807f0000, 0x80150000);
nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000);
@@ -61,38 +92,10 @@ nv50_dac_sense(struct nv50_disp_priv *priv, int or, u32 loadval)
nv_mask(priv, 0x61a004 + doff, 0x807f0000, 0x80550000);
nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000);
- nv_debug(priv, "DAC%d sense: 0x%08x\n", or, loadval);
+ nv_debug(priv, "DAC%d sense: 0x%08x\n", outp->or, loadval);
if (!(loadval & 0x80000000))
return -ETIMEDOUT;
- return (loadval & 0x38000000) >> 27;
-}
-
-int
-nv50_dac_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
-{
- struct nv50_disp_priv *priv = (void *)object->engine;
- const u8 or = (mthd & NV50_DISP_DAC_MTHD_OR);
- u32 *data = args;
- int ret;
-
- if (size < sizeof(u32))
- return -EINVAL;
-
- switch (mthd & ~0x3f) {
- case NV50_DISP_DAC_PWR:
- ret = priv->dac.power(priv, or, data[0]);
- break;
- case NV50_DISP_DAC_LOAD:
- ret = priv->dac.sense(priv, or, data[0]);
- if (ret >= 0) {
- data[0] = ret;
- ret = 0;
- }
- break;
- default:
- BUG_ON(1);
- }
-
- return ret;
+ args->v0.load = (loadval & 0x38000000) >> 27;
+ return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
index 5a5b59b21130..39890221b91c 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
@@ -30,7 +30,7 @@
#include <engine/disp.h>
-#include <core/class.h>
+#include <nvif/class.h>
#include "dport.h"
#include "outpdp.h"
@@ -335,7 +335,7 @@ nouveau_dp_train(struct work_struct *w)
int ret;
/* bring capabilities within encoder limits */
- if (nv_mclass(disp) < NVD0_DISP_CLASS)
+ if (nv_mclass(disp) < GF110_DISP)
outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED;
if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) {
outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT;
@@ -354,7 +354,7 @@ nouveau_dp_train(struct work_struct *w)
cfg--;
/* disable link interrupt handling during link training */
- nouveau_event_put(outp->irq);
+ nvkm_notify_put(&outp->irq);
/* enable down-spreading and execute pre-train script from vbios */
dp_link_train_init(dp, outp->dpcd[3] & 0x01);
@@ -395,5 +395,5 @@ nouveau_dp_train(struct work_struct *w)
DBG("training complete\n");
atomic_set(&outp->lt.done, 1);
wake_up(&outp->lt.wait);
- nouveau_event_get(outp->irq);
+ nvkm_notify_get(&outp->irq);
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c
index 9fc7447fec90..b3df3fe2dc09 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c
@@ -25,7 +25,7 @@
#include <engine/software.h>
#include <engine/disp.h>
-#include <core/class.h>
+#include <nvif/class.h>
#include "nv50.h"
@@ -35,17 +35,17 @@
static struct nouveau_oclass
gm107_disp_sclass[] = {
- { GM107_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs },
- { GM107_DISP_SYNC_CLASS, &nvd0_disp_sync_ofuncs },
- { GM107_DISP_OVLY_CLASS, &nvd0_disp_ovly_ofuncs },
- { GM107_DISP_OIMM_CLASS, &nvd0_disp_oimm_ofuncs },
- { GM107_DISP_CURS_CLASS, &nvd0_disp_curs_ofuncs },
+ { GM107_DISP_CORE_CHANNEL_DMA, &nvd0_disp_mast_ofuncs.base },
+ { GK110_DISP_BASE_CHANNEL_DMA, &nvd0_disp_sync_ofuncs.base },
+ { GK104_DISP_OVERLAY_CONTROL_DMA, &nvd0_disp_ovly_ofuncs.base },
+ { GK104_DISP_OVERLAY, &nvd0_disp_oimm_ofuncs.base },
+ { GK104_DISP_CURSOR, &nvd0_disp_curs_ofuncs.base },
{}
};
static struct nouveau_oclass
gm107_disp_base_oclass[] = {
- { GM107_DISP_CLASS, &nvd0_disp_base_ofuncs, nvd0_disp_base_omthds },
+ { GM107_DISP, &nvd0_disp_base_ofuncs },
{}
};
@@ -68,6 +68,10 @@ gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent);
+ if (ret)
+ return ret;
+
nv_engine(priv)->sclass = gm107_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nvd0_disp_intr;
@@ -80,7 +84,7 @@ gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld;
- priv->sor.hdmi = nvd0_hdmi_ctrl;
+ priv->sor.hdmi = nve0_hdmi_ctrl;
return 0;
}
@@ -93,9 +97,11 @@ gm107_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .base.vblank = &nvd0_disp_vblank_func,
.base.outp = nvd0_disp_outp_sclass,
.mthd.core = &nve0_disp_mast_mthd_chan,
.mthd.base = &nvd0_disp_sync_mthd_chan,
.mthd.ovly = &nve0_disp_ovly_mthd_chan,
.mthd.prev = -0x020000,
+ .head.scanoutpos = nvd0_disp_base_scanoutpos,
}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c
index a19e7d79b847..fe9ef5894dd4 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c
@@ -22,28 +22,47 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
+
+#include <subdev/timer.h>
#include "nv50.h"
int
-nva3_hda_eld(struct nv50_disp_priv *priv, int or, u8 *data, u32 size)
+nva3_hda_eld(NV50_DISP_MTHD_V1)
{
- const u32 soff = (or * 0x800);
- int i;
+ union {
+ struct nv50_disp_sor_hda_eld_v0 v0;
+ } *args = data;
+ const u32 soff = outp->or * 0x800;
+ int ret, i;
+
+ nv_ioctl(object, "disp sor hda eld size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ nv_ioctl(object, "disp sor hda eld vers %d\n", args->v0.version);
+ if (size > 0x60)
+ return -E2BIG;
+ } else
+ return ret;
- if (data && data[0]) {
+ if (size && args->v0.data[0]) {
+ if (outp->info.type == DCB_OUTPUT_DP) {
+ nv_mask(priv, 0x61c1e0 + soff, 0x8000000d, 0x80000001);
+ nv_wait(priv, 0x61c1e0 + soff, 0x80000000, 0x00000000);
+ }
for (i = 0; i < size; i++)
- nv_wr32(priv, 0x61c440 + soff, (i << 8) | data[i]);
+ nv_wr32(priv, 0x61c440 + soff, (i << 8) | args->v0.data[0]);
for (; i < 0x60; i++)
nv_wr32(priv, 0x61c440 + soff, (i << 8));
nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000003);
- } else
- if (data) {
- nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000001);
} else {
- nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000000);
+ if (outp->info.type == DCB_OUTPUT_DP) {
+ nv_mask(priv, 0x61c1e0 + soff, 0x80000001, 0x80000000);
+ nv_wait(priv, 0x61c1e0 + soff, 0x80000000, 0x00000000);
+ }
+ nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000000 | !!size);
}
return 0;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c
index 717639386ced..1d4e8432d857 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c
@@ -22,33 +22,49 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
-#include <subdev/bios.h>
-#include <subdev/bios/dcb.h>
-#include <subdev/bios/dp.h>
-#include <subdev/bios/init.h>
+#include <subdev/timer.h>
#include "nv50.h"
int
-nvd0_hda_eld(struct nv50_disp_priv *priv, int or, u8 *data, u32 size)
+nvd0_hda_eld(NV50_DISP_MTHD_V1)
{
- const u32 soff = (or * 0x030);
- int i;
+ union {
+ struct nv50_disp_sor_hda_eld_v0 v0;
+ } *args = data;
+ const u32 soff = outp->or * 0x030;
+ const u32 hoff = head * 0x800;
+ int ret, i;
- if (data && data[0]) {
+ nv_ioctl(object, "disp sor hda eld size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ nv_ioctl(object, "disp sor hda eld vers %d\n", args->v0.version);
+ if (size > 0x60)
+ return -E2BIG;
+ } else
+ return ret;
+
+ if (size && args->v0.data[0]) {
+ if (outp->info.type == DCB_OUTPUT_DP) {
+ nv_mask(priv, 0x616618 + hoff, 0x8000000c, 0x80000001);
+ nv_wait(priv, 0x616618 + hoff, 0x80000000, 0x00000000);
+ }
+ nv_mask(priv, 0x616548 + hoff, 0x00000070, 0x00000000);
for (i = 0; i < size; i++)
- nv_wr32(priv, 0x10ec00 + soff, (i << 8) | data[i]);
+ nv_wr32(priv, 0x10ec00 + soff, (i << 8) | args->v0.data[i]);
for (; i < 0x60; i++)
nv_wr32(priv, 0x10ec00 + soff, (i << 8));
nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000003);
- } else
- if (data) {
- nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000001);
} else {
- nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000000);
+ if (outp->info.type == DCB_OUTPUT_DP) {
+ nv_mask(priv, 0x616618 + hoff, 0x80000001, 0x80000000);
+ nv_wait(priv, 0x616618 + hoff, 0x80000000, 0x00000000);
+ }
+ nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000000 | !!size);
}
return 0;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdminv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdminv84.c
index 7fdade6e604d..fa276dede9cd 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/hdminv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdminv84.c
@@ -22,17 +22,38 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include "nv50.h"
int
-nv84_hdmi_ctrl(struct nv50_disp_priv *priv, int head, int or, u32 data)
+nv84_hdmi_ctrl(NV50_DISP_MTHD_V1)
{
const u32 hoff = (head * 0x800);
+ union {
+ struct nv50_disp_sor_hdmi_pwr_v0 v0;
+ } *args = data;
+ u32 ctrl;
+ int ret;
- if (!(data & NV84_DISP_SOR_HDMI_PWR_STATE_ON)) {
+ nv_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
+ "max_ac_packet %d rekey %d\n",
+ args->v0.version, args->v0.state,
+ args->v0.max_ac_packet, args->v0.rekey);
+ if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
+ return -EINVAL;
+ ctrl = 0x40000000 * !!args->v0.state;
+ ctrl |= args->v0.max_ac_packet << 16;
+ ctrl |= args->v0.rekey;
+ ctrl |= 0x1f000000; /* ??? */
+ } else
+ return ret;
+
+ if (!(ctrl & 0x40000000)) {
nv_mask(priv, 0x6165a4 + hoff, 0x40000000, 0x00000000);
nv_mask(priv, 0x616520 + hoff, 0x00000001, 0x00000000);
nv_mask(priv, 0x616500 + hoff, 0x00000001, 0x00000000);
@@ -65,6 +86,6 @@ nv84_hdmi_ctrl(struct nv50_disp_priv *priv, int head, int or, u32 data)
nv_mask(priv, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */
/* HDMI_CTRL */
- nv_mask(priv, 0x6165a4 + hoff, 0x5f1f007f, data | 0x1f000000 /* ??? */);
+ nv_mask(priv, 0x6165a4 + hoff, 0x5f1f007f, ctrl);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c
index db8c6fd46278..57eeed1d1942 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c
@@ -22,17 +22,38 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include "nv50.h"
int
-nva3_hdmi_ctrl(struct nv50_disp_priv *priv, int head, int or, u32 data)
+nva3_hdmi_ctrl(NV50_DISP_MTHD_V1)
{
- const u32 soff = (or * 0x800);
+ const u32 soff = outp->or * 0x800;
+ union {
+ struct nv50_disp_sor_hdmi_pwr_v0 v0;
+ } *args = data;
+ u32 ctrl;
+ int ret;
- if (!(data & NV84_DISP_SOR_HDMI_PWR_STATE_ON)) {
+ nv_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
+ "max_ac_packet %d rekey %d\n",
+ args->v0.version, args->v0.state,
+ args->v0.max_ac_packet, args->v0.rekey);
+ if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
+ return -EINVAL;
+ ctrl = 0x40000000 * !!args->v0.state;
+ ctrl |= args->v0.max_ac_packet << 16;
+ ctrl |= args->v0.rekey;
+ ctrl |= 0x1f000000; /* ??? */
+ } else
+ return ret;
+
+ if (!(ctrl & 0x40000000)) {
nv_mask(priv, 0x61c5a4 + soff, 0x40000000, 0x00000000);
nv_mask(priv, 0x61c520 + soff, 0x00000001, 0x00000000);
nv_mask(priv, 0x61c500 + soff, 0x00000001, 0x00000000);
@@ -65,6 +86,6 @@ nva3_hdmi_ctrl(struct nv50_disp_priv *priv, int head, int or, u32 data)
nv_mask(priv, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */
/* HDMI_CTRL */
- nv_mask(priv, 0x61c5a4 + soff, 0x5f1f007f, data | 0x1f000000 /* ??? */);
+ nv_mask(priv, 0x61c5a4 + soff, 0x5f1f007f, ctrl);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c
index 5151bb261832..bac4fc4570f0 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c
@@ -22,17 +22,37 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include "nv50.h"
int
-nvd0_hdmi_ctrl(struct nv50_disp_priv *priv, int head, int or, u32 data)
+nvd0_hdmi_ctrl(NV50_DISP_MTHD_V1)
{
const u32 hoff = (head * 0x800);
+ union {
+ struct nv50_disp_sor_hdmi_pwr_v0 v0;
+ } *args = data;
+ u32 ctrl;
+ int ret;
- if (!(data & NV84_DISP_SOR_HDMI_PWR_STATE_ON)) {
+ nv_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
+ "max_ac_packet %d rekey %d\n",
+ args->v0.version, args->v0.state,
+ args->v0.max_ac_packet, args->v0.rekey);
+ if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
+ return -EINVAL;
+ ctrl = 0x40000000 * !!args->v0.state;
+ ctrl |= args->v0.max_ac_packet << 16;
+ ctrl |= args->v0.rekey;
+ } else
+ return ret;
+
+ if (!(ctrl & 0x40000000)) {
nv_mask(priv, 0x616798 + hoff, 0x40000000, 0x00000000);
nv_mask(priv, 0x6167a4 + hoff, 0x00000001, 0x00000000);
nv_mask(priv, 0x616714 + hoff, 0x00000001, 0x00000000);
@@ -54,9 +74,6 @@ nvd0_hdmi_ctrl(struct nv50_disp_priv *priv, int head, int or, u32 data)
nv_mask(priv, 0x6167a4 + hoff, 0x00000001, 0x00000001);
/* HDMI_CTRL */
- nv_mask(priv, 0x616798 + hoff, 0x401f007f, data);
-
- /* NFI, audio doesn't work without it though.. */
- nv_mask(priv, 0x616548 + hoff, 0x00000070, 0x00000000);
+ nv_mask(priv, 0x616798 + hoff, 0x401f007f, ctrl);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdminve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdminve0.c
new file mode 100644
index 000000000000..528d14ec2f7f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdminve0.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
+
+#include "nv50.h"
+
+int
+nve0_hdmi_ctrl(NV50_DISP_MTHD_V1)
+{
+ const u32 hoff = (head * 0x800);
+ const u32 hdmi = (head * 0x400);
+ union {
+ struct nv50_disp_sor_hdmi_pwr_v0 v0;
+ } *args = data;
+ u32 ctrl;
+ int ret;
+
+ nv_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
+ "max_ac_packet %d rekey %d\n",
+ args->v0.version, args->v0.state,
+ args->v0.max_ac_packet, args->v0.rekey);
+ if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
+ return -EINVAL;
+ ctrl = 0x40000000 * !!args->v0.state;
+ ctrl |= args->v0.max_ac_packet << 16;
+ ctrl |= args->v0.rekey;
+ } else
+ return ret;
+
+ if (!(ctrl & 0x40000000)) {
+ nv_mask(priv, 0x616798 + hoff, 0x40000000, 0x00000000);
+ nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000000);
+ nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000000);
+ return 0;
+ }
+
+ /* AVI InfoFrame */
+ nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000000);
+ nv_wr32(priv, 0x690008 + hdmi, 0x000d0282);
+ nv_wr32(priv, 0x69000c + hdmi, 0x0000006f);
+ nv_wr32(priv, 0x690010 + hdmi, 0x00000000);
+ nv_wr32(priv, 0x690014 + hdmi, 0x00000000);
+ nv_wr32(priv, 0x690018 + hdmi, 0x00000000);
+ nv_mask(priv, 0x690000 + hdmi, 0x00000001, 0x00000001);
+
+ /* ??? InfoFrame? */
+ nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000000);
+ nv_wr32(priv, 0x6900cc + hdmi, 0x00000010);
+ nv_mask(priv, 0x6900c0 + hdmi, 0x00000001, 0x00000001);
+
+ /* ??? */
+ nv_wr32(priv, 0x690080 + hdmi, 0x82000000);
+
+ /* HDMI_CTRL */
+ nv_mask(priv, 0x616798 + hoff, 0x401f007f, ctrl);
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
index a32666ed0c47..366f315fc9a5 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
@@ -24,60 +24,100 @@
#include "priv.h"
+#include <core/client.h>
#include <core/event.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
struct nv04_disp_priv {
struct nouveau_disp base;
};
static int
-nv04_disp_scanoutpos(struct nouveau_object *object, u32 mthd,
- void *data, u32 size)
+nv04_disp_scanoutpos(struct nouveau_object *object, struct nv04_disp_priv *priv,
+ void *data, u32 size, int head)
{
- struct nv04_disp_priv *priv = (void *)object->engine;
- struct nv04_display_scanoutpos *args = data;
- const int head = (mthd & NV04_DISP_MTHD_HEAD);
+ const u32 hoff = head * 0x2000;
+ union {
+ struct nv04_disp_scanoutpos_v0 v0;
+ } *args = data;
u32 line;
+ int ret;
+
+ nv_ioctl(object, "disp scanoutpos size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp scanoutpos vers %d\n", args->v0.version);
+ args->v0.vblanks = nv_rd32(priv, 0x680800 + hoff) & 0xffff;
+ args->v0.vtotal = nv_rd32(priv, 0x680804 + hoff) & 0xffff;
+ args->v0.vblanke = args->v0.vtotal - 1;
+
+ args->v0.hblanks = nv_rd32(priv, 0x680820 + hoff) & 0xffff;
+ args->v0.htotal = nv_rd32(priv, 0x680824 + hoff) & 0xffff;
+ args->v0.hblanke = args->v0.htotal - 1;
+
+ /*
+ * If output is vga instead of digital then vtotal/htotal is
+ * invalid so we have to give up and trigger the timestamping
+ * fallback in the drm core.
+ */
+ if (!args->v0.vtotal || !args->v0.htotal)
+ return -ENOTSUPP;
+
+ args->v0.time[0] = ktime_to_ns(ktime_get());
+ line = nv_rd32(priv, 0x600868 + hoff);
+ args->v0.time[1] = ktime_to_ns(ktime_get());
+ args->v0.hline = (line & 0xffff0000) >> 16;
+ args->v0.vline = (line & 0x0000ffff);
+ } else
+ return ret;
- if (size < sizeof(*args))
- return -EINVAL;
-
- args->vblanks = nv_rd32(priv, 0x680800 + (head * 0x2000)) & 0xffff;
- args->vtotal = nv_rd32(priv, 0x680804 + (head * 0x2000)) & 0xffff;
- args->vblanke = args->vtotal - 1;
-
- args->hblanks = nv_rd32(priv, 0x680820 + (head * 0x2000)) & 0xffff;
- args->htotal = nv_rd32(priv, 0x680824 + (head * 0x2000)) & 0xffff;
- args->hblanke = args->htotal - 1;
-
- /*
- * If output is vga instead of digital then vtotal/htotal is invalid
- * so we have to give up and trigger the timestamping fallback in the
- * drm core.
- */
- if (!args->vtotal || !args->htotal)
- return -ENOTSUPP;
-
- args->time[0] = ktime_to_ns(ktime_get());
- line = nv_rd32(priv, 0x600868 + (head * 0x2000));
- args->time[1] = ktime_to_ns(ktime_get());
- args->hline = (line & 0xffff0000) >> 16;
- args->vline = (line & 0x0000ffff);
return 0;
}
-#define HEAD_MTHD(n) (n), (n) + 0x01
+static int
+nv04_disp_mthd(struct nouveau_object *object, u32 mthd, void *data, u32 size)
+{
+ union {
+ struct nv04_disp_mthd_v0 v0;
+ } *args = data;
+ struct nv04_disp_priv *priv = (void *)object->engine;
+ int head, ret;
+
+ nv_ioctl(object, "disp mthd size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ nv_ioctl(object, "disp mthd vers %d mthd %02x head %d\n",
+ args->v0.version, args->v0.method, args->v0.head);
+ mthd = args->v0.method;
+ head = args->v0.head;
+ } else
+ return ret;
-static struct nouveau_omthds
-nv04_disp_omthds[] = {
- { HEAD_MTHD(NV04_DISP_SCANOUTPOS), nv04_disp_scanoutpos },
- {}
+ if (head < 0 || head >= 2)
+ return -ENXIO;
+
+ switch (mthd) {
+ case NV04_DISP_SCANOUTPOS:
+ return nv04_disp_scanoutpos(object, priv, data, size, head);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static struct nouveau_ofuncs
+nv04_disp_ofuncs = {
+ .ctor = _nouveau_object_ctor,
+ .dtor = nouveau_object_destroy,
+ .init = nouveau_object_init,
+ .fini = nouveau_object_fini,
+ .mthd = nv04_disp_mthd,
+ .ntfy = nouveau_disp_ntfy,
};
static struct nouveau_oclass
nv04_disp_sclass[] = {
- { NV04_DISP_CLASS, &nouveau_object_ofuncs, nv04_disp_omthds },
+ { NV04_DISP, &nv04_disp_ofuncs },
{},
};
@@ -86,17 +126,26 @@ nv04_disp_sclass[] = {
******************************************************************************/
static void
-nv04_disp_vblank_enable(struct nouveau_event *event, int type, int head)
+nv04_disp_vblank_init(struct nvkm_event *event, int type, int head)
{
- nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000001);
+ struct nouveau_disp *disp = container_of(event, typeof(*disp), vblank);
+ nv_wr32(disp, 0x600140 + (head * 0x2000) , 0x00000001);
}
static void
-nv04_disp_vblank_disable(struct nouveau_event *event, int type, int head)
+nv04_disp_vblank_fini(struct nvkm_event *event, int type, int head)
{
- nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000000);
+ struct nouveau_disp *disp = container_of(event, typeof(*disp), vblank);
+ nv_wr32(disp, 0x600140 + (head * 0x2000) , 0x00000000);
}
+static const struct nvkm_event_func
+nv04_disp_vblank_func = {
+ .ctor = nouveau_disp_vblank_ctor,
+ .init = nv04_disp_vblank_init,
+ .fini = nv04_disp_vblank_fini,
+};
+
static void
nv04_disp_intr(struct nouveau_subdev *subdev)
{
@@ -106,12 +155,12 @@ nv04_disp_intr(struct nouveau_subdev *subdev)
u32 pvideo;
if (crtc0 & 0x00000001) {
- nouveau_event_trigger(priv->base.vblank, 1, 0);
+ nouveau_disp_vblank(&priv->base, 0);
nv_wr32(priv, 0x600100, 0x00000001);
}
if (crtc1 & 0x00000001) {
- nouveau_event_trigger(priv->base.vblank, 1, 1);
+ nouveau_disp_vblank(&priv->base, 1);
nv_wr32(priv, 0x602100, 0x00000001);
}
@@ -140,9 +189,6 @@ nv04_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_engine(priv)->sclass = nv04_disp_sclass;
nv_subdev(priv)->intr = nv04_disp_intr;
- priv->base.vblank->priv = priv;
- priv->base.vblank->enable = nv04_disp_vblank_enable;
- priv->base.vblank->disable = nv04_disp_vblank_disable;
return 0;
}
@@ -155,4 +201,5 @@ nv04_disp_oclass = &(struct nouveau_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .vblank = &nv04_disp_vblank_func,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
index 2283c442a10d..2df3a937037d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
@@ -23,10 +23,13 @@
*/
#include <core/object.h>
+#include <core/client.h>
#include <core/parent.h>
#include <core/handle.h>
-#include <core/class.h>
#include <core/enum.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
+#include <nvif/event.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
@@ -43,14 +46,16 @@
* EVO channel base class
******************************************************************************/
-int
+static int
nv50_disp_chan_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
- struct nouveau_oclass *oclass, int chid,
+ struct nouveau_oclass *oclass, int head,
int length, void **pobject)
{
+ const struct nv50_disp_chan_impl *impl = (void *)oclass->ofuncs;
struct nv50_disp_base *base = (void *)parent;
struct nv50_disp_chan *chan;
+ int chid = impl->chid + head;
int ret;
if (base->chan & (1 << chid))
@@ -63,12 +68,14 @@ nv50_disp_chan_create_(struct nouveau_object *parent,
chan = *pobject;
if (ret)
return ret;
-
chan->chid = chid;
+
+ nv_parent(chan)->object_attach = impl->attach;
+ nv_parent(chan)->object_detach = impl->detach;
return 0;
}
-void
+static void
nv50_disp_chan_destroy(struct nv50_disp_chan *chan)
{
struct nv50_disp_base *base = (void *)nv_object(chan)->parent;
@@ -76,6 +83,81 @@ nv50_disp_chan_destroy(struct nv50_disp_chan *chan)
nouveau_namedb_destroy(&chan->base);
}
+static void
+nv50_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index)
+{
+ struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent);
+ nv_mask(priv, 0x610028, 0x00000001 << index, 0x00000000 << index);
+}
+
+static void
+nv50_disp_chan_uevent_init(struct nvkm_event *event, int types, int index)
+{
+ struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent);
+ nv_mask(priv, 0x610028, 0x00000001 << index, 0x00000001 << index);
+}
+
+void
+nv50_disp_chan_uevent_send(struct nv50_disp_priv *priv, int chid)
+{
+ struct nvif_notify_uevent_rep {
+ } rep;
+
+ nvkm_event_send(&priv->uevent, 1, chid, &rep, sizeof(rep));
+}
+
+int
+nv50_disp_chan_uevent_ctor(struct nouveau_object *object, void *data, u32 size,
+ struct nvkm_notify *notify)
+{
+ struct nv50_disp_dmac *dmac = (void *)object;
+ union {
+ struct nvif_notify_uevent_req none;
+ } *args = data;
+ int ret;
+
+ if (nvif_unvers(args->none)) {
+ notify->size = sizeof(struct nvif_notify_uevent_rep);
+ notify->types = 1;
+ notify->index = dmac->base.chid;
+ return 0;
+ }
+
+ return ret;
+}
+
+const struct nvkm_event_func
+nv50_disp_chan_uevent = {
+ .ctor = nv50_disp_chan_uevent_ctor,
+ .init = nv50_disp_chan_uevent_init,
+ .fini = nv50_disp_chan_uevent_fini,
+};
+
+int
+nv50_disp_chan_ntfy(struct nouveau_object *object, u32 type,
+ struct nvkm_event **pevent)
+{
+ struct nv50_disp_priv *priv = (void *)object->engine;
+ switch (type) {
+ case NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT:
+ *pevent = &priv->uevent;
+ return 0;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+int
+nv50_disp_chan_map(struct nouveau_object *object, u64 *addr, u32 *size)
+{
+ struct nv50_disp_chan *chan = (void *)object;
+ *addr = nv_device_resource_start(nv_device(object), 0) +
+ 0x640000 + (chan->chid * 0x1000);
+ *size = 0x001000;
+ return 0;
+}
+
u32
nv50_disp_chan_rd32(struct nouveau_object *object, u64 addr)
{
@@ -115,16 +197,16 @@ nv50_disp_dmac_object_detach(struct nouveau_object *parent, int cookie)
nouveau_ramht_remove(base->ramht, cookie);
}
-int
+static int
nv50_disp_dmac_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
- struct nouveau_oclass *oclass, u32 pushbuf, int chid,
+ struct nouveau_oclass *oclass, u32 pushbuf, int head,
int length, void **pobject)
{
struct nv50_disp_dmac *dmac;
int ret;
- ret = nv50_disp_chan_create_(parent, engine, oclass, chid,
+ ret = nv50_disp_chan_create_(parent, engine, oclass, head,
length, pobject);
dmac = *pobject;
if (ret)
@@ -179,7 +261,7 @@ nv50_disp_dmac_init(struct nouveau_object *object)
return ret;
/* enable error reporting */
- nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00010001 << chid);
+ nv_mask(priv, 0x610028, 0x00010000 << chid, 0x00010000 << chid);
/* initialise channel for dma command submission */
nv_wr32(priv, 0x610204 + (chid * 0x0010), dmac->push);
@@ -216,7 +298,7 @@ nv50_disp_dmac_fini(struct nouveau_object *object, bool suspend)
return -EBUSY;
}
- /* disable error reporting */
+ /* disable error reporting and completion notifications */
nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00000000 << chid);
return nv50_disp_chan_fini(&dmac->base, suspend);
@@ -397,27 +479,32 @@ nv50_disp_mast_mthd_chan = {
}
};
-static int
+int
nv50_disp_mast_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
- struct nv50_display_mast_class *args = data;
+ union {
+ struct nv50_disp_core_channel_dma_v0 v0;
+ } *args = data;
struct nv50_disp_dmac *mast;
int ret;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create disp core channel dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create disp core channel dma vers %d "
+ "pushbuf %08x\n",
+ args->v0.version, args->v0.pushbuf);
+ } else
+ return ret;
- ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
+ ret = nv50_disp_dmac_create_(parent, engine, oclass, args->v0.pushbuf,
0, sizeof(*mast), (void **)&mast);
*pobject = nv_object(mast);
if (ret)
return ret;
- nv_parent(mast)->object_attach = nv50_disp_dmac_object_attach;
- nv_parent(mast)->object_detach = nv50_disp_dmac_object_detach;
return 0;
}
@@ -433,7 +520,7 @@ nv50_disp_mast_init(struct nouveau_object *object)
return ret;
/* enable error reporting */
- nv_mask(priv, 0x610028, 0x00010001, 0x00010001);
+ nv_mask(priv, 0x610028, 0x00010000, 0x00010000);
/* attempt to unstick channel from some unknown state */
if ((nv_rd32(priv, 0x610200) & 0x009f0000) == 0x00020000)
@@ -473,20 +560,25 @@ nv50_disp_mast_fini(struct nouveau_object *object, bool suspend)
return -EBUSY;
}
- /* disable error reporting */
+ /* disable error reporting and completion notifications */
nv_mask(priv, 0x610028, 0x00010001, 0x00000000);
return nv50_disp_chan_fini(&mast->base, suspend);
}
-struct nouveau_ofuncs
+struct nv50_disp_chan_impl
nv50_disp_mast_ofuncs = {
- .ctor = nv50_disp_mast_ctor,
- .dtor = nv50_disp_dmac_dtor,
- .init = nv50_disp_mast_init,
- .fini = nv50_disp_mast_fini,
- .rd32 = nv50_disp_chan_rd32,
- .wr32 = nv50_disp_chan_wr32,
+ .base.ctor = nv50_disp_mast_ctor,
+ .base.dtor = nv50_disp_dmac_dtor,
+ .base.init = nv50_disp_mast_init,
+ .base.fini = nv50_disp_mast_fini,
+ .base.map = nv50_disp_chan_map,
+ .base.ntfy = nv50_disp_chan_ntfy,
+ .base.rd32 = nv50_disp_chan_rd32,
+ .base.wr32 = nv50_disp_chan_wr32,
+ .chid = 0,
+ .attach = nv50_disp_dmac_object_attach,
+ .detach = nv50_disp_dmac_object_detach,
};
/*******************************************************************************
@@ -543,39 +635,52 @@ nv50_disp_sync_mthd_chan = {
}
};
-static int
+int
nv50_disp_sync_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
- struct nv50_display_sync_class *args = data;
+ union {
+ struct nv50_disp_base_channel_dma_v0 v0;
+ } *args = data;
+ struct nv50_disp_priv *priv = (void *)engine;
struct nv50_disp_dmac *dmac;
int ret;
- if (size < sizeof(*args) || args->head > 1)
- return -EINVAL;
+ nv_ioctl(parent, "create disp base channel dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create disp base channel dma vers %d "
+ "pushbuf %08x head %d\n",
+ args->v0.version, args->v0.pushbuf, args->v0.head);
+ if (args->v0.head > priv->head.nr)
+ return -EINVAL;
+ } else
+ return ret;
- ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
- 1 + args->head, sizeof(*dmac),
+ ret = nv50_disp_dmac_create_(parent, engine, oclass, args->v0.pushbuf,
+ args->v0.head, sizeof(*dmac),
(void **)&dmac);
*pobject = nv_object(dmac);
if (ret)
return ret;
- nv_parent(dmac)->object_attach = nv50_disp_dmac_object_attach;
- nv_parent(dmac)->object_detach = nv50_disp_dmac_object_detach;
return 0;
}
-struct nouveau_ofuncs
+struct nv50_disp_chan_impl
nv50_disp_sync_ofuncs = {
- .ctor = nv50_disp_sync_ctor,
- .dtor = nv50_disp_dmac_dtor,
- .init = nv50_disp_dmac_init,
- .fini = nv50_disp_dmac_fini,
- .rd32 = nv50_disp_chan_rd32,
- .wr32 = nv50_disp_chan_wr32,
+ .base.ctor = nv50_disp_sync_ctor,
+ .base.dtor = nv50_disp_dmac_dtor,
+ .base.init = nv50_disp_dmac_init,
+ .base.fini = nv50_disp_dmac_fini,
+ .base.ntfy = nv50_disp_chan_ntfy,
+ .base.map = nv50_disp_chan_map,
+ .base.rd32 = nv50_disp_chan_rd32,
+ .base.wr32 = nv50_disp_chan_wr32,
+ .chid = 1,
+ .attach = nv50_disp_dmac_object_attach,
+ .detach = nv50_disp_dmac_object_detach,
};
/*******************************************************************************
@@ -620,39 +725,52 @@ nv50_disp_ovly_mthd_chan = {
}
};
-static int
+int
nv50_disp_ovly_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
- struct nv50_display_ovly_class *args = data;
+ union {
+ struct nv50_disp_overlay_channel_dma_v0 v0;
+ } *args = data;
+ struct nv50_disp_priv *priv = (void *)engine;
struct nv50_disp_dmac *dmac;
int ret;
- if (size < sizeof(*args) || args->head > 1)
- return -EINVAL;
+ nv_ioctl(parent, "create disp overlay channel dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create disp overlay channel dma vers %d "
+ "pushbuf %08x head %d\n",
+ args->v0.version, args->v0.pushbuf, args->v0.head);
+ if (args->v0.head > priv->head.nr)
+ return -EINVAL;
+ } else
+ return ret;
- ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
- 3 + args->head, sizeof(*dmac),
+ ret = nv50_disp_dmac_create_(parent, engine, oclass, args->v0.pushbuf,
+ args->v0.head, sizeof(*dmac),
(void **)&dmac);
*pobject = nv_object(dmac);
if (ret)
return ret;
- nv_parent(dmac)->object_attach = nv50_disp_dmac_object_attach;
- nv_parent(dmac)->object_detach = nv50_disp_dmac_object_detach;
return 0;
}
-struct nouveau_ofuncs
+struct nv50_disp_chan_impl
nv50_disp_ovly_ofuncs = {
- .ctor = nv50_disp_ovly_ctor,
- .dtor = nv50_disp_dmac_dtor,
- .init = nv50_disp_dmac_init,
- .fini = nv50_disp_dmac_fini,
- .rd32 = nv50_disp_chan_rd32,
- .wr32 = nv50_disp_chan_wr32,
+ .base.ctor = nv50_disp_ovly_ctor,
+ .base.dtor = nv50_disp_dmac_dtor,
+ .base.init = nv50_disp_dmac_init,
+ .base.fini = nv50_disp_dmac_fini,
+ .base.ntfy = nv50_disp_chan_ntfy,
+ .base.map = nv50_disp_chan_map,
+ .base.rd32 = nv50_disp_chan_rd32,
+ .base.wr32 = nv50_disp_chan_wr32,
+ .chid = 3,
+ .attach = nv50_disp_dmac_object_attach,
+ .detach = nv50_disp_dmac_object_detach,
};
/*******************************************************************************
@@ -662,14 +780,14 @@ nv50_disp_ovly_ofuncs = {
static int
nv50_disp_pioc_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
- struct nouveau_oclass *oclass, int chid,
+ struct nouveau_oclass *oclass, int head,
int length, void **pobject)
{
- return nv50_disp_chan_create_(parent, engine, oclass, chid,
+ return nv50_disp_chan_create_(parent, engine, oclass, head,
length, pobject);
}
-static void
+void
nv50_disp_pioc_dtor(struct nouveau_object *object)
{
struct nv50_disp_pioc *pioc = (void *)object;
@@ -727,20 +845,29 @@ nv50_disp_pioc_fini(struct nouveau_object *object, bool suspend)
* EVO immediate overlay channel objects
******************************************************************************/
-static int
+int
nv50_disp_oimm_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
- struct nv50_display_oimm_class *args = data;
+ union {
+ struct nv50_disp_overlay_v0 v0;
+ } *args = data;
+ struct nv50_disp_priv *priv = (void *)engine;
struct nv50_disp_pioc *pioc;
int ret;
- if (size < sizeof(*args) || args->head > 1)
- return -EINVAL;
+ nv_ioctl(parent, "create disp overlay size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create disp overlay vers %d head %d\n",
+ args->v0.version, args->v0.head);
+ if (args->v0.head > priv->head.nr)
+ return -EINVAL;
+ } else
+ return ret;
- ret = nv50_disp_pioc_create_(parent, engine, oclass, 5 + args->head,
+ ret = nv50_disp_pioc_create_(parent, engine, oclass, args->v0.head,
sizeof(*pioc), (void **)&pioc);
*pobject = nv_object(pioc);
if (ret)
@@ -749,34 +876,46 @@ nv50_disp_oimm_ctor(struct nouveau_object *parent,
return 0;
}
-struct nouveau_ofuncs
+struct nv50_disp_chan_impl
nv50_disp_oimm_ofuncs = {
- .ctor = nv50_disp_oimm_ctor,
- .dtor = nv50_disp_pioc_dtor,
- .init = nv50_disp_pioc_init,
- .fini = nv50_disp_pioc_fini,
- .rd32 = nv50_disp_chan_rd32,
- .wr32 = nv50_disp_chan_wr32,
+ .base.ctor = nv50_disp_oimm_ctor,
+ .base.dtor = nv50_disp_pioc_dtor,
+ .base.init = nv50_disp_pioc_init,
+ .base.fini = nv50_disp_pioc_fini,
+ .base.ntfy = nv50_disp_chan_ntfy,
+ .base.map = nv50_disp_chan_map,
+ .base.rd32 = nv50_disp_chan_rd32,
+ .base.wr32 = nv50_disp_chan_wr32,
+ .chid = 5,
};
/*******************************************************************************
* EVO cursor channel objects
******************************************************************************/
-static int
+int
nv50_disp_curs_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
- struct nv50_display_curs_class *args = data;
+ union {
+ struct nv50_disp_cursor_v0 v0;
+ } *args = data;
+ struct nv50_disp_priv *priv = (void *)engine;
struct nv50_disp_pioc *pioc;
int ret;
- if (size < sizeof(*args) || args->head > 1)
- return -EINVAL;
+ nv_ioctl(parent, "create disp cursor size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create disp cursor vers %d head %d\n",
+ args->v0.version, args->v0.head);
+ if (args->v0.head > priv->head.nr)
+ return -EINVAL;
+ } else
+ return ret;
- ret = nv50_disp_pioc_create_(parent, engine, oclass, 7 + args->head,
+ ret = nv50_disp_pioc_create_(parent, engine, oclass, args->v0.head,
sizeof(*pioc), (void **)&pioc);
*pobject = nv_object(pioc);
if (ret)
@@ -785,14 +924,17 @@ nv50_disp_curs_ctor(struct nouveau_object *parent,
return 0;
}
-struct nouveau_ofuncs
+struct nv50_disp_chan_impl
nv50_disp_curs_ofuncs = {
- .ctor = nv50_disp_curs_ctor,
- .dtor = nv50_disp_pioc_dtor,
- .init = nv50_disp_pioc_init,
- .fini = nv50_disp_pioc_fini,
- .rd32 = nv50_disp_chan_rd32,
- .wr32 = nv50_disp_chan_wr32,
+ .base.ctor = nv50_disp_curs_ctor,
+ .base.dtor = nv50_disp_pioc_dtor,
+ .base.init = nv50_disp_pioc_init,
+ .base.fini = nv50_disp_pioc_fini,
+ .base.ntfy = nv50_disp_chan_ntfy,
+ .base.map = nv50_disp_chan_map,
+ .base.rd32 = nv50_disp_chan_rd32,
+ .base.wr32 = nv50_disp_chan_wr32,
+ .chid = 7,
};
/*******************************************************************************
@@ -800,47 +942,162 @@ nv50_disp_curs_ofuncs = {
******************************************************************************/
int
-nv50_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd,
- void *data, u32 size)
+nv50_disp_base_scanoutpos(NV50_DISP_MTHD_V0)
{
- struct nv50_disp_priv *priv = (void *)object->engine;
- struct nv04_display_scanoutpos *args = data;
- const int head = (mthd & NV50_DISP_MTHD_HEAD);
- u32 blanke, blanks, total;
+ const u32 blanke = nv_rd32(priv, 0x610aec + (head * 0x540));
+ const u32 blanks = nv_rd32(priv, 0x610af4 + (head * 0x540));
+ const u32 total = nv_rd32(priv, 0x610afc + (head * 0x540));
+ union {
+ struct nv04_disp_scanoutpos_v0 v0;
+ } *args = data;
+ int ret;
+
+ nv_ioctl(object, "disp scanoutpos size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp scanoutpos vers %d\n", args->v0.version);
+ args->v0.vblanke = (blanke & 0xffff0000) >> 16;
+ args->v0.hblanke = (blanke & 0x0000ffff);
+ args->v0.vblanks = (blanks & 0xffff0000) >> 16;
+ args->v0.hblanks = (blanks & 0x0000ffff);
+ args->v0.vtotal = ( total & 0xffff0000) >> 16;
+ args->v0.htotal = ( total & 0x0000ffff);
+ args->v0.time[0] = ktime_to_ns(ktime_get());
+ args->v0.vline = /* vline read locks hline */
+ nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff;
+ args->v0.time[1] = ktime_to_ns(ktime_get());
+ args->v0.hline =
+ nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff;
+ } else
+ return ret;
- if (size < sizeof(*args) || head >= priv->head.nr)
- return -EINVAL;
- blanke = nv_rd32(priv, 0x610aec + (head * 0x540));
- blanks = nv_rd32(priv, 0x610af4 + (head * 0x540));
- total = nv_rd32(priv, 0x610afc + (head * 0x540));
-
- args->vblanke = (blanke & 0xffff0000) >> 16;
- args->hblanke = (blanke & 0x0000ffff);
- args->vblanks = (blanks & 0xffff0000) >> 16;
- args->hblanks = (blanks & 0x0000ffff);
- args->vtotal = ( total & 0xffff0000) >> 16;
- args->htotal = ( total & 0x0000ffff);
-
- args->time[0] = ktime_to_ns(ktime_get());
- args->vline = nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff;
- args->time[1] = ktime_to_ns(ktime_get()); /* vline read locks hline */
- args->hline = nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff;
return 0;
}
-static void
-nv50_disp_base_vblank_enable(struct nouveau_event *event, int type, int head)
+int
+nv50_disp_base_mthd(struct nouveau_object *object, u32 mthd,
+ void *data, u32 size)
{
- nv_mask(event->priv, 0x61002c, (4 << head), (4 << head));
-}
+ const struct nv50_disp_impl *impl = (void *)nv_oclass(object->engine);
+ union {
+ struct nv50_disp_mthd_v0 v0;
+ struct nv50_disp_mthd_v1 v1;
+ } *args = data;
+ struct nv50_disp_priv *priv = (void *)object->engine;
+ struct nvkm_output *outp = NULL;
+ struct nvkm_output *temp;
+ u16 type, mask = 0;
+ int head, ret;
-static void
-nv50_disp_base_vblank_disable(struct nouveau_event *event, int type, int head)
-{
- nv_mask(event->priv, 0x61002c, (4 << head), 0);
+ if (mthd != NV50_DISP_MTHD)
+ return -EINVAL;
+
+ nv_ioctl(object, "disp mthd size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ nv_ioctl(object, "disp mthd vers %d mthd %02x head %d\n",
+ args->v0.version, args->v0.method, args->v0.head);
+ mthd = args->v0.method;
+ head = args->v0.head;
+ } else
+ if (nvif_unpack(args->v1, 1, 1, true)) {
+ nv_ioctl(object, "disp mthd vers %d mthd %02x "
+ "type %04x mask %04x\n",
+ args->v1.version, args->v1.method,
+ args->v1.hasht, args->v1.hashm);
+ mthd = args->v1.method;
+ type = args->v1.hasht;
+ mask = args->v1.hashm;
+ head = ffs((mask >> 8) & 0x0f) - 1;
+ } else
+ return ret;
+
+ if (head < 0 || head >= priv->head.nr)
+ return -ENXIO;
+
+ if (mask) {
+ list_for_each_entry(temp, &priv->base.outp, head) {
+ if ((temp->info.hasht == type) &&
+ (temp->info.hashm & mask) == mask) {
+ outp = temp;
+ break;
+ }
+ }
+ if (outp == NULL)
+ return -ENXIO;
+ }
+
+ switch (mthd) {
+ case NV50_DISP_SCANOUTPOS:
+ return impl->head.scanoutpos(object, priv, data, size, head);
+ default:
+ break;
+ }
+
+ switch (mthd * !!outp) {
+ case NV50_DISP_MTHD_V1_DAC_PWR:
+ return priv->dac.power(object, priv, data, size, head, outp);
+ case NV50_DISP_MTHD_V1_DAC_LOAD:
+ return priv->dac.sense(object, priv, data, size, head, outp);
+ case NV50_DISP_MTHD_V1_SOR_PWR:
+ return priv->sor.power(object, priv, data, size, head, outp);
+ case NV50_DISP_MTHD_V1_SOR_HDA_ELD:
+ if (!priv->sor.hda_eld)
+ return -ENODEV;
+ return priv->sor.hda_eld(object, priv, data, size, head, outp);
+ case NV50_DISP_MTHD_V1_SOR_HDMI_PWR:
+ if (!priv->sor.hdmi)
+ return -ENODEV;
+ return priv->sor.hdmi(object, priv, data, size, head, outp);
+ case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: {
+ union {
+ struct nv50_disp_sor_lvds_script_v0 v0;
+ } *args = data;
+ nv_ioctl(object, "disp sor lvds script size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp sor lvds script "
+ "vers %d name %04x\n",
+ args->v0.version, args->v0.script);
+ priv->sor.lvdsconf = args->v0.script;
+ return 0;
+ } else
+ return ret;
+ }
+ break;
+ case NV50_DISP_MTHD_V1_SOR_DP_PWR: {
+ struct nvkm_output_dp *outpdp = (void *)outp;
+ union {
+ struct nv50_disp_sor_dp_pwr_v0 v0;
+ } *args = data;
+ nv_ioctl(object, "disp sor dp pwr size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp sor dp pwr vers %d state %d\n",
+ args->v0.version, args->v0.state);
+ if (args->v0.state == 0) {
+ nvkm_notify_put(&outpdp->irq);
+ ((struct nvkm_output_dp_impl *)nv_oclass(outp))
+ ->lnk_pwr(outpdp, 0);
+ atomic_set(&outpdp->lt.done, 0);
+ return 0;
+ } else
+ if (args->v0.state != 0) {
+ nvkm_output_dp_train(&outpdp->base, 0, true);
+ return 0;
+ }
+ } else
+ return ret;
+ }
+ break;
+ case NV50_DISP_MTHD_V1_PIOR_PWR:
+ if (!priv->pior.power)
+ return -ENODEV;
+ return priv->pior.power(object, priv, data, size, head, outp);
+ default:
+ break;
+ }
+
+ return -EINVAL;
}
-static int
+int
nv50_disp_base_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -856,14 +1113,11 @@ nv50_disp_base_ctor(struct nouveau_object *parent,
if (ret)
return ret;
- priv->base.vblank->priv = priv;
- priv->base.vblank->enable = nv50_disp_base_vblank_enable;
- priv->base.vblank->disable = nv50_disp_base_vblank_disable;
return nouveau_ramht_new(nv_object(base), nv_object(base), 0x1000, 0,
&base->ramht);
}
-static void
+void
nv50_disp_base_dtor(struct nouveau_object *object)
{
struct nv50_disp_base *base = (void *)object;
@@ -958,34 +1212,23 @@ nv50_disp_base_ofuncs = {
.dtor = nv50_disp_base_dtor,
.init = nv50_disp_base_init,
.fini = nv50_disp_base_fini,
-};
-
-static struct nouveau_omthds
-nv50_disp_base_omthds[] = {
- { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nv50_disp_base_scanoutpos },
- { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd },
- { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
- { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd },
- { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd },
- {},
+ .mthd = nv50_disp_base_mthd,
+ .ntfy = nouveau_disp_ntfy,
};
static struct nouveau_oclass
nv50_disp_base_oclass[] = {
- { NV50_DISP_CLASS, &nv50_disp_base_ofuncs, nv50_disp_base_omthds },
+ { NV50_DISP, &nv50_disp_base_ofuncs },
{}
};
static struct nouveau_oclass
nv50_disp_sclass[] = {
- { NV50_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs },
- { NV50_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs },
- { NV50_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs },
- { NV50_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs },
- { NV50_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs },
+ { NV50_DISP_CORE_CHANNEL_DMA, &nv50_disp_mast_ofuncs.base },
+ { NV50_DISP_BASE_CHANNEL_DMA, &nv50_disp_sync_ofuncs.base },
+ { NV50_DISP_OVERLAY_CHANNEL_DMA, &nv50_disp_ovly_ofuncs.base },
+ { NV50_DISP_OVERLAY, &nv50_disp_oimm_ofuncs.base },
+ { NV50_DISP_CURSOR, &nv50_disp_curs_ofuncs.base },
{}
};
@@ -1005,7 +1248,7 @@ nv50_disp_data_ctor(struct nouveau_object *parent,
int ret = -EBUSY;
/* no context needed for channel objects... */
- if (nv_mclass(parent) != NV_DEVICE_CLASS) {
+ if (nv_mclass(parent) != NV_DEVICE) {
atomic_inc(&parent->refcount);
*pobject = parent;
return 1;
@@ -1040,6 +1283,27 @@ nv50_disp_cclass = {
* Display engine implementation
******************************************************************************/
+static void
+nv50_disp_vblank_fini(struct nvkm_event *event, int type, int head)
+{
+ struct nouveau_disp *disp = container_of(event, typeof(*disp), vblank);
+ nv_mask(disp, 0x61002c, (4 << head), 0);
+}
+
+static void
+nv50_disp_vblank_init(struct nvkm_event *event, int type, int head)
+{
+ struct nouveau_disp *disp = container_of(event, typeof(*disp), vblank);
+ nv_mask(disp, 0x61002c, (4 << head), (4 << head));
+}
+
+const struct nvkm_event_func
+nv50_disp_vblank_func = {
+ .ctor = nouveau_disp_vblank_ctor,
+ .init = nv50_disp_vblank_init,
+ .fini = nv50_disp_vblank_fini,
+};
+
static const struct nouveau_enum
nv50_disp_intr_error_type[] = {
{ 3, "ILLEGAL_MTHD" },
@@ -1366,7 +1630,7 @@ nv50_disp_intr_unk20_1(struct nv50_disp_priv *priv, int head)
}
static void
-nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv,
+nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv, int head,
struct dcb_output *outp, u32 pclk)
{
const int link = !(outp->sorconf.link & 1);
@@ -1375,24 +1639,36 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv,
const u32 loff = (link * 0x080) + soff;
const u32 ctrl = nv_rd32(priv, 0x610794 + (or * 8));
const u32 symbol = 100000;
- u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x0000f0000;
+ const s32 vactive = nv_rd32(priv, 0x610af8 + (head * 0x540)) & 0xffff;
+ const s32 vblanke = nv_rd32(priv, 0x610ae8 + (head * 0x540)) & 0xffff;
+ const s32 vblanks = nv_rd32(priv, 0x610af0 + (head * 0x540)) & 0xffff;
+ u32 dpctrl = nv_rd32(priv, 0x61c10c + loff);
u32 clksor = nv_rd32(priv, 0x614300 + soff);
int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
int TU, VTUi, VTUf, VTUa;
u64 link_data_rate, link_ratio, unk;
u32 best_diff = 64 * symbol;
- u32 link_nr, link_bw, bits, r;
-
- /* calculate packed data rate for each lane */
- if (dpctrl > 0x00030000) link_nr = 4;
- else if (dpctrl > 0x00010000) link_nr = 2;
- else link_nr = 1;
-
- if (clksor & 0x000c0000)
- link_bw = 270000;
- else
- link_bw = 162000;
-
+ u32 link_nr, link_bw, bits;
+ u64 value;
+
+ link_bw = (clksor & 0x000c0000) ? 270000 : 162000;
+ link_nr = hweight32(dpctrl & 0x000f0000);
+
+ /* symbols/hblank - algorithm taken from comments in tegra driver */
+ value = vblanke + vactive - vblanks - 7;
+ value = value * link_bw;
+ do_div(value, pclk);
+ value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
+ nv_mask(priv, 0x61c1e8 + soff, 0x0000ffff, value);
+
+ /* symbols/vblank - algorithm taken from comments in tegra driver */
+ value = vblanks - vblanke - 25;
+ value = value * link_bw;
+ do_div(value, pclk);
+ value = value - ((36 / link_nr) + 3) - 1;
+ nv_mask(priv, 0x61c1ec + soff, 0x00ffffff, value);
+
+ /* watermark / activesym */
if ((ctrl & 0xf0000) == 0x60000) bits = 30;
else if ((ctrl & 0xf0000) == 0x50000) bits = 24;
else bits = 18;
@@ -1401,7 +1677,7 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv,
/* calculate ratio of packed data rate to link symbol rate */
link_ratio = link_data_rate * symbol;
- r = do_div(link_ratio, link_bw);
+ do_div(link_ratio, link_bw);
for (TU = 64; TU >= 32; TU--) {
/* calculate average number of valid symbols in each TU */
@@ -1462,8 +1738,8 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv,
/* XXX close to vbios numbers, but not right */
unk = (symbol - link_ratio) * bestTU;
unk *= link_ratio;
- r = do_div(unk, symbol);
- r = do_div(unk, symbol);
+ do_div(unk, symbol);
+ do_div(unk, symbol);
unk += 6;
nv_mask(priv, 0x61c10c + loff, 0x000001fc, bestTU << 2);
@@ -1538,7 +1814,7 @@ nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head)
} else
if (!outp->info.location) {
if (outp->info.type == DCB_OUTPUT_DP)
- nv50_disp_intr_unk20_2_dp(priv, &outp->info, pclk);
+ nv50_disp_intr_unk20_2_dp(priv, head, &outp->info, pclk);
oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800;
oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
hval = 0x00000000;
@@ -1570,9 +1846,10 @@ nv50_disp_intr_unk40_0_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp
const int or = ffs(outp->or) - 1;
const u32 loff = (or * 0x800) + (link * 0x80);
const u16 mask = (outp->sorconf.link << 6) | outp->or;
+ struct dcb_output match;
u8 ver, hdr;
- if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, outp))
+ if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, &match))
nv_mask(priv, 0x61c10c + loff, 0x00000001, 0x00000000);
}
@@ -1653,14 +1930,20 @@ nv50_disp_intr(struct nouveau_subdev *subdev)
intr0 &= ~(0x00010000 << chid);
}
+ while (intr0 & 0x0000001f) {
+ u32 chid = __ffs(intr0 & 0x0000001f);
+ nv50_disp_chan_uevent_send(priv, chid);
+ intr0 &= ~(0x00000001 << chid);
+ }
+
if (intr1 & 0x00000004) {
- nouveau_event_trigger(priv->base.vblank, 1, 0);
+ nouveau_disp_vblank(&priv->base, 0);
nv_wr32(priv, 0x610024, 0x00000004);
intr1 &= ~0x00000004;
}
if (intr1 & 0x00000008) {
- nouveau_event_trigger(priv->base.vblank, 1, 1);
+ nouveau_disp_vblank(&priv->base, 1);
nv_wr32(priv, 0x610024, 0x00000008);
intr1 &= ~0x00000008;
}
@@ -1687,6 +1970,10 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent);
+ if (ret)
+ return ret;
+
nv_engine(priv)->sclass = nv50_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr;
@@ -1718,9 +2005,11 @@ nv50_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .base.vblank = &nv50_disp_vblank_func,
.base.outp = nv50_disp_outp_sclass,
.mthd.core = &nv50_disp_mast_mthd_chan,
.mthd.base = &nv50_disp_sync_mthd_chan,
.mthd.ovly = &nv50_disp_ovly_mthd_chan,
.mthd.prev = 0x000004,
+ .head.scanoutpos = nv50_disp_base_scanoutpos,
}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
index 1a886472b6f5..5279feefec06 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
@@ -14,15 +14,10 @@
#include "outp.h"
#include "outpdp.h"
-struct nv50_disp_impl {
- struct nouveau_disp_impl base;
- struct {
- const struct nv50_disp_mthd_chan *core;
- const struct nv50_disp_mthd_chan *base;
- const struct nv50_disp_mthd_chan *ovly;
- int prev;
- } mthd;
-};
+#define NV50_DISP_MTHD_ struct nouveau_object *object, \
+ struct nv50_disp_priv *priv, void *data, u32 size
+#define NV50_DISP_MTHD_V0 NV50_DISP_MTHD_, int head
+#define NV50_DISP_MTHD_V1 NV50_DISP_MTHD_, int head, struct nvkm_output *outp
struct nv50_disp_priv {
struct nouveau_disp base;
@@ -31,49 +26,60 @@ struct nv50_disp_priv {
struct work_struct supervisor;
u32 super;
+ struct nvkm_event uevent;
+
struct {
int nr;
} head;
struct {
int nr;
- int (*power)(struct nv50_disp_priv *, int dac, u32 data);
- int (*sense)(struct nv50_disp_priv *, int dac, u32 load);
+ int (*power)(NV50_DISP_MTHD_V1);
+ int (*sense)(NV50_DISP_MTHD_V1);
} dac;
struct {
int nr;
- int (*power)(struct nv50_disp_priv *, int sor, u32 data);
- int (*hda_eld)(struct nv50_disp_priv *, int sor, u8 *, u32);
- int (*hdmi)(struct nv50_disp_priv *, int head, int sor, u32);
+ int (*power)(NV50_DISP_MTHD_V1);
+ int (*hda_eld)(NV50_DISP_MTHD_V1);
+ int (*hdmi)(NV50_DISP_MTHD_V1);
u32 lvdsconf;
} sor;
struct {
int nr;
- int (*power)(struct nv50_disp_priv *, int ext, u32 data);
+ int (*power)(NV50_DISP_MTHD_V1);
u8 type[3];
} pior;
};
-#define HEAD_MTHD(n) (n), (n) + 0x03
-
-int nv50_disp_base_scanoutpos(struct nouveau_object *, u32, void *, u32);
+struct nv50_disp_impl {
+ struct nouveau_disp_impl base;
+ struct {
+ const struct nv50_disp_mthd_chan *core;
+ const struct nv50_disp_mthd_chan *base;
+ const struct nv50_disp_mthd_chan *ovly;
+ int prev;
+ } mthd;
+ struct {
+ int (*scanoutpos)(NV50_DISP_MTHD_V0);
+ } head;
+};
-#define DAC_MTHD(n) (n), (n) + 0x03
+int nv50_disp_base_scanoutpos(NV50_DISP_MTHD_V0);
+int nv50_disp_base_mthd(struct nouveau_object *, u32, void *, u32);
-int nv50_dac_mthd(struct nouveau_object *, u32, void *, u32);
-int nv50_dac_power(struct nv50_disp_priv *, int, u32);
-int nv50_dac_sense(struct nv50_disp_priv *, int, u32);
+int nvd0_disp_base_scanoutpos(NV50_DISP_MTHD_V0);
-#define SOR_MTHD(n) (n), (n) + 0x3f
+int nv50_dac_power(NV50_DISP_MTHD_V1);
+int nv50_dac_sense(NV50_DISP_MTHD_V1);
-int nva3_hda_eld(struct nv50_disp_priv *, int, u8 *, u32);
-int nvd0_hda_eld(struct nv50_disp_priv *, int, u8 *, u32);
+int nva3_hda_eld(NV50_DISP_MTHD_V1);
+int nvd0_hda_eld(NV50_DISP_MTHD_V1);
-int nv84_hdmi_ctrl(struct nv50_disp_priv *, int, int, u32);
-int nva3_hdmi_ctrl(struct nv50_disp_priv *, int, int, u32);
-int nvd0_hdmi_ctrl(struct nv50_disp_priv *, int, int, u32);
+int nv84_hdmi_ctrl(NV50_DISP_MTHD_V1);
+int nva3_hdmi_ctrl(NV50_DISP_MTHD_V1);
+int nvd0_hdmi_ctrl(NV50_DISP_MTHD_V1);
+int nve0_hdmi_ctrl(NV50_DISP_MTHD_V1);
-int nv50_sor_mthd(struct nouveau_object *, u32, void *, u32);
-int nv50_sor_power(struct nv50_disp_priv *, int, u32);
+int nv50_sor_power(NV50_DISP_MTHD_V1);
int nv94_sor_dp_train_init(struct nv50_disp_priv *, int, int, int, u16, u16,
u32, struct dcb_output *);
@@ -93,10 +99,7 @@ int nvd0_sor_dp_lnkctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32,
int nvd0_sor_dp_drvctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32,
struct dcb_output *);
-#define PIOR_MTHD(n) (n), (n) + 0x03
-
-int nv50_pior_mthd(struct nouveau_object *, u32, void *, u32);
-int nv50_pior_power(struct nv50_disp_priv *, int, u32);
+int nv50_pior_power(NV50_DISP_MTHD_V1);
struct nv50_disp_base {
struct nouveau_parent base;
@@ -104,36 +107,48 @@ struct nv50_disp_base {
u32 chan;
};
+struct nv50_disp_chan_impl {
+ struct nouveau_ofuncs base;
+ int chid;
+ int (*attach)(struct nouveau_object *, struct nouveau_object *, u32);
+ void (*detach)(struct nouveau_object *, int);
+};
+
struct nv50_disp_chan {
struct nouveau_namedb base;
int chid;
};
-int nv50_disp_chan_create_(struct nouveau_object *, struct nouveau_object *,
- struct nouveau_oclass *, int, int, void **);
-void nv50_disp_chan_destroy(struct nv50_disp_chan *);
+int nv50_disp_chan_ntfy(struct nouveau_object *, u32, struct nvkm_event **);
+int nv50_disp_chan_map(struct nouveau_object *, u64 *, u32 *);
u32 nv50_disp_chan_rd32(struct nouveau_object *, u64);
void nv50_disp_chan_wr32(struct nouveau_object *, u64, u32);
+extern const struct nvkm_event_func nv50_disp_chan_uevent;
+int nv50_disp_chan_uevent_ctor(struct nouveau_object *, void *, u32,
+ struct nvkm_notify *);
+void nv50_disp_chan_uevent_send(struct nv50_disp_priv *, int);
+
+extern const struct nvkm_event_func nvd0_disp_chan_uevent;
#define nv50_disp_chan_init(a) \
nouveau_namedb_init(&(a)->base)
#define nv50_disp_chan_fini(a,b) \
nouveau_namedb_fini(&(a)->base, (b))
-int nv50_disp_dmac_create_(struct nouveau_object *, struct nouveau_object *,
- struct nouveau_oclass *, u32, int, int, void **);
-void nv50_disp_dmac_dtor(struct nouveau_object *);
-
struct nv50_disp_dmac {
struct nv50_disp_chan base;
struct nouveau_dmaobj *pushdma;
u32 push;
};
+void nv50_disp_dmac_dtor(struct nouveau_object *);
+
struct nv50_disp_pioc {
struct nv50_disp_chan base;
};
+void nv50_disp_pioc_dtor(struct nouveau_object *);
+
struct nv50_disp_mthd_list {
u32 mthd;
u32 addr;
@@ -154,47 +169,67 @@ struct nv50_disp_mthd_chan {
} data[];
};
-extern struct nouveau_ofuncs nv50_disp_mast_ofuncs;
+extern struct nv50_disp_chan_impl nv50_disp_mast_ofuncs;
+int nv50_disp_mast_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
extern const struct nv50_disp_mthd_list nv50_disp_mast_mthd_base;
extern const struct nv50_disp_mthd_list nv50_disp_mast_mthd_sor;
extern const struct nv50_disp_mthd_list nv50_disp_mast_mthd_pior;
-extern struct nouveau_ofuncs nv50_disp_sync_ofuncs;
+extern struct nv50_disp_chan_impl nv50_disp_sync_ofuncs;
+int nv50_disp_sync_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
extern const struct nv50_disp_mthd_list nv50_disp_sync_mthd_image;
-extern struct nouveau_ofuncs nv50_disp_ovly_ofuncs;
+extern struct nv50_disp_chan_impl nv50_disp_ovly_ofuncs;
+int nv50_disp_ovly_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
extern const struct nv50_disp_mthd_list nv50_disp_ovly_mthd_base;
-extern struct nouveau_ofuncs nv50_disp_oimm_ofuncs;
-extern struct nouveau_ofuncs nv50_disp_curs_ofuncs;
+extern struct nv50_disp_chan_impl nv50_disp_oimm_ofuncs;
+int nv50_disp_oimm_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+extern struct nv50_disp_chan_impl nv50_disp_curs_ofuncs;
+int nv50_disp_curs_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
extern struct nouveau_ofuncs nv50_disp_base_ofuncs;
+int nv50_disp_base_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+void nv50_disp_base_dtor(struct nouveau_object *);
+extern struct nouveau_omthds nv50_disp_base_omthds[];
extern struct nouveau_oclass nv50_disp_cclass;
void nv50_disp_mthd_chan(struct nv50_disp_priv *, int debug, int head,
const struct nv50_disp_mthd_chan *);
void nv50_disp_intr_supervisor(struct work_struct *);
void nv50_disp_intr(struct nouveau_subdev *);
+extern const struct nvkm_event_func nv50_disp_vblank_func;
extern const struct nv50_disp_mthd_chan nv84_disp_mast_mthd_chan;
extern const struct nv50_disp_mthd_list nv84_disp_mast_mthd_dac;
extern const struct nv50_disp_mthd_list nv84_disp_mast_mthd_head;
extern const struct nv50_disp_mthd_chan nv84_disp_sync_mthd_chan;
extern const struct nv50_disp_mthd_chan nv84_disp_ovly_mthd_chan;
-extern struct nouveau_omthds nv84_disp_base_omthds[];
extern const struct nv50_disp_mthd_chan nv94_disp_mast_mthd_chan;
-extern struct nouveau_ofuncs nvd0_disp_mast_ofuncs;
+extern struct nv50_disp_chan_impl nvd0_disp_mast_ofuncs;
extern const struct nv50_disp_mthd_list nvd0_disp_mast_mthd_base;
extern const struct nv50_disp_mthd_list nvd0_disp_mast_mthd_dac;
extern const struct nv50_disp_mthd_list nvd0_disp_mast_mthd_sor;
extern const struct nv50_disp_mthd_list nvd0_disp_mast_mthd_pior;
-extern struct nouveau_ofuncs nvd0_disp_sync_ofuncs;
-extern struct nouveau_ofuncs nvd0_disp_ovly_ofuncs;
+extern struct nv50_disp_chan_impl nvd0_disp_sync_ofuncs;
+extern struct nv50_disp_chan_impl nvd0_disp_ovly_ofuncs;
extern const struct nv50_disp_mthd_chan nvd0_disp_sync_mthd_chan;
-extern struct nouveau_ofuncs nvd0_disp_oimm_ofuncs;
-extern struct nouveau_ofuncs nvd0_disp_curs_ofuncs;
-extern struct nouveau_omthds nvd0_disp_base_omthds[];
+extern struct nv50_disp_chan_impl nvd0_disp_oimm_ofuncs;
+extern struct nv50_disp_chan_impl nvd0_disp_curs_ofuncs;
extern struct nouveau_ofuncs nvd0_disp_base_ofuncs;
extern struct nouveau_oclass nvd0_disp_cclass;
void nvd0_disp_intr_supervisor(struct work_struct *);
void nvd0_disp_intr(struct nouveau_subdev *);
+extern const struct nvkm_event_func nvd0_disp_vblank_func;
extern const struct nv50_disp_mthd_chan nve0_disp_mast_mthd_chan;
extern const struct nv50_disp_mthd_chan nve0_disp_ovly_mthd_chan;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
index 1cc62e434683..d36284715b2a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
@@ -25,7 +25,7 @@
#include <engine/software.h>
#include <engine/disp.h>
-#include <core/class.h>
+#include <nvif/class.h>
#include "nv50.h"
@@ -204,31 +204,17 @@ nv84_disp_ovly_mthd_chan = {
static struct nouveau_oclass
nv84_disp_sclass[] = {
- { NV84_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs },
- { NV84_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs },
- { NV84_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs },
- { NV84_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs },
- { NV84_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs },
+ { G82_DISP_CORE_CHANNEL_DMA, &nv50_disp_mast_ofuncs.base },
+ { G82_DISP_BASE_CHANNEL_DMA, &nv50_disp_sync_ofuncs.base },
+ { G82_DISP_OVERLAY_CHANNEL_DMA, &nv50_disp_ovly_ofuncs.base },
+ { G82_DISP_OVERLAY, &nv50_disp_oimm_ofuncs.base },
+ { G82_DISP_CURSOR, &nv50_disp_curs_ofuncs.base },
{}
};
-struct nouveau_omthds
-nv84_disp_base_omthds[] = {
- { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nv50_disp_base_scanoutpos },
- { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd },
- { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd },
- { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
- { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd },
- { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd },
- {},
-};
-
static struct nouveau_oclass
nv84_disp_base_oclass[] = {
- { NV84_DISP_CLASS, &nv50_disp_base_ofuncs, nv84_disp_base_omthds },
+ { G82_DISP, &nv50_disp_base_ofuncs },
{}
};
@@ -250,6 +236,10 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent);
+ if (ret)
+ return ret;
+
nv_engine(priv)->sclass = nv84_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr;
@@ -276,9 +266,11 @@ nv84_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .base.vblank = &nv50_disp_vblank_func,
.base.outp = nv50_disp_outp_sclass,
.mthd.core = &nv84_disp_mast_mthd_chan,
.mthd.base = &nv84_disp_sync_mthd_chan,
.mthd.ovly = &nv84_disp_ovly_mthd_chan,
.mthd.prev = 0x000004,
+ .head.scanoutpos = nv50_disp_base_scanoutpos,
}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
index 4f718a9f5aef..a117064002b1 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
@@ -25,7 +25,7 @@
#include <engine/software.h>
#include <engine/disp.h>
-#include <core/class.h>
+#include <nvif/class.h>
#include "nv50.h"
@@ -63,32 +63,17 @@ nv94_disp_mast_mthd_chan = {
static struct nouveau_oclass
nv94_disp_sclass[] = {
- { NV94_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs },
- { NV94_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs },
- { NV94_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs },
- { NV94_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs },
- { NV94_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs },
+ { GT206_DISP_CORE_CHANNEL_DMA, &nv50_disp_mast_ofuncs.base },
+ { GT200_DISP_BASE_CHANNEL_DMA, &nv50_disp_sync_ofuncs.base },
+ { GT200_DISP_OVERLAY_CHANNEL_DMA, &nv50_disp_ovly_ofuncs.base },
+ { G82_DISP_OVERLAY, &nv50_disp_oimm_ofuncs.base },
+ { G82_DISP_CURSOR, &nv50_disp_curs_ofuncs.base },
{}
};
-static struct nouveau_omthds
-nv94_disp_base_omthds[] = {
- { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nv50_disp_base_scanoutpos },
- { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd },
- { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd },
- { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd },
- { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd },
- { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd },
- {},
-};
-
static struct nouveau_oclass
nv94_disp_base_oclass[] = {
- { NV94_DISP_CLASS, &nv50_disp_base_ofuncs, nv94_disp_base_omthds },
+ { GT206_DISP, &nv50_disp_base_ofuncs },
{}
};
@@ -110,6 +95,10 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent);
+ if (ret)
+ return ret;
+
nv_engine(priv)->sclass = nv94_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr;
@@ -143,9 +132,11 @@ nv94_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .base.vblank = &nv50_disp_vblank_func,
.base.outp = nv94_disp_outp_sclass,
.mthd.core = &nv94_disp_mast_mthd_chan,
.mthd.base = &nv84_disp_sync_mthd_chan,
.mthd.ovly = &nv84_disp_ovly_mthd_chan,
.mthd.prev = 0x000004,
+ .head.scanoutpos = nv50_disp_base_scanoutpos,
}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
index 6237a9a36f70..c67e68aadd45 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
@@ -25,7 +25,7 @@
#include <engine/software.h>
#include <engine/disp.h>
-#include <core/class.h>
+#include <nvif/class.h>
#include "nv50.h"
@@ -80,17 +80,17 @@ nva0_disp_ovly_mthd_chan = {
static struct nouveau_oclass
nva0_disp_sclass[] = {
- { NVA0_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs },
- { NVA0_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs },
- { NVA0_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs },
- { NVA0_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs },
- { NVA0_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs },
+ { GT200_DISP_CORE_CHANNEL_DMA, &nv50_disp_mast_ofuncs.base },
+ { GT200_DISP_BASE_CHANNEL_DMA, &nv50_disp_sync_ofuncs.base },
+ { GT200_DISP_OVERLAY_CHANNEL_DMA, &nv50_disp_ovly_ofuncs.base },
+ { G82_DISP_OVERLAY, &nv50_disp_oimm_ofuncs.base },
+ { G82_DISP_CURSOR, &nv50_disp_curs_ofuncs.base },
{}
};
static struct nouveau_oclass
nva0_disp_base_oclass[] = {
- { NVA0_DISP_CLASS, &nv50_disp_base_ofuncs, nv84_disp_base_omthds },
+ { GT200_DISP, &nv50_disp_base_ofuncs },
{}
};
@@ -112,6 +112,10 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent);
+ if (ret)
+ return ret;
+
nv_engine(priv)->sclass = nva0_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr;
@@ -138,9 +142,11 @@ nva0_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .base.vblank = &nv50_disp_vblank_func,
.base.outp = nv50_disp_outp_sclass,
.mthd.core = &nv84_disp_mast_mthd_chan,
.mthd.base = &nv84_disp_sync_mthd_chan,
.mthd.ovly = &nva0_disp_ovly_mthd_chan,
.mthd.prev = 0x000004,
+ .head.scanoutpos = nv50_disp_base_scanoutpos,
}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
index 019124d4782b..22969f355aae 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
@@ -25,7 +25,7 @@
#include <engine/software.h>
#include <engine/disp.h>
-#include <core/class.h>
+#include <nvif/class.h>
#include "nv50.h"
@@ -35,33 +35,17 @@
static struct nouveau_oclass
nva3_disp_sclass[] = {
- { NVA3_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs },
- { NVA3_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs },
- { NVA3_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs },
- { NVA3_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs },
- { NVA3_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs },
+ { GT214_DISP_CORE_CHANNEL_DMA, &nv50_disp_mast_ofuncs.base },
+ { GT214_DISP_BASE_CHANNEL_DMA, &nv50_disp_sync_ofuncs.base },
+ { GT214_DISP_OVERLAY_CHANNEL_DMA, &nv50_disp_ovly_ofuncs.base },
+ { GT214_DISP_OVERLAY, &nv50_disp_oimm_ofuncs.base },
+ { GT214_DISP_CURSOR, &nv50_disp_curs_ofuncs.base },
{}
};
-static struct nouveau_omthds
-nva3_disp_base_omthds[] = {
- { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nv50_disp_base_scanoutpos },
- { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd },
- { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd },
- { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd },
- { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd },
- { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd },
- { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd },
- {},
-};
-
static struct nouveau_oclass
nva3_disp_base_oclass[] = {
- { NVA3_DISP_CLASS, &nv50_disp_base_ofuncs, nva3_disp_base_omthds },
+ { GT214_DISP, &nv50_disp_base_ofuncs },
{}
};
@@ -83,6 +67,10 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ ret = nvkm_event_init(&nv50_disp_chan_uevent, 1, 9, &priv->uevent);
+ if (ret)
+ return ret;
+
nv_engine(priv)->sclass = nva3_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nv50_disp_intr;
@@ -110,9 +98,11 @@ nva3_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .base.vblank = &nv50_disp_vblank_func,
.base.outp = nv94_disp_outp_sclass,
.mthd.core = &nv94_disp_mast_mthd_chan,
.mthd.base = &nv84_disp_sync_mthd_chan,
.mthd.ovly = &nv84_disp_ovly_mthd_chan,
.mthd.prev = 0x000004,
+ .head.scanoutpos = nv50_disp_base_scanoutpos,
}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
index fa30d8196f35..747e64bb9c06 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
@@ -23,9 +23,11 @@
*/
#include <core/object.h>
+#include <core/client.h>
#include <core/parent.h>
#include <core/handle.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <engine/disp.h>
@@ -41,6 +43,31 @@
#include "nv50.h"
/*******************************************************************************
+ * EVO channel base class
+ ******************************************************************************/
+
+static void
+nvd0_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index)
+{
+ struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent);
+ nv_mask(priv, 0x610090, 0x00000001 << index, 0x00000000 << index);
+}
+
+static void
+nvd0_disp_chan_uevent_init(struct nvkm_event *event, int types, int index)
+{
+ struct nv50_disp_priv *priv = container_of(event, typeof(*priv), uevent);
+ nv_mask(priv, 0x610090, 0x00000001 << index, 0x00000001 << index);
+}
+
+const struct nvkm_event_func
+nvd0_disp_chan_uevent = {
+ .ctor = nv50_disp_chan_uevent_ctor,
+ .init = nvd0_disp_chan_uevent_init,
+ .fini = nvd0_disp_chan_uevent_fini,
+};
+
+/*******************************************************************************
* EVO DMA channel base class
******************************************************************************/
@@ -75,7 +102,6 @@ nvd0_disp_dmac_init(struct nouveau_object *object)
return ret;
/* enable error reporting */
- nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid);
nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid);
/* initialise channel for dma command submission */
@@ -113,7 +139,7 @@ nvd0_disp_dmac_fini(struct nouveau_object *object, bool suspend)
return -EBUSY;
}
- /* disable error reporting */
+ /* disable error reporting and completion notification */
nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000);
nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000);
@@ -265,30 +291,6 @@ nvd0_disp_mast_mthd_chan = {
};
static int
-nvd0_disp_mast_ctor(struct nouveau_object *parent,
- struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv50_display_mast_class *args = data;
- struct nv50_disp_dmac *mast;
- int ret;
-
- if (size < sizeof(*args))
- return -EINVAL;
-
- ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
- 0, sizeof(*mast), (void **)&mast);
- *pobject = nv_object(mast);
- if (ret)
- return ret;
-
- nv_parent(mast)->object_attach = nvd0_disp_dmac_object_attach;
- nv_parent(mast)->object_detach = nvd0_disp_dmac_object_detach;
- return 0;
-}
-
-static int
nvd0_disp_mast_init(struct nouveau_object *object)
{
struct nv50_disp_priv *priv = (void *)object->engine;
@@ -300,7 +302,6 @@ nvd0_disp_mast_init(struct nouveau_object *object)
return ret;
/* enable error reporting */
- nv_mask(priv, 0x610090, 0x00000001, 0x00000001);
nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001);
/* initialise channel for dma command submission */
@@ -335,21 +336,26 @@ nvd0_disp_mast_fini(struct nouveau_object *object, bool suspend)
return -EBUSY;
}
- /* disable error reporting */
+ /* disable error reporting and completion notification */
nv_mask(priv, 0x610090, 0x00000001, 0x00000000);
nv_mask(priv, 0x6100a0, 0x00000001, 0x00000000);
return nv50_disp_chan_fini(&mast->base, suspend);
}
-struct nouveau_ofuncs
+struct nv50_disp_chan_impl
nvd0_disp_mast_ofuncs = {
- .ctor = nvd0_disp_mast_ctor,
- .dtor = nv50_disp_dmac_dtor,
- .init = nvd0_disp_mast_init,
- .fini = nvd0_disp_mast_fini,
- .rd32 = nv50_disp_chan_rd32,
- .wr32 = nv50_disp_chan_wr32,
+ .base.ctor = nv50_disp_mast_ctor,
+ .base.dtor = nv50_disp_dmac_dtor,
+ .base.init = nvd0_disp_mast_init,
+ .base.fini = nvd0_disp_mast_fini,
+ .base.ntfy = nv50_disp_chan_ntfy,
+ .base.map = nv50_disp_chan_map,
+ .base.rd32 = nv50_disp_chan_rd32,
+ .base.wr32 = nv50_disp_chan_wr32,
+ .chid = 0,
+ .attach = nvd0_disp_dmac_object_attach,
+ .detach = nvd0_disp_dmac_object_detach,
};
/*******************************************************************************
@@ -431,40 +437,19 @@ nvd0_disp_sync_mthd_chan = {
}
};
-static int
-nvd0_disp_sync_ctor(struct nouveau_object *parent,
- struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv50_display_sync_class *args = data;
- struct nv50_disp_priv *priv = (void *)engine;
- struct nv50_disp_dmac *dmac;
- int ret;
-
- if (size < sizeof(*args) || args->head >= priv->head.nr)
- return -EINVAL;
-
- ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
- 1 + args->head, sizeof(*dmac),
- (void **)&dmac);
- *pobject = nv_object(dmac);
- if (ret)
- return ret;
-
- nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach;
- nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach;
- return 0;
-}
-
-struct nouveau_ofuncs
+struct nv50_disp_chan_impl
nvd0_disp_sync_ofuncs = {
- .ctor = nvd0_disp_sync_ctor,
- .dtor = nv50_disp_dmac_dtor,
- .init = nvd0_disp_dmac_init,
- .fini = nvd0_disp_dmac_fini,
- .rd32 = nv50_disp_chan_rd32,
- .wr32 = nv50_disp_chan_wr32,
+ .base.ctor = nv50_disp_sync_ctor,
+ .base.dtor = nv50_disp_dmac_dtor,
+ .base.init = nvd0_disp_dmac_init,
+ .base.fini = nvd0_disp_dmac_fini,
+ .base.ntfy = nv50_disp_chan_ntfy,
+ .base.map = nv50_disp_chan_map,
+ .base.rd32 = nv50_disp_chan_rd32,
+ .base.wr32 = nv50_disp_chan_wr32,
+ .chid = 1,
+ .attach = nvd0_disp_dmac_object_attach,
+ .detach = nvd0_disp_dmac_object_detach,
};
/*******************************************************************************
@@ -533,40 +518,19 @@ nvd0_disp_ovly_mthd_chan = {
}
};
-static int
-nvd0_disp_ovly_ctor(struct nouveau_object *parent,
- struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv50_display_ovly_class *args = data;
- struct nv50_disp_priv *priv = (void *)engine;
- struct nv50_disp_dmac *dmac;
- int ret;
-
- if (size < sizeof(*args) || args->head >= priv->head.nr)
- return -EINVAL;
-
- ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf,
- 5 + args->head, sizeof(*dmac),
- (void **)&dmac);
- *pobject = nv_object(dmac);
- if (ret)
- return ret;
-
- nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach;
- nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach;
- return 0;
-}
-
-struct nouveau_ofuncs
+struct nv50_disp_chan_impl
nvd0_disp_ovly_ofuncs = {
- .ctor = nvd0_disp_ovly_ctor,
- .dtor = nv50_disp_dmac_dtor,
- .init = nvd0_disp_dmac_init,
- .fini = nvd0_disp_dmac_fini,
- .rd32 = nv50_disp_chan_rd32,
- .wr32 = nv50_disp_chan_wr32,
+ .base.ctor = nv50_disp_ovly_ctor,
+ .base.dtor = nv50_disp_dmac_dtor,
+ .base.init = nvd0_disp_dmac_init,
+ .base.fini = nvd0_disp_dmac_fini,
+ .base.ntfy = nv50_disp_chan_ntfy,
+ .base.map = nv50_disp_chan_map,
+ .base.rd32 = nv50_disp_chan_rd32,
+ .base.wr32 = nv50_disp_chan_wr32,
+ .chid = 5,
+ .attach = nvd0_disp_dmac_object_attach,
+ .detach = nvd0_disp_dmac_object_detach,
};
/*******************************************************************************
@@ -574,23 +538,6 @@ nvd0_disp_ovly_ofuncs = {
******************************************************************************/
static int
-nvd0_disp_pioc_create_(struct nouveau_object *parent,
- struct nouveau_object *engine,
- struct nouveau_oclass *oclass, int chid,
- int length, void **pobject)
-{
- return nv50_disp_chan_create_(parent, engine, oclass, chid,
- length, pobject);
-}
-
-static void
-nvd0_disp_pioc_dtor(struct nouveau_object *object)
-{
- struct nv50_disp_pioc *pioc = (void *)object;
- nv50_disp_chan_destroy(&pioc->base);
-}
-
-static int
nvd0_disp_pioc_init(struct nouveau_object *object)
{
struct nv50_disp_priv *priv = (void *)object->engine;
@@ -603,7 +550,6 @@ nvd0_disp_pioc_init(struct nouveau_object *object)
return ret;
/* enable error reporting */
- nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid);
nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid);
/* activate channel */
@@ -632,7 +578,7 @@ nvd0_disp_pioc_fini(struct nouveau_object *object, bool suspend)
return -EBUSY;
}
- /* disable error reporting */
+ /* disable error reporting and completion notification */
nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000);
nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000);
@@ -643,152 +589,70 @@ nvd0_disp_pioc_fini(struct nouveau_object *object, bool suspend)
* EVO immediate overlay channel objects
******************************************************************************/
-static int
-nvd0_disp_oimm_ctor(struct nouveau_object *parent,
- struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv50_display_oimm_class *args = data;
- struct nv50_disp_priv *priv = (void *)engine;
- struct nv50_disp_pioc *pioc;
- int ret;
-
- if (size < sizeof(*args) || args->head >= priv->head.nr)
- return -EINVAL;
-
- ret = nvd0_disp_pioc_create_(parent, engine, oclass, 9 + args->head,
- sizeof(*pioc), (void **)&pioc);
- *pobject = nv_object(pioc);
- if (ret)
- return ret;
-
- return 0;
-}
-
-struct nouveau_ofuncs
+struct nv50_disp_chan_impl
nvd0_disp_oimm_ofuncs = {
- .ctor = nvd0_disp_oimm_ctor,
- .dtor = nvd0_disp_pioc_dtor,
- .init = nvd0_disp_pioc_init,
- .fini = nvd0_disp_pioc_fini,
- .rd32 = nv50_disp_chan_rd32,
- .wr32 = nv50_disp_chan_wr32,
+ .base.ctor = nv50_disp_oimm_ctor,
+ .base.dtor = nv50_disp_pioc_dtor,
+ .base.init = nvd0_disp_pioc_init,
+ .base.fini = nvd0_disp_pioc_fini,
+ .base.ntfy = nv50_disp_chan_ntfy,
+ .base.map = nv50_disp_chan_map,
+ .base.rd32 = nv50_disp_chan_rd32,
+ .base.wr32 = nv50_disp_chan_wr32,
+ .chid = 9,
};
/*******************************************************************************
* EVO cursor channel objects
******************************************************************************/
-static int
-nvd0_disp_curs_ctor(struct nouveau_object *parent,
- struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv50_display_curs_class *args = data;
- struct nv50_disp_priv *priv = (void *)engine;
- struct nv50_disp_pioc *pioc;
- int ret;
-
- if (size < sizeof(*args) || args->head >= priv->head.nr)
- return -EINVAL;
-
- ret = nvd0_disp_pioc_create_(parent, engine, oclass, 13 + args->head,
- sizeof(*pioc), (void **)&pioc);
- *pobject = nv_object(pioc);
- if (ret)
- return ret;
-
- return 0;
-}
-
-struct nouveau_ofuncs
+struct nv50_disp_chan_impl
nvd0_disp_curs_ofuncs = {
- .ctor = nvd0_disp_curs_ctor,
- .dtor = nvd0_disp_pioc_dtor,
- .init = nvd0_disp_pioc_init,
- .fini = nvd0_disp_pioc_fini,
- .rd32 = nv50_disp_chan_rd32,
- .wr32 = nv50_disp_chan_wr32,
+ .base.ctor = nv50_disp_curs_ctor,
+ .base.dtor = nv50_disp_pioc_dtor,
+ .base.init = nvd0_disp_pioc_init,
+ .base.fini = nvd0_disp_pioc_fini,
+ .base.ntfy = nv50_disp_chan_ntfy,
+ .base.map = nv50_disp_chan_map,
+ .base.rd32 = nv50_disp_chan_rd32,
+ .base.wr32 = nv50_disp_chan_wr32,
+ .chid = 13,
};
/*******************************************************************************
* Base display object
******************************************************************************/
-static int
-nvd0_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd,
- void *data, u32 size)
-{
- struct nv50_disp_priv *priv = (void *)object->engine;
- struct nv04_display_scanoutpos *args = data;
- const int head = (mthd & NV50_DISP_MTHD_HEAD);
- u32 blanke, blanks, total;
-
- if (size < sizeof(*args) || head >= priv->head.nr)
- return -EINVAL;
-
- total = nv_rd32(priv, 0x640414 + (head * 0x300));
- blanke = nv_rd32(priv, 0x64041c + (head * 0x300));
- blanks = nv_rd32(priv, 0x640420 + (head * 0x300));
-
- args->vblanke = (blanke & 0xffff0000) >> 16;
- args->hblanke = (blanke & 0x0000ffff);
- args->vblanks = (blanks & 0xffff0000) >> 16;
- args->hblanks = (blanks & 0x0000ffff);
- args->vtotal = ( total & 0xffff0000) >> 16;
- args->htotal = ( total & 0x0000ffff);
-
- args->time[0] = ktime_to_ns(ktime_get());
- args->vline = nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff;
- args->time[1] = ktime_to_ns(ktime_get()); /* vline read locks hline */
- args->hline = nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff;
- return 0;
-}
-
-static void
-nvd0_disp_base_vblank_enable(struct nouveau_event *event, int type, int head)
-{
- nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001);
-}
-
-static void
-nvd0_disp_base_vblank_disable(struct nouveau_event *event, int type, int head)
-{
- nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000);
-}
-
-static int
-nvd0_disp_base_ctor(struct nouveau_object *parent,
- struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
+int
+nvd0_disp_base_scanoutpos(NV50_DISP_MTHD_V0)
{
- struct nv50_disp_priv *priv = (void *)engine;
- struct nv50_disp_base *base;
+ const u32 total = nv_rd32(priv, 0x640414 + (head * 0x300));
+ const u32 blanke = nv_rd32(priv, 0x64041c + (head * 0x300));
+ const u32 blanks = nv_rd32(priv, 0x640420 + (head * 0x300));
+ union {
+ struct nv04_disp_scanoutpos_v0 v0;
+ } *args = data;
int ret;
- ret = nouveau_parent_create(parent, engine, oclass, 0,
- priv->sclass, 0, &base);
- *pobject = nv_object(base);
- if (ret)
+ nv_ioctl(object, "disp scanoutpos size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp scanoutpos vers %d\n", args->v0.version);
+ args->v0.vblanke = (blanke & 0xffff0000) >> 16;
+ args->v0.hblanke = (blanke & 0x0000ffff);
+ args->v0.vblanks = (blanks & 0xffff0000) >> 16;
+ args->v0.hblanks = (blanks & 0x0000ffff);
+ args->v0.vtotal = ( total & 0xffff0000) >> 16;
+ args->v0.htotal = ( total & 0x0000ffff);
+ args->v0.time[0] = ktime_to_ns(ktime_get());
+ args->v0.vline = /* vline read locks hline */
+ nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff;
+ args->v0.time[1] = ktime_to_ns(ktime_get());
+ args->v0.hline =
+ nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff;
+ } else
return ret;
- priv->base.vblank->priv = priv;
- priv->base.vblank->enable = nvd0_disp_base_vblank_enable;
- priv->base.vblank->disable = nvd0_disp_base_vblank_disable;
-
- return nouveau_ramht_new(nv_object(base), nv_object(base), 0x1000, 0,
- &base->ramht);
-}
-
-static void
-nvd0_disp_base_dtor(struct nouveau_object *object)
-{
- struct nv50_disp_base *base = (void *)object;
- nouveau_ramht_ref(NULL, &base->ramht);
- nouveau_parent_destroy(&base->base);
+ return 0;
}
static int
@@ -874,41 +738,27 @@ nvd0_disp_base_fini(struct nouveau_object *object, bool suspend)
struct nouveau_ofuncs
nvd0_disp_base_ofuncs = {
- .ctor = nvd0_disp_base_ctor,
- .dtor = nvd0_disp_base_dtor,
+ .ctor = nv50_disp_base_ctor,
+ .dtor = nv50_disp_base_dtor,
.init = nvd0_disp_base_init,
.fini = nvd0_disp_base_fini,
-};
-
-struct nouveau_omthds
-nvd0_disp_base_omthds[] = {
- { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nvd0_disp_base_scanoutpos },
- { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd },
- { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd },
- { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd },
- { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
- { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd },
- { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd },
- { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd },
- { PIOR_MTHD(NV50_DISP_PIOR_DP_PWR) , nv50_pior_mthd },
- {},
+ .mthd = nv50_disp_base_mthd,
+ .ntfy = nouveau_disp_ntfy,
};
static struct nouveau_oclass
nvd0_disp_base_oclass[] = {
- { NVD0_DISP_CLASS, &nvd0_disp_base_ofuncs, nvd0_disp_base_omthds },
+ { GF110_DISP, &nvd0_disp_base_ofuncs },
{}
};
static struct nouveau_oclass
nvd0_disp_sclass[] = {
- { NVD0_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs },
- { NVD0_DISP_SYNC_CLASS, &nvd0_disp_sync_ofuncs },
- { NVD0_DISP_OVLY_CLASS, &nvd0_disp_ovly_ofuncs },
- { NVD0_DISP_OIMM_CLASS, &nvd0_disp_oimm_ofuncs },
- { NVD0_DISP_CURS_CLASS, &nvd0_disp_curs_ofuncs },
+ { GF110_DISP_CORE_CHANNEL_DMA, &nvd0_disp_mast_ofuncs.base },
+ { GF110_DISP_BASE_CHANNEL_DMA, &nvd0_disp_sync_ofuncs.base },
+ { GF110_DISP_OVERLAY_CONTROL_DMA, &nvd0_disp_ovly_ofuncs.base },
+ { GF110_DISP_OVERLAY, &nvd0_disp_oimm_ofuncs.base },
+ { GF110_DISP_CURSOR, &nvd0_disp_curs_ofuncs.base },
{}
};
@@ -916,6 +766,27 @@ nvd0_disp_sclass[] = {
* Display engine implementation
******************************************************************************/
+static void
+nvd0_disp_vblank_init(struct nvkm_event *event, int type, int head)
+{
+ struct nouveau_disp *disp = container_of(event, typeof(*disp), vblank);
+ nv_mask(disp, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001);
+}
+
+static void
+nvd0_disp_vblank_fini(struct nvkm_event *event, int type, int head)
+{
+ struct nouveau_disp *disp = container_of(event, typeof(*disp), vblank);
+ nv_mask(disp, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000);
+}
+
+const struct nvkm_event_func
+nvd0_disp_vblank_func = {
+ .ctor = nouveau_disp_vblank_ctor,
+ .init = nvd0_disp_vblank_init,
+ .fini = nvd0_disp_vblank_fini,
+};
+
static struct nvkm_output *
exec_lookup(struct nv50_disp_priv *priv, int head, int or, u32 ctrl,
u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
@@ -1105,6 +976,9 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head,
const int or = ffs(outp->or) - 1;
const u32 ctrl = nv_rd32(priv, 0x660200 + (or * 0x020));
const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300));
+ const s32 vactive = nv_rd32(priv, 0x660414 + (head * 0x300)) & 0xffff;
+ const s32 vblanke = nv_rd32(priv, 0x66041c + (head * 0x300)) & 0xffff;
+ const s32 vblanks = nv_rd32(priv, 0x660420 + (head * 0x300)) & 0xffff;
const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1;
const u32 hoff = (head * 0x800);
@@ -1112,23 +986,35 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head,
const u32 loff = (link * 0x080) + soff;
const u32 symbol = 100000;
const u32 TU = 64;
- u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x000f0000;
+ u32 dpctrl = nv_rd32(priv, 0x61c10c + loff);
u32 clksor = nv_rd32(priv, 0x612300 + soff);
u32 datarate, link_nr, link_bw, bits;
u64 ratio, value;
+ link_nr = hweight32(dpctrl & 0x000f0000);
+ link_bw = (clksor & 0x007c0000) >> 18;
+ link_bw *= 27000;
+
+ /* symbols/hblank - algorithm taken from comments in tegra driver */
+ value = vblanke + vactive - vblanks - 7;
+ value = value * link_bw;
+ do_div(value, pclk);
+ value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
+ nv_mask(priv, 0x616620 + hoff, 0x0000ffff, value);
+
+ /* symbols/vblank - algorithm taken from comments in tegra driver */
+ value = vblanks - vblanke - 25;
+ value = value * link_bw;
+ do_div(value, pclk);
+ value = value - ((36 / link_nr) + 3) - 1;
+ nv_mask(priv, 0x616624 + hoff, 0x00ffffff, value);
+
+ /* watermark */
if ((conf & 0x3c0) == 0x180) bits = 30;
else if ((conf & 0x3c0) == 0x140) bits = 24;
else bits = 18;
datarate = (pclk * bits) / 8;
- if (dpctrl > 0x00030000) link_nr = 4;
- else if (dpctrl > 0x00010000) link_nr = 2;
- else link_nr = 1;
-
- link_bw = (clksor & 0x007c0000) >> 18;
- link_bw *= 27000;
-
ratio = datarate;
ratio *= symbol;
do_div(ratio, link_nr * link_bw);
@@ -1309,7 +1195,11 @@ nvd0_disp_intr(struct nouveau_subdev *subdev)
if (intr & 0x00000001) {
u32 stat = nv_rd32(priv, 0x61008c);
- nv_wr32(priv, 0x61008c, stat);
+ while (stat) {
+ int chid = __ffs(stat); stat &= ~(1 << chid);
+ nv50_disp_chan_uevent_send(priv, chid);
+ nv_wr32(priv, 0x61008c, 1 << chid);
+ }
intr &= ~0x00000001;
}
@@ -1343,7 +1233,7 @@ nvd0_disp_intr(struct nouveau_subdev *subdev)
if (mask & intr) {
u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800));
if (stat & 0x00000001)
- nouveau_event_trigger(priv->base.vblank, 1, i);
+ nouveau_disp_vblank(&priv->base, i);
nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0);
nv_rd32(priv, 0x6100c0 + (i * 0x800));
}
@@ -1365,6 +1255,10 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent);
+ if (ret)
+ return ret;
+
nv_engine(priv)->sclass = nvd0_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nvd0_disp_intr;
@@ -1396,9 +1290,11 @@ nvd0_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .base.vblank = &nvd0_disp_vblank_func,
.base.outp = nvd0_disp_outp_sclass,
.mthd.core = &nvd0_disp_mast_mthd_chan,
.mthd.base = &nvd0_disp_sync_mthd_chan,
.mthd.ovly = &nvd0_disp_ovly_mthd_chan,
.mthd.prev = -0x020000,
+ .head.scanoutpos = nvd0_disp_base_scanoutpos,
}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
index 11328e3f5df1..db144b2cf06b 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
@@ -25,7 +25,7 @@
#include <engine/software.h>
#include <engine/disp.h>
-#include <core/class.h>
+#include <nvif/class.h>
#include "nv50.h"
@@ -200,17 +200,17 @@ nve0_disp_ovly_mthd_chan = {
static struct nouveau_oclass
nve0_disp_sclass[] = {
- { NVE0_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs },
- { NVE0_DISP_SYNC_CLASS, &nvd0_disp_sync_ofuncs },
- { NVE0_DISP_OVLY_CLASS, &nvd0_disp_ovly_ofuncs },
- { NVE0_DISP_OIMM_CLASS, &nvd0_disp_oimm_ofuncs },
- { NVE0_DISP_CURS_CLASS, &nvd0_disp_curs_ofuncs },
+ { GK104_DISP_CORE_CHANNEL_DMA, &nvd0_disp_mast_ofuncs.base },
+ { GK104_DISP_BASE_CHANNEL_DMA, &nvd0_disp_sync_ofuncs.base },
+ { GK104_DISP_OVERLAY_CONTROL_DMA, &nvd0_disp_ovly_ofuncs.base },
+ { GK104_DISP_OVERLAY, &nvd0_disp_oimm_ofuncs.base },
+ { GK104_DISP_CURSOR, &nvd0_disp_curs_ofuncs.base },
{}
};
static struct nouveau_oclass
nve0_disp_base_oclass[] = {
- { NVE0_DISP_CLASS, &nvd0_disp_base_ofuncs, nvd0_disp_base_omthds },
+ { GK104_DISP, &nvd0_disp_base_ofuncs },
{}
};
@@ -233,6 +233,10 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent);
+ if (ret)
+ return ret;
+
nv_engine(priv)->sclass = nve0_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nvd0_disp_intr;
@@ -245,7 +249,7 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld;
- priv->sor.hdmi = nvd0_hdmi_ctrl;
+ priv->sor.hdmi = nve0_hdmi_ctrl;
return 0;
}
@@ -258,9 +262,11 @@ nve0_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .base.vblank = &nvd0_disp_vblank_func,
.base.outp = nvd0_disp_outp_sclass,
.mthd.core = &nve0_disp_mast_mthd_chan,
.mthd.base = &nvd0_disp_sync_mthd_chan,
.mthd.ovly = &nve0_disp_ovly_mthd_chan,
.mthd.prev = -0x020000,
+ .head.scanoutpos = nvd0_disp_base_scanoutpos,
}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c
index 104388081d73..402d7d67d806 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c
@@ -25,7 +25,7 @@
#include <engine/software.h>
#include <engine/disp.h>
-#include <core/class.h>
+#include <nvif/class.h>
#include "nv50.h"
@@ -35,17 +35,17 @@
static struct nouveau_oclass
nvf0_disp_sclass[] = {
- { NVF0_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs },
- { NVF0_DISP_SYNC_CLASS, &nvd0_disp_sync_ofuncs },
- { NVF0_DISP_OVLY_CLASS, &nvd0_disp_ovly_ofuncs },
- { NVF0_DISP_OIMM_CLASS, &nvd0_disp_oimm_ofuncs },
- { NVF0_DISP_CURS_CLASS, &nvd0_disp_curs_ofuncs },
+ { GK110_DISP_CORE_CHANNEL_DMA, &nvd0_disp_mast_ofuncs.base },
+ { GK110_DISP_BASE_CHANNEL_DMA, &nvd0_disp_sync_ofuncs.base },
+ { GK104_DISP_OVERLAY_CONTROL_DMA, &nvd0_disp_ovly_ofuncs.base },
+ { GK104_DISP_OVERLAY, &nvd0_disp_oimm_ofuncs.base },
+ { GK104_DISP_CURSOR, &nvd0_disp_curs_ofuncs.base },
{}
};
static struct nouveau_oclass
nvf0_disp_base_oclass[] = {
- { NVF0_DISP_CLASS, &nvd0_disp_base_ofuncs, nvd0_disp_base_omthds },
+ { GK110_DISP, &nvd0_disp_base_ofuncs },
{}
};
@@ -68,6 +68,10 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ ret = nvkm_event_init(&nvd0_disp_chan_uevent, 1, 17, &priv->uevent);
+ if (ret)
+ return ret;
+
nv_engine(priv)->sclass = nvf0_disp_base_oclass;
nv_engine(priv)->cclass = &nv50_disp_cclass;
nv_subdev(priv)->intr = nvd0_disp_intr;
@@ -80,7 +84,7 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld;
- priv->sor.hdmi = nvd0_hdmi_ctrl;
+ priv->sor.hdmi = nve0_hdmi_ctrl;
return 0;
}
@@ -93,9 +97,11 @@ nvf0_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .base.vblank = &nvd0_disp_vblank_func,
.base.outp = nvd0_disp_outp_sclass,
.mthd.core = &nve0_disp_mast_mthd_chan,
.mthd.base = &nvd0_disp_sync_mthd_chan,
.mthd.ovly = &nve0_disp_ovly_mthd_chan,
.mthd.prev = -0x020000,
+ .head.scanoutpos = nvd0_disp_base_scanoutpos,
}.base.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outp.c
index ad9ba7ccec7f..a5ff00a9cedc 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/outp.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/outp.c
@@ -78,6 +78,7 @@ nvkm_output_create_(struct nouveau_object *parent,
outp->info = *dcbE;
outp->index = index;
+ outp->or = ffs(outp->info.or) - 1;
DBG("type %02x loc %d or %d link %d con %x edid %x bus %d head %x\n",
dcbE->type, dcbE->location, dcbE->or, dcbE->type >= 2 ?
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.h b/drivers/gpu/drm/nouveau/core/engine/disp/outp.h
index bc76fbf85710..187f435ad0e2 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/outp.h
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/outp.h
@@ -9,6 +9,7 @@ struct nvkm_output {
struct dcb_output info;
int index;
+ int or;
struct nouveau_i2c_port *port;
struct nouveau_i2c_port *edid;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c
index eb2d7789555d..667a9070e006 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c
@@ -22,6 +22,9 @@
* Authors: Ben Skeggs
*/
+#include <core/os.h>
+#include <nvif/event.h>
+
#include <subdev/i2c.h>
#include "outpdp.h"
@@ -86,7 +89,7 @@ done:
atomic_set(&outp->lt.done, 0);
schedule_work(&outp->lt.work);
} else {
- nouveau_event_get(outp->irq);
+ nvkm_notify_get(&outp->irq);
}
if (wait) {
@@ -133,46 +136,59 @@ nvkm_output_dp_detect(struct nvkm_output_dp *outp)
}
}
-static void
-nvkm_output_dp_service_work(struct work_struct *work)
+static int
+nvkm_output_dp_hpd(struct nvkm_notify *notify)
{
- struct nvkm_output_dp *outp = container_of(work, typeof(*outp), work);
- struct nouveau_disp *disp = nouveau_disp(outp);
- int type = atomic_xchg(&outp->pending, 0);
- u32 send = 0;
-
- if (type & (NVKM_I2C_PLUG | NVKM_I2C_UNPLUG)) {
- nvkm_output_dp_detect(outp);
- if (type & NVKM_I2C_UNPLUG)
- send |= NVKM_HPD_UNPLUG;
- if (type & NVKM_I2C_PLUG)
- send |= NVKM_HPD_PLUG;
- nouveau_event_get(outp->base.conn->hpd.event);
- }
-
- if (type & NVKM_I2C_IRQ) {
- nvkm_output_dp_train(&outp->base, 0, true);
- send |= NVKM_HPD_IRQ;
+ struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd);
+ struct nvkm_output_dp *outp;
+ struct nouveau_disp *disp = nouveau_disp(conn);
+ const struct nvkm_i2c_ntfy_rep *line = notify->data;
+ struct nvif_notify_conn_rep_v0 rep = {};
+
+ list_for_each_entry(outp, &disp->outp, base.head) {
+ if (outp->base.conn == conn &&
+ outp->info.type == DCB_OUTPUT_DP) {
+ DBG("HPD: %d\n", line->mask);
+ nvkm_output_dp_detect(outp);
+
+ if (line->mask & NVKM_I2C_UNPLUG)
+ rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG;
+ if (line->mask & NVKM_I2C_PLUG)
+ rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG;
+
+ nvkm_event_send(&disp->hpd, rep.mask, conn->index,
+ &rep, sizeof(rep));
+ return NVKM_NOTIFY_KEEP;
+ }
}
- nouveau_event_trigger(disp->hpd, send, outp->base.info.connector);
+ WARN_ON(1);
+ return NVKM_NOTIFY_DROP;
}
static int
-nvkm_output_dp_service(void *data, u32 type, int index)
+nvkm_output_dp_irq(struct nvkm_notify *notify)
{
- struct nvkm_output_dp *outp = data;
- DBG("HPD: %d\n", type);
- atomic_or(type, &outp->pending);
- schedule_work(&outp->work);
- return NVKM_EVENT_DROP;
+ struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), irq);
+ struct nouveau_disp *disp = nouveau_disp(outp);
+ const struct nvkm_i2c_ntfy_rep *line = notify->data;
+ struct nvif_notify_conn_rep_v0 rep = {
+ .mask = NVIF_NOTIFY_CONN_V0_IRQ,
+ };
+ int index = outp->base.info.connector;
+
+ DBG("IRQ: %d\n", line->mask);
+ nvkm_output_dp_train(&outp->base, 0, true);
+
+ nvkm_event_send(&disp->hpd, rep.mask, index, &rep, sizeof(rep));
+ return NVKM_NOTIFY_DROP;
}
int
_nvkm_output_dp_fini(struct nouveau_object *object, bool suspend)
{
struct nvkm_output_dp *outp = (void *)object;
- nouveau_event_put(outp->irq);
+ nvkm_notify_put(&outp->irq);
nvkm_output_dp_enable(outp, false);
return nvkm_output_fini(&outp->base, suspend);
}
@@ -189,7 +205,7 @@ void
_nvkm_output_dp_dtor(struct nouveau_object *object)
{
struct nvkm_output_dp *outp = (void *)object;
- nouveau_event_ref(NULL, &outp->irq);
+ nvkm_notify_fini(&outp->irq);
nvkm_output_destroy(&outp->base);
}
@@ -213,7 +229,7 @@ nvkm_output_dp_create_(struct nouveau_object *parent,
if (ret)
return ret;
- nouveau_event_ref(NULL, &outp->base.conn->hpd.event);
+ nvkm_notify_fini(&outp->base.conn->hpd);
/* access to the aux channel is not optional... */
if (!outp->base.edid) {
@@ -238,20 +254,28 @@ nvkm_output_dp_create_(struct nouveau_object *parent,
atomic_set(&outp->lt.done, 0);
/* link maintenance */
- ret = nouveau_event_new(i2c->ntfy, NVKM_I2C_IRQ, outp->base.edid->index,
- nvkm_output_dp_service, outp, &outp->irq);
+ ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_irq, true,
+ &(struct nvkm_i2c_ntfy_req) {
+ .mask = NVKM_I2C_IRQ,
+ .port = outp->base.edid->index,
+ },
+ sizeof(struct nvkm_i2c_ntfy_req),
+ sizeof(struct nvkm_i2c_ntfy_rep),
+ &outp->irq);
if (ret) {
ERR("error monitoring aux irq event: %d\n", ret);
return ret;
}
- INIT_WORK(&outp->work, nvkm_output_dp_service_work);
-
/* hotplug detect, replaces gpio-based mechanism with aux events */
- ret = nouveau_event_new(i2c->ntfy, NVKM_I2C_PLUG | NVKM_I2C_UNPLUG,
- outp->base.edid->index,
- nvkm_output_dp_service, outp,
- &outp->base.conn->hpd.event);
+ ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_hpd, true,
+ &(struct nvkm_i2c_ntfy_req) {
+ .mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG,
+ .port = outp->base.edid->index,
+ },
+ sizeof(struct nvkm_i2c_ntfy_req),
+ sizeof(struct nvkm_i2c_ntfy_rep),
+ &outp->base.conn->hpd);
if (ret) {
ERR("error monitoring aux hpd events: %d\n", ret);
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h
index ff33ba12cb67..1fac367cc867 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h
@@ -12,10 +12,7 @@ struct nvkm_output_dp {
struct nvbios_dpout info;
u8 version;
- struct nouveau_eventh *irq;
- struct nouveau_eventh *hpd;
- struct work_struct work;
- atomic_t pending;
+ struct nvkm_notify irq;
bool present;
u8 dpcd[16];
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c
index fe0f256f11bf..d00f89a468a7 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c
@@ -22,8 +22,9 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
@@ -143,38 +144,29 @@ nv50_pior_dp_impl = {
*****************************************************************************/
int
-nv50_pior_power(struct nv50_disp_priv *priv, int or, u32 data)
+nv50_pior_power(NV50_DISP_MTHD_V1)
{
- const u32 stat = data & NV50_DISP_PIOR_PWR_STATE;
- const u32 soff = (or * 0x800);
+ const u32 soff = outp->or * 0x800;
+ union {
+ struct nv50_disp_pior_pwr_v0 v0;
+ } *args = data;
+ u32 ctrl, type;
+ int ret;
+
+ nv_ioctl(object, "disp pior pwr size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp pior pwr vers %d state %d type %x\n",
+ args->v0.version, args->v0.state, args->v0.type);
+ if (args->v0.type > 0x0f)
+ return -EINVAL;
+ ctrl = !!args->v0.state;
+ type = args->v0.type;
+ } else
+ return ret;
+
nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000);
- nv_mask(priv, 0x61e004 + soff, 0x80000101, 0x80000000 | stat);
+ nv_mask(priv, 0x61e004 + soff, 0x80000101, 0x80000000 | ctrl);
nv_wait(priv, 0x61e004 + soff, 0x80000000, 0x00000000);
+ priv->pior.type[outp->or] = type;
return 0;
}
-
-int
-nv50_pior_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
-{
- struct nv50_disp_priv *priv = (void *)object->engine;
- const u8 type = (mthd & NV50_DISP_PIOR_MTHD_TYPE) >> 12;
- const u8 or = (mthd & NV50_DISP_PIOR_MTHD_OR);
- u32 *data = args;
- int ret;
-
- if (size < sizeof(u32))
- return -EINVAL;
-
- mthd &= ~NV50_DISP_PIOR_MTHD_TYPE;
- mthd &= ~NV50_DISP_PIOR_MTHD_OR;
- switch (mthd) {
- case NV50_DISP_PIOR_PWR:
- ret = priv->pior.power(priv, or, data[0]);
- priv->pior.type[or] = type;
- break;
- default:
- return -EINVAL;
- }
-
- return ret;
-}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/priv.h b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h
index 26e9a42569c7..6a0511d54ce6 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/priv.h
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h
@@ -11,6 +11,7 @@ struct nouveau_disp_impl {
struct nouveau_oclass base;
struct nouveau_oclass **outp;
struct nouveau_oclass **conn;
+ const struct nvkm_event_func *vblank;
};
#define nouveau_disp_create(p,e,c,h,i,x,d) \
@@ -39,4 +40,9 @@ int _nouveau_disp_fini(struct nouveau_object *, bool);
extern struct nouveau_oclass *nvkm_output_oclass;
extern struct nouveau_oclass *nvkm_connector_oclass;
+int nouveau_disp_vblank_ctor(struct nouveau_object *, void *data, u32 size,
+ struct nvkm_notify *);
+void nouveau_disp_vblank(struct nouveau_disp *, int head);
+int nouveau_disp_ntfy(struct nouveau_object *, u32, struct nvkm_event **);
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
index 7a1ebdfa9e1b..ddf1760c4400 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
@@ -22,8 +22,9 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
@@ -32,77 +33,26 @@
#include "nv50.h"
int
-nv50_sor_power(struct nv50_disp_priv *priv, int or, u32 data)
+nv50_sor_power(NV50_DISP_MTHD_V1)
{
- const u32 stat = data & NV50_DISP_SOR_PWR_STATE;
- const u32 soff = (or * 0x800);
+ union {
+ struct nv50_disp_sor_pwr_v0 v0;
+ } *args = data;
+ const u32 soff = outp->or * 0x800;
+ u32 stat;
+ int ret;
+
+ nv_ioctl(object, "disp sor pwr size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "disp sor pwr vers %d state %d\n",
+ args->v0.version, args->v0.state);
+ stat = !!args->v0.state;
+ } else
+ return ret;
+
nv_wait(priv, 0x61c004 + soff, 0x80000000, 0x00000000);
nv_mask(priv, 0x61c004 + soff, 0x80000001, 0x80000000 | stat);
nv_wait(priv, 0x61c004 + soff, 0x80000000, 0x00000000);
nv_wait(priv, 0x61c030 + soff, 0x10000000, 0x00000000);
return 0;
}
-
-int
-nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
-{
- struct nv50_disp_priv *priv = (void *)object->engine;
- const u8 type = (mthd & NV50_DISP_SOR_MTHD_TYPE) >> 12;
- const u8 head = (mthd & NV50_DISP_SOR_MTHD_HEAD) >> 3;
- const u8 link = (mthd & NV50_DISP_SOR_MTHD_LINK) >> 2;
- const u8 or = (mthd & NV50_DISP_SOR_MTHD_OR);
- const u16 mask = (0x0100 << head) | (0x0040 << link) | (0x0001 << or);
- struct nvkm_output *outp = NULL, *temp;
- u32 data;
- int ret = -EINVAL;
-
- if (size < sizeof(u32))
- return -EINVAL;
- data = *(u32 *)args;
-
- list_for_each_entry(temp, &priv->base.outp, head) {
- if ((temp->info.hasht & 0xff) == type &&
- (temp->info.hashm & mask) == mask) {
- outp = temp;
- break;
- }
- }
-
- switch (mthd & ~0x3f) {
- case NV50_DISP_SOR_PWR:
- ret = priv->sor.power(priv, or, data);
- break;
- case NVA3_DISP_SOR_HDA_ELD:
- ret = priv->sor.hda_eld(priv, or, args, size);
- break;
- case NV84_DISP_SOR_HDMI_PWR:
- ret = priv->sor.hdmi(priv, head, or, data);
- break;
- case NV50_DISP_SOR_LVDS_SCRIPT:
- priv->sor.lvdsconf = data & NV50_DISP_SOR_LVDS_SCRIPT_ID;
- ret = 0;
- break;
- case NV94_DISP_SOR_DP_PWR:
- if (outp) {
- struct nvkm_output_dp *outpdp = (void *)outp;
- switch (data) {
- case NV94_DISP_SOR_DP_PWR_STATE_OFF:
- nouveau_event_put(outpdp->irq);
- ((struct nvkm_output_dp_impl *)nv_oclass(outp))
- ->lnk_pwr(outpdp, 0);
- atomic_set(&outpdp->lt.done, 0);
- break;
- case NV94_DISP_SOR_DP_PWR_STATE_ON:
- nvkm_output_dp_train(&outpdp->base, 0, true);
- break;
- default:
- return -EINVAL;
- }
- }
- break;
- default:
- BUG_ON(1);
- }
-
- return ret;
-}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
index 05487cda84a8..39f85d627336 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
@@ -23,7 +23,6 @@
*/
#include <core/os.h>
-#include <core/class.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
index 97f0e9cd3d40..7b7bbc3e459e 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
@@ -23,7 +23,6 @@
*/
#include <core/os.h>
-#include <core/class.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c
index 5103e88d1877..e1500f77a56a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c
@@ -23,98 +23,143 @@
*/
#include <core/object.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <subdev/fb.h>
-#include <engine/dmaobj.h>
+#include <subdev/instmem.h>
+
+#include "priv.h"
static int
-nouveau_dmaobj_ctor(struct nouveau_object *parent,
+nvkm_dmaobj_bind(struct nouveau_dmaobj *dmaobj, struct nouveau_object *parent,
+ struct nouveau_gpuobj **pgpuobj)
+{
+ const struct nvkm_dmaeng_impl *impl = (void *)
+ nv_oclass(nv_object(dmaobj)->engine);
+ int ret = 0;
+
+ if (nv_object(dmaobj) == parent) { /* ctor bind */
+ if (nv_mclass(parent->parent) == NV_DEVICE) {
+ /* delayed, or no, binding */
+ return 0;
+ }
+ ret = impl->bind(dmaobj, parent, pgpuobj);
+ if (ret == 0)
+ nouveau_object_ref(NULL, &parent);
+ return ret;
+ }
+
+ return impl->bind(dmaobj, parent, pgpuobj);
+}
+
+int
+nvkm_dmaobj_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
+ struct nouveau_oclass *oclass, void **pdata, u32 *psize,
+ int length, void **pobject)
{
- struct nouveau_dmaeng *dmaeng = (void *)engine;
+ union {
+ struct nv_dma_v0 v0;
+ } *args = *pdata;
+ struct nouveau_instmem *instmem = nouveau_instmem(parent);
+ struct nouveau_client *client = nouveau_client(parent);
+ struct nouveau_device *device = nv_device(parent);
+ struct nouveau_fb *pfb = nouveau_fb(parent);
struct nouveau_dmaobj *dmaobj;
- struct nouveau_gpuobj *gpuobj;
- struct nv_dma_class *args = data;
+ void *data = *pdata;
+ u32 size = *psize;
int ret;
- if (size < sizeof(*args))
- return -EINVAL;
-
- ret = nouveau_object_create(parent, engine, oclass, 0, &dmaobj);
- *pobject = nv_object(dmaobj);
+ ret = nouveau_object_create_(parent, engine, oclass, 0, length, pobject);
+ dmaobj = *pobject;
if (ret)
return ret;
- switch (args->flags & NV_DMA_TARGET_MASK) {
- case NV_DMA_TARGET_VM:
+ nv_ioctl(parent, "create dma size %d\n", *psize);
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ nv_ioctl(parent, "create dma vers %d target %d access %d "
+ "start %016llx limit %016llx\n",
+ args->v0.version, args->v0.target, args->v0.access,
+ args->v0.start, args->v0.limit);
+ dmaobj->target = args->v0.target;
+ dmaobj->access = args->v0.access;
+ dmaobj->start = args->v0.start;
+ dmaobj->limit = args->v0.limit;
+ } else
+ return ret;
+
+ *pdata = data;
+ *psize = size;
+
+ if (dmaobj->start > dmaobj->limit)
+ return -EINVAL;
+
+ switch (dmaobj->target) {
+ case NV_DMA_V0_TARGET_VM:
dmaobj->target = NV_MEM_TARGET_VM;
break;
- case NV_DMA_TARGET_VRAM:
+ case NV_DMA_V0_TARGET_VRAM:
+ if (!client->super) {
+ if (dmaobj->limit >= pfb->ram->size - instmem->reserved)
+ return -EACCES;
+ if (device->card_type >= NV_50)
+ return -EACCES;
+ }
dmaobj->target = NV_MEM_TARGET_VRAM;
break;
- case NV_DMA_TARGET_PCI:
+ case NV_DMA_V0_TARGET_PCI:
+ if (!client->super)
+ return -EACCES;
dmaobj->target = NV_MEM_TARGET_PCI;
break;
- case NV_DMA_TARGET_PCI_US:
- case NV_DMA_TARGET_AGP:
+ case NV_DMA_V0_TARGET_PCI_US:
+ case NV_DMA_V0_TARGET_AGP:
+ if (!client->super)
+ return -EACCES;
dmaobj->target = NV_MEM_TARGET_PCI_NOSNOOP;
break;
default:
return -EINVAL;
}
- switch (args->flags & NV_DMA_ACCESS_MASK) {
- case NV_DMA_ACCESS_VM:
+ switch (dmaobj->access) {
+ case NV_DMA_V0_ACCESS_VM:
dmaobj->access = NV_MEM_ACCESS_VM;
break;
- case NV_DMA_ACCESS_RD:
+ case NV_DMA_V0_ACCESS_RD:
dmaobj->access = NV_MEM_ACCESS_RO;
break;
- case NV_DMA_ACCESS_WR:
+ case NV_DMA_V0_ACCESS_WR:
dmaobj->access = NV_MEM_ACCESS_WO;
break;
- case NV_DMA_ACCESS_RDWR:
+ case NV_DMA_V0_ACCESS_RDWR:
dmaobj->access = NV_MEM_ACCESS_RW;
break;
default:
return -EINVAL;
}
- dmaobj->start = args->start;
- dmaobj->limit = args->limit;
- dmaobj->conf0 = args->conf0;
-
- switch (nv_mclass(parent)) {
- case NV_DEVICE_CLASS:
- /* delayed, or no, binding */
- break;
- default:
- ret = dmaeng->bind(dmaeng, *pobject, dmaobj, &gpuobj);
- if (ret == 0) {
- nouveau_object_ref(NULL, pobject);
- *pobject = nv_object(gpuobj);
- }
- break;
- }
-
return ret;
}
-static struct nouveau_ofuncs
-nouveau_dmaobj_ofuncs = {
- .ctor = nouveau_dmaobj_ctor,
- .dtor = nouveau_object_destroy,
- .init = nouveau_object_init,
- .fini = nouveau_object_fini,
-};
-
-struct nouveau_oclass
-nouveau_dmaobj_sclass[] = {
- { NV_DMA_FROM_MEMORY_CLASS, &nouveau_dmaobj_ofuncs },
- { NV_DMA_TO_MEMORY_CLASS, &nouveau_dmaobj_ofuncs },
- { NV_DMA_IN_MEMORY_CLASS, &nouveau_dmaobj_ofuncs },
- {}
-};
+int
+_nvkm_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ const struct nvkm_dmaeng_impl *impl = (void *)oclass;
+ struct nouveau_dmaeng *dmaeng;
+ int ret;
+
+ ret = nouveau_engine_create(parent, engine, oclass, true, "DMAOBJ",
+ "dmaobj", &dmaeng);
+ *pobject = nv_object(dmaeng);
+ if (ret)
+ return ret;
+
+ nv_engine(dmaeng)->sclass = impl->sclass;
+ dmaeng->bind = nvkm_dmaobj_bind;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c
index 027d8217c0fa..20c9dbfe3b2e 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c
@@ -23,121 +23,143 @@
*/
#include <core/gpuobj.h>
-#include <core/class.h>
+#include <nvif/class.h>
#include <subdev/fb.h>
#include <subdev/vm/nv04.h>
-#include <engine/dmaobj.h>
+#include "priv.h"
-struct nv04_dmaeng_priv {
- struct nouveau_dmaeng base;
+struct nv04_dmaobj_priv {
+ struct nouveau_dmaobj base;
+ bool clone;
+ u32 flags0;
+ u32 flags2;
};
static int
-nv04_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
+nv04_dmaobj_bind(struct nouveau_dmaobj *dmaobj,
struct nouveau_object *parent,
- struct nouveau_dmaobj *dmaobj,
struct nouveau_gpuobj **pgpuobj)
{
- struct nv04_vmmgr_priv *vmm = nv04_vmmgr(dmaeng);
+ struct nv04_dmaobj_priv *priv = (void *)dmaobj;
struct nouveau_gpuobj *gpuobj;
- u32 flags0 = nv_mclass(dmaobj);
- u32 flags2 = 0x00000000;
- u64 offset = dmaobj->start & 0xfffff000;
- u64 adjust = dmaobj->start & 0x00000fff;
- u32 length = dmaobj->limit - dmaobj->start;
+ u64 offset = priv->base.start & 0xfffff000;
+ u64 adjust = priv->base.start & 0x00000fff;
+ u32 length = priv->base.limit - priv->base.start;
int ret;
if (!nv_iclass(parent, NV_ENGCTX_CLASS)) {
switch (nv_mclass(parent->parent)) {
- case NV03_CHANNEL_DMA_CLASS:
- case NV10_CHANNEL_DMA_CLASS:
- case NV17_CHANNEL_DMA_CLASS:
- case NV40_CHANNEL_DMA_CLASS:
+ case NV03_CHANNEL_DMA:
+ case NV10_CHANNEL_DMA:
+ case NV17_CHANNEL_DMA:
+ case NV40_CHANNEL_DMA:
break;
default:
return -EINVAL;
}
}
- if (dmaobj->target == NV_MEM_TARGET_VM) {
- if (nv_object(vmm)->oclass == &nv04_vmmgr_oclass) {
- struct nouveau_gpuobj *pgt = vmm->vm->pgt[0].obj[0];
- if (!dmaobj->start)
- return nouveau_gpuobj_dup(parent, pgt, pgpuobj);
- offset = nv_ro32(pgt, 8 + (offset >> 10));
- offset &= 0xfffff000;
- }
+ if (priv->clone) {
+ struct nv04_vmmgr_priv *vmm = nv04_vmmgr(dmaobj);
+ struct nouveau_gpuobj *pgt = vmm->vm->pgt[0].obj[0];
+ if (!dmaobj->start)
+ return nouveau_gpuobj_dup(parent, pgt, pgpuobj);
+ offset = nv_ro32(pgt, 8 + (offset >> 10));
+ offset &= 0xfffff000;
+ }
+
+ ret = nouveau_gpuobj_new(parent, parent, 16, 16, 0, &gpuobj);
+ *pgpuobj = gpuobj;
+ if (ret == 0) {
+ nv_wo32(*pgpuobj, 0x00, priv->flags0 | (adjust << 20));
+ nv_wo32(*pgpuobj, 0x04, length);
+ nv_wo32(*pgpuobj, 0x08, priv->flags2 | offset);
+ nv_wo32(*pgpuobj, 0x0c, priv->flags2 | offset);
+ }
+
+ return ret;
+}
+
+static int
+nv04_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct nouveau_dmaeng *dmaeng = (void *)engine;
+ struct nv04_vmmgr_priv *vmm = nv04_vmmgr(engine);
+ struct nv04_dmaobj_priv *priv;
+ int ret;
+
+ ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
+ *pobject = nv_object(priv);
+ if (ret || (ret = -ENOSYS, size))
+ return ret;
- dmaobj->target = NV_MEM_TARGET_PCI;
- dmaobj->access = NV_MEM_ACCESS_RW;
+ if (priv->base.target == NV_MEM_TARGET_VM) {
+ if (nv_object(vmm)->oclass == &nv04_vmmgr_oclass)
+ priv->clone = true;
+ priv->base.target = NV_MEM_TARGET_PCI;
+ priv->base.access = NV_MEM_ACCESS_RW;
}
- switch (dmaobj->target) {
+ priv->flags0 = nv_mclass(priv);
+ switch (priv->base.target) {
case NV_MEM_TARGET_VRAM:
- flags0 |= 0x00003000;
+ priv->flags0 |= 0x00003000;
break;
case NV_MEM_TARGET_PCI:
- flags0 |= 0x00023000;
+ priv->flags0 |= 0x00023000;
break;
case NV_MEM_TARGET_PCI_NOSNOOP:
- flags0 |= 0x00033000;
+ priv->flags0 |= 0x00033000;
break;
default:
return -EINVAL;
}
- switch (dmaobj->access) {
+ switch (priv->base.access) {
case NV_MEM_ACCESS_RO:
- flags0 |= 0x00004000;
+ priv->flags0 |= 0x00004000;
break;
case NV_MEM_ACCESS_WO:
- flags0 |= 0x00008000;
+ priv->flags0 |= 0x00008000;
case NV_MEM_ACCESS_RW:
- flags2 |= 0x00000002;
+ priv->flags2 |= 0x00000002;
break;
default:
return -EINVAL;
}
- ret = nouveau_gpuobj_new(parent, parent, 16, 16, 0, &gpuobj);
- *pgpuobj = gpuobj;
- if (ret == 0) {
- nv_wo32(*pgpuobj, 0x00, flags0 | (adjust << 20));
- nv_wo32(*pgpuobj, 0x04, length);
- nv_wo32(*pgpuobj, 0x08, flags2 | offset);
- nv_wo32(*pgpuobj, 0x0c, flags2 | offset);
- }
-
- return ret;
+ return dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject);
}
-static int
-nv04_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv04_dmaeng_priv *priv;
- int ret;
-
- ret = nouveau_dmaeng_create(parent, engine, oclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
+static struct nouveau_ofuncs
+nv04_dmaobj_ofuncs = {
+ .ctor = nv04_dmaobj_ctor,
+ .dtor = _nvkm_dmaobj_dtor,
+ .init = _nvkm_dmaobj_init,
+ .fini = _nvkm_dmaobj_fini,
+};
- nv_engine(priv)->sclass = nouveau_dmaobj_sclass;
- priv->base.bind = nv04_dmaobj_bind;
- return 0;
-}
+static struct nouveau_oclass
+nv04_dmaeng_sclass[] = {
+ { NV_DMA_FROM_MEMORY, &nv04_dmaobj_ofuncs },
+ { NV_DMA_TO_MEMORY, &nv04_dmaobj_ofuncs },
+ { NV_DMA_IN_MEMORY, &nv04_dmaobj_ofuncs },
+ {}
+};
-struct nouveau_oclass
-nv04_dmaeng_oclass = {
- .handle = NV_ENGINE(DMAOBJ, 0x04),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nv04_dmaeng_ctor,
- .dtor = _nouveau_dmaeng_dtor,
- .init = _nouveau_dmaeng_init,
- .fini = _nouveau_dmaeng_fini,
+struct nouveau_oclass *
+nv04_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
+ .base.handle = NV_ENGINE(DMAOBJ, 0x04),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nvkm_dmaeng_ctor,
+ .dtor = _nvkm_dmaeng_dtor,
+ .init = _nvkm_dmaeng_init,
+ .fini = _nvkm_dmaeng_fini,
},
-};
+ .sclass = nv04_dmaeng_sclass,
+ .bind = nv04_dmaobj_bind,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c
index 750183f7c057..a740ddba2ee2 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c
@@ -22,140 +22,176 @@
* Authors: Ben Skeggs
*/
+#include <core/client.h>
#include <core/gpuobj.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <subdev/fb.h>
-#include <engine/dmaobj.h>
-struct nv50_dmaeng_priv {
- struct nouveau_dmaeng base;
+#include "priv.h"
+
+struct nv50_dmaobj_priv {
+ struct nouveau_dmaobj base;
+ u32 flags0;
+ u32 flags5;
};
static int
-nv50_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
+nv50_dmaobj_bind(struct nouveau_dmaobj *dmaobj,
struct nouveau_object *parent,
- struct nouveau_dmaobj *dmaobj,
struct nouveau_gpuobj **pgpuobj)
{
- u32 flags0 = nv_mclass(dmaobj);
- u32 flags5 = 0x00000000;
+ struct nv50_dmaobj_priv *priv = (void *)dmaobj;
int ret;
if (!nv_iclass(parent, NV_ENGCTX_CLASS)) {
switch (nv_mclass(parent->parent)) {
- case NV50_CHANNEL_DMA_CLASS:
- case NV84_CHANNEL_DMA_CLASS:
- case NV50_CHANNEL_IND_CLASS:
- case NV84_CHANNEL_IND_CLASS:
- case NV50_DISP_MAST_CLASS:
- case NV84_DISP_MAST_CLASS:
- case NV94_DISP_MAST_CLASS:
- case NVA0_DISP_MAST_CLASS:
- case NVA3_DISP_MAST_CLASS:
- case NV50_DISP_SYNC_CLASS:
- case NV84_DISP_SYNC_CLASS:
- case NV94_DISP_SYNC_CLASS:
- case NVA0_DISP_SYNC_CLASS:
- case NVA3_DISP_SYNC_CLASS:
- case NV50_DISP_OVLY_CLASS:
- case NV84_DISP_OVLY_CLASS:
- case NV94_DISP_OVLY_CLASS:
- case NVA0_DISP_OVLY_CLASS:
- case NVA3_DISP_OVLY_CLASS:
+ case NV40_CHANNEL_DMA:
+ case NV50_CHANNEL_GPFIFO:
+ case G82_CHANNEL_GPFIFO:
+ case NV50_DISP_CORE_CHANNEL_DMA:
+ case G82_DISP_CORE_CHANNEL_DMA:
+ case GT206_DISP_CORE_CHANNEL_DMA:
+ case GT200_DISP_CORE_CHANNEL_DMA:
+ case GT214_DISP_CORE_CHANNEL_DMA:
+ case NV50_DISP_BASE_CHANNEL_DMA:
+ case G82_DISP_BASE_CHANNEL_DMA:
+ case GT200_DISP_BASE_CHANNEL_DMA:
+ case GT214_DISP_BASE_CHANNEL_DMA:
+ case NV50_DISP_OVERLAY_CHANNEL_DMA:
+ case G82_DISP_OVERLAY_CHANNEL_DMA:
+ case GT200_DISP_OVERLAY_CHANNEL_DMA:
+ case GT214_DISP_OVERLAY_CHANNEL_DMA:
break;
default:
return -EINVAL;
}
}
- if (!(dmaobj->conf0 & NV50_DMA_CONF0_ENABLE)) {
- if (dmaobj->target == NV_MEM_TARGET_VM) {
- dmaobj->conf0 = NV50_DMA_CONF0_PRIV_VM;
- dmaobj->conf0 |= NV50_DMA_CONF0_PART_VM;
- dmaobj->conf0 |= NV50_DMA_CONF0_COMP_VM;
- dmaobj->conf0 |= NV50_DMA_CONF0_TYPE_VM;
+ ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
+ if (ret == 0) {
+ nv_wo32(*pgpuobj, 0x00, priv->flags0 | nv_mclass(dmaobj));
+ nv_wo32(*pgpuobj, 0x04, lower_32_bits(priv->base.limit));
+ nv_wo32(*pgpuobj, 0x08, lower_32_bits(priv->base.start));
+ nv_wo32(*pgpuobj, 0x0c, upper_32_bits(priv->base.limit) << 24 |
+ upper_32_bits(priv->base.start));
+ nv_wo32(*pgpuobj, 0x10, 0x00000000);
+ nv_wo32(*pgpuobj, 0x14, priv->flags5);
+ }
+
+ return ret;
+}
+
+static int
+nv50_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct nouveau_dmaeng *dmaeng = (void *)engine;
+ union {
+ struct nv50_dma_v0 v0;
+ } *args;
+ struct nv50_dmaobj_priv *priv;
+ u32 user, part, comp, kind;
+ int ret;
+
+ ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+ args = data;
+
+ nv_ioctl(parent, "create nv50 dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create nv50 dma vers %d priv %d part %d "
+ "comp %d kind %02x\n", args->v0.version,
+ args->v0.priv, args->v0.part, args->v0.comp,
+ args->v0.kind);
+ user = args->v0.priv;
+ part = args->v0.part;
+ comp = args->v0.comp;
+ kind = args->v0.kind;
+ } else
+ if (size == 0) {
+ if (priv->base.target != NV_MEM_TARGET_VM) {
+ user = NV50_DMA_V0_PRIV_US;
+ part = NV50_DMA_V0_PART_256;
+ comp = NV50_DMA_V0_COMP_NONE;
+ kind = NV50_DMA_V0_KIND_PITCH;
} else {
- dmaobj->conf0 = NV50_DMA_CONF0_PRIV_US;
- dmaobj->conf0 |= NV50_DMA_CONF0_PART_256;
- dmaobj->conf0 |= NV50_DMA_CONF0_COMP_NONE;
- dmaobj->conf0 |= NV50_DMA_CONF0_TYPE_LINEAR;
+ user = NV50_DMA_V0_PRIV_VM;
+ part = NV50_DMA_V0_PART_VM;
+ comp = NV50_DMA_V0_COMP_VM;
+ kind = NV50_DMA_V0_KIND_VM;
}
- }
+ } else
+ return ret;
- flags0 |= (dmaobj->conf0 & NV50_DMA_CONF0_COMP) << 22;
- flags0 |= (dmaobj->conf0 & NV50_DMA_CONF0_TYPE) << 22;
- flags0 |= (dmaobj->conf0 & NV50_DMA_CONF0_PRIV);
- flags5 |= (dmaobj->conf0 & NV50_DMA_CONF0_PART);
+ if (user > 2 || part > 2 || comp > 3 || kind > 0x7f)
+ return -EINVAL;
+ priv->flags0 = (comp << 29) | (kind << 22) | (user << 20);
+ priv->flags5 = (part << 16);
- switch (dmaobj->target) {
+ switch (priv->base.target) {
case NV_MEM_TARGET_VM:
- flags0 |= 0x00000000;
+ priv->flags0 |= 0x00000000;
break;
case NV_MEM_TARGET_VRAM:
- flags0 |= 0x00010000;
+ priv->flags0 |= 0x00010000;
break;
case NV_MEM_TARGET_PCI:
- flags0 |= 0x00020000;
+ priv->flags0 |= 0x00020000;
break;
case NV_MEM_TARGET_PCI_NOSNOOP:
- flags0 |= 0x00030000;
+ priv->flags0 |= 0x00030000;
break;
default:
return -EINVAL;
}
- switch (dmaobj->access) {
+ switch (priv->base.access) {
case NV_MEM_ACCESS_VM:
break;
case NV_MEM_ACCESS_RO:
- flags0 |= 0x00040000;
+ priv->flags0 |= 0x00040000;
break;
case NV_MEM_ACCESS_WO:
case NV_MEM_ACCESS_RW:
- flags0 |= 0x00080000;
+ priv->flags0 |= 0x00080000;
break;
+ default:
+ return -EINVAL;
}
- ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
- if (ret == 0) {
- nv_wo32(*pgpuobj, 0x00, flags0);
- nv_wo32(*pgpuobj, 0x04, lower_32_bits(dmaobj->limit));
- nv_wo32(*pgpuobj, 0x08, lower_32_bits(dmaobj->start));
- nv_wo32(*pgpuobj, 0x0c, upper_32_bits(dmaobj->limit) << 24 |
- upper_32_bits(dmaobj->start));
- nv_wo32(*pgpuobj, 0x10, 0x00000000);
- nv_wo32(*pgpuobj, 0x14, flags5);
- }
-
- return ret;
+ return dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject);
}
-static int
-nv50_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv50_dmaeng_priv *priv;
- int ret;
-
- ret = nouveau_dmaeng_create(parent, engine, oclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
+static struct nouveau_ofuncs
+nv50_dmaobj_ofuncs = {
+ .ctor = nv50_dmaobj_ctor,
+ .dtor = _nvkm_dmaobj_dtor,
+ .init = _nvkm_dmaobj_init,
+ .fini = _nvkm_dmaobj_fini,
+};
- nv_engine(priv)->sclass = nouveau_dmaobj_sclass;
- priv->base.bind = nv50_dmaobj_bind;
- return 0;
-}
+static struct nouveau_oclass
+nv50_dmaeng_sclass[] = {
+ { NV_DMA_FROM_MEMORY, &nv50_dmaobj_ofuncs },
+ { NV_DMA_TO_MEMORY, &nv50_dmaobj_ofuncs },
+ { NV_DMA_IN_MEMORY, &nv50_dmaobj_ofuncs },
+ {}
+};
-struct nouveau_oclass
-nv50_dmaeng_oclass = {
- .handle = NV_ENGINE(DMAOBJ, 0x50),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nv50_dmaeng_ctor,
- .dtor = _nouveau_dmaeng_dtor,
- .init = _nouveau_dmaeng_init,
- .fini = _nouveau_dmaeng_fini,
+struct nouveau_oclass *
+nv50_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
+ .base.handle = NV_ENGINE(DMAOBJ, 0x50),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nvkm_dmaeng_ctor,
+ .dtor = _nvkm_dmaeng_dtor,
+ .init = _nvkm_dmaeng_init,
+ .fini = _nvkm_dmaeng_fini,
},
-};
+ .sclass = nv50_dmaeng_sclass,
+ .bind = nv50_dmaobj_bind,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c
index cd3970d03b80..88ec33b20048 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c
@@ -22,32 +22,35 @@
* Authors: Ben Skeggs
*/
+#include <core/client.h>
#include <core/device.h>
#include <core/gpuobj.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <subdev/fb.h>
-#include <engine/dmaobj.h>
-struct nvc0_dmaeng_priv {
- struct nouveau_dmaeng base;
+#include "priv.h"
+
+struct nvc0_dmaobj_priv {
+ struct nouveau_dmaobj base;
+ u32 flags0;
+ u32 flags5;
};
static int
-nvc0_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
+nvc0_dmaobj_bind(struct nouveau_dmaobj *dmaobj,
struct nouveau_object *parent,
- struct nouveau_dmaobj *dmaobj,
struct nouveau_gpuobj **pgpuobj)
{
- u32 flags0 = nv_mclass(dmaobj);
- u32 flags5 = 0x00000000;
+ struct nvc0_dmaobj_priv *priv = (void *)dmaobj;
int ret;
if (!nv_iclass(parent, NV_ENGCTX_CLASS)) {
switch (nv_mclass(parent->parent)) {
- case NVA3_DISP_MAST_CLASS:
- case NVA3_DISP_SYNC_CLASS:
- case NVA3_DISP_OVLY_CLASS:
+ case GT214_DISP_CORE_CHANNEL_DMA:
+ case GT214_DISP_BASE_CHANNEL_DMA:
+ case GT214_DISP_OVERLAY_CHANNEL_DMA:
break;
default:
return -EINVAL;
@@ -55,89 +58,122 @@ nvc0_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
} else
return 0;
- if (!(dmaobj->conf0 & NVC0_DMA_CONF0_ENABLE)) {
- if (dmaobj->target == NV_MEM_TARGET_VM) {
- dmaobj->conf0 = NVC0_DMA_CONF0_PRIV_VM;
- dmaobj->conf0 |= NVC0_DMA_CONF0_TYPE_VM;
+ ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
+ if (ret == 0) {
+ nv_wo32(*pgpuobj, 0x00, priv->flags0 | nv_mclass(dmaobj));
+ nv_wo32(*pgpuobj, 0x04, lower_32_bits(priv->base.limit));
+ nv_wo32(*pgpuobj, 0x08, lower_32_bits(priv->base.start));
+ nv_wo32(*pgpuobj, 0x0c, upper_32_bits(priv->base.limit) << 24 |
+ upper_32_bits(priv->base.start));
+ nv_wo32(*pgpuobj, 0x10, 0x00000000);
+ nv_wo32(*pgpuobj, 0x14, priv->flags5);
+ }
+
+ return ret;
+}
+
+static int
+nvc0_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct nouveau_dmaeng *dmaeng = (void *)engine;
+ union {
+ struct gf100_dma_v0 v0;
+ } *args;
+ struct nvc0_dmaobj_priv *priv;
+ u32 kind, user, unkn;
+ int ret;
+
+ ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+ args = data;
+
+ nv_ioctl(parent, "create gf100 dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create gf100 dma vers %d priv %d kind %02x\n",
+ args->v0.version, args->v0.priv, args->v0.kind);
+ kind = args->v0.kind;
+ user = args->v0.priv;
+ unkn = 0;
+ } else
+ if (size == 0) {
+ if (priv->base.target != NV_MEM_TARGET_VM) {
+ kind = GF100_DMA_V0_KIND_PITCH;
+ user = GF100_DMA_V0_PRIV_US;
+ unkn = 2;
} else {
- dmaobj->conf0 = NVC0_DMA_CONF0_PRIV_US;
- dmaobj->conf0 |= NVC0_DMA_CONF0_TYPE_LINEAR;
- dmaobj->conf0 |= 0x00020000;
+ kind = GF100_DMA_V0_KIND_VM;
+ user = GF100_DMA_V0_PRIV_VM;
+ unkn = 0;
}
- }
+ } else
+ return ret;
- flags0 |= (dmaobj->conf0 & NVC0_DMA_CONF0_TYPE) << 22;
- flags0 |= (dmaobj->conf0 & NVC0_DMA_CONF0_PRIV);
- flags5 |= (dmaobj->conf0 & NVC0_DMA_CONF0_UNKN);
+ if (user > 2)
+ return -EINVAL;
+ priv->flags0 |= (kind << 22) | (user << 20);
+ priv->flags5 |= (unkn << 16);
- switch (dmaobj->target) {
+ switch (priv->base.target) {
case NV_MEM_TARGET_VM:
- flags0 |= 0x00000000;
+ priv->flags0 |= 0x00000000;
break;
case NV_MEM_TARGET_VRAM:
- flags0 |= 0x00010000;
+ priv->flags0 |= 0x00010000;
break;
case NV_MEM_TARGET_PCI:
- flags0 |= 0x00020000;
+ priv->flags0 |= 0x00020000;
break;
case NV_MEM_TARGET_PCI_NOSNOOP:
- flags0 |= 0x00030000;
+ priv->flags0 |= 0x00030000;
break;
default:
return -EINVAL;
}
- switch (dmaobj->access) {
+ switch (priv->base.access) {
case NV_MEM_ACCESS_VM:
break;
case NV_MEM_ACCESS_RO:
- flags0 |= 0x00040000;
+ priv->flags0 |= 0x00040000;
break;
case NV_MEM_ACCESS_WO:
case NV_MEM_ACCESS_RW:
- flags0 |= 0x00080000;
+ priv->flags0 |= 0x00080000;
break;
}
- ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
- if (ret == 0) {
- nv_wo32(*pgpuobj, 0x00, flags0);
- nv_wo32(*pgpuobj, 0x04, lower_32_bits(dmaobj->limit));
- nv_wo32(*pgpuobj, 0x08, lower_32_bits(dmaobj->start));
- nv_wo32(*pgpuobj, 0x0c, upper_32_bits(dmaobj->limit) << 24 |
- upper_32_bits(dmaobj->start));
- nv_wo32(*pgpuobj, 0x10, 0x00000000);
- nv_wo32(*pgpuobj, 0x14, flags5);
- }
-
- return ret;
+ return dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject);
}
-static int
-nvc0_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nvc0_dmaeng_priv *priv;
- int ret;
-
- ret = nouveau_dmaeng_create(parent, engine, oclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
+static struct nouveau_ofuncs
+nvc0_dmaobj_ofuncs = {
+ .ctor = nvc0_dmaobj_ctor,
+ .dtor = _nvkm_dmaobj_dtor,
+ .init = _nvkm_dmaobj_init,
+ .fini = _nvkm_dmaobj_fini,
+};
- nv_engine(priv)->sclass = nouveau_dmaobj_sclass;
- priv->base.bind = nvc0_dmaobj_bind;
- return 0;
-}
+static struct nouveau_oclass
+nvc0_dmaeng_sclass[] = {
+ { NV_DMA_FROM_MEMORY, &nvc0_dmaobj_ofuncs },
+ { NV_DMA_TO_MEMORY, &nvc0_dmaobj_ofuncs },
+ { NV_DMA_IN_MEMORY, &nvc0_dmaobj_ofuncs },
+ {}
+};
-struct nouveau_oclass
-nvc0_dmaeng_oclass = {
- .handle = NV_ENGINE(DMAOBJ, 0xc0),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nvc0_dmaeng_ctor,
- .dtor = _nouveau_dmaeng_dtor,
- .init = _nouveau_dmaeng_init,
- .fini = _nouveau_dmaeng_fini,
+struct nouveau_oclass *
+nvc0_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
+ .base.handle = NV_ENGINE(DMAOBJ, 0xc0),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nvkm_dmaeng_ctor,
+ .dtor = _nvkm_dmaeng_dtor,
+ .init = _nvkm_dmaeng_init,
+ .fini = _nvkm_dmaeng_fini,
},
-};
+ .sclass = nvc0_dmaeng_sclass,
+ .bind = nvc0_dmaobj_bind,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c
index 1cfb3bb90131..3fc4f0b0eaca 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c
@@ -22,40 +22,40 @@
* Authors: Ben Skeggs
*/
+#include <core/client.h>
#include <core/device.h>
#include <core/gpuobj.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <subdev/fb.h>
-#include <engine/dmaobj.h>
-struct nvd0_dmaeng_priv {
- struct nouveau_dmaeng base;
+#include "priv.h"
+
+struct nvd0_dmaobj_priv {
+ struct nouveau_dmaobj base;
+ u32 flags0;
};
static int
-nvd0_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
+nvd0_dmaobj_bind(struct nouveau_dmaobj *dmaobj,
struct nouveau_object *parent,
- struct nouveau_dmaobj *dmaobj,
struct nouveau_gpuobj **pgpuobj)
{
- u32 flags0 = 0x00000000;
+ struct nvd0_dmaobj_priv *priv = (void *)dmaobj;
int ret;
if (!nv_iclass(parent, NV_ENGCTX_CLASS)) {
switch (nv_mclass(parent->parent)) {
- case NVD0_DISP_MAST_CLASS:
- case NVD0_DISP_SYNC_CLASS:
- case NVD0_DISP_OVLY_CLASS:
- case NVE0_DISP_MAST_CLASS:
- case NVE0_DISP_SYNC_CLASS:
- case NVE0_DISP_OVLY_CLASS:
- case NVF0_DISP_MAST_CLASS:
- case NVF0_DISP_SYNC_CLASS:
- case NVF0_DISP_OVLY_CLASS:
- case GM107_DISP_MAST_CLASS:
- case GM107_DISP_SYNC_CLASS:
- case GM107_DISP_OVLY_CLASS:
+ case GF110_DISP_CORE_CHANNEL_DMA:
+ case GK104_DISP_CORE_CHANNEL_DMA:
+ case GK110_DISP_CORE_CHANNEL_DMA:
+ case GM107_DISP_CORE_CHANNEL_DMA:
+ case GF110_DISP_BASE_CHANNEL_DMA:
+ case GK104_DISP_BASE_CHANNEL_DMA:
+ case GK110_DISP_BASE_CHANNEL_DMA:
+ case GF110_DISP_OVERLAY_CONTROL_DMA:
+ case GK104_DISP_OVERLAY_CONTROL_DMA:
break;
default:
return -EINVAL;
@@ -63,33 +63,11 @@ nvd0_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
} else
return 0;
- if (!(dmaobj->conf0 & NVD0_DMA_CONF0_ENABLE)) {
- if (dmaobj->target == NV_MEM_TARGET_VM) {
- dmaobj->conf0 |= NVD0_DMA_CONF0_TYPE_VM;
- dmaobj->conf0 |= NVD0_DMA_CONF0_PAGE_LP;
- } else {
- dmaobj->conf0 |= NVD0_DMA_CONF0_TYPE_LINEAR;
- dmaobj->conf0 |= NVD0_DMA_CONF0_PAGE_SP;
- }
- }
-
- flags0 |= (dmaobj->conf0 & NVD0_DMA_CONF0_TYPE) << 20;
- flags0 |= (dmaobj->conf0 & NVD0_DMA_CONF0_PAGE) >> 4;
-
- switch (dmaobj->target) {
- case NV_MEM_TARGET_VRAM:
- flags0 |= 0x00000009;
- break;
- default:
- return -EINVAL;
- break;
- }
-
ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
if (ret == 0) {
- nv_wo32(*pgpuobj, 0x00, flags0);
- nv_wo32(*pgpuobj, 0x04, dmaobj->start >> 8);
- nv_wo32(*pgpuobj, 0x08, dmaobj->limit >> 8);
+ nv_wo32(*pgpuobj, 0x00, priv->flags0);
+ nv_wo32(*pgpuobj, 0x04, priv->base.start >> 8);
+ nv_wo32(*pgpuobj, 0x08, priv->base.limit >> 8);
nv_wo32(*pgpuobj, 0x0c, 0x00000000);
nv_wo32(*pgpuobj, 0x10, 0x00000000);
nv_wo32(*pgpuobj, 0x14, 0x00000000);
@@ -99,30 +77,91 @@ nvd0_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
}
static int
-nvd0_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+nvd0_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
- struct nvd0_dmaeng_priv *priv;
+ struct nouveau_dmaeng *dmaeng = (void *)engine;
+ union {
+ struct gf110_dma_v0 v0;
+ } *args;
+ struct nvd0_dmaobj_priv *priv;
+ u32 kind, page;
int ret;
- ret = nouveau_dmaeng_create(parent, engine, oclass, &priv);
+ ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
+ args = data;
- nv_engine(priv)->sclass = nouveau_dmaobj_sclass;
- priv->base.bind = nvd0_dmaobj_bind;
- return 0;
+ nv_ioctl(parent, "create gf110 dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create gf100 dma vers %d page %d kind %02x\n",
+ args->v0.version, args->v0.page, args->v0.kind);
+ kind = args->v0.kind;
+ page = args->v0.page;
+ } else
+ if (size == 0) {
+ if (priv->base.target != NV_MEM_TARGET_VM) {
+ kind = GF110_DMA_V0_KIND_PITCH;
+ page = GF110_DMA_V0_PAGE_SP;
+ } else {
+ kind = GF110_DMA_V0_KIND_VM;
+ page = GF110_DMA_V0_PAGE_LP;
+ }
+ } else
+ return ret;
+
+ if (page > 1)
+ return -EINVAL;
+ priv->flags0 = (kind << 20) | (page << 6);
+
+ switch (priv->base.target) {
+ case NV_MEM_TARGET_VRAM:
+ priv->flags0 |= 0x00000009;
+ break;
+ case NV_MEM_TARGET_VM:
+ case NV_MEM_TARGET_PCI:
+ case NV_MEM_TARGET_PCI_NOSNOOP:
+ /* XXX: don't currently know how to construct a real one
+ * of these. we only use them to represent pushbufs
+ * on these chipsets, and the classes that use them
+ * deal with the target themselves.
+ */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject);
}
-struct nouveau_oclass
-nvd0_dmaeng_oclass = {
- .handle = NV_ENGINE(DMAOBJ, 0xd0),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nvd0_dmaeng_ctor,
- .dtor = _nouveau_dmaeng_dtor,
- .init = _nouveau_dmaeng_init,
- .fini = _nouveau_dmaeng_fini,
- },
+static struct nouveau_ofuncs
+nvd0_dmaobj_ofuncs = {
+ .ctor = nvd0_dmaobj_ctor,
+ .dtor = _nvkm_dmaobj_dtor,
+ .init = _nvkm_dmaobj_init,
+ .fini = _nvkm_dmaobj_fini,
};
+
+static struct nouveau_oclass
+nvd0_dmaeng_sclass[] = {
+ { NV_DMA_FROM_MEMORY, &nvd0_dmaobj_ofuncs },
+ { NV_DMA_TO_MEMORY, &nvd0_dmaobj_ofuncs },
+ { NV_DMA_IN_MEMORY, &nvd0_dmaobj_ofuncs },
+ {}
+};
+
+struct nouveau_oclass *
+nvd0_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
+ .base.handle = NV_ENGINE(DMAOBJ, 0xd0),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nvkm_dmaeng_ctor,
+ .dtor = _nvkm_dmaeng_dtor,
+ .init = _nvkm_dmaeng_init,
+ .fini = _nvkm_dmaeng_fini,
+ },
+ .sclass = nvd0_dmaeng_sclass,
+ .bind = nvd0_dmaobj_bind,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/priv.h b/drivers/gpu/drm/nouveau/core/engine/dmaobj/priv.h
new file mode 100644
index 000000000000..36f743866937
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/priv.h
@@ -0,0 +1,30 @@
+#ifndef __NVKM_DMAOBJ_PRIV_H__
+#define __NVKM_DMAOBJ_PRIV_H__
+
+#include <engine/dmaobj.h>
+
+#define nvkm_dmaobj_create(p,e,c,pa,sa,d) \
+ nvkm_dmaobj_create_((p), (e), (c), (pa), (sa), sizeof(**d), (void **)d)
+
+int nvkm_dmaobj_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void **, u32 *,
+ int, void **);
+#define _nvkm_dmaobj_dtor nouveau_object_destroy
+#define _nvkm_dmaobj_init nouveau_object_init
+#define _nvkm_dmaobj_fini nouveau_object_fini
+
+int _nvkm_dmaeng_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+#define _nvkm_dmaeng_dtor _nouveau_engine_dtor
+#define _nvkm_dmaeng_init _nouveau_engine_init
+#define _nvkm_dmaeng_fini _nouveau_engine_fini
+
+struct nvkm_dmaeng_impl {
+ struct nouveau_oclass base;
+ struct nouveau_oclass *sclass;
+ int (*bind)(struct nouveau_dmaobj *, struct nouveau_object *,
+ struct nouveau_gpuobj **);
+};
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
index 56ed3d73bf8e..ac8375cf4eef 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
@@ -26,11 +26,31 @@
#include <core/object.h>
#include <core/handle.h>
#include <core/event.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
+#include <nvif/event.h>
#include <engine/dmaobj.h>
#include <engine/fifo.h>
+static int
+nouveau_fifo_event_ctor(struct nouveau_object *object, void *data, u32 size,
+ struct nvkm_notify *notify)
+{
+ if (size == 0) {
+ notify->size = 0;
+ notify->types = 1;
+ notify->index = 0;
+ return 0;
+ }
+ return -ENOSYS;
+}
+
+static const struct nvkm_event_func
+nouveau_fifo_event_func = {
+ .ctor = nouveau_fifo_event_ctor,
+};
+
int
nouveau_fifo_channel_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
@@ -59,14 +79,14 @@ nouveau_fifo_channel_create_(struct nouveau_object *parent,
dmaeng = (void *)chan->pushdma->base.engine;
switch (chan->pushdma->base.oclass->handle) {
- case NV_DMA_FROM_MEMORY_CLASS:
- case NV_DMA_IN_MEMORY_CLASS:
+ case NV_DMA_FROM_MEMORY:
+ case NV_DMA_IN_MEMORY:
break;
default:
return -EINVAL;
}
- ret = dmaeng->bind(dmaeng, parent, chan->pushdma, &chan->pushgpu);
+ ret = dmaeng->bind(chan->pushdma, parent, &chan->pushgpu);
if (ret)
return ret;
@@ -85,15 +105,10 @@ nouveau_fifo_channel_create_(struct nouveau_object *parent,
return -ENOSPC;
}
- /* map fifo control registers */
- chan->user = ioremap(nv_device_resource_start(device, bar) + addr +
- (chan->chid * size), size);
- if (!chan->user)
- return -EFAULT;
-
- nouveau_event_trigger(priv->cevent, 1, 0);
-
+ chan->addr = nv_device_resource_start(device, bar) +
+ addr + size * chan->chid;
chan->size = size;
+ nvkm_event_send(&priv->cevent, 1, 0, NULL, 0);
return 0;
}
@@ -103,7 +118,8 @@ nouveau_fifo_channel_destroy(struct nouveau_fifo_chan *chan)
struct nouveau_fifo *priv = (void *)nv_object(chan)->engine;
unsigned long flags;
- iounmap(chan->user);
+ if (chan->user)
+ iounmap(chan->user);
spin_lock_irqsave(&priv->lock, flags);
priv->channel[chan->chid] = NULL;
@@ -121,10 +137,24 @@ _nouveau_fifo_channel_dtor(struct nouveau_object *object)
nouveau_fifo_channel_destroy(chan);
}
+int
+_nouveau_fifo_channel_map(struct nouveau_object *object, u64 *addr, u32 *size)
+{
+ struct nouveau_fifo_chan *chan = (void *)object;
+ *addr = chan->addr;
+ *size = chan->size;
+ return 0;
+}
+
u32
_nouveau_fifo_channel_rd32(struct nouveau_object *object, u64 addr)
{
struct nouveau_fifo_chan *chan = (void *)object;
+ if (unlikely(!chan->user)) {
+ chan->user = ioremap(chan->addr, chan->size);
+ if (WARN_ON_ONCE(chan->user == NULL))
+ return 0;
+ }
return ioread32_native(chan->user + addr);
}
@@ -132,9 +162,58 @@ void
_nouveau_fifo_channel_wr32(struct nouveau_object *object, u64 addr, u32 data)
{
struct nouveau_fifo_chan *chan = (void *)object;
+ if (unlikely(!chan->user)) {
+ chan->user = ioremap(chan->addr, chan->size);
+ if (WARN_ON_ONCE(chan->user == NULL))
+ return;
+ }
iowrite32_native(data, chan->user + addr);
}
+int
+nouveau_fifo_uevent_ctor(struct nouveau_object *object, void *data, u32 size,
+ struct nvkm_notify *notify)
+{
+ union {
+ struct nvif_notify_uevent_req none;
+ } *req = data;
+ int ret;
+
+ if (nvif_unvers(req->none)) {
+ notify->size = sizeof(struct nvif_notify_uevent_rep);
+ notify->types = 1;
+ notify->index = 0;
+ }
+
+ return ret;
+}
+
+void
+nouveau_fifo_uevent(struct nouveau_fifo *fifo)
+{
+ struct nvif_notify_uevent_rep rep = {
+ };
+ nvkm_event_send(&fifo->uevent, 1, 0, &rep, sizeof(rep));
+}
+
+int
+_nouveau_fifo_channel_ntfy(struct nouveau_object *object, u32 type,
+ struct nvkm_event **event)
+{
+ struct nouveau_fifo *fifo = (void *)object->engine;
+ switch (type) {
+ case G82_CHANNEL_DMA_V0_NTFY_UEVENT:
+ if (nv_mclass(object) >= G82_CHANNEL_DMA) {
+ *event = &fifo->uevent;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
static int
nouveau_fifo_chid(struct nouveau_fifo *priv, struct nouveau_object *object)
{
@@ -168,8 +247,8 @@ void
nouveau_fifo_destroy(struct nouveau_fifo *priv)
{
kfree(priv->channel);
- nouveau_event_destroy(&priv->uevent);
- nouveau_event_destroy(&priv->cevent);
+ nvkm_event_fini(&priv->uevent);
+ nvkm_event_fini(&priv->cevent);
nouveau_engine_destroy(&priv->base);
}
@@ -194,11 +273,7 @@ nouveau_fifo_create_(struct nouveau_object *parent,
if (!priv->channel)
return -ENOMEM;
- ret = nouveau_event_create(1, 1, &priv->cevent);
- if (ret)
- return ret;
-
- ret = nouveau_event_create(1, 1, &priv->uevent);
+ ret = nvkm_event_init(&nouveau_fifo_event_func, 1, 1, &priv->cevent);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
index c61b16a63884..5ae6a43893b5 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
@@ -22,8 +22,9 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <core/engctx.h>
#include <core/namedb.h>
#include <core/handle.h>
@@ -117,16 +118,23 @@ nv04_fifo_chan_ctor(struct nouveau_object *parent,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ union {
+ struct nv03_channel_dma_v0 v0;
+ } *args = data;
struct nv04_fifo_priv *priv = (void *)engine;
struct nv04_fifo_chan *chan;
- struct nv03_channel_dma_class *args = data;
int ret;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create channel dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create channel dma vers %d pushbuf %08x "
+ "offset %016llx\n", args->v0.version,
+ args->v0.pushbuf, args->v0.offset);
+ } else
+ return ret;
ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0x800000,
- 0x10000, args->pushbuf,
+ 0x10000, args->v0.pushbuf,
(1ULL << NVDEV_ENGINE_DMAOBJ) |
(1ULL << NVDEV_ENGINE_SW) |
(1ULL << NVDEV_ENGINE_GR), &chan);
@@ -134,13 +142,15 @@ nv04_fifo_chan_ctor(struct nouveau_object *parent,
if (ret)
return ret;
+ args->v0.chid = chan->base.chid;
+
nv_parent(chan)->object_attach = nv04_fifo_object_attach;
nv_parent(chan)->object_detach = nv04_fifo_object_detach;
nv_parent(chan)->context_attach = nv04_fifo_context_attach;
chan->ramfc = chan->base.chid * 32;
- nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->offset);
- nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->offset);
+ nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->v0.offset);
+ nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->v0.offset);
nv_wo32(priv->ramfc, chan->ramfc + 0x08, chan->base.pushgpu->addr >> 4);
nv_wo32(priv->ramfc, chan->ramfc + 0x10,
NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
@@ -242,13 +252,15 @@ nv04_fifo_ofuncs = {
.dtor = nv04_fifo_chan_dtor,
.init = nv04_fifo_chan_init,
.fini = nv04_fifo_chan_fini,
+ .map = _nouveau_fifo_channel_map,
.rd32 = _nouveau_fifo_channel_rd32,
.wr32 = _nouveau_fifo_channel_wr32,
+ .ntfy = _nouveau_fifo_channel_ntfy
};
static struct nouveau_oclass
nv04_fifo_sclass[] = {
- { NV03_CHANNEL_DMA_CLASS, &nv04_fifo_ofuncs },
+ { NV03_CHANNEL_DMA, &nv04_fifo_ofuncs },
{}
};
@@ -539,7 +551,7 @@ nv04_fifo_intr(struct nouveau_subdev *subdev)
}
if (status & 0x40000000) {
- nouveau_event_trigger(priv->base.uevent, 1, 0);
+ nouveau_fifo_uevent(&priv->base);
nv_wr32(priv, 0x002100, 0x40000000);
status &= ~0x40000000;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c
index 571a22aa1ae5..2a32add51c81 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c
@@ -22,8 +22,9 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <core/engctx.h>
#include <core/ramht.h>
@@ -59,16 +60,23 @@ nv10_fifo_chan_ctor(struct nouveau_object *parent,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ union {
+ struct nv03_channel_dma_v0 v0;
+ } *args = data;
struct nv04_fifo_priv *priv = (void *)engine;
struct nv04_fifo_chan *chan;
- struct nv03_channel_dma_class *args = data;
int ret;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create channel dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create channel dma vers %d pushbuf %08x "
+ "offset %016llx\n", args->v0.version,
+ args->v0.pushbuf, args->v0.offset);
+ } else
+ return ret;
ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0x800000,
- 0x10000, args->pushbuf,
+ 0x10000, args->v0.pushbuf,
(1ULL << NVDEV_ENGINE_DMAOBJ) |
(1ULL << NVDEV_ENGINE_SW) |
(1ULL << NVDEV_ENGINE_GR), &chan);
@@ -76,13 +84,15 @@ nv10_fifo_chan_ctor(struct nouveau_object *parent,
if (ret)
return ret;
+ args->v0.chid = chan->base.chid;
+
nv_parent(chan)->object_attach = nv04_fifo_object_attach;
nv_parent(chan)->object_detach = nv04_fifo_object_detach;
nv_parent(chan)->context_attach = nv04_fifo_context_attach;
chan->ramfc = chan->base.chid * 32;
- nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->offset);
- nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->offset);
+ nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->v0.offset);
+ nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->v0.offset);
nv_wo32(priv->ramfc, chan->ramfc + 0x0c, chan->base.pushgpu->addr >> 4);
nv_wo32(priv->ramfc, chan->ramfc + 0x14,
NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
@@ -100,13 +110,15 @@ nv10_fifo_ofuncs = {
.dtor = nv04_fifo_chan_dtor,
.init = nv04_fifo_chan_init,
.fini = nv04_fifo_chan_fini,
+ .map = _nouveau_fifo_channel_map,
.rd32 = _nouveau_fifo_channel_rd32,
.wr32 = _nouveau_fifo_channel_wr32,
+ .ntfy = _nouveau_fifo_channel_ntfy
};
static struct nouveau_oclass
nv10_fifo_sclass[] = {
- { NV10_CHANNEL_DMA_CLASS, &nv10_fifo_ofuncs },
+ { NV10_CHANNEL_DMA, &nv10_fifo_ofuncs },
{}
};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c
index f25760209316..12d76c8adb23 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c
@@ -22,8 +22,9 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <core/engctx.h>
#include <core/ramht.h>
@@ -64,16 +65,23 @@ nv17_fifo_chan_ctor(struct nouveau_object *parent,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ union {
+ struct nv03_channel_dma_v0 v0;
+ } *args = data;
struct nv04_fifo_priv *priv = (void *)engine;
struct nv04_fifo_chan *chan;
- struct nv03_channel_dma_class *args = data;
int ret;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create channel dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create channel dma vers %d pushbuf %08x "
+ "offset %016llx\n", args->v0.version,
+ args->v0.pushbuf, args->v0.offset);
+ } else
+ return ret;
ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0x800000,
- 0x10000, args->pushbuf,
+ 0x10000, args->v0.pushbuf,
(1ULL << NVDEV_ENGINE_DMAOBJ) |
(1ULL << NVDEV_ENGINE_SW) |
(1ULL << NVDEV_ENGINE_GR) |
@@ -83,13 +91,15 @@ nv17_fifo_chan_ctor(struct nouveau_object *parent,
if (ret)
return ret;
+ args->v0.chid = chan->base.chid;
+
nv_parent(chan)->object_attach = nv04_fifo_object_attach;
nv_parent(chan)->object_detach = nv04_fifo_object_detach;
nv_parent(chan)->context_attach = nv04_fifo_context_attach;
chan->ramfc = chan->base.chid * 64;
- nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->offset);
- nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->offset);
+ nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->v0.offset);
+ nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->v0.offset);
nv_wo32(priv->ramfc, chan->ramfc + 0x0c, chan->base.pushgpu->addr >> 4);
nv_wo32(priv->ramfc, chan->ramfc + 0x14,
NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
@@ -107,13 +117,15 @@ nv17_fifo_ofuncs = {
.dtor = nv04_fifo_chan_dtor,
.init = nv04_fifo_chan_init,
.fini = nv04_fifo_chan_fini,
+ .map = _nouveau_fifo_channel_map,
.rd32 = _nouveau_fifo_channel_rd32,
.wr32 = _nouveau_fifo_channel_wr32,
+ .ntfy = _nouveau_fifo_channel_ntfy
};
static struct nouveau_oclass
nv17_fifo_sclass[] = {
- { NV17_CHANNEL_DMA_CLASS, &nv17_fifo_ofuncs },
+ { NV17_CHANNEL_DMA, &nv17_fifo_ofuncs },
{}
};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c
index 343487ed2238..9f49c3a24dc6 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c
@@ -22,8 +22,9 @@
* Authors: Ben Skeggs
*/
-#include <core/os.h>
-#include <core/class.h>
+#include <core/client.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <core/engctx.h>
#include <core/ramht.h>
@@ -182,16 +183,23 @@ nv40_fifo_chan_ctor(struct nouveau_object *parent,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ union {
+ struct nv03_channel_dma_v0 v0;
+ } *args = data;
struct nv04_fifo_priv *priv = (void *)engine;
struct nv04_fifo_chan *chan;
- struct nv03_channel_dma_class *args = data;
int ret;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create channel dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create channel dma vers %d pushbuf %08x "
+ "offset %016llx\n", args->v0.version,
+ args->v0.pushbuf, args->v0.offset);
+ } else
+ return ret;
ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000,
- 0x1000, args->pushbuf,
+ 0x1000, args->v0.pushbuf,
(1ULL << NVDEV_ENGINE_DMAOBJ) |
(1ULL << NVDEV_ENGINE_SW) |
(1ULL << NVDEV_ENGINE_GR) |
@@ -200,14 +208,16 @@ nv40_fifo_chan_ctor(struct nouveau_object *parent,
if (ret)
return ret;
+ args->v0.chid = chan->base.chid;
+
nv_parent(chan)->context_attach = nv40_fifo_context_attach;
nv_parent(chan)->context_detach = nv40_fifo_context_detach;
nv_parent(chan)->object_attach = nv40_fifo_object_attach;
nv_parent(chan)->object_detach = nv04_fifo_object_detach;
chan->ramfc = chan->base.chid * 128;
- nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->offset);
- nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->offset);
+ nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->v0.offset);
+ nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->v0.offset);
nv_wo32(priv->ramfc, chan->ramfc + 0x0c, chan->base.pushgpu->addr >> 4);
nv_wo32(priv->ramfc, chan->ramfc + 0x18, 0x30000000 |
NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
@@ -226,13 +236,15 @@ nv40_fifo_ofuncs = {
.dtor = nv04_fifo_chan_dtor,
.init = nv04_fifo_chan_init,
.fini = nv04_fifo_chan_fini,
+ .map = _nouveau_fifo_channel_map,
.rd32 = _nouveau_fifo_channel_rd32,
.wr32 = _nouveau_fifo_channel_wr32,
+ .ntfy = _nouveau_fifo_channel_ntfy
};
static struct nouveau_oclass
nv40_fifo_sclass[] = {
- { NV40_CHANNEL_DMA_CLASS, &nv40_fifo_ofuncs },
+ { NV40_CHANNEL_DMA, &nv40_fifo_ofuncs },
{}
};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
index e6352bd5b4ff..5d1e86bc244c 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
@@ -25,7 +25,8 @@
#include <core/client.h>
#include <core/engctx.h>
#include <core/ramht.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <subdev/timer.h>
#include <subdev/bar.h>
@@ -194,17 +195,24 @@ nv50_fifo_chan_ctor_dma(struct nouveau_object *parent,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ union {
+ struct nv03_channel_dma_v0 v0;
+ } *args = data;
struct nouveau_bar *bar = nouveau_bar(parent);
struct nv50_fifo_base *base = (void *)parent;
struct nv50_fifo_chan *chan;
- struct nv03_channel_dma_class *args = data;
int ret;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create channel dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create channel dma vers %d pushbuf %08x "
+ "offset %016llx\n", args->v0.version,
+ args->v0.pushbuf, args->v0.offset);
+ } else
+ return ret;
ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000,
- 0x2000, args->pushbuf,
+ 0x2000, args->v0.pushbuf,
(1ULL << NVDEV_ENGINE_DMAOBJ) |
(1ULL << NVDEV_ENGINE_SW) |
(1ULL << NVDEV_ENGINE_GR) |
@@ -213,6 +221,8 @@ nv50_fifo_chan_ctor_dma(struct nouveau_object *parent,
if (ret)
return ret;
+ args->v0.chid = chan->base.chid;
+
nv_parent(chan)->context_attach = nv50_fifo_context_attach;
nv_parent(chan)->context_detach = nv50_fifo_context_detach;
nv_parent(chan)->object_attach = nv50_fifo_object_attach;
@@ -223,10 +233,10 @@ nv50_fifo_chan_ctor_dma(struct nouveau_object *parent,
if (ret)
return ret;
- nv_wo32(base->ramfc, 0x08, lower_32_bits(args->offset));
- nv_wo32(base->ramfc, 0x0c, upper_32_bits(args->offset));
- nv_wo32(base->ramfc, 0x10, lower_32_bits(args->offset));
- nv_wo32(base->ramfc, 0x14, upper_32_bits(args->offset));
+ nv_wo32(base->ramfc, 0x08, lower_32_bits(args->v0.offset));
+ nv_wo32(base->ramfc, 0x0c, upper_32_bits(args->v0.offset));
+ nv_wo32(base->ramfc, 0x10, lower_32_bits(args->v0.offset));
+ nv_wo32(base->ramfc, 0x14, upper_32_bits(args->v0.offset));
nv_wo32(base->ramfc, 0x3c, 0x003f6078);
nv_wo32(base->ramfc, 0x44, 0x01003fff);
nv_wo32(base->ramfc, 0x48, chan->base.pushgpu->node->offset >> 4);
@@ -247,18 +257,26 @@ nv50_fifo_chan_ctor_ind(struct nouveau_object *parent,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
- struct nv50_channel_ind_class *args = data;
+ union {
+ struct nv50_channel_gpfifo_v0 v0;
+ } *args = data;
struct nouveau_bar *bar = nouveau_bar(parent);
struct nv50_fifo_base *base = (void *)parent;
struct nv50_fifo_chan *chan;
u64 ioffset, ilength;
int ret;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create channel gpfifo size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create channel gpfifo vers %d pushbuf %08x "
+ "ioffset %016llx ilength %08x\n",
+ args->v0.version, args->v0.pushbuf, args->v0.ioffset,
+ args->v0.ilength);
+ } else
+ return ret;
ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000,
- 0x2000, args->pushbuf,
+ 0x2000, args->v0.pushbuf,
(1ULL << NVDEV_ENGINE_DMAOBJ) |
(1ULL << NVDEV_ENGINE_SW) |
(1ULL << NVDEV_ENGINE_GR) |
@@ -267,6 +285,8 @@ nv50_fifo_chan_ctor_ind(struct nouveau_object *parent,
if (ret)
return ret;
+ args->v0.chid = chan->base.chid;
+
nv_parent(chan)->context_attach = nv50_fifo_context_attach;
nv_parent(chan)->context_detach = nv50_fifo_context_detach;
nv_parent(chan)->object_attach = nv50_fifo_object_attach;
@@ -277,8 +297,8 @@ nv50_fifo_chan_ctor_ind(struct nouveau_object *parent,
if (ret)
return ret;
- ioffset = args->ioffset;
- ilength = order_base_2(args->ilength / 8);
+ ioffset = args->v0.ioffset;
+ ilength = order_base_2(args->v0.ilength / 8);
nv_wo32(base->ramfc, 0x3c, 0x403f6078);
nv_wo32(base->ramfc, 0x44, 0x01003fff);
@@ -343,8 +363,10 @@ nv50_fifo_ofuncs_dma = {
.dtor = nv50_fifo_chan_dtor,
.init = nv50_fifo_chan_init,
.fini = nv50_fifo_chan_fini,
+ .map = _nouveau_fifo_channel_map,
.rd32 = _nouveau_fifo_channel_rd32,
.wr32 = _nouveau_fifo_channel_wr32,
+ .ntfy = _nouveau_fifo_channel_ntfy
};
static struct nouveau_ofuncs
@@ -353,14 +375,16 @@ nv50_fifo_ofuncs_ind = {
.dtor = nv50_fifo_chan_dtor,
.init = nv50_fifo_chan_init,
.fini = nv50_fifo_chan_fini,
+ .map = _nouveau_fifo_channel_map,
.rd32 = _nouveau_fifo_channel_rd32,
.wr32 = _nouveau_fifo_channel_wr32,
+ .ntfy = _nouveau_fifo_channel_ntfy
};
static struct nouveau_oclass
nv50_fifo_sclass[] = {
- { NV50_CHANNEL_DMA_CLASS, &nv50_fifo_ofuncs_dma },
- { NV50_CHANNEL_IND_CLASS, &nv50_fifo_ofuncs_ind },
+ { NV50_CHANNEL_DMA, &nv50_fifo_ofuncs_dma },
+ { NV50_CHANNEL_GPFIFO, &nv50_fifo_ofuncs_ind },
{}
};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
index 6e5ac16e5460..1f42996b354a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
@@ -27,7 +27,8 @@
#include <core/engctx.h>
#include <core/ramht.h>
#include <core/event.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <subdev/timer.h>
#include <subdev/bar.h>
@@ -160,17 +161,24 @@ nv84_fifo_chan_ctor_dma(struct nouveau_object *parent,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ union {
+ struct nv03_channel_dma_v0 v0;
+ } *args = data;
struct nouveau_bar *bar = nouveau_bar(parent);
struct nv50_fifo_base *base = (void *)parent;
struct nv50_fifo_chan *chan;
- struct nv03_channel_dma_class *args = data;
int ret;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create channel dma size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create channel dma vers %d pushbuf %08x "
+ "offset %016llx\n", args->v0.version,
+ args->v0.pushbuf, args->v0.offset);
+ } else
+ return ret;
ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000,
- 0x2000, args->pushbuf,
+ 0x2000, args->v0.pushbuf,
(1ULL << NVDEV_ENGINE_DMAOBJ) |
(1ULL << NVDEV_ENGINE_SW) |
(1ULL << NVDEV_ENGINE_GR) |
@@ -186,6 +194,8 @@ nv84_fifo_chan_ctor_dma(struct nouveau_object *parent,
if (ret)
return ret;
+ args->v0.chid = chan->base.chid;
+
ret = nouveau_ramht_new(nv_object(chan), nv_object(chan), 0x8000, 16,
&chan->ramht);
if (ret)
@@ -196,10 +206,10 @@ nv84_fifo_chan_ctor_dma(struct nouveau_object *parent,
nv_parent(chan)->object_attach = nv84_fifo_object_attach;
nv_parent(chan)->object_detach = nv50_fifo_object_detach;
- nv_wo32(base->ramfc, 0x08, lower_32_bits(args->offset));
- nv_wo32(base->ramfc, 0x0c, upper_32_bits(args->offset));
- nv_wo32(base->ramfc, 0x10, lower_32_bits(args->offset));
- nv_wo32(base->ramfc, 0x14, upper_32_bits(args->offset));
+ nv_wo32(base->ramfc, 0x08, lower_32_bits(args->v0.offset));
+ nv_wo32(base->ramfc, 0x0c, upper_32_bits(args->v0.offset));
+ nv_wo32(base->ramfc, 0x10, lower_32_bits(args->v0.offset));
+ nv_wo32(base->ramfc, 0x14, upper_32_bits(args->v0.offset));
nv_wo32(base->ramfc, 0x3c, 0x003f6078);
nv_wo32(base->ramfc, 0x44, 0x01003fff);
nv_wo32(base->ramfc, 0x48, chan->base.pushgpu->node->offset >> 4);
@@ -222,18 +232,26 @@ nv84_fifo_chan_ctor_ind(struct nouveau_object *parent,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ union {
+ struct nv50_channel_gpfifo_v0 v0;
+ } *args = data;
struct nouveau_bar *bar = nouveau_bar(parent);
struct nv50_fifo_base *base = (void *)parent;
struct nv50_fifo_chan *chan;
- struct nv50_channel_ind_class *args = data;
u64 ioffset, ilength;
int ret;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create channel gpfifo size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create channel gpfifo vers %d pushbuf %08x "
+ "ioffset %016llx ilength %08x\n",
+ args->v0.version, args->v0.pushbuf, args->v0.ioffset,
+ args->v0.ilength);
+ } else
+ return ret;
ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000,
- 0x2000, args->pushbuf,
+ 0x2000, args->v0.pushbuf,
(1ULL << NVDEV_ENGINE_DMAOBJ) |
(1ULL << NVDEV_ENGINE_SW) |
(1ULL << NVDEV_ENGINE_GR) |
@@ -249,6 +267,8 @@ nv84_fifo_chan_ctor_ind(struct nouveau_object *parent,
if (ret)
return ret;
+ args->v0.chid = chan->base.chid;
+
ret = nouveau_ramht_new(nv_object(chan), nv_object(chan), 0x8000, 16,
&chan->ramht);
if (ret)
@@ -259,8 +279,8 @@ nv84_fifo_chan_ctor_ind(struct nouveau_object *parent,
nv_parent(chan)->object_attach = nv84_fifo_object_attach;
nv_parent(chan)->object_detach = nv50_fifo_object_detach;
- ioffset = args->ioffset;
- ilength = order_base_2(args->ilength / 8);
+ ioffset = args->v0.ioffset;
+ ilength = order_base_2(args->v0.ilength / 8);
nv_wo32(base->ramfc, 0x3c, 0x403f6078);
nv_wo32(base->ramfc, 0x44, 0x01003fff);
@@ -304,8 +324,10 @@ nv84_fifo_ofuncs_dma = {
.dtor = nv50_fifo_chan_dtor,
.init = nv84_fifo_chan_init,
.fini = nv50_fifo_chan_fini,
+ .map = _nouveau_fifo_channel_map,
.rd32 = _nouveau_fifo_channel_rd32,
.wr32 = _nouveau_fifo_channel_wr32,
+ .ntfy = _nouveau_fifo_channel_ntfy
};
static struct nouveau_ofuncs
@@ -314,14 +336,16 @@ nv84_fifo_ofuncs_ind = {
.dtor = nv50_fifo_chan_dtor,
.init = nv84_fifo_chan_init,
.fini = nv50_fifo_chan_fini,
+ .map = _nouveau_fifo_channel_map,
.rd32 = _nouveau_fifo_channel_rd32,
.wr32 = _nouveau_fifo_channel_wr32,
+ .ntfy = _nouveau_fifo_channel_ntfy
};
static struct nouveau_oclass
nv84_fifo_sclass[] = {
- { NV84_CHANNEL_DMA_CLASS, &nv84_fifo_ofuncs_dma },
- { NV84_CHANNEL_IND_CLASS, &nv84_fifo_ofuncs_ind },
+ { G82_CHANNEL_DMA, &nv84_fifo_ofuncs_dma },
+ { G82_CHANNEL_GPFIFO, &nv84_fifo_ofuncs_ind },
{}
};
@@ -389,19 +413,26 @@ nv84_fifo_cclass = {
******************************************************************************/
static void
-nv84_fifo_uevent_enable(struct nouveau_event *event, int type, int index)
+nv84_fifo_uevent_init(struct nvkm_event *event, int type, int index)
{
- struct nv84_fifo_priv *priv = event->priv;
- nv_mask(priv, 0x002140, 0x40000000, 0x40000000);
+ struct nouveau_fifo *fifo = container_of(event, typeof(*fifo), uevent);
+ nv_mask(fifo, 0x002140, 0x40000000, 0x40000000);
}
static void
-nv84_fifo_uevent_disable(struct nouveau_event *event, int type, int index)
+nv84_fifo_uevent_fini(struct nvkm_event *event, int type, int index)
{
- struct nv84_fifo_priv *priv = event->priv;
- nv_mask(priv, 0x002140, 0x40000000, 0x00000000);
+ struct nouveau_fifo *fifo = container_of(event, typeof(*fifo), uevent);
+ nv_mask(fifo, 0x002140, 0x40000000, 0x00000000);
}
+static const struct nvkm_event_func
+nv84_fifo_uevent_func = {
+ .ctor = nouveau_fifo_uevent_ctor,
+ .init = nv84_fifo_uevent_init,
+ .fini = nv84_fifo_uevent_fini,
+};
+
static int
nv84_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -425,9 +456,9 @@ nv84_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
- priv->base.uevent->enable = nv84_fifo_uevent_enable;
- priv->base.uevent->disable = nv84_fifo_uevent_disable;
- priv->base.uevent->priv = priv;
+ ret = nvkm_event_init(&nv84_fifo_uevent_func, 1, 1, &priv->base.uevent);
+ if (ret)
+ return ret;
nv_subdev(priv)->unit = 0x00000100;
nv_subdev(priv)->intr = nv04_fifo_intr;
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
index ae4a4dc5642a..1fe1f8fbda0c 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
@@ -28,7 +28,8 @@
#include <core/gpuobj.h>
#include <core/engctx.h>
#include <core/event.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <core/enum.h>
#include <subdev/timer.h>
@@ -187,20 +188,28 @@ nvc0_fifo_chan_ctor(struct nouveau_object *parent,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ union {
+ struct nv50_channel_gpfifo_v0 v0;
+ } *args = data;
struct nouveau_bar *bar = nouveau_bar(parent);
struct nvc0_fifo_priv *priv = (void *)engine;
struct nvc0_fifo_base *base = (void *)parent;
struct nvc0_fifo_chan *chan;
- struct nv50_channel_ind_class *args = data;
u64 usermem, ioffset, ilength;
int ret, i;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create channel gpfifo size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create channel gpfifo vers %d pushbuf %08x "
+ "ioffset %016llx ilength %08x\n",
+ args->v0.version, args->v0.pushbuf, args->v0.ioffset,
+ args->v0.ilength);
+ } else
+ return ret;
ret = nouveau_fifo_channel_create(parent, engine, oclass, 1,
priv->user.bar.offset, 0x1000,
- args->pushbuf,
+ args->v0.pushbuf,
(1ULL << NVDEV_ENGINE_SW) |
(1ULL << NVDEV_ENGINE_GR) |
(1ULL << NVDEV_ENGINE_COPY0) |
@@ -212,12 +221,14 @@ nvc0_fifo_chan_ctor(struct nouveau_object *parent,
if (ret)
return ret;
+ args->v0.chid = chan->base.chid;
+
nv_parent(chan)->context_attach = nvc0_fifo_context_attach;
nv_parent(chan)->context_detach = nvc0_fifo_context_detach;
usermem = chan->base.chid * 0x1000;
- ioffset = args->ioffset;
- ilength = order_base_2(args->ilength / 8);
+ ioffset = args->v0.ioffset;
+ ilength = order_base_2(args->v0.ilength / 8);
for (i = 0; i < 0x1000; i += 4)
nv_wo32(priv->user.mem, usermem + i, 0x00000000);
@@ -291,13 +302,15 @@ nvc0_fifo_ofuncs = {
.dtor = _nouveau_fifo_channel_dtor,
.init = nvc0_fifo_chan_init,
.fini = nvc0_fifo_chan_fini,
+ .map = _nouveau_fifo_channel_map,
.rd32 = _nouveau_fifo_channel_rd32,
.wr32 = _nouveau_fifo_channel_wr32,
+ .ntfy = _nouveau_fifo_channel_ntfy
};
static struct nouveau_oclass
nvc0_fifo_sclass[] = {
- { NVC0_CHANNEL_IND_CLASS, &nvc0_fifo_ofuncs },
+ { FERMI_CHANNEL_GPFIFO, &nvc0_fifo_ofuncs },
{}
};
@@ -654,7 +667,7 @@ nvc0_fifo_intr_fault(struct nvc0_fifo_priv *priv, int unit)
object = engctx;
while (object) {
switch (nv_mclass(object)) {
- case NVC0_CHANNEL_IND_CLASS:
+ case FERMI_CHANNEL_GPFIFO:
nvc0_fifo_recover(priv, engine, (void *)object);
break;
}
@@ -730,7 +743,7 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn)
for (unkn = 0; unkn < 8; unkn++) {
u32 ints = (intr >> (unkn * 0x04)) & inte;
if (ints & 0x1) {
- nouveau_event_trigger(priv->base.uevent, 1, 0);
+ nouveau_fifo_uevent(&priv->base);
ints &= ~1;
}
if (ints) {
@@ -827,19 +840,26 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev)
}
static void
-nvc0_fifo_uevent_enable(struct nouveau_event *event, int type, int index)
+nvc0_fifo_uevent_init(struct nvkm_event *event, int type, int index)
{
- struct nvc0_fifo_priv *priv = event->priv;
- nv_mask(priv, 0x002140, 0x80000000, 0x80000000);
+ struct nouveau_fifo *fifo = container_of(event, typeof(*fifo), uevent);
+ nv_mask(fifo, 0x002140, 0x80000000, 0x80000000);
}
static void
-nvc0_fifo_uevent_disable(struct nouveau_event *event, int type, int index)
+nvc0_fifo_uevent_fini(struct nvkm_event *event, int type, int index)
{
- struct nvc0_fifo_priv *priv = event->priv;
- nv_mask(priv, 0x002140, 0x80000000, 0x00000000);
+ struct nouveau_fifo *fifo = container_of(event, typeof(*fifo), uevent);
+ nv_mask(fifo, 0x002140, 0x80000000, 0x00000000);
}
+static const struct nvkm_event_func
+nvc0_fifo_uevent_func = {
+ .ctor = nouveau_fifo_uevent_ctor,
+ .init = nvc0_fifo_uevent_init,
+ .fini = nvc0_fifo_uevent_fini,
+};
+
static int
nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -877,9 +897,9 @@ nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
- priv->base.uevent->enable = nvc0_fifo_uevent_enable;
- priv->base.uevent->disable = nvc0_fifo_uevent_disable;
- priv->base.uevent->priv = priv;
+ ret = nvkm_event_init(&nvc0_fifo_uevent_func, 1, 1, &priv->base.uevent);
+ if (ret)
+ return ret;
nv_subdev(priv)->unit = 0x00000100;
nv_subdev(priv)->intr = nvc0_fifo_intr;
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
index 298063edb92d..d2f0fd39c145 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
@@ -28,7 +28,8 @@
#include <core/gpuobj.h>
#include <core/engctx.h>
#include <core/event.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
#include <core/enum.h>
#include <subdev/timer.h>
@@ -216,46 +217,56 @@ nve0_fifo_chan_ctor(struct nouveau_object *parent,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ union {
+ struct kepler_channel_gpfifo_a_v0 v0;
+ } *args = data;
struct nouveau_bar *bar = nouveau_bar(parent);
struct nve0_fifo_priv *priv = (void *)engine;
struct nve0_fifo_base *base = (void *)parent;
struct nve0_fifo_chan *chan;
- struct nve0_channel_ind_class *args = data;
u64 usermem, ioffset, ilength;
int ret, i;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create channel gpfifo size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create channel gpfifo vers %d pushbuf %08x "
+ "ioffset %016llx ilength %08x engine %08x\n",
+ args->v0.version, args->v0.pushbuf, args->v0.ioffset,
+ args->v0.ilength, args->v0.engine);
+ } else
+ return ret;
for (i = 0; i < FIFO_ENGINE_NR; i++) {
- if (args->engine & (1 << i)) {
+ if (args->v0.engine & (1 << i)) {
if (nouveau_engine(parent, fifo_engine[i].subdev)) {
- args->engine = (1 << i);
+ args->v0.engine = (1 << i);
break;
}
}
}
if (i == FIFO_ENGINE_NR) {
- nv_error(priv, "unsupported engines 0x%08x\n", args->engine);
+ nv_error(priv, "unsupported engines 0x%08x\n", args->v0.engine);
return -ENODEV;
}
ret = nouveau_fifo_channel_create(parent, engine, oclass, 1,
priv->user.bar.offset, 0x200,
- args->pushbuf,
+ args->v0.pushbuf,
fifo_engine[i].mask, &chan);
*pobject = nv_object(chan);
if (ret)
return ret;
+ args->v0.chid = chan->base.chid;
+
nv_parent(chan)->context_attach = nve0_fifo_context_attach;
nv_parent(chan)->context_detach = nve0_fifo_context_detach;
chan->engine = i;
usermem = chan->base.chid * 0x200;
- ioffset = args->ioffset;
- ilength = order_base_2(args->ilength / 8);
+ ioffset = args->v0.ioffset;
+ ilength = order_base_2(args->v0.ilength / 8);
for (i = 0; i < 0x200; i += 4)
nv_wo32(priv->user.mem, usermem + i, 0x00000000);
@@ -325,13 +336,15 @@ nve0_fifo_ofuncs = {
.dtor = _nouveau_fifo_channel_dtor,
.init = nve0_fifo_chan_init,
.fini = nve0_fifo_chan_fini,
+ .map = _nouveau_fifo_channel_map,
.rd32 = _nouveau_fifo_channel_rd32,
.wr32 = _nouveau_fifo_channel_wr32,
+ .ntfy = _nouveau_fifo_channel_ntfy
};
static struct nouveau_oclass
nve0_fifo_sclass[] = {
- { NVE0_CHANNEL_IND_CLASS, &nve0_fifo_ofuncs },
+ { KEPLER_CHANNEL_GPFIFO_A, &nve0_fifo_ofuncs },
{}
};
@@ -769,7 +782,7 @@ nve0_fifo_intr_fault(struct nve0_fifo_priv *priv, int unit)
object = engctx;
while (object) {
switch (nv_mclass(object)) {
- case NVE0_CHANNEL_IND_CLASS:
+ case KEPLER_CHANNEL_GPFIFO_A:
nve0_fifo_recover(priv, engine, (void *)object);
break;
}
@@ -859,7 +872,7 @@ nve0_fifo_intr_runlist(struct nve0_fifo_priv *priv)
static void
nve0_fifo_intr_engine(struct nve0_fifo_priv *priv)
{
- nouveau_event_trigger(priv->base.uevent, 1, 0);
+ nouveau_fifo_uevent(&priv->base);
}
static void
@@ -952,19 +965,26 @@ nve0_fifo_intr(struct nouveau_subdev *subdev)
}
static void
-nve0_fifo_uevent_enable(struct nouveau_event *event, int type, int index)
+nve0_fifo_uevent_init(struct nvkm_event *event, int type, int index)
{
- struct nve0_fifo_priv *priv = event->priv;
- nv_mask(priv, 0x002140, 0x80000000, 0x80000000);
+ struct nouveau_fifo *fifo = container_of(event, typeof(*fifo), uevent);
+ nv_mask(fifo, 0x002140, 0x80000000, 0x80000000);
}
static void
-nve0_fifo_uevent_disable(struct nouveau_event *event, int type, int index)
+nve0_fifo_uevent_fini(struct nvkm_event *event, int type, int index)
{
- struct nve0_fifo_priv *priv = event->priv;
- nv_mask(priv, 0x002140, 0x80000000, 0x00000000);
+ struct nouveau_fifo *fifo = container_of(event, typeof(*fifo), uevent);
+ nv_mask(fifo, 0x002140, 0x80000000, 0x00000000);
}
+static const struct nvkm_event_func
+nve0_fifo_uevent_func = {
+ .ctor = nouveau_fifo_uevent_ctor,
+ .init = nve0_fifo_uevent_init,
+ .fini = nve0_fifo_uevent_fini,
+};
+
int
nve0_fifo_fini(struct nouveau_object *object, bool suspend)
{
@@ -1067,9 +1087,9 @@ nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
- priv->base.uevent->enable = nve0_fifo_uevent_enable;
- priv->base.uevent->disable = nve0_fifo_uevent_disable;
- priv->base.uevent->priv = priv;
+ ret = nvkm_event_init(&nve0_fifo_uevent_func, 1, 1, &priv->base.uevent);
+ if (ret)
+ return ret;
nv_subdev(priv)->unit = 0x00000100;
nv_subdev(priv)->intr = nve0_fifo_intr;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk110b.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk110b.c
new file mode 100644
index 000000000000..3adb7fe91772
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk110b.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "ctxnvc0.h"
+
+/*******************************************************************************
+ * PGRAPH context register lists
+ ******************************************************************************/
+
+static const struct nvc0_graph_init
+gk110b_grctx_init_sm_0[] = {
+ { 0x419e04, 1, 0x04, 0x00000000 },
+ { 0x419e08, 1, 0x04, 0x0000001d },
+ { 0x419e0c, 1, 0x04, 0x00000000 },
+ { 0x419e10, 1, 0x04, 0x00001c02 },
+ { 0x419e44, 1, 0x04, 0x0013eff2 },
+ { 0x419e48, 1, 0x04, 0x00000000 },
+ { 0x419e4c, 1, 0x04, 0x0000007f },
+ { 0x419e50, 2, 0x04, 0x00000000 },
+ { 0x419e58, 1, 0x04, 0x00000001 },
+ { 0x419e5c, 3, 0x04, 0x00000000 },
+ { 0x419e68, 1, 0x04, 0x00000002 },
+ { 0x419e6c, 12, 0x04, 0x00000000 },
+ { 0x419eac, 1, 0x04, 0x00001f8f },
+ { 0x419eb0, 1, 0x04, 0x0db00d2f },
+ { 0x419eb8, 1, 0x04, 0x00000000 },
+ { 0x419ec8, 1, 0x04, 0x0001304f },
+ { 0x419f30, 4, 0x04, 0x00000000 },
+ { 0x419f40, 1, 0x04, 0x00000018 },
+ { 0x419f44, 3, 0x04, 0x00000000 },
+ { 0x419f58, 1, 0x04, 0x00000000 },
+ { 0x419f70, 1, 0x04, 0x00006300 },
+ { 0x419f78, 1, 0x04, 0x000000eb },
+ { 0x419f7c, 1, 0x04, 0x00000404 },
+ {}
+};
+
+static const struct nvc0_graph_pack
+gk110b_grctx_pack_tpc[] = {
+ { nvd7_grctx_init_pe_0 },
+ { nvf0_grctx_init_tex_0 },
+ { nvf0_grctx_init_mpc_0 },
+ { nvf0_grctx_init_l1c_0 },
+ { gk110b_grctx_init_sm_0 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
+
+struct nouveau_oclass *
+gk110b_grctx_oclass = &(struct nvc0_grctx_oclass) {
+ .base.handle = NV_ENGCTX(GR, 0xf1),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nvc0_graph_context_ctor,
+ .dtor = nvc0_graph_context_dtor,
+ .init = _nouveau_graph_context_init,
+ .fini = _nouveau_graph_context_fini,
+ .rd32 = _nouveau_graph_context_rd32,
+ .wr32 = _nouveau_graph_context_wr32,
+ },
+ .main = nve4_grctx_generate_main,
+ .unkn = nve4_grctx_generate_unkn,
+ .hub = nvf0_grctx_pack_hub,
+ .gpc = nvf0_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = gk110b_grctx_pack_tpc,
+ .ppc = nvf0_grctx_pack_ppc,
+ .icmd = nvf0_grctx_pack_icmd,
+ .mthd = nvf0_grctx_pack_mthd,
+ .bundle = nve4_grctx_generate_bundle,
+ .bundle_size = 0x3000,
+ .bundle_min_gpm_fifo_depth = 0x180,
+ .bundle_token_limit = 0x600,
+ .pagepool = nve4_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = nvd7_grctx_generate_attrib,
+ .attrib_nr_max = 0x324,
+ .attrib_nr = 0x218,
+ .alpha_nr_max = 0x7ff,
+ .alpha_nr = 0x648,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c
index 224ee0287ab7..36fc9831cc93 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c
@@ -41,7 +41,6 @@ gk20a_grctx_oclass = &(struct nvc0_grctx_oclass) {
.wr32 = _nouveau_graph_context_wr32,
},
.main = nve4_grctx_generate_main,
- .mods = nve4_grctx_generate_mods,
.unkn = nve4_grctx_generate_unkn,
.hub = nve4_grctx_pack_hub,
.gpc = nve4_grctx_pack_gpc,
@@ -50,4 +49,15 @@ gk20a_grctx_oclass = &(struct nvc0_grctx_oclass) {
.ppc = nve4_grctx_pack_ppc,
.icmd = nve4_grctx_pack_icmd,
.mthd = gk20a_grctx_pack_mthd,
+ .bundle = nve4_grctx_generate_bundle,
+ .bundle_size = 0x1800,
+ .bundle_min_gpm_fifo_depth = 0x62,
+ .bundle_token_limit = 0x100,
+ .pagepool = nve4_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = nvd7_grctx_generate_attrib,
+ .attrib_nr_max = 0x240,
+ .attrib_nr = 0x240,
+ .alpha_nr_max = 0x648 + (0x648 / 2),
+ .alpha_nr = 0x648,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c
index b0d0fb2f4d08..62e918b9fa81 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgm107.c
@@ -859,45 +859,74 @@ gm107_grctx_pack_ppc[] = {
******************************************************************************/
static void
-gm107_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+gm107_grctx_generate_bundle(struct nvc0_grctx *info)
{
- mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x200000, 0x1000, NV_MEM_ACCESS_RW);
-
- mmio_list(0x40800c, 0x00000000, 8, 1);
- mmio_list(0x408010, 0x80000000, 0, 0);
- mmio_list(0x419004, 0x00000000, 8, 1);
- mmio_list(0x419008, 0x00000000, 0, 0);
- mmio_list(0x4064cc, 0x80000000, 0, 0);
- mmio_list(0x418e30, 0x80000000, 0, 0);
-
- mmio_list(0x408004, 0x00000000, 8, 0);
- mmio_list(0x408008, 0x80000030, 0, 0);
- mmio_list(0x418e24, 0x00000000, 8, 0);
- mmio_list(0x418e28, 0x80000030, 0, 0);
-
- mmio_list(0x4064c8, 0x018002c0, 0, 0);
-
- mmio_list(0x418810, 0x80000000, 12, 2);
- mmio_list(0x419848, 0x10000000, 12, 2);
- mmio_list(0x419c2c, 0x10000000, 12, 2);
-
- mmio_list(0x405830, 0x0aa01000, 0, 0);
- mmio_list(0x4064c4, 0x0400ffff, 0, 0);
-
- /*XXX*/
- mmio_list(0x5030c0, 0x00001540, 0, 0);
- mmio_list(0x5030f4, 0x00000000, 0, 0);
- mmio_list(0x5030e4, 0x00002000, 0, 0);
- mmio_list(0x5030f8, 0x00003fc0, 0, 0);
- mmio_list(0x418ea0, 0x07151540, 0, 0);
-
- mmio_list(0x5032c0, 0x00001540, 0, 0);
- mmio_list(0x5032f4, 0x00001fe0, 0, 0);
- mmio_list(0x5032e4, 0x00002000, 0, 0);
- mmio_list(0x5032f8, 0x00006fc0, 0, 0);
- mmio_list(0x418ea4, 0x07151540, 0, 0);
+ const struct nvc0_grctx_oclass *impl = nvc0_grctx_impl(info->priv);
+ const u32 state_limit = min(impl->bundle_min_gpm_fifo_depth,
+ impl->bundle_size / 0x20);
+ const u32 token_limit = impl->bundle_token_limit;
+ const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS;
+ const int s = 8;
+ const int b = mmio_vram(info, impl->bundle_size, (1 << s), access);
+ mmio_refn(info, 0x408004, 0x00000000, s, b);
+ mmio_refn(info, 0x408008, 0x80000000 | (impl->bundle_size >> s), 0, b);
+ mmio_refn(info, 0x418e24, 0x00000000, s, b);
+ mmio_refn(info, 0x418e28, 0x80000000 | (impl->bundle_size >> s), 0, b);
+ mmio_wr32(info, 0x4064c8, (state_limit << 16) | token_limit);
+}
+
+static void
+gm107_grctx_generate_pagepool(struct nvc0_grctx *info)
+{
+ const struct nvc0_grctx_oclass *impl = nvc0_grctx_impl(info->priv);
+ const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS;
+ const int s = 8;
+ const int b = mmio_vram(info, impl->pagepool_size, (1 << s), access);
+ mmio_refn(info, 0x40800c, 0x00000000, s, b);
+ mmio_wr32(info, 0x408010, 0x80000000);
+ mmio_refn(info, 0x419004, 0x00000000, s, b);
+ mmio_wr32(info, 0x419008, 0x00000000);
+ mmio_wr32(info, 0x4064cc, 0x80000000);
+ mmio_wr32(info, 0x418e30, 0x80000000); /* guess at it being related */
+}
+
+static void
+gm107_grctx_generate_attrib(struct nvc0_grctx *info)
+{
+ struct nvc0_graph_priv *priv = info->priv;
+ const struct nvc0_grctx_oclass *impl = (void *)nvc0_grctx_impl(priv);
+ const u32 alpha = impl->alpha_nr;
+ const u32 attrib = impl->attrib_nr;
+ const u32 size = 0x20 * (impl->attrib_nr_max + impl->alpha_nr_max);
+ const u32 access = NV_MEM_ACCESS_RW;
+ const int s = 12;
+ const int b = mmio_vram(info, size * priv->tpc_total, (1 << s), access);
+ const int max_batches = 0xffff;
+ u32 bo = 0;
+ u32 ao = bo + impl->attrib_nr_max * priv->tpc_total;
+ int gpc, ppc, n = 0;
+
+ mmio_refn(info, 0x418810, 0x80000000, s, b);
+ mmio_refn(info, 0x419848, 0x10000000, s, b);
+ mmio_refn(info, 0x419c2c, 0x10000000, s, b);
+ mmio_wr32(info, 0x405830, (attrib << 16) | alpha);
+ mmio_wr32(info, 0x4064c4, ((alpha / 4) << 16) | max_batches);
+
+ for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
+ for (ppc = 0; ppc < priv->ppc_nr[gpc]; ppc++, n++) {
+ const u32 as = alpha * priv->ppc_tpc_nr[gpc][ppc];
+ const u32 bs = attrib * priv->ppc_tpc_nr[gpc][ppc];
+ const u32 u = 0x418ea0 + (n * 0x04);
+ const u32 o = PPC_UNIT(gpc, ppc, 0);
+ mmio_wr32(info, o + 0xc0, bs);
+ mmio_wr32(info, o + 0xf4, bo);
+ bo += impl->attrib_nr_max * priv->ppc_tpc_nr[gpc][ppc];
+ mmio_wr32(info, o + 0xe4, as);
+ mmio_wr32(info, o + 0xf8, ao);
+ ao += impl->alpha_nr_max * priv->ppc_tpc_nr[gpc][ppc];
+ mmio_wr32(info, u, (0x715 /*XXX*/ << 16) | bs);
+ }
+ }
}
static void
@@ -934,7 +963,9 @@ gm107_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
nv_wr32(priv, 0x404154, 0x00000000);
- oclass->mods(priv, info);
+ oclass->bundle(info);
+ oclass->pagepool(info);
+ oclass->attrib(info);
oclass->unkn(priv);
gm107_grctx_generate_tpcid(priv);
@@ -979,7 +1010,6 @@ gm107_grctx_oclass = &(struct nvc0_grctx_oclass) {
.wr32 = _nouveau_graph_context_wr32,
},
.main = gm107_grctx_generate_main,
- .mods = gm107_grctx_generate_mods,
.unkn = nve4_grctx_generate_unkn,
.hub = gm107_grctx_pack_hub,
.gpc = gm107_grctx_pack_gpc,
@@ -988,4 +1018,15 @@ gm107_grctx_oclass = &(struct nvc0_grctx_oclass) {
.ppc = gm107_grctx_pack_ppc,
.icmd = gm107_grctx_pack_icmd,
.mthd = gm107_grctx_pack_mthd,
+ .bundle = gm107_grctx_generate_bundle,
+ .bundle_size = 0x3000,
+ .bundle_min_gpm_fifo_depth = 0x180,
+ .bundle_token_limit = 0x2c0,
+ .pagepool = gm107_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = gm107_grctx_generate_attrib,
+ .attrib_nr_max = 0xff0,
+ .attrib_nr = 0xaa0,
+ .alpha_nr_max = 0x1800,
+ .alpha_nr = 0x1000,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c
index 8de4a4291548..ce252adbef81 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c
@@ -531,50 +531,6 @@ nv108_grctx_pack_ppc[] = {
* PGRAPH context implementation
******************************************************************************/
-static void
-nv108_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
-{
- u32 magic[GPC_MAX][2];
- u32 offset;
- int gpc;
-
- mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
- mmio_list(0x40800c, 0x00000000, 8, 1);
- mmio_list(0x408010, 0x80000000, 0, 0);
- mmio_list(0x419004, 0x00000000, 8, 1);
- mmio_list(0x419008, 0x00000000, 0, 0);
- mmio_list(0x4064cc, 0x80000000, 0, 0);
- mmio_list(0x408004, 0x00000000, 8, 0);
- mmio_list(0x408008, 0x80000030, 0, 0);
- mmio_list(0x418808, 0x00000000, 8, 0);
- mmio_list(0x41880c, 0x80000030, 0, 0);
- mmio_list(0x4064c8, 0x00c20200, 0, 0);
- mmio_list(0x418810, 0x80000000, 12, 2);
- mmio_list(0x419848, 0x10000000, 12, 2);
-
- mmio_list(0x405830, 0x02180648, 0, 0);
- mmio_list(0x4064c4, 0x0192ffff, 0, 0);
-
- for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
- u16 magic0 = 0x0218 * priv->tpc_nr[gpc];
- u16 magic1 = 0x0648 * priv->tpc_nr[gpc];
- magic[gpc][0] = 0x10000000 | (magic0 << 16) | offset;
- magic[gpc][1] = 0x00000000 | (magic1 << 16);
- offset += 0x0324 * priv->tpc_nr[gpc];
- }
-
- for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
- mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0);
- mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0);
- offset += 0x07ff * priv->tpc_nr[gpc];
- }
-
- mmio_list(0x17e91c, 0x0b040a0b, 0, 0);
- mmio_list(0x17e920, 0x00090d08, 0, 0);
-}
-
struct nouveau_oclass *
nv108_grctx_oclass = &(struct nvc0_grctx_oclass) {
.base.handle = NV_ENGCTX(GR, 0x08),
@@ -587,7 +543,6 @@ nv108_grctx_oclass = &(struct nvc0_grctx_oclass) {
.wr32 = _nouveau_graph_context_wr32,
},
.main = nve4_grctx_generate_main,
- .mods = nv108_grctx_generate_mods,
.unkn = nve4_grctx_generate_unkn,
.hub = nv108_grctx_pack_hub,
.gpc = nv108_grctx_pack_gpc,
@@ -596,4 +551,15 @@ nv108_grctx_oclass = &(struct nvc0_grctx_oclass) {
.ppc = nv108_grctx_pack_ppc,
.icmd = nv108_grctx_pack_icmd,
.mthd = nvf0_grctx_pack_mthd,
+ .bundle = nve4_grctx_generate_bundle,
+ .bundle_size = 0x3000,
+ .bundle_min_gpm_fifo_depth = 0xc2,
+ .bundle_token_limit = 0x200,
+ .pagepool = nve4_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = nvd7_grctx_generate_attrib,
+ .attrib_nr_max = 0x324,
+ .attrib_nr = 0x218,
+ .alpha_nr_max = 0x7ff,
+ .alpha_nr = 0x648,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c
index 552fdbd45ebe..1d0e33fb5f61 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c
@@ -113,6 +113,8 @@
#define IS_NVA3F(x) (((x) > 0xa0 && (x) < 0xaa) || (x) == 0xaf)
#define IS_NVAAF(x) ((x) >= 0xaa && (x) <= 0xac)
+#include <subdev/fb.h>
+
/*
* This code deals with PGRAPH contexts on NV50 family cards. Like NV40, it's
* the GPU itself that does context-switching, but it needs a special
@@ -569,8 +571,12 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
gr_def(ctx, 0x407d08, 0x00010040);
else if (device->chipset < 0xa0)
gr_def(ctx, 0x407d08, 0x00390040);
- else
- gr_def(ctx, 0x407d08, 0x003d0040);
+ else {
+ if (nouveau_fb(device)->ram->type != NV_MEM_TYPE_GDDR5)
+ gr_def(ctx, 0x407d08, 0x003d0040);
+ else
+ gr_def(ctx, 0x407d08, 0x003c0040);
+ }
gr_def(ctx, 0x407d0c, 0x00000022);
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c
index 833a96508c4e..b8e5fe60a1eb 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c
@@ -982,34 +982,93 @@ nvc0_grctx_pack_tpc[] = {
* PGRAPH context implementation
******************************************************************************/
+int
+nvc0_grctx_mmio_data(struct nvc0_grctx *info, u32 size, u32 align, u32 access)
+{
+ if (info->data) {
+ info->buffer[info->buffer_nr] = round_up(info->addr, align);
+ info->addr = info->buffer[info->buffer_nr] + size;
+ info->data->size = size;
+ info->data->align = align;
+ info->data->access = access;
+ info->data++;
+ return info->buffer_nr++;
+ }
+ return -1;
+}
+
+void
+nvc0_grctx_mmio_item(struct nvc0_grctx *info, u32 addr, u32 data,
+ int shift, int buffer)
+{
+ if (info->data) {
+ if (shift >= 0) {
+ info->mmio->addr = addr;
+ info->mmio->data = data;
+ info->mmio->shift = shift;
+ info->mmio->buffer = buffer;
+ if (buffer >= 0)
+ data |= info->buffer[buffer] >> shift;
+ info->mmio++;
+ } else
+ return;
+ } else {
+ if (buffer >= 0)
+ return;
+ }
+
+ nv_wr32(info->priv, addr, data);
+}
+
+void
+nvc0_grctx_generate_bundle(struct nvc0_grctx *info)
+{
+ const struct nvc0_grctx_oclass *impl = nvc0_grctx_impl(info->priv);
+ const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS;
+ const int s = 8;
+ const int b = mmio_vram(info, impl->bundle_size, (1 << s), access);
+ mmio_refn(info, 0x408004, 0x00000000, s, b);
+ mmio_refn(info, 0x408008, 0x80000000 | (impl->bundle_size >> s), 0, b);
+ mmio_refn(info, 0x418808, 0x00000000, s, b);
+ mmio_refn(info, 0x41880c, 0x80000000 | (impl->bundle_size >> s), 0, b);
+}
+
void
-nvc0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+nvc0_grctx_generate_pagepool(struct nvc0_grctx *info)
{
+ const struct nvc0_grctx_oclass *impl = nvc0_grctx_impl(info->priv);
+ const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS;
+ const int s = 8;
+ const int b = mmio_vram(info, impl->pagepool_size, (1 << s), access);
+ mmio_refn(info, 0x40800c, 0x00000000, s, b);
+ mmio_wr32(info, 0x408010, 0x80000000);
+ mmio_refn(info, 0x419004, 0x00000000, s, b);
+ mmio_wr32(info, 0x419008, 0x00000000);
+}
+
+void
+nvc0_grctx_generate_attrib(struct nvc0_grctx *info)
+{
+ struct nvc0_graph_priv *priv = info->priv;
+ const struct nvc0_grctx_oclass *impl = nvc0_grctx_impl(priv);
+ const u32 attrib = impl->attrib_nr;
+ const u32 size = 0x20 * (impl->attrib_nr_max + impl->alpha_nr_max);
+ const u32 access = NV_MEM_ACCESS_RW;
+ const int s = 12;
+ const int b = mmio_vram(info, size * priv->tpc_total, (1 << s), access);
int gpc, tpc;
- u32 offset;
-
- mmio_data(0x002000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
-
- mmio_list(0x408004, 0x00000000, 8, 0);
- mmio_list(0x408008, 0x80000018, 0, 0);
- mmio_list(0x40800c, 0x00000000, 8, 1);
- mmio_list(0x408010, 0x80000000, 0, 0);
- mmio_list(0x418810, 0x80000000, 12, 2);
- mmio_list(0x419848, 0x10000000, 12, 2);
- mmio_list(0x419004, 0x00000000, 8, 1);
- mmio_list(0x419008, 0x00000000, 0, 0);
- mmio_list(0x418808, 0x00000000, 8, 0);
- mmio_list(0x41880c, 0x80000018, 0, 0);
-
- mmio_list(0x405830, 0x02180000, 0, 0);
-
- for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
+ u32 bo = 0;
+
+ mmio_refn(info, 0x418810, 0x80000000, s, b);
+ mmio_refn(info, 0x419848, 0x10000000, s, b);
+ mmio_wr32(info, 0x405830, (attrib << 16));
+
+ for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
- u32 addr = TPC_UNIT(gpc, tpc, 0x0520);
- mmio_list(addr, 0x02180000 | offset, 0, 0);
- offset += 0x0324;
+ const u32 o = TPC_UNIT(gpc, tpc, 0x0520);
+ mmio_skip(info, o, (attrib << 16) | ++bo);
+ mmio_wr32(info, o, (attrib << 16) | --bo);
+ bo += impl->attrib_nr_max;
}
}
}
@@ -1170,7 +1229,7 @@ nvc0_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
{
struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass;
- nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
+ nouveau_mc(priv)->unk260(nouveau_mc(priv), 0);
nvc0_graph_mmio(priv, oclass->hub);
nvc0_graph_mmio(priv, oclass->gpc);
@@ -1180,7 +1239,9 @@ nvc0_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
nv_wr32(priv, 0x404154, 0x00000000);
- oclass->mods(priv, info);
+ oclass->bundle(info);
+ oclass->pagepool(info);
+ oclass->attrib(info);
oclass->unkn(priv);
nvc0_grctx_generate_tpcid(priv);
@@ -1192,7 +1253,7 @@ nvc0_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
nvc0_graph_icmd(priv, oclass->icmd);
nv_wr32(priv, 0x404154, 0x00000400);
nvc0_graph_mthd(priv, oclass->mthd);
- nv_mask(priv, 0x000260, 0x00000001, 0x00000001);
+ nouveau_mc(priv)->unk260(nouveau_mc(priv), 1);
}
int
@@ -1308,7 +1369,6 @@ nvc0_grctx_oclass = &(struct nvc0_grctx_oclass) {
.wr32 = _nouveau_graph_context_wr32,
},
.main = nvc0_grctx_generate_main,
- .mods = nvc0_grctx_generate_mods,
.unkn = nvc0_grctx_generate_unkn,
.hub = nvc0_grctx_pack_hub,
.gpc = nvc0_grctx_pack_gpc,
@@ -1316,4 +1376,11 @@ nvc0_grctx_oclass = &(struct nvc0_grctx_oclass) {
.tpc = nvc0_grctx_pack_tpc,
.icmd = nvc0_grctx_pack_icmd,
.mthd = nvc0_grctx_pack_mthd,
+ .bundle = nvc0_grctx_generate_bundle,
+ .bundle_size = 0x1800,
+ .pagepool = nvc0_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = nvc0_grctx_generate_attrib,
+ .attrib_nr_max = 0x324,
+ .attrib_nr = 0x218,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h
index 8da8b627b9d0..c776cd715e33 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h
@@ -12,12 +12,19 @@ struct nvc0_grctx {
u64 addr;
};
+int nvc0_grctx_mmio_data(struct nvc0_grctx *, u32 size, u32 align, u32 access);
+void nvc0_grctx_mmio_item(struct nvc0_grctx *, u32 addr, u32 data, int s, int);
+
+#define mmio_vram(a,b,c,d) nvc0_grctx_mmio_data((a), (b), (c), (d))
+#define mmio_refn(a,b,c,d,e) nvc0_grctx_mmio_item((a), (b), (c), (d), (e))
+#define mmio_skip(a,b,c) mmio_refn((a), (b), (c), -1, -1)
+#define mmio_wr32(a,b,c) mmio_refn((a), (b), (c), 0, -1)
+
struct nvc0_grctx_oclass {
struct nouveau_oclass base;
/* main context generation function */
void (*main)(struct nvc0_graph_priv *, struct nvc0_grctx *);
/* context-specific modify-on-first-load list generation function */
- void (*mods)(struct nvc0_graph_priv *, struct nvc0_grctx *);
void (*unkn)(struct nvc0_graph_priv *);
/* mmio context data */
const struct nvc0_graph_pack *hub;
@@ -28,30 +35,34 @@ struct nvc0_grctx_oclass {
/* indirect context data, generated with icmds/mthds */
const struct nvc0_graph_pack *icmd;
const struct nvc0_graph_pack *mthd;
+ /* bundle circular buffer */
+ void (*bundle)(struct nvc0_grctx *);
+ u32 bundle_size;
+ u32 bundle_min_gpm_fifo_depth;
+ u32 bundle_token_limit;
+ /* pagepool */
+ void (*pagepool)(struct nvc0_grctx *);
+ u32 pagepool_size;
+ /* attribute(/alpha) circular buffer */
+ void (*attrib)(struct nvc0_grctx *);
+ u32 attrib_nr_max;
+ u32 attrib_nr;
+ u32 alpha_nr_max;
+ u32 alpha_nr;
};
-#define mmio_data(s,a,p) do { \
- info->buffer[info->buffer_nr] = round_up(info->addr, (a)); \
- info->addr = info->buffer[info->buffer_nr++] + (s); \
- info->data->size = (s); \
- info->data->align = (a); \
- info->data->access = (p); \
- info->data++; \
-} while(0)
-
-#define mmio_list(r,d,s,b) do { \
- info->mmio->addr = (r); \
- info->mmio->data = (d); \
- info->mmio->shift = (s); \
- info->mmio->buffer = (b); \
- info->mmio++; \
- nv_wr32(priv, (r), (d) | ((s) ? (info->buffer[(b)] >> (s)) : 0)); \
-} while(0)
+static inline const struct nvc0_grctx_oclass *
+nvc0_grctx_impl(struct nvc0_graph_priv *priv)
+{
+ return (void *)nv_engine(priv)->cclass;
+}
extern struct nouveau_oclass *nvc0_grctx_oclass;
int nvc0_grctx_generate(struct nvc0_graph_priv *);
void nvc0_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *);
-void nvc0_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nvc0_grctx_generate_bundle(struct nvc0_grctx *);
+void nvc0_grctx_generate_pagepool(struct nvc0_grctx *);
+void nvc0_grctx_generate_attrib(struct nvc0_grctx *);
void nvc0_grctx_generate_unkn(struct nvc0_graph_priv *);
void nvc0_grctx_generate_tpcid(struct nvc0_graph_priv *);
void nvc0_grctx_generate_r406028(struct nvc0_graph_priv *);
@@ -60,22 +71,27 @@ void nvc0_grctx_generate_r418bb8(struct nvc0_graph_priv *);
void nvc0_grctx_generate_r406800(struct nvc0_graph_priv *);
extern struct nouveau_oclass *nvc1_grctx_oclass;
-void nvc1_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nvc1_grctx_generate_attrib(struct nvc0_grctx *);
void nvc1_grctx_generate_unkn(struct nvc0_graph_priv *);
extern struct nouveau_oclass *nvc4_grctx_oclass;
extern struct nouveau_oclass *nvc8_grctx_oclass;
+
extern struct nouveau_oclass *nvd7_grctx_oclass;
+void nvd7_grctx_generate_attrib(struct nvc0_grctx *);
+
extern struct nouveau_oclass *nvd9_grctx_oclass;
extern struct nouveau_oclass *nve4_grctx_oclass;
extern struct nouveau_oclass *gk20a_grctx_oclass;
void nve4_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *);
-void nve4_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *);
+void nve4_grctx_generate_bundle(struct nvc0_grctx *);
+void nve4_grctx_generate_pagepool(struct nvc0_grctx *);
void nve4_grctx_generate_unkn(struct nvc0_graph_priv *);
void nve4_grctx_generate_r418bb8(struct nvc0_graph_priv *);
extern struct nouveau_oclass *nvf0_grctx_oclass;
+extern struct nouveau_oclass *gk110b_grctx_oclass;
extern struct nouveau_oclass *nv108_grctx_oclass;
extern struct nouveau_oclass *gm107_grctx_oclass;
@@ -160,16 +176,23 @@ extern const struct nvc0_graph_pack nve4_grctx_pack_ppc[];
extern const struct nvc0_graph_pack nve4_grctx_pack_icmd[];
extern const struct nvc0_graph_init nve4_grctx_init_a097_0[];
+extern const struct nvc0_graph_pack nvf0_grctx_pack_icmd[];
+
extern const struct nvc0_graph_pack nvf0_grctx_pack_mthd[];
+extern const struct nvc0_graph_pack nvf0_grctx_pack_hub[];
extern const struct nvc0_graph_init nvf0_grctx_init_pri_0[];
extern const struct nvc0_graph_init nvf0_grctx_init_cwd_0[];
+extern const struct nvc0_graph_pack nvf0_grctx_pack_gpc[];
extern const struct nvc0_graph_init nvf0_grctx_init_gpc_unk_2[];
+extern const struct nvc0_graph_init nvf0_grctx_init_tex_0[];
extern const struct nvc0_graph_init nvf0_grctx_init_mpc_0[];
extern const struct nvc0_graph_init nvf0_grctx_init_l1c_0[];
+extern const struct nvc0_graph_pack nvf0_grctx_pack_ppc[];
+
extern const struct nvc0_graph_init nv108_grctx_init_rstr2d_0[];
extern const struct nvc0_graph_init nv108_grctx_init_prop_0[];
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c
index 24a92c569c0a..c6ba8fed18f1 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c
@@ -727,38 +727,38 @@ nvc1_grctx_pack_tpc[] = {
******************************************************************************/
void
-nvc1_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+nvc1_grctx_generate_attrib(struct nvc0_grctx *info)
{
+ struct nvc0_graph_priv *priv = info->priv;
+ const struct nvc0_grctx_oclass *impl = nvc0_grctx_impl(priv);
+ const u32 alpha = impl->alpha_nr;
+ const u32 beta = impl->attrib_nr;
+ const u32 size = 0x20 * (impl->attrib_nr_max + impl->alpha_nr_max);
+ const u32 access = NV_MEM_ACCESS_RW;
+ const int s = 12;
+ const int b = mmio_vram(info, size * priv->tpc_total, (1 << s), access);
+ const int timeslice_mode = 1;
+ const int max_batches = 0xffff;
+ u32 bo = 0;
+ u32 ao = bo + impl->attrib_nr_max * priv->tpc_total;
int gpc, tpc;
- u32 offset;
- mmio_data(0x002000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
- mmio_list(0x408004, 0x00000000, 8, 0);
- mmio_list(0x408008, 0x80000018, 0, 0);
- mmio_list(0x40800c, 0x00000000, 8, 1);
- mmio_list(0x408010, 0x80000000, 0, 0);
- mmio_list(0x418810, 0x80000000, 12, 2);
- mmio_list(0x419848, 0x10000000, 12, 2);
- mmio_list(0x419004, 0x00000000, 8, 1);
- mmio_list(0x419008, 0x00000000, 0, 0);
- mmio_list(0x418808, 0x00000000, 8, 0);
- mmio_list(0x41880c, 0x80000018, 0, 0);
+ mmio_refn(info, 0x418810, 0x80000000, s, b);
+ mmio_refn(info, 0x419848, 0x10000000, s, b);
+ mmio_wr32(info, 0x405830, (beta << 16) | alpha);
+ mmio_wr32(info, 0x4064c4, ((alpha / 4) << 16) | max_batches);
- mmio_list(0x405830, 0x02180218, 0, 0);
- mmio_list(0x4064c4, 0x0086ffff, 0, 0);
-
- for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
- for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
- u32 addr = TPC_UNIT(gpc, tpc, 0x0520);
- mmio_list(addr, 0x12180000 | offset, 0, 0);
- offset += 0x0324;
- }
+ for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) {
- u32 addr = TPC_UNIT(gpc, tpc, 0x0544);
- mmio_list(addr, 0x02180000 | offset, 0, 0);
- offset += 0x0324;
+ const u32 a = alpha;
+ const u32 b = beta;
+ const u32 t = timeslice_mode;
+ const u32 o = TPC_UNIT(gpc, tpc, 0x500);
+ mmio_skip(info, o + 0x20, (t << 28) | (b << 16) | ++bo);
+ mmio_wr32(info, o + 0x20, (t << 28) | (b << 16) | --bo);
+ bo += impl->attrib_nr_max;
+ mmio_wr32(info, o + 0x44, (a << 16) | ao);
+ ao += impl->alpha_nr_max;
}
}
}
@@ -786,7 +786,6 @@ nvc1_grctx_oclass = &(struct nvc0_grctx_oclass) {
.wr32 = _nouveau_graph_context_wr32,
},
.main = nvc0_grctx_generate_main,
- .mods = nvc1_grctx_generate_mods,
.unkn = nvc1_grctx_generate_unkn,
.hub = nvc1_grctx_pack_hub,
.gpc = nvc1_grctx_pack_gpc,
@@ -794,4 +793,13 @@ nvc1_grctx_oclass = &(struct nvc0_grctx_oclass) {
.tpc = nvc1_grctx_pack_tpc,
.icmd = nvc1_grctx_pack_icmd,
.mthd = nvc1_grctx_pack_mthd,
+ .bundle = nvc0_grctx_generate_bundle,
+ .bundle_size = 0x1800,
+ .pagepool = nvc0_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = nvc1_grctx_generate_attrib,
+ .attrib_nr_max = 0x324,
+ .attrib_nr = 0x218,
+ .alpha_nr_max = 0x324,
+ .alpha_nr = 0x218,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc4.c
index e11ed5538193..41705c60cc47 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc4.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc4.c
@@ -92,7 +92,6 @@ nvc4_grctx_oclass = &(struct nvc0_grctx_oclass) {
.wr32 = _nouveau_graph_context_wr32,
},
.main = nvc0_grctx_generate_main,
- .mods = nvc0_grctx_generate_mods,
.unkn = nvc0_grctx_generate_unkn,
.hub = nvc0_grctx_pack_hub,
.gpc = nvc0_grctx_pack_gpc,
@@ -100,4 +99,11 @@ nvc4_grctx_oclass = &(struct nvc0_grctx_oclass) {
.tpc = nvc4_grctx_pack_tpc,
.icmd = nvc0_grctx_pack_icmd,
.mthd = nvc0_grctx_pack_mthd,
+ .bundle = nvc0_grctx_generate_bundle,
+ .bundle_size = 0x1800,
+ .pagepool = nvc0_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = nvc0_grctx_generate_attrib,
+ .attrib_nr_max = 0x324,
+ .attrib_nr = 0x218,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c
index feebd58dfe8d..8f804cd8f9c7 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c
@@ -343,7 +343,6 @@ nvc8_grctx_oclass = &(struct nvc0_grctx_oclass) {
.wr32 = _nouveau_graph_context_wr32,
},
.main = nvc0_grctx_generate_main,
- .mods = nvc0_grctx_generate_mods,
.unkn = nvc0_grctx_generate_unkn,
.hub = nvc0_grctx_pack_hub,
.gpc = nvc8_grctx_pack_gpc,
@@ -351,4 +350,11 @@ nvc8_grctx_oclass = &(struct nvc0_grctx_oclass) {
.tpc = nvc0_grctx_pack_tpc,
.icmd = nvc8_grctx_pack_icmd,
.mthd = nvc8_grctx_pack_mthd,
+ .bundle = nvc0_grctx_generate_bundle,
+ .bundle_size = 0x1800,
+ .pagepool = nvc0_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = nvc0_grctx_generate_attrib,
+ .attrib_nr_max = 0x324,
+ .attrib_nr = 0x218,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c
index 1dbc8d7f2e86..fcf534fd9e65 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c
@@ -177,44 +177,41 @@ nvd7_grctx_pack_ppc[] = {
* PGRAPH context implementation
******************************************************************************/
-static void
-nvd7_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+void
+nvd7_grctx_generate_attrib(struct nvc0_grctx *info)
{
- u32 magic[GPC_MAX][2];
- u32 offset;
- int gpc;
-
- mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
- mmio_list(0x40800c, 0x00000000, 8, 1);
- mmio_list(0x408010, 0x80000000, 0, 0);
- mmio_list(0x419004, 0x00000000, 8, 1);
- mmio_list(0x419008, 0x00000000, 0, 0);
- mmio_list(0x408004, 0x00000000, 8, 0);
- mmio_list(0x408008, 0x80000018, 0, 0);
- mmio_list(0x418808, 0x00000000, 8, 0);
- mmio_list(0x41880c, 0x80000018, 0, 0);
- mmio_list(0x418810, 0x80000000, 12, 2);
- mmio_list(0x419848, 0x10000000, 12, 2);
+ struct nvc0_graph_priv *priv = info->priv;
+ const struct nvc0_grctx_oclass *impl = nvc0_grctx_impl(priv);
+ const u32 alpha = impl->alpha_nr;
+ const u32 beta = impl->attrib_nr;
+ const u32 size = 0x20 * (impl->attrib_nr_max + impl->alpha_nr_max);
+ const u32 access = NV_MEM_ACCESS_RW;
+ const int s = 12;
+ const int b = mmio_vram(info, size * priv->tpc_total, (1 << s), access);
+ const int timeslice_mode = 1;
+ const int max_batches = 0xffff;
+ u32 bo = 0;
+ u32 ao = bo + impl->attrib_nr_max * priv->tpc_total;
+ int gpc, ppc;
- mmio_list(0x405830, 0x02180324, 0, 0);
- mmio_list(0x4064c4, 0x00c9ffff, 0, 0);
-
- for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
- u16 magic0 = 0x0218 * priv->tpc_nr[gpc];
- u16 magic1 = 0x0324 * priv->tpc_nr[gpc];
- magic[gpc][0] = 0x10000000 | (magic0 << 16) | offset;
- magic[gpc][1] = 0x00000000 | (magic1 << 16);
- offset += 0x0324 * priv->tpc_nr[gpc];
- }
+ mmio_refn(info, 0x418810, 0x80000000, s, b);
+ mmio_refn(info, 0x419848, 0x10000000, s, b);
+ mmio_wr32(info, 0x405830, (beta << 16) | alpha);
+ mmio_wr32(info, 0x4064c4, ((alpha / 4) << 16) | max_batches);
for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
- mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0);
- mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0);
- offset += 0x07ff * priv->tpc_nr[gpc];
+ for (ppc = 0; ppc < priv->ppc_nr[gpc]; ppc++) {
+ const u32 a = alpha * priv->ppc_tpc_nr[gpc][ppc];
+ const u32 b = beta * priv->ppc_tpc_nr[gpc][ppc];
+ const u32 t = timeslice_mode;
+ const u32 o = PPC_UNIT(gpc, ppc, 0);
+ mmio_skip(info, o + 0xc0, (t << 28) | (b << 16) | ++bo);
+ mmio_wr32(info, o + 0xc0, (t << 28) | (b << 16) | --bo);
+ bo += impl->attrib_nr_max * priv->ppc_tpc_nr[gpc][ppc];
+ mmio_wr32(info, o + 0xe4, (a << 16) | ao);
+ ao += impl->alpha_nr_max * priv->ppc_tpc_nr[gpc][ppc];
+ }
}
- mmio_list(0x17e91c, 0x03060609, 0, 0); /* different from kepler */
}
void
@@ -223,7 +220,7 @@ nvd7_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass;
int i;
- nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
+ nouveau_mc(priv)->unk260(nouveau_mc(priv), 0);
nvc0_graph_mmio(priv, oclass->hub);
nvc0_graph_mmio(priv, oclass->gpc);
@@ -233,7 +230,9 @@ nvd7_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
nv_wr32(priv, 0x404154, 0x00000000);
- oclass->mods(priv, info);
+ oclass->bundle(info);
+ oclass->pagepool(info);
+ oclass->attrib(info);
oclass->unkn(priv);
nvc0_grctx_generate_tpcid(priv);
@@ -248,7 +247,7 @@ nvd7_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
nvc0_graph_icmd(priv, oclass->icmd);
nv_wr32(priv, 0x404154, 0x00000400);
nvc0_graph_mthd(priv, oclass->mthd);
- nv_mask(priv, 0x000260, 0x00000001, 0x00000001);
+ nouveau_mc(priv)->unk260(nouveau_mc(priv), 1);
}
struct nouveau_oclass *
@@ -263,7 +262,6 @@ nvd7_grctx_oclass = &(struct nvc0_grctx_oclass) {
.wr32 = _nouveau_graph_context_wr32,
},
.main = nvd7_grctx_generate_main,
- .mods = nvd7_grctx_generate_mods,
.unkn = nve4_grctx_generate_unkn,
.hub = nvd7_grctx_pack_hub,
.gpc = nvd7_grctx_pack_gpc,
@@ -272,4 +270,13 @@ nvd7_grctx_oclass = &(struct nvc0_grctx_oclass) {
.ppc = nvd7_grctx_pack_ppc,
.icmd = nvd9_grctx_pack_icmd,
.mthd = nvd9_grctx_pack_mthd,
+ .bundle = nvc0_grctx_generate_bundle,
+ .bundle_size = 0x1800,
+ .pagepool = nvc0_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = nvd7_grctx_generate_attrib,
+ .attrib_nr_max = 0x324,
+ .attrib_nr = 0x218,
+ .alpha_nr_max = 0x7ff,
+ .alpha_nr = 0x324,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c
index c665fb7e4660..b9a301b6fd9f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c
@@ -511,7 +511,6 @@ nvd9_grctx_oclass = &(struct nvc0_grctx_oclass) {
.wr32 = _nouveau_graph_context_wr32,
},
.main = nvc0_grctx_generate_main,
- .mods = nvc1_grctx_generate_mods,
.unkn = nvc1_grctx_generate_unkn,
.hub = nvd9_grctx_pack_hub,
.gpc = nvd9_grctx_pack_gpc,
@@ -519,4 +518,13 @@ nvd9_grctx_oclass = &(struct nvc0_grctx_oclass) {
.tpc = nvd9_grctx_pack_tpc,
.icmd = nvd9_grctx_pack_icmd,
.mthd = nvd9_grctx_pack_mthd,
+ .bundle = nvc0_grctx_generate_bundle,
+ .bundle_size = 0x1800,
+ .pagepool = nvc0_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = nvc1_grctx_generate_attrib,
+ .attrib_nr_max = 0x324,
+ .attrib_nr = 0x218,
+ .alpha_nr_max = 0x324,
+ .alpha_nr = 0x218,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c
index c5b249238587..ccac2ee1a1cb 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c
@@ -839,47 +839,34 @@ nve4_grctx_pack_ppc[] = {
******************************************************************************/
void
-nve4_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
+nve4_grctx_generate_bundle(struct nvc0_grctx *info)
{
- u32 magic[GPC_MAX][2];
- u32 offset;
- int gpc;
-
- mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
- mmio_list(0x40800c, 0x00000000, 8, 1);
- mmio_list(0x408010, 0x80000000, 0, 0);
- mmio_list(0x419004, 0x00000000, 8, 1);
- mmio_list(0x419008, 0x00000000, 0, 0);
- mmio_list(0x4064cc, 0x80000000, 0, 0);
- mmio_list(0x408004, 0x00000000, 8, 0);
- mmio_list(0x408008, 0x80000030, 0, 0);
- mmio_list(0x418808, 0x00000000, 8, 0);
- mmio_list(0x41880c, 0x80000030, 0, 0);
- mmio_list(0x4064c8, 0x01800600, 0, 0);
- mmio_list(0x418810, 0x80000000, 12, 2);
- mmio_list(0x419848, 0x10000000, 12, 2);
-
- mmio_list(0x405830, 0x02180648, 0, 0);
- mmio_list(0x4064c4, 0x0192ffff, 0, 0);
-
- for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
- u16 magic0 = 0x0218 * priv->tpc_nr[gpc];
- u16 magic1 = 0x0648 * priv->tpc_nr[gpc];
- magic[gpc][0] = 0x10000000 | (magic0 << 16) | offset;
- magic[gpc][1] = 0x00000000 | (magic1 << 16);
- offset += 0x0324 * priv->tpc_nr[gpc];
- }
-
- for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
- mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0);
- mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0);
- offset += 0x07ff * priv->tpc_nr[gpc];
- }
+ const struct nvc0_grctx_oclass *impl = nvc0_grctx_impl(info->priv);
+ const u32 state_limit = min(impl->bundle_min_gpm_fifo_depth,
+ impl->bundle_size / 0x20);
+ const u32 token_limit = impl->bundle_token_limit;
+ const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS;
+ const int s = 8;
+ const int b = mmio_vram(info, impl->bundle_size, (1 << s), access);
+ mmio_refn(info, 0x408004, 0x00000000, s, b);
+ mmio_refn(info, 0x408008, 0x80000000 | (impl->bundle_size >> s), 0, b);
+ mmio_refn(info, 0x418808, 0x00000000, s, b);
+ mmio_refn(info, 0x41880c, 0x80000000 | (impl->bundle_size >> s), 0, b);
+ mmio_wr32(info, 0x4064c8, (state_limit << 16) | token_limit);
+}
- mmio_list(0x17e91c, 0x06060609, 0, 0);
- mmio_list(0x17e920, 0x00090a05, 0, 0);
+void
+nve4_grctx_generate_pagepool(struct nvc0_grctx *info)
+{
+ const struct nvc0_grctx_oclass *impl = nvc0_grctx_impl(info->priv);
+ const u32 access = NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS;
+ const int s = 8;
+ const int b = mmio_vram(info, impl->pagepool_size, (1 << s), access);
+ mmio_refn(info, 0x40800c, 0x00000000, s, b);
+ mmio_wr32(info, 0x408010, 0x80000000);
+ mmio_refn(info, 0x419004, 0x00000000, s, b);
+ mmio_wr32(info, 0x419008, 0x00000000);
+ mmio_wr32(info, 0x4064cc, 0x80000000);
}
void
@@ -957,7 +944,7 @@ nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass;
int i;
- nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
+ nouveau_mc(priv)->unk260(nouveau_mc(priv), 0);
nvc0_graph_mmio(priv, oclass->hub);
nvc0_graph_mmio(priv, oclass->gpc);
@@ -967,7 +954,9 @@ nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
nv_wr32(priv, 0x404154, 0x00000000);
- oclass->mods(priv, info);
+ oclass->bundle(info);
+ oclass->pagepool(info);
+ oclass->attrib(info);
oclass->unkn(priv);
nvc0_grctx_generate_tpcid(priv);
@@ -991,7 +980,7 @@ nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
nvc0_graph_icmd(priv, oclass->icmd);
nv_wr32(priv, 0x404154, 0x00000400);
nvc0_graph_mthd(priv, oclass->mthd);
- nv_mask(priv, 0x000260, 0x00000001, 0x00000001);
+ nouveau_mc(priv)->unk260(nouveau_mc(priv), 1);
nv_mask(priv, 0x418800, 0x00200000, 0x00200000);
nv_mask(priv, 0x41be10, 0x00800000, 0x00800000);
@@ -1009,7 +998,6 @@ nve4_grctx_oclass = &(struct nvc0_grctx_oclass) {
.wr32 = _nouveau_graph_context_wr32,
},
.main = nve4_grctx_generate_main,
- .mods = nve4_grctx_generate_mods,
.unkn = nve4_grctx_generate_unkn,
.hub = nve4_grctx_pack_hub,
.gpc = nve4_grctx_pack_gpc,
@@ -1018,4 +1006,15 @@ nve4_grctx_oclass = &(struct nvc0_grctx_oclass) {
.ppc = nve4_grctx_pack_ppc,
.icmd = nve4_grctx_pack_icmd,
.mthd = nve4_grctx_pack_mthd,
+ .bundle = nve4_grctx_generate_bundle,
+ .bundle_size = 0x3000,
+ .bundle_min_gpm_fifo_depth = 0x180,
+ .bundle_token_limit = 0x600,
+ .pagepool = nve4_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = nvd7_grctx_generate_attrib,
+ .attrib_nr_max = 0x324,
+ .attrib_nr = 0x218,
+ .alpha_nr_max = 0x7ff,
+ .alpha_nr = 0x648,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c
index dec03f04114d..e9b0dcf95a49 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c
@@ -279,7 +279,7 @@ nvf0_grctx_init_icmd_0[] = {
{}
};
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
nvf0_grctx_pack_icmd[] = {
{ nvf0_grctx_init_icmd_0 },
{}
@@ -668,7 +668,7 @@ nvf0_grctx_init_be_0[] = {
{}
};
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
nvf0_grctx_pack_hub[] = {
{ nvc0_grctx_init_main_0 },
{ nvf0_grctx_init_fe_0 },
@@ -704,7 +704,7 @@ nvf0_grctx_init_gpc_unk_2[] = {
{}
};
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
nvf0_grctx_pack_gpc[] = {
{ nvc0_grctx_init_gpc_unk_0 },
{ nvd9_grctx_init_prop_0 },
@@ -718,7 +718,7 @@ nvf0_grctx_pack_gpc[] = {
{}
};
-static const struct nvc0_graph_init
+const struct nvc0_graph_init
nvf0_grctx_init_tex_0[] = {
{ 0x419a00, 1, 0x04, 0x000000f0 },
{ 0x419a04, 1, 0x04, 0x00000001 },
@@ -797,7 +797,7 @@ nvf0_grctx_init_cbm_0[] = {
{}
};
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
nvf0_grctx_pack_ppc[] = {
{ nve4_grctx_init_pes_0 },
{ nvf0_grctx_init_cbm_0 },
@@ -809,58 +809,6 @@ nvf0_grctx_pack_ppc[] = {
* PGRAPH context implementation
******************************************************************************/
-static void
-nvf0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
-{
- u32 magic[GPC_MAX][4];
- u32 offset;
- int gpc;
-
- mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS);
- mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW);
- mmio_list(0x40800c, 0x00000000, 8, 1);
- mmio_list(0x408010, 0x80000000, 0, 0);
- mmio_list(0x419004, 0x00000000, 8, 1);
- mmio_list(0x419008, 0x00000000, 0, 0);
- mmio_list(0x4064cc, 0x80000000, 0, 0);
- mmio_list(0x408004, 0x00000000, 8, 0);
- mmio_list(0x408008, 0x80000030, 0, 0);
- mmio_list(0x418808, 0x00000000, 8, 0);
- mmio_list(0x41880c, 0x80000030, 0, 0);
- mmio_list(0x4064c8, 0x01800600, 0, 0);
- mmio_list(0x418810, 0x80000000, 12, 2);
- mmio_list(0x419848, 0x10000000, 12, 2);
-
- mmio_list(0x405830, 0x02180648, 0, 0);
- mmio_list(0x4064c4, 0x0192ffff, 0, 0);
-
- for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) {
- u16 magic0 = 0x0218 * (priv->tpc_nr[gpc] - 1);
- u16 magic1 = 0x0648 * (priv->tpc_nr[gpc] - 1);
- u16 magic2 = 0x0218;
- u16 magic3 = 0x0648;
- magic[gpc][0] = 0x10000000 | (magic0 << 16) | offset;
- magic[gpc][1] = 0x00000000 | (magic1 << 16);
- offset += 0x0324 * (priv->tpc_nr[gpc] - 1);
- magic[gpc][2] = 0x10000000 | (magic2 << 16) | offset;
- magic[gpc][3] = 0x00000000 | (magic3 << 16);
- offset += 0x0324;
- }
-
- for (gpc = 0; gpc < priv->gpc_nr; gpc++) {
- mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0);
- mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0);
- offset += 0x07ff * (priv->tpc_nr[gpc] - 1);
- mmio_list(GPC_UNIT(gpc, 0x32c0), magic[gpc][2], 0, 0);
- mmio_list(GPC_UNIT(gpc, 0x32e4), magic[gpc][3] | offset, 0, 0);
- offset += 0x07ff;
- }
-
- mmio_list(0x17e91c, 0x06060609, 0, 0);
- mmio_list(0x17e920, 0x00090a05, 0, 0);
-}
-
struct nouveau_oclass *
nvf0_grctx_oclass = &(struct nvc0_grctx_oclass) {
.base.handle = NV_ENGCTX(GR, 0xf0),
@@ -873,7 +821,6 @@ nvf0_grctx_oclass = &(struct nvc0_grctx_oclass) {
.wr32 = _nouveau_graph_context_wr32,
},
.main = nve4_grctx_generate_main,
- .mods = nvf0_grctx_generate_mods,
.unkn = nve4_grctx_generate_unkn,
.hub = nvf0_grctx_pack_hub,
.gpc = nvf0_grctx_pack_gpc,
@@ -882,4 +829,15 @@ nvf0_grctx_oclass = &(struct nvc0_grctx_oclass) {
.ppc = nvf0_grctx_pack_ppc,
.icmd = nvf0_grctx_pack_icmd,
.mthd = nvf0_grctx_pack_mthd,
+ .bundle = nve4_grctx_generate_bundle,
+ .bundle_size = 0x3000,
+ .bundle_min_gpm_fifo_depth = 0x180,
+ .bundle_token_limit = 0x7c0,
+ .pagepool = nve4_grctx_generate_pagepool,
+ .pagepool_size = 0x8000,
+ .attrib = nvd7_grctx_generate_attrib,
+ .attrib_nr_max = 0x324,
+ .attrib_nr = 0x218,
+ .alpha_nr_max = 0x7ff,
+ .alpha_nr = 0x648,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/gk110b.c b/drivers/gpu/drm/nouveau/core/engine/graph/gk110b.c
new file mode 100644
index 000000000000..d07b19dc168d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/gk110b.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "nvc0.h"
+#include "ctxnvc0.h"
+
+/*******************************************************************************
+ * PGRAPH register lists
+ ******************************************************************************/
+
+static const struct nvc0_graph_init
+gk110b_graph_init_l1c_0[] = {
+ { 0x419c98, 1, 0x04, 0x00000000 },
+ { 0x419ca8, 1, 0x04, 0x00000000 },
+ { 0x419cb0, 1, 0x04, 0x09000000 },
+ { 0x419cb4, 1, 0x04, 0x00000000 },
+ { 0x419cb8, 1, 0x04, 0x00b08bea },
+ { 0x419c84, 1, 0x04, 0x00010384 },
+ { 0x419cbc, 1, 0x04, 0x281b3646 },
+ { 0x419cc0, 2, 0x04, 0x00000000 },
+ { 0x419c80, 1, 0x04, 0x00020230 },
+ { 0x419ccc, 2, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_init
+gk110b_graph_init_sm_0[] = {
+ { 0x419e00, 1, 0x04, 0x00000080 },
+ { 0x419ea0, 1, 0x04, 0x00000000 },
+ { 0x419ee4, 1, 0x04, 0x00000000 },
+ { 0x419ea4, 1, 0x04, 0x00000100 },
+ { 0x419ea8, 1, 0x04, 0x00000000 },
+ { 0x419eb4, 1, 0x04, 0x00000000 },
+ { 0x419ebc, 2, 0x04, 0x00000000 },
+ { 0x419edc, 1, 0x04, 0x00000000 },
+ { 0x419f00, 1, 0x04, 0x00000000 },
+ { 0x419ed0, 1, 0x04, 0x00002616 },
+ { 0x419f74, 1, 0x04, 0x00015555 },
+ { 0x419f80, 4, 0x04, 0x00000000 },
+ {}
+};
+
+static const struct nvc0_graph_pack
+gk110b_graph_pack_mmio[] = {
+ { nve4_graph_init_main_0 },
+ { nvf0_graph_init_fe_0 },
+ { nvc0_graph_init_pri_0 },
+ { nvc0_graph_init_rstr2d_0 },
+ { nvd9_graph_init_pd_0 },
+ { nvf0_graph_init_ds_0 },
+ { nvc0_graph_init_scc_0 },
+ { nvf0_graph_init_sked_0 },
+ { nvf0_graph_init_cwd_0 },
+ { nvd9_graph_init_prop_0 },
+ { nvc1_graph_init_gpc_unk_0 },
+ { nvc0_graph_init_setup_0 },
+ { nvc0_graph_init_crstr_0 },
+ { nvc1_graph_init_setup_1 },
+ { nvc0_graph_init_zcull_0 },
+ { nvd9_graph_init_gpm_0 },
+ { nvf0_graph_init_gpc_unk_1 },
+ { nvc0_graph_init_gcc_0 },
+ { nve4_graph_init_tpccs_0 },
+ { nvf0_graph_init_tex_0 },
+ { nve4_graph_init_pe_0 },
+ { gk110b_graph_init_l1c_0 },
+ { nvc0_graph_init_mpc_0 },
+ { gk110b_graph_init_sm_0 },
+ { nvd7_graph_init_pes_0 },
+ { nvd7_graph_init_wwdx_0 },
+ { nvd7_graph_init_cbm_0 },
+ { nve4_graph_init_be_0 },
+ { nvc0_graph_init_fe_1 },
+ {}
+};
+
+/*******************************************************************************
+ * PGRAPH engine/subdev functions
+ ******************************************************************************/
+
+struct nouveau_oclass *
+gk110b_graph_oclass = &(struct nvc0_graph_oclass) {
+ .base.handle = NV_ENGINE(GR, 0xf1),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nvc0_graph_ctor,
+ .dtor = nvc0_graph_dtor,
+ .init = nve4_graph_init,
+ .fini = nvf0_graph_fini,
+ },
+ .cclass = &gk110b_grctx_oclass,
+ .sclass = nvf0_graph_sclass,
+ .mmio = gk110b_graph_pack_mmio,
+ .fecs.ucode = &nvf0_graph_fecs_ucode,
+ .gpccs.ucode = &nvf0_graph_gpccs_ucode,
+ .ppc_nr = 2,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c b/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c
index 83048a56430d..7d0abe9f3fe7 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c
@@ -27,8 +27,8 @@ static struct nouveau_oclass
gk20a_graph_sclass[] = {
{ 0x902d, &nouveau_object_ofuncs },
{ 0xa040, &nouveau_object_ofuncs },
- { 0xa297, &nouveau_object_ofuncs },
- { 0xa0c0, &nouveau_object_ofuncs },
+ { KEPLER_C, &nvc0_fermi_ofuncs, nvc0_graph_9097_omthds },
+ { KEPLER_COMPUTE_A, &nouveau_object_ofuncs, nvc0_graph_90c0_omthds },
{}
};
@@ -39,9 +39,10 @@ gk20a_graph_oclass = &(struct nvc0_graph_oclass) {
.ctor = nvc0_graph_ctor,
.dtor = nvc0_graph_dtor,
.init = nve4_graph_init,
- .fini = nve4_graph_fini,
+ .fini = _nouveau_graph_fini,
},
.cclass = &gk20a_grctx_oclass,
.sclass = gk20a_graph_sclass,
.mmio = nve4_graph_pack_mmio,
+ .ppc_nr = 1,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/gm107.c b/drivers/gpu/drm/nouveau/core/engine/graph/gm107.c
index 21c5f31d607f..4bdbdab2fd9a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/gm107.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/gm107.c
@@ -36,8 +36,8 @@ static struct nouveau_oclass
gm107_graph_sclass[] = {
{ 0x902d, &nouveau_object_ofuncs },
{ 0xa140, &nouveau_object_ofuncs },
- { 0xb097, &nouveau_object_ofuncs },
- { 0xb0c0, &nouveau_object_ofuncs },
+ { MAXWELL_A, &nvc0_fermi_ofuncs, nvc0_graph_9097_omthds },
+ { MAXWELL_COMPUTE_A, &nouveau_object_ofuncs, nvc0_graph_90c0_omthds },
{}
};
@@ -425,6 +425,9 @@ gm107_graph_init(struct nouveau_object *object)
nv_wr32(priv, 0x400134, 0xffffffff);
nv_wr32(priv, 0x400054, 0x2c350f63);
+
+ nvc0_graph_zbc_init(priv);
+
return nvc0_graph_init_ctxctl(priv);
}
@@ -462,4 +465,5 @@ gm107_graph_oclass = &(struct nvc0_graph_oclass) {
.mmio = gm107_graph_pack_mmio,
.fecs.ucode = 0 ? &gm107_graph_fecs_ucode : NULL,
.gpccs.ucode = &gm107_graph_gpccs_ucode,
+ .ppc_nr = 2,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c
index ad13dcdd15f9..f70e2f67a4dd 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c
@@ -24,7 +24,6 @@
#include <core/client.h>
#include <core/os.h>
-#include <core/class.h>
#include <core/handle.h>
#include <core/namedb.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
index 4532f7e5618c..2b12b09683c8 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
@@ -24,7 +24,6 @@
#include <core/client.h>
#include <core/os.h>
-#include <core/class.h>
#include <core/handle.h>
#include <subdev/fb.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv108.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv108.c
index 00ea1a089822..2b0e8f48c029 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv108.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv108.c
@@ -33,7 +33,7 @@ static struct nouveau_oclass
nv108_graph_sclass[] = {
{ 0x902d, &nouveau_object_ofuncs },
{ 0xa140, &nouveau_object_ofuncs },
- { 0xa197, &nouveau_object_ofuncs },
+ { KEPLER_B, &nvc0_fermi_ofuncs },
{ 0xa1c0, &nouveau_object_ofuncs },
{}
};
@@ -220,4 +220,5 @@ nv108_graph_oclass = &(struct nvc0_graph_oclass) {
.mmio = nv108_graph_pack_mmio,
.fecs.ucode = &nv108_graph_fecs_ucode,
.gpccs.ucode = &nv108_graph_gpccs_ucode,
+ .ppc_nr = 1,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
index d145e080899a..ceb9c746d94e 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
@@ -1,6 +1,5 @@
#include <core/client.h>
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <core/handle.h>
#include <core/enum.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv25.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv25.c
index 7a80d005a974..f8a6fdd7d5e8 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv25.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv25.c
@@ -1,5 +1,4 @@
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <core/enum.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv2a.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv2a.c
index 3e1f32ee43d4..5de9caa2ef67 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv2a.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv2a.c
@@ -1,5 +1,4 @@
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <core/enum.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv30.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv30.c
index e451db32e92a..2f9dbc709389 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv30.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv30.c
@@ -1,5 +1,4 @@
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <core/enum.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv34.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv34.c
index 9385ac7b44a4..34dd26c70b64 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv34.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv34.c
@@ -1,5 +1,4 @@
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <core/enum.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv35.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv35.c
index 9ce84b73f86a..2fb5756d9f66 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv35.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv35.c
@@ -1,5 +1,4 @@
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <core/enum.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
index 6477fbf6a550..4f401174868d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
@@ -24,7 +24,6 @@
#include <core/client.h>
#include <core/os.h>
-#include <core/class.h>
#include <core/handle.h>
#include <core/engctx.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
index 20665c21d80e..38e0aa26f1cd 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
@@ -23,7 +23,6 @@
*/
#include <core/os.h>
-#include <core/class.h>
#include <core/client.h>
#include <core/handle.h>
#include <core/engctx.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
index aa0838916354..30fd1dc64f93 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
@@ -26,15 +26,232 @@
#include "ctxnvc0.h"
/*******************************************************************************
+ * Zero Bandwidth Clear
+ ******************************************************************************/
+
+static void
+nvc0_graph_zbc_clear_color(struct nvc0_graph_priv *priv, int zbc)
+{
+ if (priv->zbc_color[zbc].format) {
+ nv_wr32(priv, 0x405804, priv->zbc_color[zbc].ds[0]);
+ nv_wr32(priv, 0x405808, priv->zbc_color[zbc].ds[1]);
+ nv_wr32(priv, 0x40580c, priv->zbc_color[zbc].ds[2]);
+ nv_wr32(priv, 0x405810, priv->zbc_color[zbc].ds[3]);
+ }
+ nv_wr32(priv, 0x405814, priv->zbc_color[zbc].format);
+ nv_wr32(priv, 0x405820, zbc);
+ nv_wr32(priv, 0x405824, 0x00000004); /* TRIGGER | WRITE | COLOR */
+}
+
+static int
+nvc0_graph_zbc_color_get(struct nvc0_graph_priv *priv, int format,
+ const u32 ds[4], const u32 l2[4])
+{
+ struct nouveau_ltc *ltc = nouveau_ltc(priv);
+ int zbc = -ENOSPC, i;
+
+ for (i = ltc->zbc_min; i <= ltc->zbc_max; i++) {
+ if (priv->zbc_color[i].format) {
+ if (priv->zbc_color[i].format != format)
+ continue;
+ if (memcmp(priv->zbc_color[i].ds, ds, sizeof(
+ priv->zbc_color[i].ds)))
+ continue;
+ if (memcmp(priv->zbc_color[i].l2, l2, sizeof(
+ priv->zbc_color[i].l2))) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ return i;
+ } else {
+ zbc = (zbc < 0) ? i : zbc;
+ }
+ }
+
+ if (zbc < 0)
+ return zbc;
+
+ memcpy(priv->zbc_color[zbc].ds, ds, sizeof(priv->zbc_color[zbc].ds));
+ memcpy(priv->zbc_color[zbc].l2, l2, sizeof(priv->zbc_color[zbc].l2));
+ priv->zbc_color[zbc].format = format;
+ ltc->zbc_color_get(ltc, zbc, l2);
+ nvc0_graph_zbc_clear_color(priv, zbc);
+ return zbc;
+}
+
+static void
+nvc0_graph_zbc_clear_depth(struct nvc0_graph_priv *priv, int zbc)
+{
+ if (priv->zbc_depth[zbc].format)
+ nv_wr32(priv, 0x405818, priv->zbc_depth[zbc].ds);
+ nv_wr32(priv, 0x40581c, priv->zbc_depth[zbc].format);
+ nv_wr32(priv, 0x405820, zbc);
+ nv_wr32(priv, 0x405824, 0x00000005); /* TRIGGER | WRITE | DEPTH */
+}
+
+static int
+nvc0_graph_zbc_depth_get(struct nvc0_graph_priv *priv, int format,
+ const u32 ds, const u32 l2)
+{
+ struct nouveau_ltc *ltc = nouveau_ltc(priv);
+ int zbc = -ENOSPC, i;
+
+ for (i = ltc->zbc_min; i <= ltc->zbc_max; i++) {
+ if (priv->zbc_depth[i].format) {
+ if (priv->zbc_depth[i].format != format)
+ continue;
+ if (priv->zbc_depth[i].ds != ds)
+ continue;
+ if (priv->zbc_depth[i].l2 != l2) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ return i;
+ } else {
+ zbc = (zbc < 0) ? i : zbc;
+ }
+ }
+
+ if (zbc < 0)
+ return zbc;
+
+ priv->zbc_depth[zbc].format = format;
+ priv->zbc_depth[zbc].ds = ds;
+ priv->zbc_depth[zbc].l2 = l2;
+ ltc->zbc_depth_get(ltc, zbc, l2);
+ nvc0_graph_zbc_clear_depth(priv, zbc);
+ return zbc;
+}
+
+/*******************************************************************************
* Graphics object classes
******************************************************************************/
+static int
+nvc0_fermi_mthd_zbc_color(struct nouveau_object *object, void *data, u32 size)
+{
+ struct nvc0_graph_priv *priv = (void *)object->engine;
+ union {
+ struct fermi_a_zbc_color_v0 v0;
+ } *args = data;
+ int ret;
+
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ switch (args->v0.format) {
+ case FERMI_A_ZBC_COLOR_V0_FMT_ZERO:
+ case FERMI_A_ZBC_COLOR_V0_FMT_UNORM_ONE:
+ case FERMI_A_ZBC_COLOR_V0_FMT_RF32_GF32_BF32_AF32:
+ case FERMI_A_ZBC_COLOR_V0_FMT_R16_G16_B16_A16:
+ case FERMI_A_ZBC_COLOR_V0_FMT_RN16_GN16_BN16_AN16:
+ case FERMI_A_ZBC_COLOR_V0_FMT_RS16_GS16_BS16_AS16:
+ case FERMI_A_ZBC_COLOR_V0_FMT_RU16_GU16_BU16_AU16:
+ case FERMI_A_ZBC_COLOR_V0_FMT_RF16_GF16_BF16_AF16:
+ case FERMI_A_ZBC_COLOR_V0_FMT_A8R8G8B8:
+ case FERMI_A_ZBC_COLOR_V0_FMT_A8RL8GL8BL8:
+ case FERMI_A_ZBC_COLOR_V0_FMT_A2B10G10R10:
+ case FERMI_A_ZBC_COLOR_V0_FMT_AU2BU10GU10RU10:
+ case FERMI_A_ZBC_COLOR_V0_FMT_A8B8G8R8:
+ case FERMI_A_ZBC_COLOR_V0_FMT_A8BL8GL8RL8:
+ case FERMI_A_ZBC_COLOR_V0_FMT_AN8BN8GN8RN8:
+ case FERMI_A_ZBC_COLOR_V0_FMT_AS8BS8GS8RS8:
+ case FERMI_A_ZBC_COLOR_V0_FMT_AU8BU8GU8RU8:
+ case FERMI_A_ZBC_COLOR_V0_FMT_A2R10G10B10:
+ case FERMI_A_ZBC_COLOR_V0_FMT_BF10GF11RF11:
+ ret = nvc0_graph_zbc_color_get(priv, args->v0.format,
+ args->v0.ds,
+ args->v0.l2);
+ if (ret >= 0) {
+ args->v0.index = ret;
+ return 0;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+static int
+nvc0_fermi_mthd_zbc_depth(struct nouveau_object *object, void *data, u32 size)
+{
+ struct nvc0_graph_priv *priv = (void *)object->engine;
+ union {
+ struct fermi_a_zbc_depth_v0 v0;
+ } *args = data;
+ int ret;
+
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ switch (args->v0.format) {
+ case FERMI_A_ZBC_DEPTH_V0_FMT_FP32:
+ ret = nvc0_graph_zbc_depth_get(priv, args->v0.format,
+ args->v0.ds,
+ args->v0.l2);
+ return (ret >= 0) ? 0 : -ENOSPC;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+static int
+nvc0_fermi_mthd(struct nouveau_object *object, u32 mthd, void *data, u32 size)
+{
+ switch (mthd) {
+ case FERMI_A_ZBC_COLOR:
+ return nvc0_fermi_mthd_zbc_color(object, data, size);
+ case FERMI_A_ZBC_DEPTH:
+ return nvc0_fermi_mthd_zbc_depth(object, data, size);
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+struct nouveau_ofuncs
+nvc0_fermi_ofuncs = {
+ .ctor = _nouveau_object_ctor,
+ .dtor = nouveau_object_destroy,
+ .init = nouveau_object_init,
+ .fini = nouveau_object_fini,
+ .mthd = nvc0_fermi_mthd,
+};
+
+static int
+nvc0_graph_set_shader_exceptions(struct nouveau_object *object, u32 mthd,
+ void *pdata, u32 size)
+{
+ struct nvc0_graph_priv *priv = (void *)nv_engine(object);
+ if (size >= sizeof(u32)) {
+ u32 data = *(u32 *)pdata ? 0xffffffff : 0x00000000;
+ nv_wr32(priv, 0x419e44, data);
+ nv_wr32(priv, 0x419e4c, data);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+struct nouveau_omthds
+nvc0_graph_9097_omthds[] = {
+ { 0x1528, 0x1528, nvc0_graph_set_shader_exceptions },
+ {}
+};
+
+struct nouveau_omthds
+nvc0_graph_90c0_omthds[] = {
+ { 0x1528, 0x1528, nvc0_graph_set_shader_exceptions },
+ {}
+};
+
struct nouveau_oclass
nvc0_graph_sclass[] = {
{ 0x902d, &nouveau_object_ofuncs },
{ 0x9039, &nouveau_object_ofuncs },
- { 0x9097, &nouveau_object_ofuncs },
- { 0x90c0, &nouveau_object_ofuncs },
+ { FERMI_A, &nvc0_fermi_ofuncs, nvc0_graph_9097_omthds },
+ { FERMI_COMPUTE_A, &nouveau_object_ofuncs, nvc0_graph_90c0_omthds },
{}
};
@@ -98,7 +315,7 @@ nvc0_graph_context_ctor(struct nouveau_object *parent,
u32 addr = mmio->addr;
u32 data = mmio->data;
- if (mmio->shift) {
+ if (mmio->buffer >= 0) {
u64 info = chan->data[mmio->buffer].vma.offset;
data |= info >> mmio->shift;
}
@@ -407,6 +624,35 @@ nvc0_graph_pack_mmio[] = {
******************************************************************************/
void
+nvc0_graph_zbc_init(struct nvc0_graph_priv *priv)
+{
+ const u32 zero[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000 };
+ const u32 one[] = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff };
+ const u32 f32_0[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000 };
+ const u32 f32_1[] = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000,
+ 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 };
+ struct nouveau_ltc *ltc = nouveau_ltc(priv);
+ int index;
+
+ if (!priv->zbc_color[0].format) {
+ nvc0_graph_zbc_color_get(priv, 1, & zero[0], &zero[4]);
+ nvc0_graph_zbc_color_get(priv, 2, & one[0], &one[4]);
+ nvc0_graph_zbc_color_get(priv, 4, &f32_0[0], &f32_0[4]);
+ nvc0_graph_zbc_color_get(priv, 4, &f32_1[0], &f32_1[4]);
+ nvc0_graph_zbc_depth_get(priv, 1, 0x00000000, 0x00000000);
+ nvc0_graph_zbc_depth_get(priv, 1, 0x3f800000, 0x3f800000);
+ }
+
+ for (index = ltc->zbc_min; index <= ltc->zbc_max; index++)
+ nvc0_graph_zbc_clear_color(priv, index);
+ for (index = ltc->zbc_min; index <= ltc->zbc_max; index++)
+ nvc0_graph_zbc_clear_depth(priv, index);
+}
+
+void
nvc0_graph_mmio(struct nvc0_graph_priv *priv, const struct nvc0_graph_pack *p)
{
const struct nvc0_graph_pack *pack;
@@ -969,17 +1215,16 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv)
{
struct nvc0_graph_oclass *oclass = (void *)nv_object(priv)->oclass;
struct nvc0_grctx_oclass *cclass = (void *)nv_engine(priv)->cclass;
- u32 r000260;
int i;
if (priv->firmware) {
/* load fuc microcode */
- r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
+ nouveau_mc(priv)->unk260(nouveau_mc(priv), 0);
nvc0_graph_init_fw(priv, 0x409000, &priv->fuc409c,
&priv->fuc409d);
nvc0_graph_init_fw(priv, 0x41a000, &priv->fuc41ac,
&priv->fuc41ad);
- nv_wr32(priv, 0x000260, r000260);
+ nouveau_mc(priv)->unk260(nouveau_mc(priv), 1);
/* start both of them running */
nv_wr32(priv, 0x409840, 0xffffffff);
@@ -1066,7 +1311,7 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv)
}
/* load HUB microcode */
- r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000);
+ nouveau_mc(priv)->unk260(nouveau_mc(priv), 0);
nv_wr32(priv, 0x4091c0, 0x01000000);
for (i = 0; i < oclass->fecs.ucode->data.size / 4; i++)
nv_wr32(priv, 0x4091c4, oclass->fecs.ucode->data.data[i]);
@@ -1089,7 +1334,7 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv)
nv_wr32(priv, 0x41a188, i >> 6);
nv_wr32(priv, 0x41a184, oclass->gpccs.ucode->code.data[i]);
}
- nv_wr32(priv, 0x000260, r000260);
+ nouveau_mc(priv)->unk260(nouveau_mc(priv), 1);
/* load register lists */
nvc0_graph_init_csdata(priv, cclass->hub, 0x409000, 0x000, 0x000000);
@@ -1224,6 +1469,9 @@ nvc0_graph_init(struct nouveau_object *object)
nv_wr32(priv, 0x400134, 0xffffffff);
nv_wr32(priv, 0x400054, 0x34ce3464);
+
+ nvc0_graph_zbc_init(priv);
+
return nvc0_graph_init_ctxctl(priv);
}
@@ -1287,7 +1535,7 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_device *device = nv_device(parent);
struct nvc0_graph_priv *priv;
bool use_ext_fw, enable;
- int ret, i;
+ int ret, i, j;
use_ext_fw = nouveau_boolopt(device->cfgopt, "NvGrUseFW",
oclass->fecs.ucode == NULL);
@@ -1333,6 +1581,11 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
for (i = 0; i < priv->gpc_nr; i++) {
priv->tpc_nr[i] = nv_rd32(priv, GPC_UNIT(i, 0x2608));
priv->tpc_total += priv->tpc_nr[i];
+ priv->ppc_nr[i] = oclass->ppc_nr;
+ for (j = 0; j < priv->ppc_nr[i]; j++) {
+ u8 mask = nv_rd32(priv, GPC_UNIT(i, 0x0c30 + (j * 4)));
+ priv->ppc_tpc_nr[i][j] = hweight8(mask);
+ }
}
/*XXX: these need figuring out... though it might not even matter */
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h
index ffc289198dd8..7ed9e89c3435 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h
@@ -30,10 +30,15 @@
#include <core/gpuobj.h>
#include <core/option.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
+
#include <subdev/fb.h>
#include <subdev/vm.h>
#include <subdev/bar.h>
#include <subdev/timer.h>
+#include <subdev/mc.h>
+#include <subdev/ltc.h>
#include <engine/fifo.h>
#include <engine/graph.h>
@@ -60,7 +65,7 @@ struct nvc0_graph_mmio {
u32 addr;
u32 data;
u32 shift;
- u32 buffer;
+ int buffer;
};
struct nvc0_graph_fuc {
@@ -68,6 +73,18 @@ struct nvc0_graph_fuc {
u32 size;
};
+struct nvc0_graph_zbc_color {
+ u32 format;
+ u32 ds[4];
+ u32 l2[4];
+};
+
+struct nvc0_graph_zbc_depth {
+ u32 format;
+ u32 ds;
+ u32 l2;
+};
+
struct nvc0_graph_priv {
struct nouveau_graph base;
@@ -77,10 +94,15 @@ struct nvc0_graph_priv {
struct nvc0_graph_fuc fuc41ad;
bool firmware;
+ struct nvc0_graph_zbc_color zbc_color[NOUVEAU_LTC_MAX_ZBC_CNT];
+ struct nvc0_graph_zbc_depth zbc_depth[NOUVEAU_LTC_MAX_ZBC_CNT];
+
u8 rop_nr;
u8 gpc_nr;
u8 tpc_nr[GPC_MAX];
u8 tpc_total;
+ u8 ppc_nr[GPC_MAX];
+ u8 ppc_tpc_nr[GPC_MAX][4];
struct nouveau_gpuobj *unk4188b4;
struct nouveau_gpuobj *unk4188b8;
@@ -118,12 +140,20 @@ int nvc0_graph_ctor(struct nouveau_object *, struct nouveau_object *,
struct nouveau_object **);
void nvc0_graph_dtor(struct nouveau_object *);
int nvc0_graph_init(struct nouveau_object *);
+void nvc0_graph_zbc_init(struct nvc0_graph_priv *);
+
int nve4_graph_fini(struct nouveau_object *, bool);
int nve4_graph_init(struct nouveau_object *);
-extern struct nouveau_oclass nvc0_graph_sclass[];
+int nvf0_graph_fini(struct nouveau_object *, bool);
+
+extern struct nouveau_ofuncs nvc0_fermi_ofuncs;
+extern struct nouveau_oclass nvc0_graph_sclass[];
+extern struct nouveau_omthds nvc0_graph_9097_omthds[];
+extern struct nouveau_omthds nvc0_graph_90c0_omthds[];
extern struct nouveau_oclass nvc8_graph_sclass[];
+extern struct nouveau_oclass nvf0_graph_sclass[];
struct nvc0_graph_init {
u32 addr;
@@ -149,6 +179,9 @@ struct nvc0_graph_ucode {
extern struct nvc0_graph_ucode nvc0_graph_fecs_ucode;
extern struct nvc0_graph_ucode nvc0_graph_gpccs_ucode;
+extern struct nvc0_graph_ucode nvf0_graph_fecs_ucode;
+extern struct nvc0_graph_ucode nvf0_graph_gpccs_ucode;
+
struct nvc0_graph_oclass {
struct nouveau_oclass base;
struct nouveau_oclass **cclass;
@@ -160,6 +193,7 @@ struct nvc0_graph_oclass {
struct {
struct nvc0_graph_ucode *ucode;
} gpccs;
+ int ppc_nr;
};
void nvc0_graph_mmio(struct nvc0_graph_priv *, const struct nvc0_graph_pack *);
@@ -223,9 +257,11 @@ extern const struct nvc0_graph_init nve4_graph_init_be_0[];
extern const struct nvc0_graph_pack nve4_graph_pack_mmio[];
extern const struct nvc0_graph_init nvf0_graph_init_fe_0[];
+extern const struct nvc0_graph_init nvf0_graph_init_ds_0[];
extern const struct nvc0_graph_init nvf0_graph_init_sked_0[];
extern const struct nvc0_graph_init nvf0_graph_init_cwd_0[];
extern const struct nvc0_graph_init nvf0_graph_init_gpc_unk_1[];
+extern const struct nvc0_graph_init nvf0_graph_init_tex_0[];
extern const struct nvc0_graph_init nvf0_graph_init_sm_0[];
extern const struct nvc0_graph_init nv108_graph_init_gpc_unk_0[];
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c
index 30cab0b2eba1..93d58e5b82c2 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c
@@ -33,9 +33,9 @@ static struct nouveau_oclass
nvc1_graph_sclass[] = {
{ 0x902d, &nouveau_object_ofuncs },
{ 0x9039, &nouveau_object_ofuncs },
- { 0x9097, &nouveau_object_ofuncs },
- { 0x90c0, &nouveau_object_ofuncs },
- { 0x9197, &nouveau_object_ofuncs },
+ { FERMI_A, &nvc0_fermi_ofuncs, nvc0_graph_9097_omthds },
+ { FERMI_B, &nvc0_fermi_ofuncs, nvc0_graph_9097_omthds },
+ { FERMI_COMPUTE_A, &nouveau_object_ofuncs, nvc0_graph_90c0_omthds },
{}
};
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c
index a6bf783e1256..692e1eda0eb4 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c
@@ -33,10 +33,10 @@ struct nouveau_oclass
nvc8_graph_sclass[] = {
{ 0x902d, &nouveau_object_ofuncs },
{ 0x9039, &nouveau_object_ofuncs },
- { 0x9097, &nouveau_object_ofuncs },
- { 0x90c0, &nouveau_object_ofuncs },
- { 0x9197, &nouveau_object_ofuncs },
- { 0x9297, &nouveau_object_ofuncs },
+ { FERMI_A, &nvc0_fermi_ofuncs, nvc0_graph_9097_omthds },
+ { FERMI_B, &nvc0_fermi_ofuncs, nvc0_graph_9097_omthds },
+ { FERMI_C, &nvc0_fermi_ofuncs, nvc0_graph_9097_omthds },
+ { FERMI_COMPUTE_A, &nouveau_object_ofuncs, nvc0_graph_90c0_omthds },
{}
};
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c
index 2a6a94e2a041..41e8445c7eea 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c
@@ -133,4 +133,5 @@ nvd7_graph_oclass = &(struct nvc0_graph_oclass) {
.mmio = nvd7_graph_pack_mmio,
.fecs.ucode = &nvd7_graph_fecs_ucode,
.gpccs.ucode = &nvd7_graph_gpccs_ucode,
+ .ppc_nr = 1,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c
index 51e0c075ad34..0c71f5c67ae0 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c
@@ -22,6 +22,8 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
+#include <subdev/pwr.h>
+
#include "nvc0.h"
#include "ctxnvc0.h"
@@ -33,8 +35,8 @@ static struct nouveau_oclass
nve4_graph_sclass[] = {
{ 0x902d, &nouveau_object_ofuncs },
{ 0xa040, &nouveau_object_ofuncs },
- { 0xa097, &nouveau_object_ofuncs },
- { 0xa0c0, &nouveau_object_ofuncs },
+ { KEPLER_A, &nvc0_fermi_ofuncs, nvc0_graph_9097_omthds },
+ { KEPLER_COMPUTE_A, &nouveau_object_ofuncs, nvc0_graph_90c0_omthds },
{}
};
@@ -190,39 +192,20 @@ nve4_graph_pack_mmio[] = {
******************************************************************************/
int
-nve4_graph_fini(struct nouveau_object *object, bool suspend)
-{
- struct nvc0_graph_priv *priv = (void *)object;
-
- /*XXX: this is a nasty hack to power on gr on certain boards
- * where it's disabled by therm, somehow. ideally it'd
- * be nice to know when we should be doing this, and why,
- * but, it's yet to be determined. for now we test for
- * the particular mmio error that occurs in the situation,
- * and then bash therm in the way nvidia do.
- */
- nv_mask(priv, 0x000200, 0x08001000, 0x08001000);
- nv_rd32(priv, 0x000200);
- if (nv_rd32(priv, 0x400700) == 0xbadf1000) {
- nv_mask(priv, 0x000200, 0x08001000, 0x00000000);
- nv_rd32(priv, 0x000200);
- nv_mask(priv, 0x020004, 0xc0000000, 0x40000000);
- }
-
- return nouveau_graph_fini(&priv->base, suspend);
-}
-
-int
nve4_graph_init(struct nouveau_object *object)
{
struct nvc0_graph_oclass *oclass = (void *)object->oclass;
struct nvc0_graph_priv *priv = (void *)object;
+ struct nouveau_pwr *ppwr = nouveau_pwr(priv);
const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total);
u32 data[TPC_MAX / 8] = {};
u8 tpcnr[GPC_MAX];
int gpc, tpc, rop;
int ret, i;
+ if (ppwr)
+ ppwr->pgob(ppwr, false);
+
ret = nouveau_graph_init(&priv->base);
if (ret)
return ret;
@@ -320,6 +303,9 @@ nve4_graph_init(struct nouveau_object *object)
nv_wr32(priv, 0x400134, 0xffffffff);
nv_wr32(priv, 0x400054, 0x34ce3464);
+
+ nvc0_graph_zbc_init(priv);
+
return nvc0_graph_init_ctxctl(priv);
}
@@ -350,11 +336,12 @@ nve4_graph_oclass = &(struct nvc0_graph_oclass) {
.ctor = nvc0_graph_ctor,
.dtor = nvc0_graph_dtor,
.init = nve4_graph_init,
- .fini = nve4_graph_fini,
+ .fini = _nouveau_graph_fini,
},
.cclass = &nve4_grctx_oclass,
.sclass = nve4_graph_sclass,
.mmio = nve4_graph_pack_mmio,
.fecs.ucode = &nve4_graph_fecs_ucode,
.gpccs.ucode = &nve4_graph_gpccs_ucode,
+ .ppc_nr = 1,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c
index c96762122b9b..c306c0f2fc84 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c
@@ -29,12 +29,12 @@
* Graphics object classes
******************************************************************************/
-static struct nouveau_oclass
+struct nouveau_oclass
nvf0_graph_sclass[] = {
{ 0x902d, &nouveau_object_ofuncs },
{ 0xa140, &nouveau_object_ofuncs },
- { 0xa197, &nouveau_object_ofuncs },
- { 0xa1c0, &nouveau_object_ofuncs },
+ { KEPLER_B, &nvc0_fermi_ofuncs, nvc0_graph_9097_omthds },
+ { KEPLER_COMPUTE_B, &nouveau_object_ofuncs, nvc0_graph_90c0_omthds },
{}
};
@@ -50,7 +50,7 @@ nvf0_graph_init_fe_0[] = {
{}
};
-static const struct nvc0_graph_init
+const struct nvc0_graph_init
nvf0_graph_init_ds_0[] = {
{ 0x405844, 1, 0x04, 0x00ffffff },
{ 0x405850, 1, 0x04, 0x00000000 },
@@ -88,7 +88,7 @@ nvf0_graph_init_gpc_unk_1[] = {
{}
};
-static const struct nvc0_graph_init
+const struct nvc0_graph_init
nvf0_graph_init_tex_0[] = {
{ 0x419ab0, 1, 0x04, 0x00000000 },
{ 0x419ac8, 1, 0x04, 0x00000000 },
@@ -170,7 +170,7 @@ nvf0_graph_pack_mmio[] = {
* PGRAPH engine/subdev functions
******************************************************************************/
-static int
+int
nvf0_graph_fini(struct nouveau_object *object, bool suspend)
{
struct nvc0_graph_priv *priv = (void *)object;
@@ -209,7 +209,7 @@ nvf0_graph_fini(struct nouveau_object *object, bool suspend)
#include "fuc/hubnvf0.fuc.h"
-static struct nvc0_graph_ucode
+struct nvc0_graph_ucode
nvf0_graph_fecs_ucode = {
.code.data = nvf0_grhub_code,
.code.size = sizeof(nvf0_grhub_code),
@@ -219,7 +219,7 @@ nvf0_graph_fecs_ucode = {
#include "fuc/gpcnvf0.fuc.h"
-static struct nvc0_graph_ucode
+struct nvc0_graph_ucode
nvf0_graph_gpccs_ucode = {
.code.data = nvf0_grgpc_code,
.code.size = sizeof(nvf0_grgpc_code),
@@ -241,4 +241,5 @@ nvf0_graph_oclass = &(struct nvc0_graph_oclass) {
.mmio = nvf0_graph_pack_mmio,
.fecs.ucode = &nvf0_graph_fecs_ucode,
.gpccs.ucode = &nvf0_graph_gpccs_ucode,
+ .ppc_nr = 2,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
index 7eb6d94c84e2..d88c700b2f69 100644
--- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
+++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
@@ -24,7 +24,6 @@
#include <core/client.h>
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <core/handle.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv40.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv40.c
index d4e7ec0ba68c..bdb2f20ff7b1 100644
--- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv40.c
@@ -23,7 +23,6 @@
*/
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <subdev/fb.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv44.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv44.c
index 3d8c2133e0e8..72c7f33fd29b 100644
--- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv44.c
+++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv44.c
@@ -23,7 +23,6 @@
*/
#include <core/os.h>
-#include <core/class.h>
#include <core/client.h>
#include <core/engctx.h>
#include <core/handle.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c
index 37a2bd9e8078..cae33f86b11a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c
@@ -23,7 +23,6 @@
*/
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <subdev/vm.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv84.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv84.c
index 96f5aa92677b..e9cc8b116a24 100644
--- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv84.c
@@ -23,7 +23,6 @@
*/
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <subdev/vm.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/perfmon/base.c b/drivers/gpu/drm/nouveau/core/engine/perfmon/base.c
index e9c5e51943ef..63013812f7c9 100644
--- a/drivers/gpu/drm/nouveau/core/engine/perfmon/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/perfmon/base.c
@@ -22,8 +22,11 @@
* Authors: Ben Skeggs
*/
+#include <core/client.h>
#include <core/option.h>
-#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
+#include <nvif/ioctl.h>
#include <subdev/clock.h>
@@ -101,24 +104,28 @@ nouveau_perfsig_wrap(struct nouveau_perfmon *ppm, const char *name,
* Perfmon object classes
******************************************************************************/
static int
-nouveau_perfctr_query(struct nouveau_object *object, u32 mthd,
- void *data, u32 size)
+nouveau_perfctr_query(struct nouveau_object *object, void *data, u32 size)
{
+ union {
+ struct nvif_perfctr_query_v0 v0;
+ } *args = data;
struct nouveau_device *device = nv_device(object);
struct nouveau_perfmon *ppm = (void *)object->engine;
struct nouveau_perfdom *dom = NULL, *chk;
- struct nv_perfctr_query *args = data;
const bool all = nouveau_boolopt(device->cfgopt, "NvPmShowAll", false);
const bool raw = nouveau_boolopt(device->cfgopt, "NvPmUnnamed", all);
const char *name;
int tmp = 0, di, si;
- char path[64];
-
- if (size < sizeof(*args))
- return -EINVAL;
+ int ret;
- di = (args->iter & 0xff000000) >> 24;
- si = (args->iter & 0x00ffffff) - 1;
+ nv_ioctl(object, "perfctr query size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "perfctr query vers %d iter %08x\n",
+ args->v0.version, args->v0.iter);
+ di = (args->v0.iter & 0xff000000) >> 24;
+ si = (args->v0.iter & 0x00ffffff) - 1;
+ } else
+ return ret;
list_for_each_entry(chk, &ppm->domains, head) {
if (tmp++ == di) {
@@ -132,19 +139,17 @@ nouveau_perfctr_query(struct nouveau_object *object, u32 mthd,
if (si >= 0) {
if (raw || !(name = dom->signal[si].name)) {
- snprintf(path, sizeof(path), "/%s/%02x", dom->name, si);
- name = path;
+ snprintf(args->v0.name, sizeof(args->v0.name),
+ "/%s/%02x", dom->name, si);
+ } else {
+ strncpy(args->v0.name, name, sizeof(args->v0.name));
}
-
- if (args->name)
- strncpy(args->name, name, args->size);
- args->size = strlen(name) + 1;
}
do {
while (++si < dom->signal_nr) {
if (all || dom->signal[si].name) {
- args->iter = (di << 24) | ++si;
+ args->v0.iter = (di << 24) | ++si;
return 0;
}
}
@@ -153,21 +158,26 @@ nouveau_perfctr_query(struct nouveau_object *object, u32 mthd,
dom = list_entry(dom->head.next, typeof(*dom), head);
} while (&dom->head != &ppm->domains);
- args->iter = 0xffffffff;
+ args->v0.iter = 0xffffffff;
return 0;
}
static int
-nouveau_perfctr_sample(struct nouveau_object *object, u32 mthd,
- void *data, u32 size)
+nouveau_perfctr_sample(struct nouveau_object *object, void *data, u32 size)
{
+ union {
+ struct nvif_perfctr_sample none;
+ } *args = data;
struct nouveau_perfmon *ppm = (void *)object->engine;
struct nouveau_perfctr *ctr, *tmp;
struct nouveau_perfdom *dom;
- struct nv_perfctr_sample *args = data;
+ int ret;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(object, "perfctr sample size %d\n", size);
+ if (nvif_unvers(args->none)) {
+ nv_ioctl(object, "perfctr sample\n");
+ } else
+ return ret;
ppm->sequence++;
list_for_each_entry(dom, &ppm->domains, head) {
@@ -206,22 +216,45 @@ nouveau_perfctr_sample(struct nouveau_object *object, u32 mthd,
}
static int
-nouveau_perfctr_read(struct nouveau_object *object, u32 mthd,
- void *data, u32 size)
+nouveau_perfctr_read(struct nouveau_object *object, void *data, u32 size)
{
+ union {
+ struct nvif_perfctr_read_v0 v0;
+ } *args = data;
struct nouveau_perfctr *ctr = (void *)object;
- struct nv_perfctr_read *args = data;
+ int ret;
+
+ nv_ioctl(object, "perfctr read size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(object, "perfctr read vers %d\n", args->v0.version);
+ } else
+ return ret;
- if (size < sizeof(*args))
- return -EINVAL;
if (!ctr->clk)
return -EAGAIN;
- args->clk = ctr->clk;
- args->ctr = ctr->ctr;
+ args->v0.clk = ctr->clk;
+ args->v0.ctr = ctr->ctr;
return 0;
}
+static int
+nouveau_perfctr_mthd(struct nouveau_object *object, u32 mthd,
+ void *data, u32 size)
+{
+ switch (mthd) {
+ case NVIF_PERFCTR_V0_QUERY:
+ return nouveau_perfctr_query(object, data, size);
+ case NVIF_PERFCTR_V0_SAMPLE:
+ return nouveau_perfctr_sample(object, data, size);
+ case NVIF_PERFCTR_V0_READ:
+ return nouveau_perfctr_read(object, data, size);
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
static void
nouveau_perfctr_dtor(struct nouveau_object *object)
{
@@ -237,19 +270,27 @@ nouveau_perfctr_ctor(struct nouveau_object *parent,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ union {
+ struct nvif_perfctr_v0 v0;
+ } *args = data;
struct nouveau_perfmon *ppm = (void *)engine;
struct nouveau_perfdom *dom = NULL;
struct nouveau_perfsig *sig[4] = {};
struct nouveau_perfctr *ctr;
- struct nv_perfctr_class *args = data;
int ret, i;
- if (size < sizeof(*args))
- return -EINVAL;
+ nv_ioctl(parent, "create perfctr size %d\n", size);
+ if (nvif_unpack(args->v0, 0, 0, false)) {
+ nv_ioctl(parent, "create perfctr vers %d logic_op %04x\n",
+ args->v0.version, args->v0.logic_op);
+ } else
+ return ret;
- for (i = 0; i < ARRAY_SIZE(args->signal) && args->signal[i].name; i++) {
- sig[i] = nouveau_perfsig_find(ppm, args->signal[i].name,
- args->signal[i].size, &dom);
+ for (i = 0; i < ARRAY_SIZE(args->v0.name) && args->v0.name[i][0]; i++) {
+ sig[i] = nouveau_perfsig_find(ppm, args->v0.name[i],
+ strnlen(args->v0.name[i],
+ sizeof(args->v0.name[i])),
+ &dom);
if (!sig[i])
return -EINVAL;
}
@@ -260,7 +301,7 @@ nouveau_perfctr_ctor(struct nouveau_object *parent,
return ret;
ctr->slot = -1;
- ctr->logic_op = args->logic_op;
+ ctr->logic_op = args->v0.logic_op;
ctr->signal[0] = sig[0];
ctr->signal[1] = sig[1];
ctr->signal[2] = sig[2];
@@ -276,21 +317,13 @@ nouveau_perfctr_ofuncs = {
.dtor = nouveau_perfctr_dtor,
.init = nouveau_object_init,
.fini = nouveau_object_fini,
-};
-
-static struct nouveau_omthds
-nouveau_perfctr_omthds[] = {
- { NV_PERFCTR_QUERY, NV_PERFCTR_QUERY, nouveau_perfctr_query },
- { NV_PERFCTR_SAMPLE, NV_PERFCTR_SAMPLE, nouveau_perfctr_sample },
- { NV_PERFCTR_READ, NV_PERFCTR_READ, nouveau_perfctr_read },
- {}
+ .mthd = nouveau_perfctr_mthd,
};
struct nouveau_oclass
nouveau_perfmon_sclass[] = {
- { .handle = NV_PERFCTR_CLASS,
+ { .handle = NVIF_IOCTL_NEW_V0_PERFCTR,
.ofuncs = &nouveau_perfctr_ofuncs,
- .omthds = nouveau_perfctr_omthds,
},
{},
};
@@ -303,6 +336,7 @@ nouveau_perfctx_dtor(struct nouveau_object *object)
{
struct nouveau_perfmon *ppm = (void *)object->engine;
mutex_lock(&nv_subdev(ppm)->mutex);
+ nouveau_engctx_destroy(&ppm->context->base);
ppm->context = NULL;
mutex_unlock(&nv_subdev(ppm)->mutex);
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv04.c b/drivers/gpu/drm/nouveau/core/engine/software/nv04.c
index c571758e4a27..64df15c7f051 100644
--- a/drivers/gpu/drm/nouveau/core/engine/software/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/software/nv04.c
@@ -23,7 +23,6 @@
*/
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <engine/software.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv10.c b/drivers/gpu/drm/nouveau/core/engine/software/nv10.c
index a62f11a78430..f54a2253deca 100644
--- a/drivers/gpu/drm/nouveau/core/engine/software/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/engine/software/nv10.c
@@ -23,7 +23,6 @@
*/
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <engine/software.h>
diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c
index f3b4d9dbf23c..a0fec205f9db 100644
--- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c
@@ -23,12 +23,12 @@
*/
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <core/namedb.h>
#include <core/handle.h>
#include <core/gpuobj.h>
#include <core/event.h>
+#include <nvif/event.h>
#include <subdev/bar.h>
@@ -86,10 +86,10 @@ nv50_software_mthd_vblsem_release(struct nouveau_object *object, u32 mthd,
{
struct nv50_software_chan *chan = (void *)nv_engctx(object->parent);
u32 head = *(u32 *)args;
- if (head >= chan->vblank.nr_event)
+ if (head >= nouveau_disp(chan)->vblank.index_nr)
return -EINVAL;
- nouveau_event_get(chan->vblank.event[head]);
+ nvkm_notify_get(&chan->vblank.notify[head]);
return 0;
}
@@ -124,9 +124,10 @@ nv50_software_sclass[] = {
******************************************************************************/
static int
-nv50_software_vblsem_release(void *data, u32 type, int head)
+nv50_software_vblsem_release(struct nvkm_notify *notify)
{
- struct nv50_software_chan *chan = data;
+ struct nv50_software_chan *chan =
+ container_of(notify, typeof(*chan), vblank.notify[notify->index]);
struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
struct nouveau_bar *bar = nouveau_bar(priv);
@@ -142,7 +143,7 @@ nv50_software_vblsem_release(void *data, u32 type, int head)
nv_wr32(priv, 0x060014, chan->vblank.value);
}
- return NVKM_EVENT_DROP;
+ return NVKM_NOTIFY_DROP;
}
void
@@ -151,11 +152,8 @@ nv50_software_context_dtor(struct nouveau_object *object)
struct nv50_software_chan *chan = (void *)object;
int i;
- if (chan->vblank.event) {
- for (i = 0; i < chan->vblank.nr_event; i++)
- nouveau_event_ref(NULL, &chan->vblank.event[i]);
- kfree(chan->vblank.event);
- }
+ for (i = 0; i < ARRAY_SIZE(chan->vblank.notify); i++)
+ nvkm_notify_fini(&chan->vblank.notify[i]);
nouveau_software_context_destroy(&chan->base);
}
@@ -176,15 +174,15 @@ nv50_software_context_ctor(struct nouveau_object *parent,
if (ret)
return ret;
- chan->vblank.nr_event = pdisp ? pdisp->vblank->index_nr : 0;
- chan->vblank.event = kzalloc(chan->vblank.nr_event *
- sizeof(*chan->vblank.event), GFP_KERNEL);
- if (!chan->vblank.event)
- return -ENOMEM;
-
- for (i = 0; i < chan->vblank.nr_event; i++) {
- ret = nouveau_event_new(pdisp->vblank, 1, i, pclass->vblank,
- chan, &chan->vblank.event[i]);
+ for (i = 0; pdisp && i < pdisp->vblank.index_nr; i++) {
+ ret = nvkm_notify_init(NULL, &pdisp->vblank, pclass->vblank,
+ false,
+ &(struct nvif_notify_head_req_v0) {
+ .head = i,
+ },
+ sizeof(struct nvif_notify_head_req_v0),
+ sizeof(struct nvif_notify_head_rep_v0),
+ &chan->vblank.notify[i]);
if (ret)
return ret;
}
@@ -198,7 +196,7 @@ nv50_software_cclass = {
.base.handle = NV_ENGCTX(SW, 0x50),
.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv50_software_context_ctor,
- .dtor = _nouveau_software_context_dtor,
+ .dtor = nv50_software_context_dtor,
.init = _nouveau_software_context_init,
.fini = _nouveau_software_context_fini,
},
diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.h b/drivers/gpu/drm/nouveau/core/engine/software/nv50.h
index bb49a7a20857..41542e725b4b 100644
--- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.h
+++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.h
@@ -19,14 +19,13 @@ int nv50_software_ctor(struct nouveau_object *, struct nouveau_object *,
struct nv50_software_cclass {
struct nouveau_oclass base;
- int (*vblank)(void *, u32, int);
+ int (*vblank)(struct nvkm_notify *);
};
struct nv50_software_chan {
struct nouveau_software_chan base;
struct {
- struct nouveau_eventh **event;
- int nr_event;
+ struct nvkm_notify notify[4];
u32 channel;
u32 ctxdma;
u64 offset;
diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
index 135c20f38356..6af370d3a06d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
@@ -23,7 +23,6 @@
*/
#include <core/os.h>
-#include <core/class.h>
#include <core/engctx.h>
#include <core/event.h>
@@ -104,9 +103,10 @@ nvc0_software_sclass[] = {
******************************************************************************/
static int
-nvc0_software_vblsem_release(void *data, u32 type, int head)
+nvc0_software_vblsem_release(struct nvkm_notify *notify)
{
- struct nv50_software_chan *chan = data;
+ struct nv50_software_chan *chan =
+ container_of(notify, typeof(*chan), vblank.notify[notify->index]);
struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
struct nouveau_bar *bar = nouveau_bar(priv);
@@ -116,7 +116,7 @@ nvc0_software_vblsem_release(void *data, u32 type, int head)
nv_wr32(priv, 0x060010, lower_32_bits(chan->vblank.offset));
nv_wr32(priv, 0x060014, chan->vblank.value);
- return NVKM_EVENT_DROP;
+ return NVKM_NOTIFY_DROP;
}
static struct nv50_software_cclass
@@ -124,7 +124,7 @@ nvc0_software_cclass = {
.base.handle = NV_ENGCTX(SW, 0xc0),
.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv50_software_context_ctor,
- .dtor = _nouveau_software_context_dtor,
+ .dtor = nv50_software_context_dtor,
.init = _nouveau_software_context_init,
.fini = _nouveau_software_context_fini,
},
diff --git a/drivers/gpu/drm/nouveau/core/include/core/class.h b/drivers/gpu/drm/nouveau/core/include/core/class.h
deleted file mode 100644
index e0c812bc884f..000000000000
--- a/drivers/gpu/drm/nouveau/core/include/core/class.h
+++ /dev/null
@@ -1,470 +0,0 @@
-#ifndef __NOUVEAU_CLASS_H__
-#define __NOUVEAU_CLASS_H__
-
-/* Device class
- *
- * 0080: NV_DEVICE
- */
-#define NV_DEVICE_CLASS 0x00000080
-
-#define NV_DEVICE_DISABLE_IDENTIFY 0x0000000000000001ULL
-#define NV_DEVICE_DISABLE_MMIO 0x0000000000000002ULL
-#define NV_DEVICE_DISABLE_VBIOS 0x0000000000000004ULL
-#define NV_DEVICE_DISABLE_CORE 0x0000000000000008ULL
-#define NV_DEVICE_DISABLE_DISP 0x0000000000010000ULL
-#define NV_DEVICE_DISABLE_FIFO 0x0000000000020000ULL
-#define NV_DEVICE_DISABLE_GRAPH 0x0000000100000000ULL
-#define NV_DEVICE_DISABLE_MPEG 0x0000000200000000ULL
-#define NV_DEVICE_DISABLE_ME 0x0000000400000000ULL
-#define NV_DEVICE_DISABLE_VP 0x0000000800000000ULL
-#define NV_DEVICE_DISABLE_CRYPT 0x0000001000000000ULL
-#define NV_DEVICE_DISABLE_BSP 0x0000002000000000ULL
-#define NV_DEVICE_DISABLE_PPP 0x0000004000000000ULL
-#define NV_DEVICE_DISABLE_COPY0 0x0000008000000000ULL
-#define NV_DEVICE_DISABLE_COPY1 0x0000010000000000ULL
-#define NV_DEVICE_DISABLE_VIC 0x0000020000000000ULL
-#define NV_DEVICE_DISABLE_VENC 0x0000040000000000ULL
-
-struct nv_device_class {
- u64 device; /* device identifier, ~0 for client default */
- u64 disable; /* disable particular subsystems */
- u64 debug0; /* as above, but *internal* ids, and *NOT* ABI */
-};
-
-/* DMA object classes
- *
- * 0002: NV_DMA_FROM_MEMORY
- * 0003: NV_DMA_TO_MEMORY
- * 003d: NV_DMA_IN_MEMORY
- */
-#define NV_DMA_FROM_MEMORY_CLASS 0x00000002
-#define NV_DMA_TO_MEMORY_CLASS 0x00000003
-#define NV_DMA_IN_MEMORY_CLASS 0x0000003d
-
-#define NV_DMA_TARGET_MASK 0x000000ff
-#define NV_DMA_TARGET_VM 0x00000000
-#define NV_DMA_TARGET_VRAM 0x00000001
-#define NV_DMA_TARGET_PCI 0x00000002
-#define NV_DMA_TARGET_PCI_US 0x00000003
-#define NV_DMA_TARGET_AGP 0x00000004
-#define NV_DMA_ACCESS_MASK 0x00000f00
-#define NV_DMA_ACCESS_VM 0x00000000
-#define NV_DMA_ACCESS_RD 0x00000100
-#define NV_DMA_ACCESS_WR 0x00000200
-#define NV_DMA_ACCESS_RDWR 0x00000300
-
-/* NV50:NVC0 */
-#define NV50_DMA_CONF0_ENABLE 0x80000000
-#define NV50_DMA_CONF0_PRIV 0x00300000
-#define NV50_DMA_CONF0_PRIV_VM 0x00000000
-#define NV50_DMA_CONF0_PRIV_US 0x00100000
-#define NV50_DMA_CONF0_PRIV__S 0x00200000
-#define NV50_DMA_CONF0_PART 0x00030000
-#define NV50_DMA_CONF0_PART_VM 0x00000000
-#define NV50_DMA_CONF0_PART_256 0x00010000
-#define NV50_DMA_CONF0_PART_1KB 0x00020000
-#define NV50_DMA_CONF0_COMP 0x00000180
-#define NV50_DMA_CONF0_COMP_NONE 0x00000000
-#define NV50_DMA_CONF0_COMP_VM 0x00000180
-#define NV50_DMA_CONF0_TYPE 0x0000007f
-#define NV50_DMA_CONF0_TYPE_LINEAR 0x00000000
-#define NV50_DMA_CONF0_TYPE_VM 0x0000007f
-
-/* NVC0:NVD9 */
-#define NVC0_DMA_CONF0_ENABLE 0x80000000
-#define NVC0_DMA_CONF0_PRIV 0x00300000
-#define NVC0_DMA_CONF0_PRIV_VM 0x00000000
-#define NVC0_DMA_CONF0_PRIV_US 0x00100000
-#define NVC0_DMA_CONF0_PRIV__S 0x00200000
-#define NVC0_DMA_CONF0_UNKN /* PART? */ 0x00030000
-#define NVC0_DMA_CONF0_TYPE 0x000000ff
-#define NVC0_DMA_CONF0_TYPE_LINEAR 0x00000000
-#define NVC0_DMA_CONF0_TYPE_VM 0x000000ff
-
-/* NVD9- */
-#define NVD0_DMA_CONF0_ENABLE 0x80000000
-#define NVD0_DMA_CONF0_PAGE 0x00000400
-#define NVD0_DMA_CONF0_PAGE_LP 0x00000000
-#define NVD0_DMA_CONF0_PAGE_SP 0x00000400
-#define NVD0_DMA_CONF0_TYPE 0x000000ff
-#define NVD0_DMA_CONF0_TYPE_LINEAR 0x00000000
-#define NVD0_DMA_CONF0_TYPE_VM 0x000000ff
-
-struct nv_dma_class {
- u32 flags;
- u32 pad0;
- u64 start;
- u64 limit;
- u32 conf0;
-};
-
-/* Perfmon counter class
- *
- * XXXX: NV_PERFCTR
- */
-#define NV_PERFCTR_CLASS 0x0000ffff
-#define NV_PERFCTR_QUERY 0x00000000
-#define NV_PERFCTR_SAMPLE 0x00000001
-#define NV_PERFCTR_READ 0x00000002
-
-struct nv_perfctr_class {
- u16 logic_op;
- struct {
- char __user *name; /*XXX: use cfu when exposed to userspace */
- u32 size;
- } signal[4];
-};
-
-struct nv_perfctr_query {
- u32 iter;
- u32 size;
- char __user *name; /*XXX: use ctu when exposed to userspace */
-};
-
-struct nv_perfctr_sample {
-};
-
-struct nv_perfctr_read {
- u32 ctr;
- u32 clk;
-};
-
-/* Device control class
- *
- * XXXX: NV_CONTROL
- */
-#define NV_CONTROL_CLASS 0x0000fffe
-
-#define NV_CONTROL_PSTATE_INFO 0x00000000
-#define NV_CONTROL_PSTATE_INFO_USTATE_DISABLE (-1)
-#define NV_CONTROL_PSTATE_INFO_USTATE_PERFMON (-2)
-#define NV_CONTROL_PSTATE_INFO_PSTATE_UNKNOWN (-1)
-#define NV_CONTROL_PSTATE_INFO_PSTATE_PERFMON (-2)
-#define NV_CONTROL_PSTATE_ATTR 0x00000001
-#define NV_CONTROL_PSTATE_ATTR_STATE_CURRENT (-1)
-#define NV_CONTROL_PSTATE_USER 0x00000002
-#define NV_CONTROL_PSTATE_USER_STATE_UNKNOWN (-1)
-#define NV_CONTROL_PSTATE_USER_STATE_PERFMON (-2)
-
-struct nv_control_pstate_info {
- u32 count; /* out: number of power states */
- s32 ustate; /* out: current target pstate index */
- u32 pstate; /* out: current pstate index */
-};
-
-struct nv_control_pstate_attr {
- s32 state; /* in: index of pstate to query
- * out: pstate identifier
- */
- u32 index; /* in: index of attribute to query
- * out: index of next attribute, or 0 if no more
- */
- char name[32];
- char unit[16];
- u32 min;
- u32 max;
-};
-
-struct nv_control_pstate_user {
- s32 state; /* in: pstate identifier */
-};
-
-/* DMA FIFO channel classes
- *
- * 006b: NV03_CHANNEL_DMA
- * 006e: NV10_CHANNEL_DMA
- * 176e: NV17_CHANNEL_DMA
- * 406e: NV40_CHANNEL_DMA
- * 506e: NV50_CHANNEL_DMA
- * 826e: NV84_CHANNEL_DMA
- */
-#define NV03_CHANNEL_DMA_CLASS 0x0000006b
-#define NV10_CHANNEL_DMA_CLASS 0x0000006e
-#define NV17_CHANNEL_DMA_CLASS 0x0000176e
-#define NV40_CHANNEL_DMA_CLASS 0x0000406e
-#define NV50_CHANNEL_DMA_CLASS 0x0000506e
-#define NV84_CHANNEL_DMA_CLASS 0x0000826e
-
-struct nv03_channel_dma_class {
- u32 pushbuf;
- u32 pad0;
- u64 offset;
-};
-
-/* Indirect FIFO channel classes
- *
- * 506f: NV50_CHANNEL_IND
- * 826f: NV84_CHANNEL_IND
- * 906f: NVC0_CHANNEL_IND
- * a06f: NVE0_CHANNEL_IND
- */
-
-#define NV50_CHANNEL_IND_CLASS 0x0000506f
-#define NV84_CHANNEL_IND_CLASS 0x0000826f
-#define NVC0_CHANNEL_IND_CLASS 0x0000906f
-#define NVE0_CHANNEL_IND_CLASS 0x0000a06f
-
-struct nv50_channel_ind_class {
- u32 pushbuf;
- u32 ilength;
- u64 ioffset;
-};
-
-#define NVE0_CHANNEL_IND_ENGINE_GR 0x00000001
-#define NVE0_CHANNEL_IND_ENGINE_VP 0x00000002
-#define NVE0_CHANNEL_IND_ENGINE_PPP 0x00000004
-#define NVE0_CHANNEL_IND_ENGINE_BSP 0x00000008
-#define NVE0_CHANNEL_IND_ENGINE_CE0 0x00000010
-#define NVE0_CHANNEL_IND_ENGINE_CE1 0x00000020
-#define NVE0_CHANNEL_IND_ENGINE_ENC 0x00000040
-
-struct nve0_channel_ind_class {
- u32 pushbuf;
- u32 ilength;
- u64 ioffset;
- u32 engine;
-};
-
-/* 0046: NV04_DISP
- */
-
-#define NV04_DISP_CLASS 0x00000046
-
-#define NV04_DISP_MTHD 0x00000000
-#define NV04_DISP_MTHD_HEAD 0x00000001
-
-#define NV04_DISP_SCANOUTPOS 0x00000000
-
-struct nv04_display_class {
-};
-
-struct nv04_display_scanoutpos {
- s64 time[2];
- u32 vblanks;
- u32 vblanke;
- u32 vtotal;
- u32 vline;
- u32 hblanks;
- u32 hblanke;
- u32 htotal;
- u32 hline;
-};
-
-/* 5070: NV50_DISP
- * 8270: NV84_DISP
- * 8370: NVA0_DISP
- * 8870: NV94_DISP
- * 8570: NVA3_DISP
- * 9070: NVD0_DISP
- * 9170: NVE0_DISP
- * 9270: NVF0_DISP
- * 9470: GM107_DISP
- */
-
-#define NV50_DISP_CLASS 0x00005070
-#define NV84_DISP_CLASS 0x00008270
-#define NVA0_DISP_CLASS 0x00008370
-#define NV94_DISP_CLASS 0x00008870
-#define NVA3_DISP_CLASS 0x00008570
-#define NVD0_DISP_CLASS 0x00009070
-#define NVE0_DISP_CLASS 0x00009170
-#define NVF0_DISP_CLASS 0x00009270
-#define GM107_DISP_CLASS 0x00009470
-
-#define NV50_DISP_MTHD 0x00000000
-#define NV50_DISP_MTHD_HEAD 0x00000003
-
-#define NV50_DISP_SCANOUTPOS 0x00000000
-
-#define NV50_DISP_SOR_MTHD 0x00010000
-#define NV50_DISP_SOR_MTHD_TYPE 0x0000f000
-#define NV50_DISP_SOR_MTHD_HEAD 0x00000018
-#define NV50_DISP_SOR_MTHD_LINK 0x00000004
-#define NV50_DISP_SOR_MTHD_OR 0x00000003
-
-#define NV50_DISP_SOR_PWR 0x00010000
-#define NV50_DISP_SOR_PWR_STATE 0x00000001
-#define NV50_DISP_SOR_PWR_STATE_ON 0x00000001
-#define NV50_DISP_SOR_PWR_STATE_OFF 0x00000000
-#define NVA3_DISP_SOR_HDA_ELD 0x00010100
-#define NV84_DISP_SOR_HDMI_PWR 0x00012000
-#define NV84_DISP_SOR_HDMI_PWR_STATE 0x40000000
-#define NV84_DISP_SOR_HDMI_PWR_STATE_OFF 0x00000000
-#define NV84_DISP_SOR_HDMI_PWR_STATE_ON 0x40000000
-#define NV84_DISP_SOR_HDMI_PWR_MAX_AC_PACKET 0x001f0000
-#define NV84_DISP_SOR_HDMI_PWR_REKEY 0x0000007f
-#define NV50_DISP_SOR_LVDS_SCRIPT 0x00013000
-#define NV50_DISP_SOR_LVDS_SCRIPT_ID 0x0000ffff
-#define NV94_DISP_SOR_DP_PWR 0x00016000
-#define NV94_DISP_SOR_DP_PWR_STATE 0x00000001
-#define NV94_DISP_SOR_DP_PWR_STATE_OFF 0x00000000
-#define NV94_DISP_SOR_DP_PWR_STATE_ON 0x00000001
-
-#define NV50_DISP_DAC_MTHD 0x00020000
-#define NV50_DISP_DAC_MTHD_TYPE 0x0000f000
-#define NV50_DISP_DAC_MTHD_OR 0x00000003
-
-#define NV50_DISP_DAC_PWR 0x00020000
-#define NV50_DISP_DAC_PWR_HSYNC 0x00000001
-#define NV50_DISP_DAC_PWR_HSYNC_ON 0x00000000
-#define NV50_DISP_DAC_PWR_HSYNC_LO 0x00000001
-#define NV50_DISP_DAC_PWR_VSYNC 0x00000004
-#define NV50_DISP_DAC_PWR_VSYNC_ON 0x00000000
-#define NV50_DISP_DAC_PWR_VSYNC_LO 0x00000004
-#define NV50_DISP_DAC_PWR_DATA 0x00000010
-#define NV50_DISP_DAC_PWR_DATA_ON 0x00000000
-#define NV50_DISP_DAC_PWR_DATA_LO 0x00000010
-#define NV50_DISP_DAC_PWR_STATE 0x00000040
-#define NV50_DISP_DAC_PWR_STATE_ON 0x00000000
-#define NV50_DISP_DAC_PWR_STATE_OFF 0x00000040
-#define NV50_DISP_DAC_LOAD 0x00020100
-#define NV50_DISP_DAC_LOAD_VALUE 0x00000007
-
-#define NV50_DISP_PIOR_MTHD 0x00030000
-#define NV50_DISP_PIOR_MTHD_TYPE 0x0000f000
-#define NV50_DISP_PIOR_MTHD_OR 0x00000003
-
-#define NV50_DISP_PIOR_PWR 0x00030000
-#define NV50_DISP_PIOR_PWR_STATE 0x00000001
-#define NV50_DISP_PIOR_PWR_STATE_ON 0x00000001
-#define NV50_DISP_PIOR_PWR_STATE_OFF 0x00000000
-#define NV50_DISP_PIOR_TMDS_PWR 0x00032000
-#define NV50_DISP_PIOR_TMDS_PWR_STATE 0x00000001
-#define NV50_DISP_PIOR_TMDS_PWR_STATE_ON 0x00000001
-#define NV50_DISP_PIOR_TMDS_PWR_STATE_OFF 0x00000000
-#define NV50_DISP_PIOR_DP_PWR 0x00036000
-#define NV50_DISP_PIOR_DP_PWR_STATE 0x00000001
-#define NV50_DISP_PIOR_DP_PWR_STATE_ON 0x00000001
-#define NV50_DISP_PIOR_DP_PWR_STATE_OFF 0x00000000
-
-struct nv50_display_class {
-};
-
-/* 507a: NV50_DISP_CURS
- * 827a: NV84_DISP_CURS
- * 837a: NVA0_DISP_CURS
- * 887a: NV94_DISP_CURS
- * 857a: NVA3_DISP_CURS
- * 907a: NVD0_DISP_CURS
- * 917a: NVE0_DISP_CURS
- * 927a: NVF0_DISP_CURS
- * 947a: GM107_DISP_CURS
- */
-
-#define NV50_DISP_CURS_CLASS 0x0000507a
-#define NV84_DISP_CURS_CLASS 0x0000827a
-#define NVA0_DISP_CURS_CLASS 0x0000837a
-#define NV94_DISP_CURS_CLASS 0x0000887a
-#define NVA3_DISP_CURS_CLASS 0x0000857a
-#define NVD0_DISP_CURS_CLASS 0x0000907a
-#define NVE0_DISP_CURS_CLASS 0x0000917a
-#define NVF0_DISP_CURS_CLASS 0x0000927a
-#define GM107_DISP_CURS_CLASS 0x0000947a
-
-struct nv50_display_curs_class {
- u32 head;
-};
-
-/* 507b: NV50_DISP_OIMM
- * 827b: NV84_DISP_OIMM
- * 837b: NVA0_DISP_OIMM
- * 887b: NV94_DISP_OIMM
- * 857b: NVA3_DISP_OIMM
- * 907b: NVD0_DISP_OIMM
- * 917b: NVE0_DISP_OIMM
- * 927b: NVE0_DISP_OIMM
- * 947b: GM107_DISP_OIMM
- */
-
-#define NV50_DISP_OIMM_CLASS 0x0000507b
-#define NV84_DISP_OIMM_CLASS 0x0000827b
-#define NVA0_DISP_OIMM_CLASS 0x0000837b
-#define NV94_DISP_OIMM_CLASS 0x0000887b
-#define NVA3_DISP_OIMM_CLASS 0x0000857b
-#define NVD0_DISP_OIMM_CLASS 0x0000907b
-#define NVE0_DISP_OIMM_CLASS 0x0000917b
-#define NVF0_DISP_OIMM_CLASS 0x0000927b
-#define GM107_DISP_OIMM_CLASS 0x0000947b
-
-struct nv50_display_oimm_class {
- u32 head;
-};
-
-/* 507c: NV50_DISP_SYNC
- * 827c: NV84_DISP_SYNC
- * 837c: NVA0_DISP_SYNC
- * 887c: NV94_DISP_SYNC
- * 857c: NVA3_DISP_SYNC
- * 907c: NVD0_DISP_SYNC
- * 917c: NVE0_DISP_SYNC
- * 927c: NVF0_DISP_SYNC
- * 947c: GM107_DISP_SYNC
- */
-
-#define NV50_DISP_SYNC_CLASS 0x0000507c
-#define NV84_DISP_SYNC_CLASS 0x0000827c
-#define NVA0_DISP_SYNC_CLASS 0x0000837c
-#define NV94_DISP_SYNC_CLASS 0x0000887c
-#define NVA3_DISP_SYNC_CLASS 0x0000857c
-#define NVD0_DISP_SYNC_CLASS 0x0000907c
-#define NVE0_DISP_SYNC_CLASS 0x0000917c
-#define NVF0_DISP_SYNC_CLASS 0x0000927c
-#define GM107_DISP_SYNC_CLASS 0x0000947c
-
-struct nv50_display_sync_class {
- u32 pushbuf;
- u32 head;
-};
-
-/* 507d: NV50_DISP_MAST
- * 827d: NV84_DISP_MAST
- * 837d: NVA0_DISP_MAST
- * 887d: NV94_DISP_MAST
- * 857d: NVA3_DISP_MAST
- * 907d: NVD0_DISP_MAST
- * 917d: NVE0_DISP_MAST
- * 927d: NVF0_DISP_MAST
- * 947d: GM107_DISP_MAST
- */
-
-#define NV50_DISP_MAST_CLASS 0x0000507d
-#define NV84_DISP_MAST_CLASS 0x0000827d
-#define NVA0_DISP_MAST_CLASS 0x0000837d
-#define NV94_DISP_MAST_CLASS 0x0000887d
-#define NVA3_DISP_MAST_CLASS 0x0000857d
-#define NVD0_DISP_MAST_CLASS 0x0000907d
-#define NVE0_DISP_MAST_CLASS 0x0000917d
-#define NVF0_DISP_MAST_CLASS 0x0000927d
-#define GM107_DISP_MAST_CLASS 0x0000947d
-
-struct nv50_display_mast_class {
- u32 pushbuf;
-};
-
-/* 507e: NV50_DISP_OVLY
- * 827e: NV84_DISP_OVLY
- * 837e: NVA0_DISP_OVLY
- * 887e: NV94_DISP_OVLY
- * 857e: NVA3_DISP_OVLY
- * 907e: NVD0_DISP_OVLY
- * 917e: NVE0_DISP_OVLY
- * 927e: NVF0_DISP_OVLY
- * 947e: GM107_DISP_OVLY
- */
-
-#define NV50_DISP_OVLY_CLASS 0x0000507e
-#define NV84_DISP_OVLY_CLASS 0x0000827e
-#define NVA0_DISP_OVLY_CLASS 0x0000837e
-#define NV94_DISP_OVLY_CLASS 0x0000887e
-#define NVA3_DISP_OVLY_CLASS 0x0000857e
-#define NVD0_DISP_OVLY_CLASS 0x0000907e
-#define NVE0_DISP_OVLY_CLASS 0x0000917e
-#define NVF0_DISP_OVLY_CLASS 0x0000927e
-#define GM107_DISP_OVLY_CLASS 0x0000947e
-
-struct nv50_display_ovly_class {
- u32 pushbuf;
- u32 head;
-};
-
-#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/core/client.h b/drivers/gpu/drm/nouveau/core/include/core/client.h
index c66eac513803..b0ce9f6680b5 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/client.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/client.h
@@ -10,6 +10,11 @@ struct nouveau_client {
char name[32];
u32 debug;
struct nouveau_vm *vm;
+ bool super;
+ void *data;
+
+ int (*ntfy)(const void *, u32, const void *, u32);
+ struct nvkm_client_notify *notify[16];
};
static inline struct nouveau_client *
@@ -43,4 +48,10 @@ int nouveau_client_init(struct nouveau_client *);
int nouveau_client_fini(struct nouveau_client *, bool suspend);
const char *nouveau_client_name(void *obj);
+int nvkm_client_notify_new(struct nouveau_object *, struct nvkm_event *,
+ void *data, u32 size);
+int nvkm_client_notify_del(struct nouveau_client *, int index);
+int nvkm_client_notify_get(struct nouveau_client *, int index);
+int nvkm_client_notify_put(struct nouveau_client *, int index);
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/core/device.h b/drivers/gpu/drm/nouveau/core/include/core/device.h
index a8a9a9cf16cb..1d9d893929bb 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/device.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/device.h
@@ -4,6 +4,7 @@
#include <core/object.h>
#include <core/subdev.h>
#include <core/engine.h>
+#include <core/event.h>
enum nv_subdev_type {
NVDEV_ENGINE_DEVICE,
@@ -23,12 +24,13 @@ enum nv_subdev_type {
* been created, and are allowed to assume any subdevs in the
* list above them exist and have been initialised.
*/
+ NVDEV_SUBDEV_FUSE,
NVDEV_SUBDEV_MXM,
NVDEV_SUBDEV_MC,
NVDEV_SUBDEV_BUS,
NVDEV_SUBDEV_TIMER,
NVDEV_SUBDEV_FB,
- NVDEV_SUBDEV_LTCG,
+ NVDEV_SUBDEV_LTC,
NVDEV_SUBDEV_IBUS,
NVDEV_SUBDEV_INSTMEM,
NVDEV_SUBDEV_VM,
@@ -69,6 +71,8 @@ struct nouveau_device {
struct platform_device *platformdev;
u64 handle;
+ struct nvkm_event event;
+
const char *cfgopt;
const char *dbgopt;
const char *name;
@@ -84,7 +88,6 @@ struct nouveau_device {
NV_40 = 0x40,
NV_50 = 0x50,
NV_C0 = 0xc0,
- NV_D0 = 0xd0,
NV_E0 = 0xe0,
GM100 = 0x110,
} card_type;
@@ -93,8 +96,14 @@ struct nouveau_device {
struct nouveau_oclass *oclass[NVDEV_SUBDEV_NR];
struct nouveau_object *subdev[NVDEV_SUBDEV_NR];
+
+ struct {
+ struct notifier_block nb;
+ } acpi;
};
+int nouveau_device_list(u64 *name, int size);
+
static inline struct nouveau_device *
nv_device(void *obj)
{
@@ -162,12 +171,6 @@ nv_device_resource_start(struct nouveau_device *device, unsigned int bar);
resource_size_t
nv_device_resource_len(struct nouveau_device *device, unsigned int bar);
-dma_addr_t
-nv_device_map_page(struct nouveau_device *device, struct page *page);
-
-void
-nv_device_unmap_page(struct nouveau_device *device, dma_addr_t addr);
-
int
nv_device_get_irq(struct nouveau_device *device, bool stall);
diff --git a/drivers/gpu/drm/nouveau/core/include/core/event.h b/drivers/gpu/drm/nouveau/core/include/core/event.h
index ba3f1a76a815..92876528972f 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/event.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/event.h
@@ -1,47 +1,35 @@
#ifndef __NVKM_EVENT_H__
#define __NVKM_EVENT_H__
-/* return codes from event handlers */
-#define NVKM_EVENT_DROP 0
-#define NVKM_EVENT_KEEP 1
-
-/* nouveau_eventh.flags bit #s */
-#define NVKM_EVENT_ENABLE 0
-
-struct nouveau_eventh {
- struct nouveau_event *event;
- struct list_head head;
- unsigned long flags;
- u32 types;
- int index;
- int (*func)(void *, u32, int);
- void *priv;
+#include <core/notify.h>
+
+struct nvkm_event_func {
+ int (*ctor)(struct nouveau_object *, void *data, u32 size,
+ struct nvkm_notify *);
+ void (*send)(void *data, u32 size, struct nvkm_notify *);
+ void (*init)(struct nvkm_event *, int type, int index);
+ void (*fini)(struct nvkm_event *, int type, int index);
};
-struct nouveau_event {
- void *priv;
- int (*check)(struct nouveau_event *, u32 type, int index);
- void (*enable)(struct nouveau_event *, int type, int index);
- void (*disable)(struct nouveau_event *, int type, int index);
+struct nvkm_event {
+ const struct nvkm_event_func *func;
int types_nr;
int index_nr;
- spinlock_t list_lock;
- struct list_head *list;
spinlock_t refs_lock;
- int refs[];
+ spinlock_t list_lock;
+ struct list_head list;
+ int *refs;
};
-int nouveau_event_create(int types_nr, int index_nr, struct nouveau_event **);
-void nouveau_event_destroy(struct nouveau_event **);
-void nouveau_event_trigger(struct nouveau_event *, u32 types, int index);
-
-int nouveau_event_new(struct nouveau_event *, u32 types, int index,
- int (*func)(void *, u32, int), void *,
- struct nouveau_eventh **);
-void nouveau_event_ref(struct nouveau_eventh *, struct nouveau_eventh **);
-void nouveau_event_get(struct nouveau_eventh *);
-void nouveau_event_put(struct nouveau_eventh *);
+int nvkm_event_init(const struct nvkm_event_func *func,
+ int types_nr, int index_nr,
+ struct nvkm_event *);
+void nvkm_event_fini(struct nvkm_event *);
+void nvkm_event_get(struct nvkm_event *, u32 types, int index);
+void nvkm_event_put(struct nvkm_event *, u32 types, int index);
+void nvkm_event_send(struct nvkm_event *, u32 types, int index,
+ void *data, u32 size);
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/core/handle.h b/drivers/gpu/drm/nouveau/core/include/core/handle.h
index 363674cdf8ab..ceb67d770875 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/handle.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/handle.h
@@ -10,6 +10,9 @@ struct nouveau_handle {
u32 name;
u32 priv;
+ u8 route;
+ u64 token;
+
struct nouveau_handle *parent;
struct nouveau_object *object;
};
@@ -20,6 +23,11 @@ void nouveau_handle_destroy(struct nouveau_handle *);
int nouveau_handle_init(struct nouveau_handle *);
int nouveau_handle_fini(struct nouveau_handle *, bool suspend);
+int nouveau_handle_new(struct nouveau_object *, u32 parent, u32 handle,
+ u16 oclass, void *data, u32 size,
+ struct nouveau_object **);
+int nouveau_handle_del(struct nouveau_object *, u32 parent, u32 handle);
+
struct nouveau_object *
nouveau_handle_ref(struct nouveau_object *, u32 name);
diff --git a/drivers/gpu/drm/nouveau/core/include/core/ioctl.h b/drivers/gpu/drm/nouveau/core/include/core/ioctl.h
new file mode 100644
index 000000000000..ac7935c2474e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/core/ioctl.h
@@ -0,0 +1,6 @@
+#ifndef __NVKM_IOCTL_H__
+#define __NVKM_IOCTL_H__
+
+int nvkm_ioctl(struct nouveau_client *, bool, void *, u32, void **);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/core/mm.h b/drivers/gpu/drm/nouveau/core/include/core/mm.h
index 2bf7d0e32261..bfe6931544fe 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/mm.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/mm.h
@@ -6,6 +6,10 @@ struct nouveau_mm_node {
struct list_head fl_entry;
struct list_head rl_entry;
+#define NVKM_MM_HEAP_ANY 0x00
+ u8 heap;
+#define NVKM_MM_TYPE_NONE 0x00
+#define NVKM_MM_TYPE_HOLE 0xff
u8 type;
u32 offset;
u32 length;
@@ -27,10 +31,10 @@ nouveau_mm_initialised(struct nouveau_mm *mm)
int nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block);
int nouveau_mm_fini(struct nouveau_mm *);
-int nouveau_mm_head(struct nouveau_mm *, u8 type, u32 size_max, u32 size_min,
- u32 align, struct nouveau_mm_node **);
-int nouveau_mm_tail(struct nouveau_mm *, u8 type, u32 size_max, u32 size_min,
- u32 align, struct nouveau_mm_node **);
+int nouveau_mm_head(struct nouveau_mm *, u8 heap, u8 type, u32 size_max,
+ u32 size_min, u32 align, struct nouveau_mm_node **);
+int nouveau_mm_tail(struct nouveau_mm *, u8 heap, u8 type, u32 size_max,
+ u32 size_min, u32 align, struct nouveau_mm_node **);
void nouveau_mm_free(struct nouveau_mm *, struct nouveau_mm_node **);
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/core/notify.h b/drivers/gpu/drm/nouveau/core/include/core/notify.h
new file mode 100644
index 000000000000..a7c3c5f578cc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/core/notify.h
@@ -0,0 +1,37 @@
+#ifndef __NVKM_NOTIFY_H__
+#define __NVKM_NOTIFY_H__
+
+struct nvkm_notify {
+ struct nvkm_event *event;
+ struct list_head head;
+#define NVKM_NOTIFY_USER 0
+#define NVKM_NOTIFY_WORK 1
+ unsigned long flags;
+ int block;
+#define NVKM_NOTIFY_DROP 0
+#define NVKM_NOTIFY_KEEP 1
+ int (*func)(struct nvkm_notify *);
+
+ /* set by nvkm_event ctor */
+ u32 types;
+ int index;
+ u32 size;
+
+ struct work_struct work;
+ /* this is const for a *very* good reason - the data might be on the
+ * stack from an irq handler. if you're not core/notify.c then you
+ * should probably think twice before casting it away...
+ */
+ const void *data;
+};
+
+int nvkm_notify_init(struct nouveau_object *, struct nvkm_event *,
+ int (*func)(struct nvkm_notify *), bool work,
+ void *data, u32 size, u32 reply,
+ struct nvkm_notify *);
+void nvkm_notify_fini(struct nvkm_notify *);
+void nvkm_notify_get(struct nvkm_notify *);
+void nvkm_notify_put(struct nvkm_notify *);
+void nvkm_notify_send(struct nvkm_notify *, void *data, u32 size);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/core/object.h b/drivers/gpu/drm/nouveau/core/include/core/object.h
index 62e68baef087..d7039482d6fd 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/object.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/object.h
@@ -48,6 +48,10 @@ void nouveau_object_destroy(struct nouveau_object *);
int nouveau_object_init(struct nouveau_object *);
int nouveau_object_fini(struct nouveau_object *, bool suspend);
+int _nouveau_object_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+
extern struct nouveau_ofuncs nouveau_object_ofuncs;
/* Don't allocate dynamically, because lockdep needs lock_class_keys to be in
@@ -78,6 +82,7 @@ struct nouveau_omthds {
int (*call)(struct nouveau_object *, u32, void *, u32);
};
+struct nvkm_event;
struct nouveau_ofuncs {
int (*ctor)(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *, void *data, u32 size,
@@ -85,6 +90,9 @@ struct nouveau_ofuncs {
void (*dtor)(struct nouveau_object *);
int (*init)(struct nouveau_object *);
int (*fini)(struct nouveau_object *, bool suspend);
+ int (*mthd)(struct nouveau_object *, u32, void *, u32);
+ int (*ntfy)(struct nouveau_object *, u32, struct nvkm_event **);
+ int (* map)(struct nouveau_object *, u64 *, u32 *);
u8 (*rd08)(struct nouveau_object *, u64 offset);
u16 (*rd16)(struct nouveau_object *, u64 offset);
u32 (*rd32)(struct nouveau_object *, u64 offset);
@@ -106,10 +114,6 @@ void nouveau_object_ref(struct nouveau_object *, struct nouveau_object **);
int nouveau_object_inc(struct nouveau_object *);
int nouveau_object_dec(struct nouveau_object *, bool suspend);
-int nouveau_object_new(struct nouveau_object *, u32 parent, u32 handle,
- u16 oclass, void *data, u32 size,
- struct nouveau_object **);
-int nouveau_object_del(struct nouveau_object *, u32 parent, u32 handle);
void nouveau_object_debug(void);
static inline int
@@ -199,4 +203,21 @@ nv_memcmp(void *obj, u32 addr, const char *str, u32 len)
return 0;
}
+#include <core/handle.h>
+
+static inline int
+nouveau_object_new(struct nouveau_object *client, u32 parent, u32 handle,
+ u16 oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ return nouveau_handle_new(client, parent, handle, oclass,
+ data, size, pobject);
+}
+
+static inline int
+nouveau_object_del(struct nouveau_object *client, u32 parent, u32 handle)
+{
+ return nouveau_handle_del(client, parent, handle);
+}
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/core/parent.h b/drivers/gpu/drm/nouveau/core/include/core/parent.h
index 9f5ea900ff00..12da418ec70a 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/parent.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/parent.h
@@ -57,5 +57,6 @@ void _nouveau_parent_dtor(struct nouveau_object *);
int nouveau_parent_sclass(struct nouveau_object *, u16 handle,
struct nouveau_object **pengine,
struct nouveau_oclass **poclass);
+int nouveau_parent_lclass(struct nouveau_object *, u32 *, int);
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/core/printk.h b/drivers/gpu/drm/nouveau/core/include/core/printk.h
index 0f9a37bd32b0..451b6ed20b7e 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/printk.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/printk.h
@@ -21,6 +21,7 @@ nv_printk_(struct nouveau_object *, int, const char *, ...);
#define nv_debug(o,f,a...) nv_printk((o), DEBUG, f, ##a)
#define nv_trace(o,f,a...) nv_printk((o), TRACE, f, ##a)
#define nv_spam(o,f,a...) nv_printk((o), SPAM, f, ##a)
+#define nv_ioctl(o,f,a...) nv_trace(nouveau_client(o), "ioctl: "f, ##a)
#define nv_assert(f,a...) do { \
if (NV_DBG_FATAL <= CONFIG_NOUVEAU_DEBUG) \
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/disp.h b/drivers/gpu/drm/nouveau/core/include/engine/disp.h
index fde842896806..7a64f347b385 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/disp.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/disp.h
@@ -6,20 +6,13 @@
#include <core/device.h>
#include <core/event.h>
-enum nvkm_hpd_event {
- NVKM_HPD_PLUG = 1,
- NVKM_HPD_UNPLUG = 2,
- NVKM_HPD_IRQ = 4,
- NVKM_HPD = (NVKM_HPD_PLUG | NVKM_HPD_UNPLUG | NVKM_HPD_IRQ)
-};
-
struct nouveau_disp {
struct nouveau_engine base;
struct list_head outp;
- struct nouveau_event *hpd;
- struct nouveau_event *vblank;
+ struct nvkm_event hpd;
+ struct nvkm_event vblank;
};
static inline struct nouveau_disp *
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h b/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h
index b28914ed1752..1b283a7b78e6 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h
@@ -12,37 +12,20 @@ struct nouveau_dmaobj {
u32 access;
u64 start;
u64 limit;
- u32 conf0;
};
struct nouveau_dmaeng {
struct nouveau_engine base;
/* creates a "physical" dma object from a struct nouveau_dmaobj */
- int (*bind)(struct nouveau_dmaeng *dmaeng,
+ int (*bind)(struct nouveau_dmaobj *dmaobj,
struct nouveau_object *parent,
- struct nouveau_dmaobj *dmaobj,
struct nouveau_gpuobj **);
};
-#define nouveau_dmaeng_create(p,e,c,d) \
- nouveau_engine_create((p), (e), (c), true, "DMAOBJ", "dmaobj", (d))
-#define nouveau_dmaeng_destroy(p) \
- nouveau_engine_destroy(&(p)->base)
-#define nouveau_dmaeng_init(p) \
- nouveau_engine_init(&(p)->base)
-#define nouveau_dmaeng_fini(p,s) \
- nouveau_engine_fini(&(p)->base, (s))
-
-#define _nouveau_dmaeng_dtor _nouveau_engine_dtor
-#define _nouveau_dmaeng_init _nouveau_engine_init
-#define _nouveau_dmaeng_fini _nouveau_engine_fini
-
-extern struct nouveau_oclass nv04_dmaeng_oclass;
-extern struct nouveau_oclass nv50_dmaeng_oclass;
-extern struct nouveau_oclass nvc0_dmaeng_oclass;
-extern struct nouveau_oclass nvd0_dmaeng_oclass;
-
-extern struct nouveau_oclass nouveau_dmaobj_sclass[];
+extern struct nouveau_oclass *nv04_dmaeng_oclass;
+extern struct nouveau_oclass *nv50_dmaeng_oclass;
+extern struct nouveau_oclass *nvc0_dmaeng_oclass;
+extern struct nouveau_oclass *nvd0_dmaeng_oclass;
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h
index b639eb2c74ff..2007453f6fce 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h
@@ -4,12 +4,14 @@
#include <core/namedb.h>
#include <core/gpuobj.h>
#include <core/engine.h>
+#include <core/event.h>
struct nouveau_fifo_chan {
struct nouveau_namedb base;
struct nouveau_dmaobj *pushdma;
struct nouveau_gpuobj *pushgpu;
void __iomem *user;
+ u64 addr;
u32 size;
u16 chid;
atomic_t refcnt; /* NV04_NVSW_SET_REF */
@@ -40,8 +42,10 @@ void nouveau_fifo_channel_destroy(struct nouveau_fifo_chan *);
#define _nouveau_fifo_channel_fini _nouveau_namedb_fini
void _nouveau_fifo_channel_dtor(struct nouveau_object *);
+int _nouveau_fifo_channel_map(struct nouveau_object *, u64 *, u32 *);
u32 _nouveau_fifo_channel_rd32(struct nouveau_object *, u64);
void _nouveau_fifo_channel_wr32(struct nouveau_object *, u64, u32);
+int _nouveau_fifo_channel_ntfy(struct nouveau_object *, u32, struct nvkm_event **);
struct nouveau_fifo_base {
struct nouveau_gpuobj base;
@@ -65,8 +69,8 @@ struct nouveau_fifo_base {
struct nouveau_fifo {
struct nouveau_engine base;
- struct nouveau_event *cevent; /* channel creation event */
- struct nouveau_event *uevent; /* async user trigger */
+ struct nvkm_event cevent; /* channel creation event */
+ struct nvkm_event uevent; /* async user trigger */
struct nouveau_object **channel;
spinlock_t lock;
@@ -112,6 +116,10 @@ extern struct nouveau_oclass *nve0_fifo_oclass;
extern struct nouveau_oclass *gk20a_fifo_oclass;
extern struct nouveau_oclass *nv108_fifo_oclass;
+int nouveau_fifo_uevent_ctor(struct nouveau_object *, void *, u32,
+ struct nvkm_notify *);
+void nouveau_fifo_uevent(struct nouveau_fifo *);
+
void nv04_fifo_intr(struct nouveau_subdev *);
int nv04_fifo_context_attach(struct nouveau_object *, struct nouveau_object *);
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/graph.h b/drivers/gpu/drm/nouveau/core/include/engine/graph.h
index 8c1d4772da0c..d5055570d01b 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/graph.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/graph.h
@@ -70,6 +70,7 @@ extern struct nouveau_oclass *nvd9_graph_oclass;
extern struct nouveau_oclass *nve4_graph_oclass;
extern struct nouveau_oclass *gk20a_graph_oclass;
extern struct nouveau_oclass *nvf0_graph_oclass;
+extern struct nouveau_oclass *gk110b_graph_oclass;
extern struct nouveau_oclass *nv108_graph_oclass;
extern struct nouveau_oclass *gm107_graph_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/perfmon.h b/drivers/gpu/drm/nouveau/core/include/engine/perfmon.h
index 49b0024910fe..88cc812baaa3 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/perfmon.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/perfmon.h
@@ -4,7 +4,6 @@
#include <core/device.h>
#include <core/engine.h>
#include <core/engctx.h>
-#include <core/class.h>
struct nouveau_perfdom;
struct nouveau_perfctr;
diff --git a/drivers/gpu/drm/nouveau/core/include/nvif/class.h b/drivers/gpu/drm/nouveau/core/include/nvif/class.h
new file mode 120000
index 000000000000..f1ac4859edd4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/nvif/class.h
@@ -0,0 +1 @@
+../../../nvif/class.h \ No newline at end of file
diff --git a/drivers/gpu/drm/nouveau/core/include/nvif/event.h b/drivers/gpu/drm/nouveau/core/include/nvif/event.h
new file mode 120000
index 000000000000..1b798538a725
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/nvif/event.h
@@ -0,0 +1 @@
+../../../nvif/event.h \ No newline at end of file
diff --git a/drivers/gpu/drm/nouveau/core/include/nvif/ioctl.h b/drivers/gpu/drm/nouveau/core/include/nvif/ioctl.h
new file mode 120000
index 000000000000..8569c86907c5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/nvif/ioctl.h
@@ -0,0 +1 @@
+../../../nvif/ioctl.h \ No newline at end of file
diff --git a/drivers/gpu/drm/nouveau/core/include/nvif/unpack.h b/drivers/gpu/drm/nouveau/core/include/nvif/unpack.h
new file mode 120000
index 000000000000..69d99292bca4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/nvif/unpack.h
@@ -0,0 +1 @@
+../../../nvif/unpack.h \ No newline at end of file
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bar.h b/drivers/gpu/drm/nouveau/core/include/subdev/bar.h
index 9faa98e67ad8..257ddf6d36d4 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/bar.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bar.h
@@ -12,7 +12,6 @@ struct nouveau_bar {
int (*alloc)(struct nouveau_bar *, struct nouveau_object *,
struct nouveau_mem *, struct nouveau_object **);
- void __iomem *iomem;
int (*kmap)(struct nouveau_bar *, struct nouveau_mem *,
u32 flags, struct nouveau_vma *);
@@ -20,6 +19,9 @@ struct nouveau_bar {
u32 flags, struct nouveau_vma *);
void (*unmap)(struct nouveau_bar *, struct nouveau_vma *);
void (*flush)(struct nouveau_bar *);
+
+ /* whether the BAR supports to be ioremapped WC or should be uncached */
+ bool iomap_uncached;
};
static inline struct nouveau_bar *
@@ -30,5 +32,6 @@ nouveau_bar(void *obj)
extern struct nouveau_oclass nv50_bar_oclass;
extern struct nouveau_oclass nvc0_bar_oclass;
+extern struct nouveau_oclass gk20a_bar_oclass;
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0205.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0205.h
new file mode 100644
index 000000000000..e171120cec81
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0205.h
@@ -0,0 +1,32 @@
+#ifndef __NVBIOS_M0205_H__
+#define __NVBIOS_M0205_H__
+
+struct nvbios_M0205T {
+ u16 freq;
+};
+
+u32 nvbios_M0205Te(struct nouveau_bios *,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz);
+u32 nvbios_M0205Tp(struct nouveau_bios *,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz,
+ struct nvbios_M0205T *);
+
+struct nvbios_M0205E {
+ u8 type;
+};
+
+u32 nvbios_M0205Ee(struct nouveau_bios *, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u32 nvbios_M0205Ep(struct nouveau_bios *, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_M0205E *);
+
+struct nvbios_M0205S {
+ u8 data;
+};
+
+u32 nvbios_M0205Se(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr);
+u32 nvbios_M0205Sp(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_M0205S *);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0209.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0209.h
new file mode 100644
index 000000000000..67dc50d837bc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/M0209.h
@@ -0,0 +1,30 @@
+#ifndef __NVBIOS_M0209_H__
+#define __NVBIOS_M0209_H__
+
+u32 nvbios_M0209Te(struct nouveau_bios *,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz);
+
+struct nvbios_M0209E {
+ u8 v00_40;
+ u8 bits;
+ u8 modulo;
+ u8 v02_40;
+ u8 v02_07;
+ u8 v03;
+};
+
+u32 nvbios_M0209Ee(struct nouveau_bios *, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u32 nvbios_M0209Ep(struct nouveau_bios *, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_M0209E *);
+
+struct nvbios_M0209S {
+ u32 data[0x200];
+};
+
+u32 nvbios_M0209Se(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr);
+u32 nvbios_M0209Sp(struct nouveau_bios *, int ent, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_M0209S *);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/fan.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/fan.h
new file mode 100644
index 000000000000..119d0874e041
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/fan.h
@@ -0,0 +1,8 @@
+#ifndef __NVBIOS_FAN_H__
+#define __NVBIOS_FAN_H__
+
+#include <subdev/bios/therm.h>
+
+u16 nvbios_fan_parse(struct nouveau_bios *bios, struct nvbios_therm_fan *fan);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h
index c086ac6d677d..a685bbd04568 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/ramcfg.h
@@ -4,60 +4,118 @@
struct nouveau_bios;
struct nvbios_ramcfg {
- unsigned rammap_11_08_01:1;
- unsigned rammap_11_08_0c:2;
- unsigned rammap_11_08_10:1;
- unsigned rammap_11_11_0c:2;
+ unsigned rammap_ver;
+ unsigned rammap_hdr;
+ unsigned rammap_min;
+ unsigned rammap_max;
+ union {
+ struct {
+ unsigned rammap_10_04_02:1;
+ unsigned rammap_10_04_08:1;
+ };
+ struct {
+ unsigned rammap_11_08_01:1;
+ unsigned rammap_11_08_0c:2;
+ unsigned rammap_11_08_10:1;
+ unsigned rammap_11_09_01ff:9;
+ unsigned rammap_11_0a_03fe:9;
+ unsigned rammap_11_0a_0400:1;
+ unsigned rammap_11_0a_0800:1;
+ unsigned rammap_11_0b_01f0:5;
+ unsigned rammap_11_0b_0200:1;
+ unsigned rammap_11_0b_0400:1;
+ unsigned rammap_11_0b_0800:1;
+ unsigned rammap_11_0d:8;
+ unsigned rammap_11_0e:8;
+ unsigned rammap_11_0f:8;
+ unsigned rammap_11_11_0c:2;
+ };
+ };
- unsigned ramcfg_11_01_01:1;
- unsigned ramcfg_11_01_02:1;
- unsigned ramcfg_11_01_04:1;
- unsigned ramcfg_11_01_08:1;
- unsigned ramcfg_11_01_10:1;
- unsigned ramcfg_11_01_20:1;
- unsigned ramcfg_11_01_40:1;
- unsigned ramcfg_11_01_80:1;
- unsigned ramcfg_11_02_03:2;
- unsigned ramcfg_11_02_04:1;
- unsigned ramcfg_11_02_08:1;
- unsigned ramcfg_11_02_10:1;
- unsigned ramcfg_11_02_40:1;
- unsigned ramcfg_11_02_80:1;
- unsigned ramcfg_11_03_0f:4;
- unsigned ramcfg_11_03_30:2;
- unsigned ramcfg_11_03_c0:2;
- unsigned ramcfg_11_03_f0:4;
- unsigned ramcfg_11_04:8;
- unsigned ramcfg_11_06:8;
- unsigned ramcfg_11_07_02:1;
- unsigned ramcfg_11_07_04:1;
- unsigned ramcfg_11_07_08:1;
- unsigned ramcfg_11_07_10:1;
- unsigned ramcfg_11_07_40:1;
- unsigned ramcfg_11_07_80:1;
- unsigned ramcfg_11_08_01:1;
- unsigned ramcfg_11_08_02:1;
- unsigned ramcfg_11_08_04:1;
- unsigned ramcfg_11_08_08:1;
- unsigned ramcfg_11_08_10:1;
- unsigned ramcfg_11_08_20:1;
- unsigned ramcfg_11_09:8;
+ unsigned ramcfg_ver;
+ unsigned ramcfg_hdr;
+ unsigned ramcfg_timing;
+ union {
+ struct {
+ unsigned ramcfg_10_02_01:1;
+ unsigned ramcfg_10_02_02:1;
+ unsigned ramcfg_10_02_04:1;
+ unsigned ramcfg_10_02_08:1;
+ unsigned ramcfg_10_02_10:1;
+ unsigned ramcfg_10_02_20:1;
+ unsigned ramcfg_10_02_40:1;
+ unsigned ramcfg_10_03_0f:4;
+ unsigned ramcfg_10_05:8;
+ unsigned ramcfg_10_06:8;
+ unsigned ramcfg_10_07:8;
+ unsigned ramcfg_10_08:8;
+ unsigned ramcfg_10_09_0f:4;
+ unsigned ramcfg_10_09_f0:4;
+ };
+ struct {
+ unsigned ramcfg_11_01_01:1;
+ unsigned ramcfg_11_01_02:1;
+ unsigned ramcfg_11_01_04:1;
+ unsigned ramcfg_11_01_08:1;
+ unsigned ramcfg_11_01_10:1;
+ unsigned ramcfg_11_01_20:1;
+ unsigned ramcfg_11_01_40:1;
+ unsigned ramcfg_11_01_80:1;
+ unsigned ramcfg_11_02_03:2;
+ unsigned ramcfg_11_02_04:1;
+ unsigned ramcfg_11_02_08:1;
+ unsigned ramcfg_11_02_10:1;
+ unsigned ramcfg_11_02_40:1;
+ unsigned ramcfg_11_02_80:1;
+ unsigned ramcfg_11_03_0f:4;
+ unsigned ramcfg_11_03_30:2;
+ unsigned ramcfg_11_03_c0:2;
+ unsigned ramcfg_11_03_f0:4;
+ unsigned ramcfg_11_04:8;
+ unsigned ramcfg_11_06:8;
+ unsigned ramcfg_11_07_02:1;
+ unsigned ramcfg_11_07_04:1;
+ unsigned ramcfg_11_07_08:1;
+ unsigned ramcfg_11_07_10:1;
+ unsigned ramcfg_11_07_40:1;
+ unsigned ramcfg_11_07_80:1;
+ unsigned ramcfg_11_08_01:1;
+ unsigned ramcfg_11_08_02:1;
+ unsigned ramcfg_11_08_04:1;
+ unsigned ramcfg_11_08_08:1;
+ unsigned ramcfg_11_08_10:1;
+ unsigned ramcfg_11_08_20:1;
+ unsigned ramcfg_11_09:8;
+ };
+ };
+ unsigned timing_ver;
+ unsigned timing_hdr;
unsigned timing[11];
- unsigned timing_20_2e_03:2;
- unsigned timing_20_2e_30:2;
- unsigned timing_20_2e_c0:2;
- unsigned timing_20_2f_03:2;
- unsigned timing_20_2c_003f:6;
- unsigned timing_20_2c_1fc0:7;
- unsigned timing_20_30_f8:5;
- unsigned timing_20_30_07:3;
- unsigned timing_20_31_0007:3;
- unsigned timing_20_31_0078:4;
- unsigned timing_20_31_0780:4;
- unsigned timing_20_31_0800:1;
- unsigned timing_20_31_7000:3;
- unsigned timing_20_31_8000:1;
+ union {
+ struct {
+ unsigned timing_10_WR:8;
+ unsigned timing_10_CL:8;
+ unsigned timing_10_ODT:3;
+ unsigned timing_10_CWL:8;
+ };
+ struct {
+ unsigned timing_20_2e_03:2;
+ unsigned timing_20_2e_30:2;
+ unsigned timing_20_2e_c0:2;
+ unsigned timing_20_2f_03:2;
+ unsigned timing_20_2c_003f:6;
+ unsigned timing_20_2c_1fc0:7;
+ unsigned timing_20_30_f8:5;
+ unsigned timing_20_30_07:3;
+ unsigned timing_20_31_0007:3;
+ unsigned timing_20_31_0078:4;
+ unsigned timing_20_31_0780:4;
+ unsigned timing_20_31_0800:1;
+ unsigned timing_20_31_7000:3;
+ unsigned timing_20_31_8000:1;
+ };
+ };
};
u8 nvbios_ramcfg_count(struct nouveau_bios *);
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h
index 5bdf8e4db40a..47e021d3e20d 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/rammap.h
@@ -8,9 +8,10 @@ u32 nvbios_rammapTe(struct nouveau_bios *, u8 *ver, u8 *hdr,
u32 nvbios_rammapEe(struct nouveau_bios *, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u32 nvbios_rammapEp(struct nouveau_bios *, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_ramcfg *);
u32 nvbios_rammapEm(struct nouveau_bios *, u16 mhz,
- u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
-u32 nvbios_rammapEp(struct nouveau_bios *, u16 mhz,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_ramcfg *);
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h
index 8dc5051df55d..295d093f3b30 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h
@@ -23,6 +23,12 @@ struct nvbios_therm_sensor {
struct nvbios_therm_threshold thrs_shutdown;
};
+enum nvbios_therm_fan_type {
+ NVBIOS_THERM_FAN_UNK = 0,
+ NVBIOS_THERM_FAN_TOGGLE = 1,
+ NVBIOS_THERM_FAN_PWM = 2,
+};
+
/* no vbios have more than 6 */
#define NOUVEAU_TEMP_FAN_TRIP_MAX 10
struct nouveau_therm_trip_point {
@@ -38,7 +44,9 @@ enum nvbios_therm_fan_mode {
};
struct nvbios_therm_fan {
- u16 pwm_freq;
+ enum nvbios_therm_fan_type type;
+
+ u32 pwm_freq;
u8 min_duty;
u8 max_duty;
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h
index c01e29c9f89a..36ed035d4d42 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h
@@ -29,6 +29,7 @@ enum nv_clk_src {
nv_clk_src_mdiv,
nv_clk_src_core,
+ nv_clk_src_core_intm,
nv_clk_src_shader,
nv_clk_src_mem,
@@ -71,8 +72,15 @@ struct nouveau_clock {
struct list_head states;
int state_nr;
+ struct work_struct work;
+ wait_queue_head_t wait;
+ atomic_t waiting;
+
+ struct nvkm_notify pwrsrc_ntfy;
+ int pwrsrc;
int pstate; /* current */
- int ustate; /* user-requested (-1 disabled, -2 perfmon) */
+ int ustate_ac; /* user-requested (-1 disabled, -2 perfmon) */
+ int ustate_dc; /* user-requested (-1 disabled, -2 perfmon) */
int astate; /* perfmon adjustment (base) */
int tstate; /* thermal adjustment (max-) */
int dstate; /* display adjustment (min+) */
@@ -108,8 +116,9 @@ struct nouveau_clocks {
int mdiv;
};
-#define nouveau_clock_create(p,e,o,i,r,d) \
- nouveau_clock_create_((p), (e), (o), (i), (r), sizeof(**d), (void **)d)
+#define nouveau_clock_create(p,e,o,i,r,s,n,d) \
+ nouveau_clock_create_((p), (e), (o), (i), (r), (s), (n), sizeof(**d), \
+ (void **)d)
#define nouveau_clock_destroy(p) ({ \
struct nouveau_clock *clk = (p); \
_nouveau_clock_dtor(nv_object(clk)); \
@@ -118,15 +127,18 @@ struct nouveau_clocks {
struct nouveau_clock *clk = (p); \
_nouveau_clock_init(nv_object(clk)); \
})
-#define nouveau_clock_fini(p,s) \
- nouveau_subdev_fini(&(p)->base, (s))
+#define nouveau_clock_fini(p,s) ({ \
+ struct nouveau_clock *clk = (p); \
+ _nouveau_clock_fini(nv_object(clk), (s)); \
+})
int nouveau_clock_create_(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *,
- struct nouveau_clocks *, bool, int, void **);
+ struct nouveau_clocks *, struct nouveau_pstate *,
+ int, bool, int, void **);
void _nouveau_clock_dtor(struct nouveau_object *);
-int _nouveau_clock_init(struct nouveau_object *);
-#define _nouveau_clock_fini _nouveau_subdev_fini
+int _nouveau_clock_init(struct nouveau_object *);
+int _nouveau_clock_fini(struct nouveau_object *, bool);
extern struct nouveau_oclass nv04_clock_oclass;
extern struct nouveau_oclass nv40_clock_oclass;
@@ -136,6 +148,7 @@ extern struct nouveau_oclass *nvaa_clock_oclass;
extern struct nouveau_oclass nva3_clock_oclass;
extern struct nouveau_oclass nvc0_clock_oclass;
extern struct nouveau_oclass nve0_clock_oclass;
+extern struct nouveau_oclass gk20a_clock_oclass;
int nv04_clock_pll_set(struct nouveau_clock *, u32 type, u32 freq);
int nv04_clock_pll_calc(struct nouveau_clock *, struct nvbios_pll *,
@@ -145,7 +158,7 @@ int nv04_clock_pll_prog(struct nouveau_clock *, u32 reg1,
int nva3_clock_pll_calc(struct nouveau_clock *, struct nvbios_pll *,
int clk, struct nouveau_pll_vals *);
-int nouveau_clock_ustate(struct nouveau_clock *, int req);
+int nouveau_clock_ustate(struct nouveau_clock *, int req, int pwr);
int nouveau_clock_astate(struct nouveau_clock *, int req, int rel);
int nouveau_clock_dstate(struct nouveau_clock *, int req, int rel);
int nouveau_clock_tstate(struct nouveau_clock *, int req, int rel);
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h
index 871e73914b24..8d0032f15205 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h
@@ -111,6 +111,7 @@ extern struct nouveau_oclass *gm107_fb_oclass;
#include <subdev/bios/ramcfg.h>
struct nouveau_ram_data {
+ struct list_head head;
struct nvbios_ramcfg bios;
u32 freq;
};
@@ -136,6 +137,7 @@ struct nouveau_ram {
int ranks;
int parts;
+ int part_mask;
int (*get)(struct nouveau_fb *, u64 size, u32 align,
u32 size_nc, u32 type, struct nouveau_mem **);
@@ -144,11 +146,6 @@ struct nouveau_ram {
int (*calc)(struct nouveau_fb *, u32 freq);
int (*prog)(struct nouveau_fb *);
void (*tidy)(struct nouveau_fb *);
- struct {
- u8 version;
- u32 data;
- u8 size;
- } rammap, ramcfg, timing;
u32 freq;
u32 mr[16];
u32 mr1_nuts;
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fb/regsnv04.h b/drivers/gpu/drm/nouveau/core/include/subdev/fb/regsnv04.h
new file mode 100644
index 000000000000..0f7fc0c52ab2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/fb/regsnv04.h
@@ -0,0 +1,21 @@
+#ifndef __NOUVEAU_FB_REGS_04_H__
+#define __NOUVEAU_FB_REGS_04_H__
+
+#define NV04_PFB_BOOT_0 0x00100000
+# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003
+# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000
+# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001
+# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002
+# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003
+# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004
+# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028
+# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000
+# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008
+# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010
+# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018
+# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020
+# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028
+# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100
+# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fuse.h b/drivers/gpu/drm/nouveau/core/include/subdev/fuse.h
new file mode 100644
index 000000000000..2b1ddb2a9a7d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/fuse.h
@@ -0,0 +1,30 @@
+#ifndef __NOUVEAU_FUSE_H__
+#define __NOUVEAU_FUSE_H__
+
+#include <core/subdev.h>
+#include <core/device.h>
+
+struct nouveau_fuse {
+ struct nouveau_subdev base;
+};
+
+static inline struct nouveau_fuse *
+nouveau_fuse(void *obj)
+{
+ return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_FUSE];
+}
+
+#define nouveau_fuse_create(p, e, o, d) \
+ nouveau_fuse_create_((p), (e), (o), sizeof(**d), (void **)d)
+
+int nouveau_fuse_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, int, void **);
+void _nouveau_fuse_dtor(struct nouveau_object *);
+int _nouveau_fuse_init(struct nouveau_object *);
+#define _nouveau_fuse_fini _nouveau_subdev_fini
+
+extern struct nouveau_oclass g80_fuse_oclass;
+extern struct nouveau_oclass gf100_fuse_oclass;
+extern struct nouveau_oclass gm107_fuse_oclass;
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h
index 612d82ab683d..f855140dbcb7 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h
@@ -8,16 +8,22 @@
#include <subdev/bios.h>
#include <subdev/bios/gpio.h>
-enum nvkm_gpio_event {
- NVKM_GPIO_HI = 1,
- NVKM_GPIO_LO = 2,
- NVKM_GPIO_TOGGLED = (NVKM_GPIO_HI | NVKM_GPIO_LO),
+struct nvkm_gpio_ntfy_req {
+#define NVKM_GPIO_HI 0x01
+#define NVKM_GPIO_LO 0x02
+#define NVKM_GPIO_TOGGLED 0x03
+ u8 mask;
+ u8 line;
+};
+
+struct nvkm_gpio_ntfy_rep {
+ u8 mask;
};
struct nouveau_gpio {
struct nouveau_subdev base;
- struct nouveau_event *events;
+ struct nvkm_event event;
void (*reset)(struct nouveau_gpio *, u8 func);
int (*find)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
@@ -34,7 +40,7 @@ nouveau_gpio(void *obj)
extern struct nouveau_oclass *nv10_gpio_oclass;
extern struct nouveau_oclass *nv50_gpio_oclass;
-extern struct nouveau_oclass *nv92_gpio_oclass;
+extern struct nouveau_oclass *nv94_gpio_oclass;
extern struct nouveau_oclass *nvd0_gpio_oclass;
extern struct nouveau_oclass *nve0_gpio_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
index 825f7bb46b67..1b937c2c25ae 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
@@ -14,15 +14,18 @@
#define NV_I2C_TYPE_EXTDDC(e) (0x0005 | (e) << 8)
#define NV_I2C_TYPE_EXTAUX(e) (0x0006 | (e) << 8)
-enum nvkm_i2c_event {
- NVKM_I2C_PLUG = 1,
- NVKM_I2C_UNPLUG = 2,
- NVKM_I2C_IRQ = 4,
- NVKM_I2C_DONE = 8,
- NVKM_I2C_ANY = (NVKM_I2C_PLUG |
- NVKM_I2C_UNPLUG |
- NVKM_I2C_IRQ |
- NVKM_I2C_DONE),
+struct nvkm_i2c_ntfy_req {
+#define NVKM_I2C_PLUG 0x01
+#define NVKM_I2C_UNPLUG 0x02
+#define NVKM_I2C_IRQ 0x04
+#define NVKM_I2C_DONE 0x08
+#define NVKM_I2C_ANY 0x0f
+ u8 mask;
+ u8 port;
+};
+
+struct nvkm_i2c_ntfy_rep {
+ u8 mask;
};
struct nouveau_i2c_port {
@@ -56,7 +59,7 @@ struct nouveau_i2c_board_info {
struct nouveau_i2c {
struct nouveau_subdev base;
- struct nouveau_event *ntfy;
+ struct nvkm_event event;
struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type);
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/ltc.h b/drivers/gpu/drm/nouveau/core/include/subdev/ltc.h
new file mode 100644
index 000000000000..b909a7363f6b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/ltc.h
@@ -0,0 +1,35 @@
+#ifndef __NOUVEAU_LTC_H__
+#define __NOUVEAU_LTC_H__
+
+#include <core/subdev.h>
+#include <core/device.h>
+
+#define NOUVEAU_LTC_MAX_ZBC_CNT 16
+
+struct nouveau_mm_node;
+
+struct nouveau_ltc {
+ struct nouveau_subdev base;
+
+ int (*tags_alloc)(struct nouveau_ltc *, u32 count,
+ struct nouveau_mm_node **);
+ void (*tags_free)(struct nouveau_ltc *, struct nouveau_mm_node **);
+ void (*tags_clear)(struct nouveau_ltc *, u32 first, u32 count);
+
+ int zbc_min;
+ int zbc_max;
+ int (*zbc_color_get)(struct nouveau_ltc *, int index, const u32[4]);
+ int (*zbc_depth_get)(struct nouveau_ltc *, int index, const u32);
+};
+
+static inline struct nouveau_ltc *
+nouveau_ltc(void *obj)
+{
+ return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_LTC];
+}
+
+extern struct nouveau_oclass *gf100_ltc_oclass;
+extern struct nouveau_oclass *gk104_ltc_oclass;
+extern struct nouveau_oclass *gm107_ltc_oclass;
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h b/drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h
deleted file mode 100644
index c9c1950b7743..000000000000
--- a/drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef __NOUVEAU_LTCG_H__
-#define __NOUVEAU_LTCG_H__
-
-#include <core/subdev.h>
-#include <core/device.h>
-
-struct nouveau_mm_node;
-
-struct nouveau_ltcg {
- struct nouveau_subdev base;
-
- int (*tags_alloc)(struct nouveau_ltcg *, u32 count,
- struct nouveau_mm_node **);
- void (*tags_free)(struct nouveau_ltcg *, struct nouveau_mm_node **);
- void (*tags_clear)(struct nouveau_ltcg *, u32 first, u32 count);
-};
-
-static inline struct nouveau_ltcg *
-nouveau_ltcg(void *obj)
-{
- return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_LTCG];
-}
-
-#define nouveau_ltcg_create(p,e,o,d) \
- nouveau_subdev_create_((p), (e), (o), 0, "PLTCG", "level2", \
- sizeof(**d), (void **)d)
-#define nouveau_ltcg_destroy(p) \
- nouveau_subdev_destroy(&(p)->base)
-#define nouveau_ltcg_init(p) \
- nouveau_subdev_init(&(p)->base)
-#define nouveau_ltcg_fini(p,s) \
- nouveau_subdev_fini(&(p)->base, (s))
-
-#define _nouveau_ltcg_dtor _nouveau_subdev_dtor
-#define _nouveau_ltcg_init _nouveau_subdev_init
-#define _nouveau_ltcg_fini _nouveau_subdev_fini
-
-extern struct nouveau_oclass *gf100_ltcg_oclass;
-extern struct nouveau_oclass *gm107_ltcg_oclass;
-
-#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/mc.h b/drivers/gpu/drm/nouveau/core/include/subdev/mc.h
index 72b176831be6..568e4dfc5e9e 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/mc.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/mc.h
@@ -4,15 +4,11 @@
#include <core/subdev.h>
#include <core/device.h>
-struct nouveau_mc_intr {
- u32 stat;
- u32 unit;
-};
-
struct nouveau_mc {
struct nouveau_subdev base;
bool use_msi;
unsigned int irq;
+ void (*unk260)(struct nouveau_mc *, u32);
};
static inline struct nouveau_mc *
@@ -21,30 +17,6 @@ nouveau_mc(void *obj)
return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_MC];
}
-#define nouveau_mc_create(p,e,o,d) \
- nouveau_mc_create_((p), (e), (o), sizeof(**d), (void **)d)
-#define nouveau_mc_destroy(p) ({ \
- struct nouveau_mc *pmc = (p); _nouveau_mc_dtor(nv_object(pmc)); \
-})
-#define nouveau_mc_init(p) ({ \
- struct nouveau_mc *pmc = (p); _nouveau_mc_init(nv_object(pmc)); \
-})
-#define nouveau_mc_fini(p,s) ({ \
- struct nouveau_mc *pmc = (p); _nouveau_mc_fini(nv_object(pmc), (s)); \
-})
-
-int nouveau_mc_create_(struct nouveau_object *, struct nouveau_object *,
- struct nouveau_oclass *, int, void **);
-void _nouveau_mc_dtor(struct nouveau_object *);
-int _nouveau_mc_init(struct nouveau_object *);
-int _nouveau_mc_fini(struct nouveau_object *, bool);
-
-struct nouveau_mc_oclass {
- struct nouveau_oclass base;
- const struct nouveau_mc_intr *intr;
- void (*msi_rearm)(struct nouveau_mc *);
-};
-
extern struct nouveau_oclass *nv04_mc_oclass;
extern struct nouveau_oclass *nv40_mc_oclass;
extern struct nouveau_oclass *nv44_mc_oclass;
@@ -54,5 +26,6 @@ extern struct nouveau_oclass *nv94_mc_oclass;
extern struct nouveau_oclass *nv98_mc_oclass;
extern struct nouveau_oclass *nvc0_mc_oclass;
extern struct nouveau_oclass *nvc3_mc_oclass;
+extern struct nouveau_oclass *gk20a_mc_oclass;
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/pwr.h b/drivers/gpu/drm/nouveau/core/include/subdev/pwr.h
index c5c92cbed33f..bf3d1f611333 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/pwr.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/pwr.h
@@ -8,18 +8,6 @@ struct nouveau_pwr {
struct nouveau_subdev base;
struct {
- u32 limit;
- u32 *data;
- u32 size;
- } code;
-
- struct {
- u32 limit;
- u32 *data;
- u32 size;
- } data;
-
- struct {
u32 base;
u32 size;
} send;
@@ -35,7 +23,8 @@ struct nouveau_pwr {
u32 data[2];
} recv;
- int (*message)(struct nouveau_pwr *, u32[2], u32, u32, u32, u32);
+ int (*message)(struct nouveau_pwr *, u32[2], u32, u32, u32, u32);
+ void (*pgob)(struct nouveau_pwr *, bool);
};
static inline struct nouveau_pwr *
@@ -44,29 +33,11 @@ nouveau_pwr(void *obj)
return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_PWR];
}
-#define nouveau_pwr_create(p, e, o, d) \
- nouveau_pwr_create_((p), (e), (o), sizeof(**d), (void **)d)
-#define nouveau_pwr_destroy(p) \
- nouveau_subdev_destroy(&(p)->base)
-#define nouveau_pwr_init(p) ({ \
- struct nouveau_pwr *ppwr = (p); \
- _nouveau_pwr_init(nv_object(ppwr)); \
-})
-#define nouveau_pwr_fini(p,s) ({ \
- struct nouveau_pwr *ppwr = (p); \
- _nouveau_pwr_fini(nv_object(ppwr), (s)); \
-})
-
-int nouveau_pwr_create_(struct nouveau_object *, struct nouveau_object *,
- struct nouveau_oclass *, int, void **);
-#define _nouveau_pwr_dtor _nouveau_subdev_dtor
-int _nouveau_pwr_init(struct nouveau_object *);
-int _nouveau_pwr_fini(struct nouveau_object *, bool);
-
-extern struct nouveau_oclass nva3_pwr_oclass;
-extern struct nouveau_oclass nvc0_pwr_oclass;
-extern struct nouveau_oclass nvd0_pwr_oclass;
-extern struct nouveau_oclass nv108_pwr_oclass;
+extern struct nouveau_oclass *nva3_pwr_oclass;
+extern struct nouveau_oclass *nvc0_pwr_oclass;
+extern struct nouveau_oclass *nvd0_pwr_oclass;
+extern struct nouveau_oclass *gk104_pwr_oclass;
+extern struct nouveau_oclass *nv108_pwr_oclass;
/* interface to MEMX process running on PPWR */
struct nouveau_memx;
@@ -76,5 +47,8 @@ void nouveau_memx_wr32(struct nouveau_memx *, u32 addr, u32 data);
void nouveau_memx_wait(struct nouveau_memx *,
u32 addr, u32 mask, u32 data, u32 nsec);
void nouveau_memx_nsec(struct nouveau_memx *, u32 nsec);
+void nouveau_memx_wait_vblank(struct nouveau_memx *);
+void nouveau_memx_block(struct nouveau_memx *);
+void nouveau_memx_unblock(struct nouveau_memx *);
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h
index d4a68179e586..a437597dcafc 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h
@@ -78,5 +78,6 @@ extern struct nouveau_oclass nv50_therm_oclass;
extern struct nouveau_oclass nv84_therm_oclass;
extern struct nouveau_oclass nva3_therm_oclass;
extern struct nouveau_oclass nvd0_therm_oclass;
+extern struct nouveau_oclass gm107_therm_oclass;
#endif
diff --git a/drivers/gpu/drm/nouveau/core/os.h b/drivers/gpu/drm/nouveau/core/os.h
index d0ced94ca54c..ccfa21d72ddc 100644
--- a/drivers/gpu/drm/nouveau/core/os.h
+++ b/drivers/gpu/drm/nouveau/core/os.h
@@ -21,6 +21,8 @@
#include <linux/interrupt.h>
#include <linux/log2.h>
#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
+#include <linux/clk.h>
#include <asm/unaligned.h>
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c
index 73b1ed20c8d5..b1adc69efd88 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c
@@ -38,10 +38,12 @@ struct nouveau_barobj {
static int
nouveau_barobj_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *mem, u32 size,
+ struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
+ struct nouveau_device *device = nv_device(parent);
struct nouveau_bar *bar = (void *)engine;
+ struct nouveau_mem *mem = data;
struct nouveau_barobj *barobj;
int ret;
@@ -54,7 +56,13 @@ nouveau_barobj_ctor(struct nouveau_object *parent,
if (ret)
return ret;
- barobj->iomem = bar->iomem + (u32)barobj->vma.offset;
+ barobj->iomem = ioremap(nv_device_resource_start(device, 3) +
+ (u32)barobj->vma.offset, mem->size << 12);
+ if (!barobj->iomem) {
+ nv_warn(bar, "PRAMIN ioremap failed\n");
+ return -ENOMEM;
+ }
+
return 0;
}
@@ -63,8 +71,11 @@ nouveau_barobj_dtor(struct nouveau_object *object)
{
struct nouveau_bar *bar = (void *)object->engine;
struct nouveau_barobj *barobj = (void *)object;
- if (barobj->vma.node)
+ if (barobj->vma.node) {
+ if (barobj->iomem)
+ iounmap(barobj->iomem);
bar->unmap(bar, &barobj->vma);
+ }
nouveau_object_destroy(&barobj->base);
}
@@ -99,8 +110,12 @@ nouveau_bar_alloc(struct nouveau_bar *bar, struct nouveau_object *parent,
struct nouveau_mem *mem, struct nouveau_object **pobject)
{
struct nouveau_object *engine = nv_object(bar);
- return nouveau_object_ctor(parent, engine, &nouveau_barobj_oclass,
- mem, 0, pobject);
+ struct nouveau_object *gpuobj;
+ int ret = nouveau_object_ctor(parent, engine, &nouveau_barobj_oclass,
+ mem, 0, &gpuobj);
+ if (ret == 0)
+ *pobject = gpuobj;
+ return ret;
}
int
@@ -108,7 +123,6 @@ nouveau_bar_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, int length, void **pobject)
{
- struct nouveau_device *device = nv_device(parent);
struct nouveau_bar *bar;
int ret;
@@ -118,18 +132,12 @@ nouveau_bar_create_(struct nouveau_object *parent,
if (ret)
return ret;
- if (nv_device_resource_len(device, 3) != 0)
- bar->iomem = ioremap(nv_device_resource_start(device, 3),
- nv_device_resource_len(device, 3));
-
return 0;
}
void
nouveau_bar_destroy(struct nouveau_bar *bar)
{
- if (bar->iomem)
- iounmap(bar->iomem);
nouveau_subdev_destroy(&bar->base);
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/gk20a.c b/drivers/gpu/drm/nouveau/core/subdev/bar/gk20a.c
new file mode 100644
index 000000000000..bf877af9d3bd
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/bar/gk20a.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 <subdev/bar.h>
+
+#include "priv.h"
+
+int
+gk20a_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct nouveau_bar *bar;
+ int ret;
+
+ ret = nvc0_bar_ctor(parent, engine, oclass, data, size, pobject);
+ if (ret)
+ return ret;
+
+ bar = (struct nouveau_bar *)*pobject;
+ bar->iomap_uncached = true;
+
+ return 0;
+}
+
+struct nouveau_oclass
+gk20a_bar_oclass = {
+ .handle = NV_SUBDEV(BAR, 0xea),
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gk20a_bar_ctor,
+ .dtor = nvc0_bar_dtor,
+ .init = nvc0_bar_init,
+ .fini = _nouveau_bar_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c
index ca8139b9ab27..05a278bab247 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c
@@ -133,7 +133,7 @@ nvc0_bar_init_vm(struct nvc0_bar_priv *priv, struct nvc0_bar_priv_vm *bar_vm,
return 0;
}
-static int
+int
nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
@@ -169,7 +169,7 @@ nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-static void
+void
nvc0_bar_dtor(struct nouveau_object *object)
{
struct nvc0_bar_priv *priv = (void *)object;
@@ -188,7 +188,7 @@ nvc0_bar_dtor(struct nouveau_object *object)
nouveau_bar_destroy(&priv->base);
}
-static int
+int
nvc0_bar_init(struct nouveau_object *object)
{
struct nvc0_bar_priv *priv = (void *)object;
@@ -200,7 +200,6 @@ nvc0_bar_init(struct nouveau_object *object)
nv_mask(priv, 0x000200, 0x00000100, 0x00000000);
nv_mask(priv, 0x000200, 0x00000100, 0x00000100);
- nv_mask(priv, 0x100c80, 0x00000001, 0x00000000);
nv_wr32(priv, 0x001704, 0x80000000 | priv->bar[1].mem->addr >> 12);
if (priv->bar[0].mem)
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/priv.h b/drivers/gpu/drm/nouveau/core/subdev/bar/priv.h
index ffad8f337ead..3ee8b1476d00 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bar/priv.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/bar/priv.h
@@ -23,4 +23,10 @@ int nouveau_bar_alloc(struct nouveau_bar *, struct nouveau_object *,
void nv84_bar_flush(struct nouveau_bar *);
+int nvc0_bar_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+void nvc0_bar_dtor(struct nouveau_object *);
+int nvc0_bar_init(struct nouveau_object *);
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/M0205.c b/drivers/gpu/drm/nouveau/core/subdev/bios/M0205.c
new file mode 100644
index 000000000000..ac9617c5fc2a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/M0205.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/M0205.h>
+
+u32
+nvbios_M0205Te(struct nouveau_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
+{
+ struct bit_entry bit_M;
+ u32 data = 0x00000000;
+
+ if (!bit_entry(bios, 'M', &bit_M)) {
+ if (bit_M.version == 2 && bit_M.length > 0x08)
+ data = nv_ro32(bios, bit_M.offset + 0x05);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0x00);
+ switch (*ver) {
+ case 0x10:
+ *hdr = nv_ro08(bios, data + 0x01);
+ *len = nv_ro08(bios, data + 0x02);
+ *ssz = nv_ro08(bios, data + 0x03);
+ *snr = nv_ro08(bios, data + 0x04);
+ *cnt = nv_ro08(bios, data + 0x05);
+ return data;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x00000000;
+}
+
+u32
+nvbios_M0205Tp(struct nouveau_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz,
+ struct nvbios_M0205T *info)
+{
+ u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, snr, ssz);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->freq = nv_ro16(bios, data + 0x06);
+ break;
+ default:
+ break;
+ }
+ return data;
+}
+
+u32
+nvbios_M0205Ee(struct nouveau_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u8 snr, ssz;
+ u32 data = nvbios_M0205Te(bios, ver, hdr, cnt, len, &snr, &ssz);
+ if (data && idx < *cnt) {
+ data = data + *hdr + idx * (*len + (snr * ssz));
+ *hdr = *len;
+ *cnt = snr;
+ *len = ssz;
+ return data;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0205Ep(struct nouveau_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_M0205E *info)
+{
+ u32 data = nvbios_M0205Ee(bios, idx, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->type = nv_ro08(bios, data + 0x00) & 0x0f;
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0205Se(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr)
+{
+
+ u8 cnt, len;
+ u32 data = nvbios_M0205Ee(bios, ent, ver, hdr, &cnt, &len);
+ if (data && idx < cnt) {
+ data = data + *hdr + idx * len;
+ *hdr = len;
+ return data;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0205Sp(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_M0205S *info)
+{
+ u32 data = nvbios_M0205Se(bios, ent, idx, ver, hdr);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->data = nv_ro08(bios, data + 0x00);
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/M0209.c b/drivers/gpu/drm/nouveau/core/subdev/bios/M0209.c
new file mode 100644
index 000000000000..b142a510e89f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/M0209.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/M0209.h>
+
+u32
+nvbios_M0209Te(struct nouveau_bios *bios,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *snr, u8 *ssz)
+{
+ struct bit_entry bit_M;
+ u32 data = 0x00000000;
+
+ if (!bit_entry(bios, 'M', &bit_M)) {
+ if (bit_M.version == 2 && bit_M.length > 0x0c)
+ data = nv_ro32(bios, bit_M.offset + 0x09);
+ if (data) {
+ *ver = nv_ro08(bios, data + 0x00);
+ switch (*ver) {
+ case 0x10:
+ *hdr = nv_ro08(bios, data + 0x01);
+ *len = nv_ro08(bios, data + 0x02);
+ *ssz = nv_ro08(bios, data + 0x03);
+ *snr = 1;
+ *cnt = nv_ro08(bios, data + 0x04);
+ return data;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x00000000;
+}
+
+u32
+nvbios_M0209Ee(struct nouveau_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u8 snr, ssz;
+ u32 data = nvbios_M0209Te(bios, ver, hdr, cnt, len, &snr, &ssz);
+ if (data && idx < *cnt) {
+ data = data + *hdr + idx * (*len + (snr * ssz));
+ *hdr = *len;
+ *cnt = snr;
+ *len = ssz;
+ return data;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0209Ep(struct nouveau_bios *bios, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_M0209E *info)
+{
+ u32 data = nvbios_M0209Ee(bios, idx, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ info->v00_40 = (nv_ro08(bios, data + 0x00) & 0x40) >> 6;
+ info->bits = nv_ro08(bios, data + 0x00) & 0x3f;
+ info->modulo = nv_ro08(bios, data + 0x01);
+ info->v02_40 = (nv_ro08(bios, data + 0x02) & 0x40) >> 6;
+ info->v02_07 = nv_ro08(bios, data + 0x02) & 0x07;
+ info->v03 = nv_ro08(bios, data + 0x03);
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0209Se(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr)
+{
+
+ u8 cnt, len;
+ u32 data = nvbios_M0209Ee(bios, ent, ver, hdr, &cnt, &len);
+ if (data && idx < cnt) {
+ data = data + *hdr + idx * len;
+ *hdr = len;
+ return data;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_M0209Sp(struct nouveau_bios *bios, int ent, int idx, u8 *ver, u8 *hdr,
+ struct nvbios_M0209S *info)
+{
+ struct nvbios_M0209E M0209E;
+ u8 cnt, len;
+ u32 data = nvbios_M0209Ep(bios, ent, ver, hdr, &cnt, &len, &M0209E);
+ if (data) {
+ u32 i, data = nvbios_M0209Se(bios, ent, idx, ver, hdr);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x10:
+ for (i = 0; i < ARRAY_SIZE(info->data); i++) {
+ u32 bits = (i % M0209E.modulo) * M0209E.bits;
+ u32 mask = (1ULL << M0209E.bits) - 1;
+ u16 off = bits / 8;
+ u8 mod = bits % 8;
+ info->data[i] = nv_ro32(bios, data + off);
+ info->data[i] = info->data[i] >> mod;
+ info->data[i] = info->data[i] & mask;
+ }
+ return data;
+ default:
+ break;
+ }
+ }
+ return 0x00000000;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c b/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c
index 88606bfaf847..bd8d348385b3 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c
@@ -124,6 +124,7 @@ dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len,
struct dcb_output *outp)
{
u16 dcb = dcb_outp(bios, idx, ver, len);
+ memset(outp, 0x00, sizeof(*outp));
if (dcb) {
if (*ver >= 0x20) {
u32 conn = nv_ro32(bios, dcb + 0x00);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/fan.c b/drivers/gpu/drm/nouveau/core/subdev/bios/fan.c
new file mode 100644
index 000000000000..e419892240f5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/fan.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Martin Peres
+ */
+
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/fan.h>
+
+u16
+nvbios_fan_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ struct bit_entry bit_P;
+ u16 fan = 0x0000;
+
+ if (!bit_entry(bios, 'P', &bit_P)) {
+ if (bit_P.version == 2 && bit_P.length >= 0x5a)
+ fan = nv_ro16(bios, bit_P.offset + 0x58);
+
+ if (fan) {
+ *ver = nv_ro08(bios, fan + 0);
+ switch (*ver) {
+ case 0x10:
+ *hdr = nv_ro08(bios, fan + 1);
+ *len = nv_ro08(bios, fan + 2);
+ *cnt = nv_ro08(bios, fan + 3);
+ return fan;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0x0000;
+}
+
+u16
+nvbios_fan_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *hdr,
+ u8 *cnt, u8 *len)
+{
+ u16 data = nvbios_fan_table(bios, ver, hdr, cnt, len);
+ if (data && idx < *cnt)
+ return data + *hdr + (idx * (*len));
+ return 0x0000;
+}
+
+u16
+nvbios_fan_parse(struct nouveau_bios *bios, struct nvbios_therm_fan *fan)
+{
+ u8 ver, hdr, cnt, len;
+
+ u16 data = nvbios_fan_entry(bios, 0, &ver, &hdr, &cnt, &len);
+ if (data) {
+ u8 type = nv_ro08(bios, data + 0x00);
+ switch (type) {
+ case 0:
+ fan->type = NVBIOS_THERM_FAN_TOGGLE;
+ break;
+ case 1:
+ case 2:
+ /* TODO: Understand the difference between the two! */
+ fan->type = NVBIOS_THERM_FAN_PWM;
+ break;
+ default:
+ fan->type = NVBIOS_THERM_FAN_UNK;
+ }
+
+ fan->min_duty = nv_ro08(bios, data + 0x02);
+ fan->max_duty = nv_ro08(bios, data + 0x03);
+
+ fan->pwm_freq = nv_ro32(bios, data + 0x0b) & 0xffffff;
+ }
+ return data;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c b/drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c
index 1811b2cb0472..585e69331ccc 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/rammap.c
@@ -75,31 +75,39 @@ nvbios_rammapEe(struct nouveau_bios *bios, int idx,
}
u32
-nvbios_rammapEm(struct nouveau_bios *bios, u16 khz,
- u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
-{
- int idx = 0;
- u32 data;
- while ((data = nvbios_rammapEe(bios, idx++, ver, hdr, cnt, len))) {
- if (khz >= nv_ro16(bios, data + 0x00) &&
- khz <= nv_ro16(bios, data + 0x02))
- break;
- }
- return data;
-}
-
-u32
-nvbios_rammapEp(struct nouveau_bios *bios, u16 khz,
+nvbios_rammapEp(struct nouveau_bios *bios, int idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_ramcfg *p)
{
- u32 data = nvbios_rammapEm(bios, khz, ver, hdr, cnt, len);
+ u32 data = nvbios_rammapEe(bios, idx, ver, hdr, cnt, len), temp;
memset(p, 0x00, sizeof(*p));
+ p->rammap_ver = *ver;
+ p->rammap_hdr = *hdr;
switch (!!data * *ver) {
+ case 0x10:
+ p->rammap_min = nv_ro16(bios, data + 0x00);
+ p->rammap_max = nv_ro16(bios, data + 0x02);
+ p->rammap_10_04_02 = (nv_ro08(bios, data + 0x04) & 0x02) >> 1;
+ p->rammap_10_04_08 = (nv_ro08(bios, data + 0x04) & 0x08) >> 3;
+ break;
case 0x11:
+ p->rammap_min = nv_ro16(bios, data + 0x00);
+ p->rammap_max = nv_ro16(bios, data + 0x02);
p->rammap_11_08_01 = (nv_ro08(bios, data + 0x08) & 0x01) >> 0;
p->rammap_11_08_0c = (nv_ro08(bios, data + 0x08) & 0x0c) >> 2;
p->rammap_11_08_10 = (nv_ro08(bios, data + 0x08) & 0x10) >> 4;
+ temp = nv_ro32(bios, data + 0x09);
+ p->rammap_11_09_01ff = (temp & 0x000001ff) >> 0;
+ p->rammap_11_0a_03fe = (temp & 0x0003fe00) >> 9;
+ p->rammap_11_0a_0400 = (temp & 0x00040000) >> 18;
+ p->rammap_11_0a_0800 = (temp & 0x00080000) >> 19;
+ p->rammap_11_0b_01f0 = (temp & 0x01f00000) >> 20;
+ p->rammap_11_0b_0200 = (temp & 0x02000000) >> 25;
+ p->rammap_11_0b_0400 = (temp & 0x04000000) >> 26;
+ p->rammap_11_0b_0800 = (temp & 0x08000000) >> 27;
+ p->rammap_11_0d = nv_ro08(bios, data + 0x0d);
+ p->rammap_11_0e = nv_ro08(bios, data + 0x0e);
+ p->rammap_11_0f = nv_ro08(bios, data + 0x0f);
p->rammap_11_11_0c = (nv_ro08(bios, data + 0x11) & 0x0c) >> 2;
break;
default:
@@ -110,6 +118,20 @@ nvbios_rammapEp(struct nouveau_bios *bios, u16 khz,
}
u32
+nvbios_rammapEm(struct nouveau_bios *bios, u16 mhz,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_ramcfg *info)
+{
+ int idx = 0;
+ u32 data;
+ while ((data = nvbios_rammapEp(bios, idx++, ver, hdr, cnt, len, info))) {
+ if (mhz >= info->rammap_min && mhz <= info->rammap_max)
+ break;
+ }
+ return data;
+}
+
+u32
nvbios_rammapSe(struct nouveau_bios *bios, u32 data,
u8 ever, u8 ehdr, u8 ecnt, u8 elen, int idx,
u8 *ver, u8 *hdr)
@@ -129,8 +151,28 @@ nvbios_rammapSp(struct nouveau_bios *bios, u32 data,
u8 *ver, u8 *hdr, struct nvbios_ramcfg *p)
{
data = nvbios_rammapSe(bios, data, ever, ehdr, ecnt, elen, idx, ver, hdr);
+ p->ramcfg_ver = *ver;
+ p->ramcfg_hdr = *hdr;
switch (!!data * *ver) {
+ case 0x10:
+ p->ramcfg_timing = nv_ro08(bios, data + 0x01);
+ p->ramcfg_10_02_01 = (nv_ro08(bios, data + 0x02) & 0x01) >> 0;
+ p->ramcfg_10_02_02 = (nv_ro08(bios, data + 0x02) & 0x02) >> 1;
+ p->ramcfg_10_02_04 = (nv_ro08(bios, data + 0x02) & 0x04) >> 2;
+ p->ramcfg_10_02_08 = (nv_ro08(bios, data + 0x02) & 0x08) >> 3;
+ p->ramcfg_10_02_10 = (nv_ro08(bios, data + 0x02) & 0x10) >> 4;
+ p->ramcfg_10_02_20 = (nv_ro08(bios, data + 0x02) & 0x20) >> 5;
+ p->ramcfg_10_02_40 = (nv_ro08(bios, data + 0x02) & 0x40) >> 6;
+ p->ramcfg_10_03_0f = (nv_ro08(bios, data + 0x03) & 0x0f) >> 0;
+ p->ramcfg_10_05 = (nv_ro08(bios, data + 0x05) & 0xff) >> 0;
+ p->ramcfg_10_06 = (nv_ro08(bios, data + 0x06) & 0xff) >> 0;
+ p->ramcfg_10_07 = (nv_ro08(bios, data + 0x07) & 0xff) >> 0;
+ p->ramcfg_10_08 = (nv_ro08(bios, data + 0x08) & 0xff) >> 0;
+ p->ramcfg_10_09_0f = (nv_ro08(bios, data + 0x09) & 0x0f) >> 0;
+ p->ramcfg_10_09_f0 = (nv_ro08(bios, data + 0x09) & 0xf0) >> 4;
+ break;
case 0x11:
+ p->ramcfg_timing = nv_ro08(bios, data + 0x00);
p->ramcfg_11_01_01 = (nv_ro08(bios, data + 0x01) & 0x01) >> 0;
p->ramcfg_11_01_02 = (nv_ro08(bios, data + 0x01) & 0x02) >> 1;
p->ramcfg_11_01_04 = (nv_ro08(bios, data + 0x01) & 0x04) >> 2;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/timing.c b/drivers/gpu/drm/nouveau/core/subdev/bios/timing.c
index 350d44ab2ba2..46d955eb51eb 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/timing.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/timing.c
@@ -89,7 +89,15 @@ nvbios_timingEp(struct nouveau_bios *bios, int idx,
struct nvbios_ramcfg *p)
{
u16 data = nvbios_timingEe(bios, idx, ver, hdr, cnt, len), temp;
+ p->timing_ver = *ver;
+ p->timing_hdr = *hdr;
switch (!!data * *ver) {
+ case 0x10:
+ p->timing_10_WR = nv_ro08(bios, data + 0x00);
+ p->timing_10_CL = nv_ro08(bios, data + 0x02);
+ p->timing_10_ODT = nv_ro08(bios, data + 0x0e) & 0x07;
+ p->timing_10_CWL = nv_ro08(bios, data + 0x13);
+ break;
case 0x20:
p->timing[0] = nv_ro32(bios, data + 0x00);
p->timing[1] = nv_ro32(bios, data + 0x04);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/base.c b/drivers/gpu/drm/nouveau/core/subdev/clock/base.c
index 22351f594d2a..e51b72d47129 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/base.c
@@ -90,16 +90,20 @@ nouveau_cstate_prog(struct nouveau_clock *clk,
cstate = &pstate->base;
}
- ret = nouveau_therm_cstate(ptherm, pstate->fanspeed, +1);
- if (ret && ret != -ENODEV) {
- nv_error(clk, "failed to raise fan speed: %d\n", ret);
- return ret;
+ if (ptherm) {
+ ret = nouveau_therm_cstate(ptherm, pstate->fanspeed, +1);
+ if (ret && ret != -ENODEV) {
+ nv_error(clk, "failed to raise fan speed: %d\n", ret);
+ return ret;
+ }
}
- ret = volt->set_id(volt, cstate->voltage, +1);
- if (ret && ret != -ENODEV) {
- nv_error(clk, "failed to raise voltage: %d\n", ret);
- return ret;
+ if (volt) {
+ ret = volt->set_id(volt, cstate->voltage, +1);
+ if (ret && ret != -ENODEV) {
+ nv_error(clk, "failed to raise voltage: %d\n", ret);
+ return ret;
+ }
}
ret = clk->calc(clk, cstate);
@@ -108,13 +112,17 @@ nouveau_cstate_prog(struct nouveau_clock *clk,
clk->tidy(clk);
}
- ret = volt->set_id(volt, cstate->voltage, -1);
- if (ret && ret != -ENODEV)
- nv_error(clk, "failed to lower voltage: %d\n", ret);
+ if (volt) {
+ ret = volt->set_id(volt, cstate->voltage, -1);
+ if (ret && ret != -ENODEV)
+ nv_error(clk, "failed to lower voltage: %d\n", ret);
+ }
- ret = nouveau_therm_cstate(ptherm, pstate->fanspeed, -1);
- if (ret && ret != -ENODEV)
- nv_error(clk, "failed to lower fan speed: %d\n", ret);
+ if (ptherm) {
+ ret = nouveau_therm_cstate(ptherm, pstate->fanspeed, -1);
+ if (ret && ret != -ENODEV)
+ nv_error(clk, "failed to lower fan speed: %d\n", ret);
+ }
return 0;
}
@@ -194,16 +202,23 @@ nouveau_pstate_prog(struct nouveau_clock *clk, int pstatei)
return nouveau_cstate_prog(clk, pstate, 0);
}
-static int
-nouveau_pstate_calc(struct nouveau_clock *clk)
+static void
+nouveau_pstate_work(struct work_struct *work)
{
- int pstate, ret = 0;
+ struct nouveau_clock *clk = container_of(work, typeof(*clk), work);
+ int pstate;
- nv_trace(clk, "P %d U %d A %d T %d D %d\n", clk->pstate,
- clk->ustate, clk->astate, clk->tstate, clk->dstate);
+ if (!atomic_xchg(&clk->waiting, 0))
+ return;
+ clk->pwrsrc = power_supply_is_system_supplied();
- if (clk->state_nr && clk->ustate != -1) {
- pstate = (clk->ustate < 0) ? clk->astate : clk->ustate;
+ nv_trace(clk, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d D %d\n",
+ clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc,
+ clk->astate, clk->tstate, clk->dstate);
+
+ pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc;
+ if (clk->state_nr && pstate != -1) {
+ pstate = (pstate < 0) ? clk->astate : pstate;
pstate = min(pstate, clk->state_nr - 1 - clk->tstate);
pstate = max(pstate, clk->dstate);
} else {
@@ -211,9 +226,26 @@ nouveau_pstate_calc(struct nouveau_clock *clk)
}
nv_trace(clk, "-> %d\n", pstate);
- if (pstate != clk->pstate)
- ret = nouveau_pstate_prog(clk, pstate);
- return ret;
+ if (pstate != clk->pstate) {
+ int ret = nouveau_pstate_prog(clk, pstate);
+ if (ret) {
+ nv_error(clk, "error setting pstate %d: %d\n",
+ pstate, ret);
+ }
+ }
+
+ wake_up_all(&clk->wait);
+ nvkm_notify_get(&clk->pwrsrc_ntfy);
+}
+
+static int
+nouveau_pstate_calc(struct nouveau_clock *clk, bool wait)
+{
+ atomic_set(&clk->waiting, 1);
+ schedule_work(&clk->work);
+ if (wait)
+ wait_event(clk->wait, !atomic_read(&clk->waiting));
+ return 0;
}
static void
@@ -361,17 +393,40 @@ nouveau_clock_ustate_update(struct nouveau_clock *clk, int req)
req = i;
}
- clk->ustate = req;
- return 0;
+ return req + 2;
+}
+
+static int
+nouveau_clock_nstate(struct nouveau_clock *clk, const char *mode, int arglen)
+{
+ int ret = 1;
+
+ if (strncasecmpz(mode, "disabled", arglen)) {
+ char save = mode[arglen];
+ long v;
+
+ ((char *)mode)[arglen] = '\0';
+ if (!kstrtol(mode, 0, &v)) {
+ ret = nouveau_clock_ustate_update(clk, v);
+ if (ret < 0)
+ ret = 1;
+ }
+ ((char *)mode)[arglen] = save;
+ }
+
+ return ret - 2;
}
int
-nouveau_clock_ustate(struct nouveau_clock *clk, int req)
+nouveau_clock_ustate(struct nouveau_clock *clk, int req, int pwr)
{
int ret = nouveau_clock_ustate_update(clk, req);
- if (ret)
- return ret;
- return nouveau_pstate_calc(clk);
+ if (ret >= 0) {
+ if (ret -= 2, pwr) clk->ustate_ac = ret;
+ else clk->ustate_dc = ret;
+ return nouveau_pstate_calc(clk, true);
+ }
+ return ret;
}
int
@@ -381,7 +436,7 @@ nouveau_clock_astate(struct nouveau_clock *clk, int req, int rel)
if ( rel) clk->astate += rel;
clk->astate = min(clk->astate, clk->state_nr - 1);
clk->astate = max(clk->astate, 0);
- return nouveau_pstate_calc(clk);
+ return nouveau_pstate_calc(clk, true);
}
int
@@ -391,7 +446,7 @@ nouveau_clock_tstate(struct nouveau_clock *clk, int req, int rel)
if ( rel) clk->tstate += rel;
clk->tstate = min(clk->tstate, 0);
clk->tstate = max(clk->tstate, -(clk->state_nr - 1));
- return nouveau_pstate_calc(clk);
+ return nouveau_pstate_calc(clk, true);
}
int
@@ -401,12 +456,30 @@ nouveau_clock_dstate(struct nouveau_clock *clk, int req, int rel)
if ( rel) clk->dstate += rel;
clk->dstate = min(clk->dstate, clk->state_nr - 1);
clk->dstate = max(clk->dstate, 0);
- return nouveau_pstate_calc(clk);
+ return nouveau_pstate_calc(clk, true);
+}
+
+static int
+nouveau_clock_pwrsrc(struct nvkm_notify *notify)
+{
+ struct nouveau_clock *clk =
+ container_of(notify, typeof(*clk), pwrsrc_ntfy);
+ nouveau_pstate_calc(clk, false);
+ return NVKM_NOTIFY_DROP;
}
/******************************************************************************
* subdev base class implementation
*****************************************************************************/
+
+int
+_nouveau_clock_fini(struct nouveau_object *object, bool suspend)
+{
+ struct nouveau_clock *clk = (void *)object;
+ nvkm_notify_put(&clk->pwrsrc_ntfy);
+ return nouveau_subdev_fini(&clk->base, suspend);
+}
+
int
_nouveau_clock_init(struct nouveau_object *object)
{
@@ -414,6 +487,10 @@ _nouveau_clock_init(struct nouveau_object *object)
struct nouveau_clocks *clock = clk->domains;
int ret;
+ ret = nouveau_subdev_init(&clk->base);
+ if (ret)
+ return ret;
+
memset(&clk->bstate, 0x00, sizeof(clk->bstate));
INIT_LIST_HEAD(&clk->bstate.list);
clk->bstate.pstate = 0xff;
@@ -434,7 +511,7 @@ _nouveau_clock_init(struct nouveau_object *object)
clk->tstate = 0;
clk->dstate = 0;
clk->pstate = -1;
- nouveau_pstate_calc(clk);
+ nouveau_pstate_calc(clk, true);
return 0;
}
@@ -444,6 +521,8 @@ _nouveau_clock_dtor(struct nouveau_object *object)
struct nouveau_clock *clk = (void *)object;
struct nouveau_pstate *pstate, *temp;
+ nvkm_notify_fini(&clk->pwrsrc_ntfy);
+
list_for_each_entry_safe(pstate, temp, &clk->states, head) {
nouveau_pstate_del(pstate);
}
@@ -456,6 +535,7 @@ nouveau_clock_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass,
struct nouveau_clocks *clocks,
+ struct nouveau_pstate *pstates, int nb_pstates,
bool allow_reclock,
int length, void **object)
{
@@ -472,29 +552,46 @@ nouveau_clock_create_(struct nouveau_object *parent,
INIT_LIST_HEAD(&clk->states);
clk->domains = clocks;
- clk->ustate = -1;
+ clk->ustate_ac = -1;
+ clk->ustate_dc = -1;
+
+ INIT_WORK(&clk->work, nouveau_pstate_work);
+ init_waitqueue_head(&clk->wait);
+ atomic_set(&clk->waiting, 0);
- idx = 0;
- do {
- ret = nouveau_pstate_new(clk, idx++);
- } while (ret == 0);
+ /* If no pstates are provided, try and fetch them from the BIOS */
+ if (!pstates) {
+ idx = 0;
+ do {
+ ret = nouveau_pstate_new(clk, idx++);
+ } while (ret == 0);
+ } else {
+ for (idx = 0; idx < nb_pstates; idx++)
+ list_add_tail(&pstates[idx].head, &clk->states);
+ clk->state_nr = nb_pstates;
+ }
clk->allow_reclock = allow_reclock;
+ ret = nvkm_notify_init(NULL, &device->event, nouveau_clock_pwrsrc, true,
+ NULL, 0, 0, &clk->pwrsrc_ntfy);
+ if (ret)
+ return ret;
+
mode = nouveau_stropt(device->cfgopt, "NvClkMode", &arglen);
if (mode) {
- if (!strncasecmpz(mode, "disabled", arglen)) {
- clk->ustate = -1;
- } else {
- char save = mode[arglen];
- long v;
-
- ((char *)mode)[arglen] = '\0';
- if (!kstrtol(mode, 0, &v))
- nouveau_clock_ustate_update(clk, v);
- ((char *)mode)[arglen] = save;
- }
+ clk->ustate_ac = nouveau_clock_nstate(clk, mode, arglen);
+ clk->ustate_dc = nouveau_clock_nstate(clk, mode, arglen);
}
+ mode = nouveau_stropt(device->cfgopt, "NvClkModeAC", &arglen);
+ if (mode)
+ clk->ustate_ac = nouveau_clock_nstate(clk, mode, arglen);
+
+ mode = nouveau_stropt(device->cfgopt, "NvClkModeDC", &arglen);
+ if (mode)
+ clk->ustate_dc = nouveau_clock_nstate(clk, mode, arglen);
+
+
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/gk20a.c b/drivers/gpu/drm/nouveau/core/subdev/clock/gk20a.c
new file mode 100644
index 000000000000..425a8d5e9129
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/gk20a.c
@@ -0,0 +1,665 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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.
+ *
+ * Shamelessly ripped off from ChromeOS's gk20a/clk_pllg.c
+ *
+ */
+
+#define MHZ (1000 * 1000)
+
+#define MASK(w) ((1 << w) - 1)
+
+#define SYS_GPCPLL_CFG_BASE 0x00137000
+#define GPC_BCASE_GPCPLL_CFG_BASE 0x00132800
+
+#define GPCPLL_CFG (SYS_GPCPLL_CFG_BASE + 0)
+#define GPCPLL_CFG_ENABLE BIT(0)
+#define GPCPLL_CFG_IDDQ BIT(1)
+#define GPCPLL_CFG_LOCK_DET_OFF BIT(4)
+#define GPCPLL_CFG_LOCK BIT(17)
+
+#define GPCPLL_COEFF (SYS_GPCPLL_CFG_BASE + 4)
+#define GPCPLL_COEFF_M_SHIFT 0
+#define GPCPLL_COEFF_M_WIDTH 8
+#define GPCPLL_COEFF_N_SHIFT 8
+#define GPCPLL_COEFF_N_WIDTH 8
+#define GPCPLL_COEFF_P_SHIFT 16
+#define GPCPLL_COEFF_P_WIDTH 6
+
+#define GPCPLL_CFG2 (SYS_GPCPLL_CFG_BASE + 0xc)
+#define GPCPLL_CFG2_SETUP2_SHIFT 16
+#define GPCPLL_CFG2_PLL_STEPA_SHIFT 24
+
+#define GPCPLL_CFG3 (SYS_GPCPLL_CFG_BASE + 0x18)
+#define GPCPLL_CFG3_PLL_STEPB_SHIFT 16
+
+#define GPCPLL_NDIV_SLOWDOWN (SYS_GPCPLL_CFG_BASE + 0x1c)
+#define GPCPLL_NDIV_SLOWDOWN_NDIV_LO_SHIFT 0
+#define GPCPLL_NDIV_SLOWDOWN_NDIV_MID_SHIFT 8
+#define GPCPLL_NDIV_SLOWDOWN_STEP_SIZE_LO2MID_SHIFT 16
+#define GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT 22
+#define GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT 31
+
+#define SEL_VCO (SYS_GPCPLL_CFG_BASE + 0x100)
+#define SEL_VCO_GPC2CLK_OUT_SHIFT 0
+
+#define GPC2CLK_OUT (SYS_GPCPLL_CFG_BASE + 0x250)
+#define GPC2CLK_OUT_SDIV14_INDIV4_WIDTH 1
+#define GPC2CLK_OUT_SDIV14_INDIV4_SHIFT 31
+#define GPC2CLK_OUT_SDIV14_INDIV4_MODE 1
+#define GPC2CLK_OUT_VCODIV_WIDTH 6
+#define GPC2CLK_OUT_VCODIV_SHIFT 8
+#define GPC2CLK_OUT_VCODIV1 0
+#define GPC2CLK_OUT_VCODIV_MASK (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << \
+ GPC2CLK_OUT_VCODIV_SHIFT)
+#define GPC2CLK_OUT_BYPDIV_WIDTH 6
+#define GPC2CLK_OUT_BYPDIV_SHIFT 0
+#define GPC2CLK_OUT_BYPDIV31 0x3c
+#define GPC2CLK_OUT_INIT_MASK ((MASK(GPC2CLK_OUT_SDIV14_INDIV4_WIDTH) << \
+ GPC2CLK_OUT_SDIV14_INDIV4_SHIFT)\
+ | (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << GPC2CLK_OUT_VCODIV_SHIFT)\
+ | (MASK(GPC2CLK_OUT_BYPDIV_WIDTH) << GPC2CLK_OUT_BYPDIV_SHIFT))
+#define GPC2CLK_OUT_INIT_VAL ((GPC2CLK_OUT_SDIV14_INDIV4_MODE << \
+ GPC2CLK_OUT_SDIV14_INDIV4_SHIFT) \
+ | (GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT) \
+ | (GPC2CLK_OUT_BYPDIV31 << GPC2CLK_OUT_BYPDIV_SHIFT))
+
+#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG (GPC_BCASE_GPCPLL_CFG_BASE + 0xa0)
+#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT 24
+#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK \
+ (0x1 << GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT)
+
+#include <subdev/clock.h>
+#include <subdev/timer.h>
+
+#ifdef __KERNEL__
+#include <nouveau_platform.h>
+#endif
+
+static const u8 pl_to_div[] = {
+/* PL: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */
+/* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32,
+};
+
+/* All frequencies in Mhz */
+struct gk20a_clk_pllg_params {
+ u32 min_vco, max_vco;
+ u32 min_u, max_u;
+ u32 min_m, max_m;
+ u32 min_n, max_n;
+ u32 min_pl, max_pl;
+};
+
+static const struct gk20a_clk_pllg_params gk20a_pllg_params = {
+ .min_vco = 1000, .max_vco = 1700,
+ .min_u = 12, .max_u = 38,
+ .min_m = 1, .max_m = 255,
+ .min_n = 8, .max_n = 255,
+ .min_pl = 1, .max_pl = 32,
+};
+
+struct gk20a_clock_priv {
+ struct nouveau_clock base;
+ const struct gk20a_clk_pllg_params *params;
+ u32 m, n, pl;
+ u32 parent_rate;
+};
+#define to_gk20a_clock(base) container_of(base, struct gk20a_clock_priv, base)
+
+static void
+gk20a_pllg_read_mnp(struct gk20a_clock_priv *priv)
+{
+ u32 val;
+
+ val = nv_rd32(priv, GPCPLL_COEFF);
+ priv->m = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH);
+ priv->n = (val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH);
+ priv->pl = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH);
+}
+
+static u32
+gk20a_pllg_calc_rate(struct gk20a_clock_priv *priv)
+{
+ u32 rate;
+ u32 divider;
+
+ rate = priv->parent_rate * priv->n;
+ divider = priv->m * pl_to_div[priv->pl];
+ do_div(rate, divider);
+
+ return rate / 2;
+}
+
+static int
+gk20a_pllg_calc_mnp(struct gk20a_clock_priv *priv, unsigned long rate)
+{
+ u32 target_clk_f, ref_clk_f, target_freq;
+ u32 min_vco_f, max_vco_f;
+ u32 low_pl, high_pl, best_pl;
+ u32 target_vco_f, vco_f;
+ u32 best_m, best_n;
+ u32 u_f;
+ u32 m, n, n2;
+ u32 delta, lwv, best_delta = ~0;
+ u32 pl;
+
+ target_clk_f = rate * 2 / MHZ;
+ ref_clk_f = priv->parent_rate / MHZ;
+
+ max_vco_f = priv->params->max_vco;
+ min_vco_f = priv->params->min_vco;
+ best_m = priv->params->max_m;
+ best_n = priv->params->min_n;
+ best_pl = priv->params->min_pl;
+
+ target_vco_f = target_clk_f + target_clk_f / 50;
+ if (max_vco_f < target_vco_f)
+ max_vco_f = target_vco_f;
+
+ /* min_pl <= high_pl <= max_pl */
+ high_pl = (max_vco_f + target_vco_f - 1) / target_vco_f;
+ high_pl = min(high_pl, priv->params->max_pl);
+ high_pl = max(high_pl, priv->params->min_pl);
+
+ /* min_pl <= low_pl <= max_pl */
+ low_pl = min_vco_f / target_vco_f;
+ low_pl = min(low_pl, priv->params->max_pl);
+ low_pl = max(low_pl, priv->params->min_pl);
+
+ /* Find Indices of high_pl and low_pl */
+ for (pl = 0; pl < ARRAY_SIZE(pl_to_div) - 1; pl++) {
+ if (pl_to_div[pl] >= low_pl) {
+ low_pl = pl;
+ break;
+ }
+ }
+ for (pl = 0; pl < ARRAY_SIZE(pl_to_div) - 1; pl++) {
+ if (pl_to_div[pl] >= high_pl) {
+ high_pl = pl;
+ break;
+ }
+ }
+
+ nv_debug(priv, "low_PL %d(div%d), high_PL %d(div%d)", low_pl,
+ pl_to_div[low_pl], high_pl, pl_to_div[high_pl]);
+
+ /* Select lowest possible VCO */
+ for (pl = low_pl; pl <= high_pl; pl++) {
+ target_vco_f = target_clk_f * pl_to_div[pl];
+ for (m = priv->params->min_m; m <= priv->params->max_m; m++) {
+ u_f = ref_clk_f / m;
+
+ if (u_f < priv->params->min_u)
+ break;
+ if (u_f > priv->params->max_u)
+ continue;
+
+ n = (target_vco_f * m) / ref_clk_f;
+ n2 = ((target_vco_f * m) + (ref_clk_f - 1)) / ref_clk_f;
+
+ if (n > priv->params->max_n)
+ break;
+
+ for (; n <= n2; n++) {
+ if (n < priv->params->min_n)
+ continue;
+ if (n > priv->params->max_n)
+ break;
+
+ vco_f = ref_clk_f * n / m;
+
+ if (vco_f >= min_vco_f && vco_f <= max_vco_f) {
+ lwv = (vco_f + (pl_to_div[pl] / 2))
+ / pl_to_div[pl];
+ delta = abs(lwv - target_clk_f);
+
+ if (delta < best_delta) {
+ best_delta = delta;
+ best_m = m;
+ best_n = n;
+ best_pl = pl;
+
+ if (best_delta == 0)
+ goto found_match;
+ }
+ }
+ }
+ }
+ }
+
+found_match:
+ WARN_ON(best_delta == ~0);
+
+ if (best_delta != 0)
+ nv_debug(priv, "no best match for target @ %dMHz on gpc_pll",
+ target_clk_f);
+
+ priv->m = best_m;
+ priv->n = best_n;
+ priv->pl = best_pl;
+
+ target_freq = gk20a_pllg_calc_rate(priv) / MHZ;
+
+ nv_debug(priv, "actual target freq %d MHz, M %d, N %d, PL %d(div%d)\n",
+ target_freq, priv->m, priv->n, priv->pl, pl_to_div[priv->pl]);
+
+ return 0;
+}
+
+static int
+gk20a_pllg_slide(struct gk20a_clock_priv *priv, u32 n)
+{
+ u32 val;
+ int ramp_timeout;
+
+ /* get old coefficients */
+ val = nv_rd32(priv, GPCPLL_COEFF);
+ /* do nothing if NDIV is the same */
+ if (n == ((val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH)))
+ return 0;
+
+ /* setup */
+ nv_mask(priv, GPCPLL_CFG2, 0xff << GPCPLL_CFG2_PLL_STEPA_SHIFT,
+ 0x2b << GPCPLL_CFG2_PLL_STEPA_SHIFT);
+ nv_mask(priv, GPCPLL_CFG3, 0xff << GPCPLL_CFG3_PLL_STEPB_SHIFT,
+ 0xb << GPCPLL_CFG3_PLL_STEPB_SHIFT);
+
+ /* pll slowdown mode */
+ nv_mask(priv, GPCPLL_NDIV_SLOWDOWN,
+ BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT),
+ BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT));
+
+ /* new ndiv ready for ramp */
+ val = nv_rd32(priv, GPCPLL_COEFF);
+ val &= ~(MASK(GPCPLL_COEFF_N_WIDTH) << GPCPLL_COEFF_N_SHIFT);
+ val |= (n & MASK(GPCPLL_COEFF_N_WIDTH)) << GPCPLL_COEFF_N_SHIFT;
+ udelay(1);
+ nv_wr32(priv, GPCPLL_COEFF, val);
+
+ /* dynamic ramp to new ndiv */
+ val = nv_rd32(priv, GPCPLL_NDIV_SLOWDOWN);
+ val |= 0x1 << GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT;
+ udelay(1);
+ nv_wr32(priv, GPCPLL_NDIV_SLOWDOWN, val);
+
+ for (ramp_timeout = 500; ramp_timeout > 0; ramp_timeout--) {
+ udelay(1);
+ val = nv_rd32(priv, GPC_BCAST_NDIV_SLOWDOWN_DEBUG);
+ if (val & GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK)
+ break;
+ }
+
+ /* exit slowdown mode */
+ nv_mask(priv, GPCPLL_NDIV_SLOWDOWN,
+ BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT) |
+ BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), 0);
+ nv_rd32(priv, GPCPLL_NDIV_SLOWDOWN);
+
+ if (ramp_timeout <= 0) {
+ nv_error(priv, "gpcpll dynamic ramp timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void
+_gk20a_pllg_enable(struct gk20a_clock_priv *priv)
+{
+ nv_mask(priv, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE);
+ nv_rd32(priv, GPCPLL_CFG);
+}
+
+static void
+_gk20a_pllg_disable(struct gk20a_clock_priv *priv)
+{
+ nv_mask(priv, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0);
+ nv_rd32(priv, GPCPLL_CFG);
+}
+
+static int
+_gk20a_pllg_program_mnp(struct gk20a_clock_priv *priv, bool allow_slide)
+{
+ u32 val, cfg;
+ u32 m_old, pl_old, n_lo;
+
+ /* get old coefficients */
+ val = nv_rd32(priv, GPCPLL_COEFF);
+ m_old = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH);
+ pl_old = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH);
+
+ /* do NDIV slide if there is no change in M and PL */
+ cfg = nv_rd32(priv, GPCPLL_CFG);
+ if (allow_slide && priv->m == m_old && priv->pl == pl_old &&
+ (cfg & GPCPLL_CFG_ENABLE)) {
+ return gk20a_pllg_slide(priv, priv->n);
+ }
+
+ /* slide down to NDIV_LO */
+ n_lo = DIV_ROUND_UP(m_old * priv->params->min_vco,
+ priv->parent_rate / MHZ);
+ if (allow_slide && (cfg & GPCPLL_CFG_ENABLE)) {
+ int ret = gk20a_pllg_slide(priv, n_lo);
+
+ if (ret)
+ return ret;
+ }
+
+ /* split FO-to-bypass jump in halfs by setting out divider 1:2 */
+ nv_mask(priv, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
+ 0x2 << GPC2CLK_OUT_VCODIV_SHIFT);
+
+ /* put PLL in bypass before programming it */
+ val = nv_rd32(priv, SEL_VCO);
+ val &= ~(BIT(SEL_VCO_GPC2CLK_OUT_SHIFT));
+ udelay(2);
+ nv_wr32(priv, SEL_VCO, val);
+
+ /* get out from IDDQ */
+ val = nv_rd32(priv, GPCPLL_CFG);
+ if (val & GPCPLL_CFG_IDDQ) {
+ val &= ~GPCPLL_CFG_IDDQ;
+ nv_wr32(priv, GPCPLL_CFG, val);
+ nv_rd32(priv, GPCPLL_CFG);
+ udelay(2);
+ }
+
+ _gk20a_pllg_disable(priv);
+
+ nv_debug(priv, "%s: m=%d n=%d pl=%d\n", __func__, priv->m, priv->n,
+ priv->pl);
+
+ n_lo = DIV_ROUND_UP(priv->m * priv->params->min_vco,
+ priv->parent_rate / MHZ);
+ val = priv->m << GPCPLL_COEFF_M_SHIFT;
+ val |= (allow_slide ? n_lo : priv->n) << GPCPLL_COEFF_N_SHIFT;
+ val |= priv->pl << GPCPLL_COEFF_P_SHIFT;
+ nv_wr32(priv, GPCPLL_COEFF, val);
+
+ _gk20a_pllg_enable(priv);
+
+ val = nv_rd32(priv, GPCPLL_CFG);
+ if (val & GPCPLL_CFG_LOCK_DET_OFF) {
+ val &= ~GPCPLL_CFG_LOCK_DET_OFF;
+ nv_wr32(priv, GPCPLL_CFG, val);
+ }
+
+ if (!nouveau_timer_wait_eq(priv, 300000, GPCPLL_CFG, GPCPLL_CFG_LOCK,
+ GPCPLL_CFG_LOCK)) {
+ nv_error(priv, "%s: timeout waiting for pllg lock\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ /* switch to VCO mode */
+ nv_mask(priv, SEL_VCO, 0, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT));
+
+ /* restore out divider 1:1 */
+ val = nv_rd32(priv, GPC2CLK_OUT);
+ val &= ~GPC2CLK_OUT_VCODIV_MASK;
+ udelay(2);
+ nv_wr32(priv, GPC2CLK_OUT, val);
+
+ /* slide up to new NDIV */
+ return allow_slide ? gk20a_pllg_slide(priv, priv->n) : 0;
+}
+
+static int
+gk20a_pllg_program_mnp(struct gk20a_clock_priv *priv)
+{
+ int err;
+
+ err = _gk20a_pllg_program_mnp(priv, true);
+ if (err)
+ err = _gk20a_pllg_program_mnp(priv, false);
+
+ return err;
+}
+
+static void
+gk20a_pllg_disable(struct gk20a_clock_priv *priv)
+{
+ u32 val;
+
+ /* slide to VCO min */
+ val = nv_rd32(priv, GPCPLL_CFG);
+ if (val & GPCPLL_CFG_ENABLE) {
+ u32 coeff, m, n_lo;
+
+ coeff = nv_rd32(priv, GPCPLL_COEFF);
+ m = (coeff >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH);
+ n_lo = DIV_ROUND_UP(m * priv->params->min_vco,
+ priv->parent_rate / MHZ);
+ gk20a_pllg_slide(priv, n_lo);
+ }
+
+ /* put PLL in bypass before disabling it */
+ nv_mask(priv, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0);
+
+ _gk20a_pllg_disable(priv);
+}
+
+#define GK20A_CLK_GPC_MDIV 1000
+
+static struct nouveau_clocks
+gk20a_domains[] = {
+ { nv_clk_src_crystal, 0xff },
+ { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV },
+ { nv_clk_src_max }
+};
+
+static struct nouveau_pstate
+gk20a_pstates[] = {
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 72000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 108000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 180000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 252000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 324000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 396000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 468000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 540000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 612000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 648000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 684000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 708000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 756000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 804000,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 852000,
+ },
+ },
+};
+
+static int
+gk20a_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
+{
+ struct gk20a_clock_priv *priv = (void *)clk;
+
+ switch (src) {
+ case nv_clk_src_crystal:
+ return nv_device(clk)->crystal;
+ case nv_clk_src_gpc:
+ gk20a_pllg_read_mnp(priv);
+ return gk20a_pllg_calc_rate(priv) / GK20A_CLK_GPC_MDIV;
+ default:
+ nv_error(clk, "invalid clock source %d\n", src);
+ return -EINVAL;
+ }
+}
+
+static int
+gk20a_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate)
+{
+ struct gk20a_clock_priv *priv = (void *)clk;
+
+ return gk20a_pllg_calc_mnp(priv, cstate->domain[nv_clk_src_gpc] *
+ GK20A_CLK_GPC_MDIV);
+}
+
+static int
+gk20a_clock_prog(struct nouveau_clock *clk)
+{
+ struct gk20a_clock_priv *priv = (void *)clk;
+
+ return gk20a_pllg_program_mnp(priv);
+}
+
+static void
+gk20a_clock_tidy(struct nouveau_clock *clk)
+{
+}
+
+static int
+gk20a_clock_fini(struct nouveau_object *object, bool suspend)
+{
+ struct gk20a_clock_priv *priv = (void *)object;
+ int ret;
+
+ ret = nouveau_clock_fini(&priv->base, false);
+
+ gk20a_pllg_disable(priv);
+
+ return ret;
+}
+
+static int
+gk20a_clock_init(struct nouveau_object *object)
+{
+ struct gk20a_clock_priv *priv = (void *)object;
+ int ret;
+
+ nv_mask(priv, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK, GPC2CLK_OUT_INIT_VAL);
+
+ ret = nouveau_clock_init(&priv->base);
+ if (ret)
+ return ret;
+
+ ret = gk20a_clock_prog(&priv->base);
+ if (ret) {
+ nv_error(priv, "cannot initialize clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+gk20a_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct gk20a_clock_priv *priv;
+ struct nouveau_platform_device *plat;
+ int ret;
+ int i;
+
+ /* Finish initializing the pstates */
+ for (i = 0; i < ARRAY_SIZE(gk20a_pstates); i++) {
+ INIT_LIST_HEAD(&gk20a_pstates[i].list);
+ gk20a_pstates[i].pstate = i + 1;
+ }
+
+ ret = nouveau_clock_create(parent, engine, oclass, gk20a_domains,
+ gk20a_pstates, ARRAY_SIZE(gk20a_pstates), true, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->params = &gk20a_pllg_params;
+
+ plat = nv_device_to_platform(nv_device(parent));
+ priv->parent_rate = clk_get_rate(plat->gpu->clk);
+ nv_info(priv, "parent clock rate: %d Mhz\n", priv->parent_rate / MHZ);
+
+ priv->base.read = gk20a_clock_read;
+ priv->base.calc = gk20a_clock_calc;
+ priv->base.prog = gk20a_clock_prog;
+ priv->base.tidy = gk20a_clock_tidy;
+
+ return 0;
+}
+
+struct nouveau_oclass
+gk20a_clock_oclass = {
+ .handle = NV_SUBDEV(CLOCK, 0xea),
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gk20a_clock_ctor,
+ .dtor = _nouveau_subdev_dtor,
+ .init = gk20a_clock_init,
+ .fini = gk20a_clock_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c
index eb2d4425a49e..4c48232686be 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c
@@ -82,8 +82,8 @@ nv04_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nv04_clock_priv *priv;
int ret;
- ret = nouveau_clock_create(parent, engine, oclass, nv04_domain, false,
- &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nv04_domain, NULL, 0,
+ false, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c
index 8a9e16839791..08368fe97029 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c
@@ -213,8 +213,8 @@ nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nv40_clock_priv *priv;
int ret;
- ret = nouveau_clock_create(parent, engine, oclass, nv40_domain, true,
- &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nv40_domain, NULL, 0,
+ true, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c
index 8c132772ba9e..5070ebc260f8 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c
@@ -507,7 +507,7 @@ nv50_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
int ret;
ret = nouveau_clock_create(parent, engine, oclass, pclass->domains,
- false, &priv);
+ NULL, 0, false, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c
index 9fb58354a80b..094551d8ad9b 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c
@@ -20,8 +20,10 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
+ * Roy Spliet
*/
+#include <engine/fifo.h>
#include <subdev/bios.h>
#include <subdev/bios/pll.h>
#include <subdev/timer.h>
@@ -42,9 +44,17 @@ static u32
read_vco(struct nva3_clock_priv *priv, int clk)
{
u32 sctl = nv_rd32(priv, 0x4120 + (clk * 4));
- if ((sctl & 0x00000030) != 0x00000030)
+
+ switch (sctl & 0x00000030) {
+ case 0x00000000:
+ return nv_device(priv)->crystal;
+ case 0x00000020:
return read_pll(priv, 0x41, 0x00e820);
- return read_pll(priv, 0x42, 0x00e8a0);
+ case 0x00000030:
+ return read_pll(priv, 0x42, 0x00e8a0);
+ default:
+ return 0;
+ }
}
static u32
@@ -66,14 +76,25 @@ read_clk(struct nva3_clock_priv *priv, int clk, bool ignore_en)
if (!ignore_en && !(sctl & 0x00000100))
return 0;
+ /* out_alt */
+ if (sctl & 0x00000400)
+ return 108000;
+
+ /* vco_out */
switch (sctl & 0x00003000) {
case 0x00000000:
- return nv_device(priv)->crystal;
+ if (!(sctl & 0x00000200))
+ return nv_device(priv)->crystal;
+ return 0;
case 0x00002000:
if (sctl & 0x00000040)
return 108000;
return 100000;
case 0x00003000:
+ /* vco_enable */
+ if (!(sctl & 0x00000001))
+ return 0;
+
sclk = read_vco(priv, clk);
sdiv = ((sctl & 0x003f0000) >> 16) + 2;
return (sclk * 2) / sdiv;
@@ -95,7 +116,9 @@ read_pll(struct nva3_clock_priv *priv, int clk, u32 pll)
N = (coef & 0x0000ff00) >> 8;
P = (coef & 0x003f0000) >> 16;
- /* no post-divider on these.. */
+ /* no post-divider on these..
+ * XXX: it looks more like two post-"dividers" that
+ * cross each other out in the default RPLL config */
if ((pll & 0x00ff00) == 0x00e800)
P = 1;
@@ -114,13 +137,13 @@ static int
nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
{
struct nva3_clock_priv *priv = (void *)clk;
+ u32 hsrc;
switch (src) {
case nv_clk_src_crystal:
return nv_device(priv)->crystal;
- case nv_clk_src_href:
- return 100000;
case nv_clk_src_core:
+ case nv_clk_src_core_intm:
return read_pll(priv, 0x00, 0x4200);
case nv_clk_src_shader:
return read_pll(priv, 0x01, 0x4220);
@@ -132,24 +155,33 @@ nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
return read_clk(priv, 0x21, false);
case nv_clk_src_daemon:
return read_clk(priv, 0x25, false);
+ case nv_clk_src_host:
+ hsrc = (nv_rd32(priv, 0xc040) & 0x30000000) >> 28;
+ switch (hsrc) {
+ case 0:
+ return read_clk(priv, 0x1d, false);
+ case 2:
+ case 3:
+ return 277000;
+ default:
+ nv_error(clk, "unknown HOST clock source %d\n", hsrc);
+ return -EINVAL;
+ }
default:
nv_error(clk, "invalid clock source %d\n", src);
return -EINVAL;
}
+
+ return 0;
}
int
-nva3_clock_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz,
+nva3_clk_info(struct nouveau_clock *clock, int clk, u32 khz,
struct nva3_clock_info *info)
{
- struct nouveau_bios *bios = nouveau_bios(clock);
struct nva3_clock_priv *priv = (void *)clock;
- struct nvbios_pll limits;
- u32 oclk, sclk, sdiv;
- int P, N, M, diff;
- int ret;
+ u32 oclk, sclk, sdiv, diff;
- info->pll = 0;
info->clk = 0;
switch (khz) {
@@ -164,43 +196,69 @@ nva3_clock_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz,
return khz;
default:
sclk = read_vco(priv, clk);
- sdiv = min((sclk * 2) / (khz - 2999), (u32)65);
- /* if the clock has a PLL attached, and we can get a within
- * [-2, 3) MHz of a divider, we'll disable the PLL and use
- * the divider instead.
- *
- * divider can go as low as 2, limited here because NVIDIA
+ sdiv = min((sclk * 2) / khz, (u32)65);
+ oclk = (sclk * 2) / sdiv;
+ diff = ((khz + 3000) - oclk);
+
+ /* When imprecise, play it safe and aim for a clock lower than
+ * desired rather than higher */
+ if (diff < 0) {
+ sdiv++;
+ oclk = (sclk * 2) / sdiv;
+ }
+
+ /* divider can go as low as 2, limited here because NVIDIA
* and the VBIOS on my NVA8 seem to prefer using the PLL
* for 810MHz - is there a good reason?
- */
+ * XXX: PLLs with refclk 810MHz? */
if (sdiv > 4) {
- oclk = (sclk * 2) / sdiv;
- diff = khz - oclk;
- if (!pll || (diff >= -2000 && diff < 3000)) {
- info->clk = (((sdiv - 2) << 16) | 0x00003100);
- return oclk;
- }
+ info->clk = (((sdiv - 2) << 16) | 0x00003100);
+ return oclk;
}
- if (!pll)
- return -ERANGE;
break;
}
+ return -ERANGE;
+}
+
+int
+nva3_pll_info(struct nouveau_clock *clock, int clk, u32 pll, u32 khz,
+ struct nva3_clock_info *info)
+{
+ struct nouveau_bios *bios = nouveau_bios(clock);
+ struct nva3_clock_priv *priv = (void *)clock;
+ struct nvbios_pll limits;
+ int P, N, M, diff;
+ int ret;
+
+ info->pll = 0;
+
+ /* If we can get a within [-2, 3) MHz of a divider, we'll disable the
+ * PLL and use the divider instead. */
+ ret = nva3_clk_info(clock, clk, khz, info);
+ diff = khz - ret;
+ if (!pll || (diff >= -2000 && diff < 3000)) {
+ goto out;
+ }
+
+ /* Try with PLL */
ret = nvbios_pll_parse(bios, pll, &limits);
if (ret)
return ret;
- limits.refclk = read_clk(priv, clk - 0x10, true);
- if (!limits.refclk)
+ ret = nva3_clk_info(clock, clk - 0x10, limits.refclk, info);
+ if (ret != limits.refclk)
return -EINVAL;
ret = nva3_pll_calc(nv_subdev(priv), &limits, khz, &N, NULL, &M, &P);
if (ret >= 0) {
- info->clk = nv_rd32(priv, 0x4120 + (clk * 4));
info->pll = (P << 16) | (N << 8) | M;
}
+out:
+ info->fb_delay = max(((khz + 7566) / 15133), (u32) 18);
+
return ret ? ret : -ERANGE;
}
@@ -208,13 +266,76 @@ static int
calc_clk(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate,
int clk, u32 pll, int idx)
{
- int ret = nva3_clock_info(&priv->base, clk, pll, cstate->domain[idx],
+ int ret = nva3_pll_info(&priv->base, clk, pll, cstate->domain[idx],
&priv->eng[idx]);
if (ret >= 0)
return 0;
return ret;
}
+static int
+calc_host(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate)
+{
+ int ret = 0;
+ u32 kHz = cstate->domain[nv_clk_src_host];
+ struct nva3_clock_info *info = &priv->eng[nv_clk_src_host];
+
+ if (kHz == 277000) {
+ info->clk = 0;
+ info->host_out = NVA3_HOST_277;
+ return 0;
+ }
+
+ info->host_out = NVA3_HOST_CLK;
+
+ ret = nva3_clk_info(&priv->base, 0x1d, kHz, info);
+ if (ret >= 0)
+ return 0;
+ return ret;
+}
+
+int
+nva3_clock_pre(struct nouveau_clock *clk, unsigned long *flags)
+{
+ struct nouveau_fifo *pfifo = nouveau_fifo(clk);
+
+ /* halt and idle execution engines */
+ nv_mask(clk, 0x020060, 0x00070000, 0x00000000);
+ nv_mask(clk, 0x002504, 0x00000001, 0x00000001);
+ /* Wait until the interrupt handler is finished */
+ if (!nv_wait(clk, 0x000100, 0xffffffff, 0x00000000))
+ return -EBUSY;
+
+ if (pfifo)
+ pfifo->pause(pfifo, flags);
+
+ if (!nv_wait(clk, 0x002504, 0x00000010, 0x00000010))
+ return -EIO;
+ if (!nv_wait(clk, 0x00251c, 0x0000003f, 0x0000003f))
+ return -EIO;
+
+ return 0;
+}
+
+void
+nva3_clock_post(struct nouveau_clock *clk, unsigned long *flags)
+{
+ struct nouveau_fifo *pfifo = nouveau_fifo(clk);
+
+ if (pfifo && flags)
+ pfifo->start(pfifo, flags);
+
+ nv_mask(clk, 0x002504, 0x00000001, 0x00000000);
+ nv_mask(clk, 0x020060, 0x00070000, 0x00040000);
+}
+
+static void
+disable_clk_src(struct nva3_clock_priv *priv, u32 src)
+{
+ nv_mask(priv, src, 0x00000100, 0x00000000);
+ nv_mask(priv, src, 0x00000001, 0x00000000);
+}
+
static void
prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx)
{
@@ -223,24 +344,35 @@ prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx)
const u32 src1 = 0x004160 + (clk * 4);
const u32 ctrl = pll + 0;
const u32 coef = pll + 4;
+ u32 bypass;
if (info->pll) {
- nv_mask(priv, src0, 0x00000101, 0x00000101);
+ /* Always start from a non-PLL clock */
+ bypass = nv_rd32(priv, ctrl) & 0x00000008;
+ if (!bypass) {
+ nv_mask(priv, src1, 0x00000101, 0x00000101);
+ nv_mask(priv, ctrl, 0x00000008, 0x00000008);
+ udelay(20);
+ }
+
+ nv_mask(priv, src0, 0x003f3141, 0x00000101 | info->clk);
nv_wr32(priv, coef, info->pll);
nv_mask(priv, ctrl, 0x00000015, 0x00000015);
nv_mask(priv, ctrl, 0x00000010, 0x00000000);
- nv_wait(priv, ctrl, 0x00020000, 0x00020000);
+ if (!nv_wait(priv, ctrl, 0x00020000, 0x00020000)) {
+ nv_mask(priv, ctrl, 0x00000010, 0x00000010);
+ nv_mask(priv, src0, 0x00000101, 0x00000000);
+ return;
+ }
nv_mask(priv, ctrl, 0x00000010, 0x00000010);
nv_mask(priv, ctrl, 0x00000008, 0x00000000);
- nv_mask(priv, src1, 0x00000100, 0x00000000);
- nv_mask(priv, src1, 0x00000001, 0x00000000);
+ disable_clk_src(priv, src1);
} else {
nv_mask(priv, src1, 0x003f3141, 0x00000101 | info->clk);
nv_mask(priv, ctrl, 0x00000018, 0x00000018);
udelay(20);
nv_mask(priv, ctrl, 0x00000001, 0x00000000);
- nv_mask(priv, src0, 0x00000100, 0x00000000);
- nv_mask(priv, src0, 0x00000001, 0x00000000);
+ disable_clk_src(priv, src0);
}
}
@@ -251,18 +383,72 @@ prog_clk(struct nva3_clock_priv *priv, int clk, int idx)
nv_mask(priv, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | info->clk);
}
+static void
+prog_host(struct nva3_clock_priv *priv)
+{
+ struct nva3_clock_info *info = &priv->eng[nv_clk_src_host];
+ u32 hsrc = (nv_rd32(priv, 0xc040));
+
+ switch (info->host_out) {
+ case NVA3_HOST_277:
+ if ((hsrc & 0x30000000) == 0) {
+ nv_wr32(priv, 0xc040, hsrc | 0x20000000);
+ disable_clk_src(priv, 0x4194);
+ }
+ break;
+ case NVA3_HOST_CLK:
+ prog_clk(priv, 0x1d, nv_clk_src_host);
+ if ((hsrc & 0x30000000) >= 0x20000000) {
+ nv_wr32(priv, 0xc040, hsrc & ~0x30000000);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* This seems to be a clock gating factor on idle, always set to 64 */
+ nv_wr32(priv, 0xc044, 0x3e);
+}
+
+static void
+prog_core(struct nva3_clock_priv *priv, int idx)
+{
+ struct nva3_clock_info *info = &priv->eng[idx];
+ u32 fb_delay = nv_rd32(priv, 0x10002c);
+
+ if (fb_delay < info->fb_delay)
+ nv_wr32(priv, 0x10002c, info->fb_delay);
+
+ prog_pll(priv, 0x00, 0x004200, idx);
+
+ if (fb_delay > info->fb_delay)
+ nv_wr32(priv, 0x10002c, info->fb_delay);
+}
+
static int
nva3_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate)
{
struct nva3_clock_priv *priv = (void *)clk;
+ struct nva3_clock_info *core = &priv->eng[nv_clk_src_core];
int ret;
if ((ret = calc_clk(priv, cstate, 0x10, 0x4200, nv_clk_src_core)) ||
(ret = calc_clk(priv, cstate, 0x11, 0x4220, nv_clk_src_shader)) ||
(ret = calc_clk(priv, cstate, 0x20, 0x0000, nv_clk_src_disp)) ||
- (ret = calc_clk(priv, cstate, 0x21, 0x0000, nv_clk_src_vdec)))
+ (ret = calc_clk(priv, cstate, 0x21, 0x0000, nv_clk_src_vdec)) ||
+ (ret = calc_host(priv, cstate)))
return ret;
+ /* XXX: Should be reading the highest bit in the VBIOS clock to decide
+ * whether to use a PLL or not... but using a PLL defeats the purpose */
+ if (core->pll) {
+ ret = nva3_clk_info(clk, 0x10,
+ cstate->domain[nv_clk_src_core_intm],
+ &priv->eng[nv_clk_src_core_intm]);
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}
@@ -270,11 +456,31 @@ static int
nva3_clock_prog(struct nouveau_clock *clk)
{
struct nva3_clock_priv *priv = (void *)clk;
- prog_pll(priv, 0x00, 0x004200, nv_clk_src_core);
+ struct nva3_clock_info *core = &priv->eng[nv_clk_src_core];
+ int ret = 0;
+ unsigned long flags;
+ unsigned long *f = &flags;
+
+ ret = nva3_clock_pre(clk, f);
+ if (ret)
+ goto out;
+
+ if (core->pll)
+ prog_core(priv, nv_clk_src_core_intm);
+
+ prog_core(priv, nv_clk_src_core);
prog_pll(priv, 0x01, 0x004220, nv_clk_src_shader);
prog_clk(priv, 0x20, nv_clk_src_disp);
prog_clk(priv, 0x21, nv_clk_src_vdec);
- return 0;
+ prog_host(priv);
+
+out:
+ if (ret == -EBUSY)
+ f = NULL;
+
+ nva3_clock_post(clk, f);
+
+ return ret;
}
static void
@@ -284,13 +490,14 @@ nva3_clock_tidy(struct nouveau_clock *clk)
static struct nouveau_clocks
nva3_domain[] = {
- { nv_clk_src_crystal, 0xff },
- { nv_clk_src_href , 0xff },
- { nv_clk_src_core , 0x00, 0, "core", 1000 },
- { nv_clk_src_shader , 0x01, 0, "shader", 1000 },
- { nv_clk_src_mem , 0x02, 0, "memory", 1000 },
- { nv_clk_src_vdec , 0x03 },
- { nv_clk_src_disp , 0x04 },
+ { nv_clk_src_crystal , 0xff },
+ { nv_clk_src_core , 0x00, 0, "core", 1000 },
+ { nv_clk_src_shader , 0x01, 0, "shader", 1000 },
+ { nv_clk_src_mem , 0x02, 0, "memory", 1000 },
+ { nv_clk_src_vdec , 0x03 },
+ { nv_clk_src_disp , 0x04 },
+ { nv_clk_src_host , 0x05 },
+ { nv_clk_src_core_intm, 0x06 },
{ nv_clk_src_max }
};
@@ -302,8 +509,8 @@ nva3_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nva3_clock_priv *priv;
int ret;
- ret = nouveau_clock_create(parent, engine, oclass, nva3_domain, false,
- &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nva3_domain, NULL, 0,
+ false, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h
index 6229a509b42e..a45a1038b12f 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h
@@ -6,9 +6,15 @@
struct nva3_clock_info {
u32 clk;
u32 pll;
+ enum {
+ NVA3_HOST_277,
+ NVA3_HOST_CLK,
+ } host_out;
+ u32 fb_delay;
};
-int nva3_clock_info(struct nouveau_clock *, int, u32, u32,
+int nva3_pll_info(struct nouveau_clock *, int, u32, u32,
struct nva3_clock_info *);
-
+int nva3_clock_pre(struct nouveau_clock *clk, unsigned long *flags);
+void nva3_clock_post(struct nouveau_clock *clk, unsigned long *flags);
#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c
index 6a65fc9e9663..54aeab8005a0 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c
@@ -28,6 +28,7 @@
#include <subdev/timer.h>
#include <subdev/clock.h>
+#include "nva3.h"
#include "pll.h"
struct nvaa_clock_priv {
@@ -299,25 +300,14 @@ static int
nvaa_clock_prog(struct nouveau_clock *clk)
{
struct nvaa_clock_priv *priv = (void *)clk;
- struct nouveau_fifo *pfifo = nouveau_fifo(clk);
+ u32 pllmask = 0, mast;
unsigned long flags;
- u32 pllmask = 0, mast, ptherm_gate;
- int ret = -EBUSY;
-
- /* halt and idle execution engines */
- ptherm_gate = nv_mask(clk, 0x020060, 0x00070000, 0x00000000);
- nv_mask(clk, 0x002504, 0x00000001, 0x00000001);
- /* Wait until the interrupt handler is finished */
- if (!nv_wait(clk, 0x000100, 0xffffffff, 0x00000000))
- goto resume;
-
- if (pfifo)
- pfifo->pause(pfifo, &flags);
+ unsigned long *f = &flags;
+ int ret = 0;
- if (!nv_wait(clk, 0x002504, 0x00000010, 0x00000010))
- goto resume;
- if (!nv_wait(clk, 0x00251c, 0x0000003f, 0x0000003f))
- goto resume;
+ ret = nva3_clock_pre(clk, f);
+ if (ret)
+ goto out;
/* First switch to safe clocks: href */
mast = nv_mask(clk, 0xc054, 0x03400e70, 0x03400640);
@@ -375,15 +365,8 @@ nvaa_clock_prog(struct nouveau_clock *clk)
}
nv_wr32(clk, 0xc054, mast);
- ret = 0;
resume:
- if (pfifo)
- pfifo->start(pfifo, &flags);
-
- nv_mask(clk, 0x002504, 0x00000001, 0x00000000);
- nv_wr32(clk, 0x020060, ptherm_gate);
-
/* Disable some PLLs and dividers when unused */
if (priv->csrc != nv_clk_src_core) {
nv_wr32(clk, 0x4040, 0x00000000);
@@ -395,6 +378,12 @@ resume:
nv_mask(clk, 0x4020, 0x80000000, 0x00000000);
}
+out:
+ if (ret == -EBUSY)
+ f = NULL;
+
+ nva3_clock_post(clk, f);
+
return ret;
}
@@ -421,8 +410,8 @@ nvaa_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nvaa_clock_priv *priv;
int ret;
- ret = nouveau_clock_create(parent, engine, oclass, nvaa_domains, true,
- &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nvaa_domains, NULL,
+ 0, true, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
index dbf8517f54da..1234abaab2db 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
@@ -437,8 +437,8 @@ nvc0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nvc0_clock_priv *priv;
int ret;
- ret = nouveau_clock_create(parent, engine, oclass, nvc0_domain, false,
- &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nvc0_domain, NULL, 0,
+ false, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c
index 0e62a3240144..7eccad57512e 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c
@@ -475,8 +475,8 @@ nve0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nve0_clock_priv *priv;
int ret;
- ret = nouveau_clock_create(parent, engine, oclass, nve0_domain, true,
- &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nve0_domain, NULL, 0,
+ true, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h b/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h
index 4fe49cf4c99a..6103484fea72 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/fbmem.h
@@ -26,22 +26,8 @@
#include <core/device.h>
-#define NV04_PFB_BOOT_0 0x00100000
-# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003
-# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000
-# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001
-# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002
-# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003
-# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004
-# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028
-# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000
-# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008
-# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010
-# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018
-# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020
-# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028
-# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100
-# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000
+#include <subdev/fb/regsnv04.h>
+
#define NV04_PFB_DEBUG_0 0x00100080
# define NV04_PFB_DEBUG_0_PAGE_MODE 0x00000001
# define NV04_PFB_DEBUG_0_REFRESH_OFF 0x00000010
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c b/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c
index 66fe959b4f74..7fbbe05d5c60 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/gddr5.c
@@ -40,7 +40,7 @@ nouveau_gddr5_calc(struct nouveau_ram *ram, bool nuts)
int WL, CL, WR, at[2], dt, ds;
int rq = ram->freq < 1000000; /* XXX */
- switch (ram->ramcfg.version) {
+ switch (ram->next->bios.ramcfg_ver) {
case 0x11:
pd = ram->next->bios.ramcfg_11_01_80;
lf = ram->next->bios.ramcfg_11_01_40;
@@ -54,7 +54,7 @@ nouveau_gddr5_calc(struct nouveau_ram *ram, bool nuts)
return -ENOSYS;
}
- switch (ram->timing.version) {
+ switch (ram->next->bios.timing_ver) {
case 0x20:
WL = (ram->next->bios.timing[1] & 0x00000f80) >> 7;
CL = (ram->next->bios.timing[1] & 0x0000001f);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c
index a16024a74771..fde42e4d1b56 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c
@@ -27,6 +27,20 @@ struct gk20a_fb_priv {
};
static int
+gk20a_fb_init(struct nouveau_object *object)
+{
+ struct gk20a_fb_priv *priv = (void *)object;
+ int ret;
+
+ ret = nouveau_fb_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_mask(priv, 0x100c80, 0x00000001, 0x00000000); /* 128KiB lpg */
+ return 0;
+}
+
+static int
gk20a_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
@@ -48,7 +62,7 @@ gk20a_fb_oclass = &(struct nouveau_fb_impl) {
.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = gk20a_fb_ctor,
.dtor = _nouveau_fb_dtor,
- .init = _nouveau_fb_init,
+ .init = gk20a_fb_init,
.fini = _nouveau_fb_fini,
},
.memtype = nvc0_fb_memtype_valid,
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c
index f003c1b1893f..2209ade63339 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c
@@ -45,7 +45,7 @@ nv20_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
{
u32 tiles = DIV_ROUND_UP(size, 0x40);
u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
- if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
+ if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
if (!(flags & 2)) tile->zcomp = 0x00000000; /* Z16 */
else tile->zcomp = 0x04000000; /* Z24S8 */
tile->zcomp |= tile->tag->offset;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c
index f34f4223210b..e2a66c355c50 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c
@@ -32,7 +32,7 @@ nv25_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
{
u32 tiles = DIV_ROUND_UP(size, 0x40);
u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
- if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
+ if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
if (!(flags & 2)) tile->zcomp = 0x00100000; /* Z16 */
else tile->zcomp = 0x00200000; /* Z24S8 */
tile->zcomp |= tile->tag->offset;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c
index 69093f7151f0..cbec402ba5b9 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c
@@ -51,7 +51,7 @@ nv30_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
{
u32 tiles = DIV_ROUND_UP(size, 0x40);
u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
- if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
+ if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
if (flags & 2) tile->zcomp |= 0x01000000; /* Z16 */
else tile->zcomp |= 0x02000000; /* Z24S8 */
tile->zcomp |= ((tile->tag->offset ) >> 6);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c
index 161b06e8fc3f..b2cf8c69fb2e 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c
@@ -32,7 +32,7 @@ nv35_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
{
u32 tiles = DIV_ROUND_UP(size, 0x40);
u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
- if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
+ if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
if (flags & 2) tile->zcomp |= 0x04000000; /* Z16 */
else tile->zcomp |= 0x08000000; /* Z24S8 */
tile->zcomp |= ((tile->tag->offset ) >> 6);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c
index 2dd3d0aab6bb..b4cdae2a3b2f 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c
@@ -32,7 +32,7 @@ nv36_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
{
u32 tiles = DIV_ROUND_UP(size, 0x40);
u32 tags = round_up(tiles / pfb->ram->parts, 0x40);
- if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
+ if (!nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
if (flags & 2) tile->zcomp |= 0x10000000; /* Z16 */
else tile->zcomp |= 0x20000000; /* Z24S8 */
tile->zcomp |= ((tile->tag->offset ) >> 6);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c
index 95a115ab0c86..52814258c212 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c
@@ -33,7 +33,7 @@ nv40_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags,
u32 tiles = DIV_ROUND_UP(size, 0x80);
u32 tags = round_up(tiles / pfb->ram->parts, 0x100);
if ( (flags & 2) &&
- !nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) {
+ !nouveau_mm_head(&pfb->tags, 0, 1, tags, tags, 1, &tile->tag)) {
tile->zcomp = 0x28000000; /* Z24S8_SPLIT_GRAD */
tile->zcomp |= ((tile->tag->offset ) >> 8);
tile->zcomp |= ((tile->tag->offset + tags - 1) >> 8) << 13;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
index 1fc55c1e91a1..4150b0d10af8 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
@@ -250,9 +250,11 @@ nv50_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (priv->r100c08_page) {
- priv->r100c08 = nv_device_map_page(device, priv->r100c08_page);
- if (!priv->r100c08)
- nv_warn(priv, "failed 0x100c08 page map\n");
+ priv->r100c08 = dma_map_page(nv_device_base(device),
+ priv->r100c08_page, 0, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(nv_device_base(device), priv->r100c08))
+ return -EFAULT;
} else {
nv_warn(priv, "failed 0x100c08 page alloc\n");
}
@@ -268,7 +270,8 @@ nv50_fb_dtor(struct nouveau_object *object)
struct nv50_fb_priv *priv = (void *)object;
if (priv->r100c08_page) {
- nv_device_unmap_page(device, priv->r100c08);
+ dma_unmap_page(nv_device_base(device), priv->r100c08, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
__free_page(priv->r100c08_page);
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
index 0670ae33ee45..32f28dc73ef2 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
@@ -60,6 +60,7 @@ nvc0_fb_init(struct nouveau_object *object)
if (priv->r100c10_page)
nv_wr32(priv, 0x100c10, priv->r100c10 >> 8);
+ nv_mask(priv, 0x100c80, 0x00000001, 0x00000000); /* 128KiB lpg */
return 0;
}
@@ -70,7 +71,8 @@ nvc0_fb_dtor(struct nouveau_object *object)
struct nvc0_fb_priv *priv = (void *)object;
if (priv->r100c10_page) {
- nv_device_unmap_page(device, priv->r100c10);
+ dma_unmap_page(nv_device_base(device), priv->r100c10, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
__free_page(priv->r100c10_page);
}
@@ -93,8 +95,10 @@ nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (priv->r100c10_page) {
- priv->r100c10 = nv_device_map_page(device, priv->r100c10_page);
- if (!priv->r100c10)
+ priv->r100c10 = dma_map_page(nv_device_base(device),
+ priv->r100c10_page, 0, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(nv_device_base(device), priv->r100c10))
return -EFAULT;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
index 82273f832e42..60322e906dd4 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
@@ -35,6 +35,7 @@ extern struct nouveau_oclass nve0_ram_oclass;
extern struct nouveau_oclass gk20a_ram_oclass;
extern struct nouveau_oclass gm107_ram_oclass;
+int nouveau_sddr2_calc(struct nouveau_ram *ram);
int nouveau_sddr3_calc(struct nouveau_ram *ram);
int nouveau_gddr5_calc(struct nouveau_ram *ram, bool nuts);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h b/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h
index 2af9cfd2c60f..d1fbbe4b00a2 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramfuc.h
@@ -12,16 +12,32 @@ struct ramfuc {
struct ramfuc_reg {
int sequence;
bool force;
- u32 addr[2];
+ u32 addr;
+ u32 stride; /* in bytes */
+ u32 mask;
u32 data;
};
static inline struct ramfuc_reg
+ramfuc_stride(u32 addr, u32 stride, u32 mask)
+{
+ return (struct ramfuc_reg) {
+ .sequence = 0,
+ .addr = addr,
+ .stride = stride,
+ .mask = mask,
+ .data = 0xdeadbeef,
+ };
+}
+
+static inline struct ramfuc_reg
ramfuc_reg2(u32 addr1, u32 addr2)
{
return (struct ramfuc_reg) {
.sequence = 0,
- .addr = { addr1, addr2 },
+ .addr = addr1,
+ .stride = addr2 - addr1,
+ .mask = 0x3,
.data = 0xdeadbeef,
};
}
@@ -29,7 +45,13 @@ ramfuc_reg2(u32 addr1, u32 addr2)
static noinline struct ramfuc_reg
ramfuc_reg(u32 addr)
{
- return ramfuc_reg2(addr, addr);
+ return (struct ramfuc_reg) {
+ .sequence = 0,
+ .addr = addr,
+ .stride = 0,
+ .mask = 0x1,
+ .data = 0xdeadbeef,
+ };
}
static inline int
@@ -62,18 +84,25 @@ static inline u32
ramfuc_rd32(struct ramfuc *ram, struct ramfuc_reg *reg)
{
if (reg->sequence != ram->sequence)
- reg->data = nv_rd32(ram->pfb, reg->addr[0]);
+ reg->data = nv_rd32(ram->pfb, reg->addr);
return reg->data;
}
static inline void
ramfuc_wr32(struct ramfuc *ram, struct ramfuc_reg *reg, u32 data)
{
+ unsigned int mask, off = 0;
+
reg->sequence = ram->sequence;
reg->data = data;
- if (reg->addr[0] != reg->addr[1])
- nouveau_memx_wr32(ram->memx, reg->addr[1], reg->data);
- nouveau_memx_wr32(ram->memx, reg->addr[0], reg->data);
+
+ for (mask = reg->mask; mask > 0; mask = (mask & ~1) >> 1) {
+ if (mask & 1) {
+ nouveau_memx_wr32(ram->memx, reg->addr+off, reg->data);
+ }
+
+ off += reg->stride;
+ }
}
static inline void
@@ -105,14 +134,35 @@ ramfuc_nsec(struct ramfuc *ram, u32 nsec)
nouveau_memx_nsec(ram->memx, nsec);
}
-#define ram_init(s,p) ramfuc_init(&(s)->base, (p))
-#define ram_exec(s,e) ramfuc_exec(&(s)->base, (e))
-#define ram_have(s,r) ((s)->r_##r.addr[0] != 0x000000)
-#define ram_rd32(s,r) ramfuc_rd32(&(s)->base, &(s)->r_##r)
-#define ram_wr32(s,r,d) ramfuc_wr32(&(s)->base, &(s)->r_##r, (d))
-#define ram_nuke(s,r) ramfuc_nuke(&(s)->base, &(s)->r_##r)
-#define ram_mask(s,r,m,d) ramfuc_mask(&(s)->base, &(s)->r_##r, (m), (d))
-#define ram_wait(s,r,m,d,n) ramfuc_wait(&(s)->base, (r), (m), (d), (n))
-#define ram_nsec(s,n) ramfuc_nsec(&(s)->base, (n))
+static inline void
+ramfuc_wait_vblank(struct ramfuc *ram)
+{
+ nouveau_memx_wait_vblank(ram->memx);
+}
+
+static inline void
+ramfuc_block(struct ramfuc *ram)
+{
+ nouveau_memx_block(ram->memx);
+}
+
+static inline void
+ramfuc_unblock(struct ramfuc *ram)
+{
+ nouveau_memx_unblock(ram->memx);
+}
+
+#define ram_init(s,p) ramfuc_init(&(s)->base, (p))
+#define ram_exec(s,e) ramfuc_exec(&(s)->base, (e))
+#define ram_have(s,r) ((s)->r_##r.addr != 0x000000)
+#define ram_rd32(s,r) ramfuc_rd32(&(s)->base, &(s)->r_##r)
+#define ram_wr32(s,r,d) ramfuc_wr32(&(s)->base, &(s)->r_##r, (d))
+#define ram_nuke(s,r) ramfuc_nuke(&(s)->base, &(s)->r_##r)
+#define ram_mask(s,r,m,d) ramfuc_mask(&(s)->base, &(s)->r_##r, (m), (d))
+#define ram_wait(s,r,m,d,n) ramfuc_wait(&(s)->base, (r), (m), (d), (n))
+#define ram_nsec(s,n) ramfuc_nsec(&(s)->base, (n))
+#define ram_wait_vblank(s) ramfuc_wait_vblank(&(s)->base)
+#define ram_block(s) ramfuc_block(&(s)->base)
+#define ram_unblock(s) ramfuc_unblock(&(s)->base)
#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c
index e781080d3327..1972268d1410 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c
@@ -22,22 +22,7 @@
* Authors: Ben Skeggs
*/
-#define NV04_PFB_BOOT_0 0x00100000
-# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003
-# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000
-# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001
-# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002
-# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003
-# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004
-# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028
-# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000
-# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008
-# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010
-# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018
-# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020
-# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028
-# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100
-# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000
+#include <subdev/fb/regsnv04.h>
#include "priv.h"
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
index e5d12c24cc43..64a983c96625 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
@@ -280,7 +280,7 @@ nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
if (align == 16) {
int n = (max >> 4) * comp;
- ret = nouveau_mm_head(tags, 1, n, n, 1, &mem->tag);
+ ret = nouveau_mm_head(tags, 0, 1, n, n, 1, &mem->tag);
if (ret)
mem->tag = NULL;
}
@@ -296,9 +296,9 @@ nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
type = nv50_fb_memtype[type];
do {
if (back)
- ret = nouveau_mm_tail(heap, type, max, min, align, &r);
+ ret = nouveau_mm_tail(heap, 0, type, max, min, align, &r);
else
- ret = nouveau_mm_head(heap, type, max, min, align, &r);
+ ret = nouveau_mm_head(heap, 0, type, max, min, align, &r);
if (ret) {
mutex_unlock(&pfb->base.mutex);
pfb->ram->put(pfb, &mem);
@@ -319,27 +319,22 @@ nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
static u32
nv50_fb_vram_rblock(struct nouveau_fb *pfb, struct nouveau_ram *ram)
{
- int i, parts, colbits, rowbitsa, rowbitsb, banks;
+ int colbits, rowbitsa, rowbitsb, banks;
u64 rowsize, predicted;
- u32 r0, r4, rt, ru, rblock_size;
+ u32 r0, r4, rt, rblock_size;
r0 = nv_rd32(pfb, 0x100200);
r4 = nv_rd32(pfb, 0x100204);
rt = nv_rd32(pfb, 0x100250);
- ru = nv_rd32(pfb, 0x001540);
- nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru);
-
- for (i = 0, parts = 0; i < 8; i++) {
- if (ru & (0x00010000 << i))
- parts++;
- }
+ nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt,
+ nv_rd32(pfb, 0x001540));
colbits = (r4 & 0x0000f000) >> 12;
rowbitsa = ((r4 & 0x000f0000) >> 16) + 8;
rowbitsb = ((r4 & 0x00f00000) >> 20) + 8;
banks = 1 << (((r4 & 0x03000000) >> 24) + 2);
- rowsize = parts * banks * (1 << colbits) * 8;
+ rowsize = ram->parts * banks * (1 << colbits) * 8;
predicted = rowsize << rowbitsa;
if (r0 & 0x00000004)
predicted += rowsize << rowbitsb;
@@ -376,6 +371,9 @@ nv50_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine,
ram->size = nv_rd32(pfb, 0x10020c);
ram->size = (ram->size & 0xffffff00) | ((ram->size & 0x000000ff) << 32);
+ ram->part_mask = (nv_rd32(pfb, 0x001540) & 0x00ff0000) >> 16;
+ ram->parts = hweight8(ram->part_mask);
+
switch (nv_rd32(pfb, 0x100714) & 0x00000007) {
case 0: ram->type = NV_MEM_TYPE_DDR1; break;
case 1:
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c
index 8076fb195dd5..3601deca0bd5 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c
@@ -79,20 +79,27 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
struct nva3_ram *ram = (void *)pfb->ram;
struct nva3_ramfuc *fuc = &ram->fuc;
struct nva3_clock_info mclk;
- u8 ver, cnt, len, strap;
+ struct nouveau_ram_data *next;
+ u8 ver, hdr, cnt, len, strap;
u32 data;
- struct {
- u32 data;
- u8 size;
- } rammap, ramcfg, timing;
u32 r004018, r100760, ctrl;
u32 unk714, unk718, unk71c;
- int ret;
+ int ret, i;
+
+ next = &ram->base.target;
+ next->freq = freq;
+ ram->base.next = next;
/* lookup memory config data relevant to the target frequency */
- rammap.data = nvbios_rammapEm(bios, freq / 1000, &ver, &rammap.size,
- &cnt, &ramcfg.size);
- if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) {
+ i = 0;
+ while ((data = nvbios_rammapEp(bios, i++, &ver, &hdr, &cnt, &len,
+ &next->bios))) {
+ if (freq / 1000 >= next->bios.rammap_min &&
+ freq / 1000 <= next->bios.rammap_max)
+ break;
+ }
+
+ if (!data || ver != 0x10 || hdr < 0x0e) {
nv_error(pfb, "invalid/missing rammap entry\n");
return -EINVAL;
}
@@ -104,26 +111,25 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
return -EINVAL;
}
- ramcfg.data = rammap.data + rammap.size + (strap * ramcfg.size);
- if (!ramcfg.data || ver != 0x10 || ramcfg.size < 0x0e) {
+ data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, strap,
+ &ver, &hdr, &next->bios);
+ if (!data || ver != 0x10 || hdr < 0x0e) {
nv_error(pfb, "invalid/missing ramcfg entry\n");
return -EINVAL;
}
/* lookup memory timings, if bios says they're present */
- strap = nv_ro08(bios, ramcfg.data + 0x01);
- if (strap != 0xff) {
- timing.data = nvbios_timingEe(bios, strap, &ver, &timing.size,
- &cnt, &len);
- if (!timing.data || ver != 0x10 || timing.size < 0x19) {
+ if (next->bios.ramcfg_timing != 0xff) {
+ data = nvbios_timingEp(bios, next->bios.ramcfg_timing,
+ &ver, &hdr, &cnt, &len,
+ &next->bios);
+ if (!data || ver != 0x10 || hdr < 0x19) {
nv_error(pfb, "invalid/missing timing entry\n");
return -EINVAL;
}
- } else {
- timing.data = 0;
}
- ret = nva3_clock_info(nouveau_clock(pfb), 0x12, 0x4000, freq, &mclk);
+ ret = nva3_pll_info(nouveau_clock(pfb), 0x12, 0x4000, freq, &mclk);
if (ret < 0) {
nv_error(pfb, "failed mclk calculation\n");
return ret;
@@ -163,17 +169,17 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x004168, 0x003f3141, ctrl);
}
- if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) {
+ if (next->bios.ramcfg_10_02_10) {
ram_mask(fuc, 0x111104, 0x00000600, 0x00000000);
} else {
ram_mask(fuc, 0x111100, 0x40000000, 0x40000000);
ram_mask(fuc, 0x111104, 0x00000180, 0x00000000);
}
- if (!(nv_ro08(bios, rammap.data + 0x04) & 0x02))
+ if (!next->bios.rammap_10_04_02)
ram_mask(fuc, 0x100200, 0x00000800, 0x00000000);
ram_wr32(fuc, 0x611200, 0x00003300);
- if (!(nv_ro08(bios, ramcfg.data + 0x02) & 0x10))
+ if (!next->bios.ramcfg_10_02_10)
ram_wr32(fuc, 0x111100, 0x4c020000); /*XXX*/
ram_wr32(fuc, 0x1002d4, 0x00000001);
@@ -202,17 +208,16 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
ram_wr32(fuc, 0x004018, 0x0000d000 | r004018);
}
- if ( (nv_ro08(bios, rammap.data + 0x04) & 0x08)) {
- u32 unk5a0 = (nv_ro16(bios, ramcfg.data + 0x05) << 8) |
- nv_ro08(bios, ramcfg.data + 0x05);
- u32 unk5a4 = (nv_ro16(bios, ramcfg.data + 0x07));
- u32 unk804 = (nv_ro08(bios, ramcfg.data + 0x09) & 0xf0) << 16 |
- (nv_ro08(bios, ramcfg.data + 0x03) & 0x0f) << 16 |
- (nv_ro08(bios, ramcfg.data + 0x09) & 0x0f) |
- 0x80000000;
- ram_wr32(fuc, 0x1005a0, unk5a0);
- ram_wr32(fuc, 0x1005a4, unk5a4);
- ram_wr32(fuc, 0x10f804, unk804);
+ if (next->bios.rammap_10_04_08) {
+ ram_wr32(fuc, 0x1005a0, next->bios.ramcfg_10_06 << 16 |
+ next->bios.ramcfg_10_05 << 8 |
+ next->bios.ramcfg_10_05);
+ ram_wr32(fuc, 0x1005a4, next->bios.ramcfg_10_08 << 8 |
+ next->bios.ramcfg_10_07);
+ ram_wr32(fuc, 0x10f804, next->bios.ramcfg_10_09_f0 << 20 |
+ next->bios.ramcfg_10_03_0f << 16 |
+ next->bios.ramcfg_10_09_0f |
+ 0x80000000);
ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000);
} else {
ram_mask(fuc, 0x10053c, 0x00001000, 0x00001000);
@@ -250,27 +255,26 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x100220[0], 0x00000000, 0x00000000);
ram_mask(fuc, 0x100220[8], 0x00000000, 0x00000000);
- data = (nv_ro08(bios, ramcfg.data + 0x02) & 0x08) ? 0x00000000 : 0x00001000;
- ram_mask(fuc, 0x100200, 0x00001000, data);
+ ram_mask(fuc, 0x100200, 0x00001000, !next->bios.ramcfg_10_02_08 << 12);
unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000010;
unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100;
unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100;
- if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x20))
+ if (next->bios.ramcfg_10_02_20)
unk714 |= 0xf0000000;
- if (!(nv_ro08(bios, ramcfg.data + 0x02) & 0x04))
+ if (!next->bios.ramcfg_10_02_04)
unk714 |= 0x00000010;
ram_wr32(fuc, 0x100714, unk714);
- if (nv_ro08(bios, ramcfg.data + 0x02) & 0x01)
+ if (next->bios.ramcfg_10_02_01)
unk71c |= 0x00000100;
ram_wr32(fuc, 0x10071c, unk71c);
- if (nv_ro08(bios, ramcfg.data + 0x02) & 0x02)
+ if (next->bios.ramcfg_10_02_02)
unk718 |= 0x00000100;
ram_wr32(fuc, 0x100718, unk718);
- if (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)
+ if (next->bios.ramcfg_10_02_10)
ram_wr32(fuc, 0x111100, 0x48000000); /*XXX*/
ram_mask(fuc, mr[0], 0x100, 0x100);
@@ -282,9 +286,9 @@ nva3_ram_calc(struct nouveau_fb *pfb, u32 freq)
ram_nsec(fuc, 12000);
ram_wr32(fuc, 0x611200, 0x00003330);
- if ( (nv_ro08(bios, rammap.data + 0x04) & 0x02))
+ if (next->bios.rammap_10_04_02)
ram_mask(fuc, 0x100200, 0x00000800, 0x00000800);
- if ( (nv_ro08(bios, ramcfg.data + 0x02) & 0x10)) {
+ if (next->bios.ramcfg_10_02_10) {
ram_mask(fuc, 0x111104, 0x00000180, 0x00000180);
ram_mask(fuc, 0x111100, 0x40000000, 0x00000000);
} else {
@@ -404,11 +408,11 @@ nva3_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
ram->fuc.r_0x100714 = ramfuc_reg(0x100714);
ram->fuc.r_0x100718 = ramfuc_reg(0x100718);
ram->fuc.r_0x10071c = ramfuc_reg(0x10071c);
- ram->fuc.r_0x100760 = ramfuc_reg(0x100760);
- ram->fuc.r_0x1007a0 = ramfuc_reg(0x1007a0);
- ram->fuc.r_0x1007e0 = ramfuc_reg(0x1007e0);
+ ram->fuc.r_0x100760 = ramfuc_stride(0x100760, 4, ram->base.part_mask);
+ ram->fuc.r_0x1007a0 = ramfuc_stride(0x1007a0, 4, ram->base.part_mask);
+ ram->fuc.r_0x1007e0 = ramfuc_stride(0x1007e0, 4, ram->base.part_mask);
ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804);
- ram->fuc.r_0x1110e0 = ramfuc_reg(0x1110e0);
+ ram->fuc.r_0x1110e0 = ramfuc_stride(0x1110e0, 4, ram->base.part_mask);
ram->fuc.r_0x111100 = ramfuc_reg(0x111100);
ram->fuc.r_0x111104 = ramfuc_reg(0x111104);
ram->fuc.r_0x611200 = ramfuc_reg(0x611200);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
index 5a6a5027f749..735cb9580abe 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
@@ -26,7 +26,7 @@
#include <subdev/bios/pll.h>
#include <subdev/bios/rammap.h>
#include <subdev/bios/timing.h>
-#include <subdev/ltcg.h>
+#include <subdev/ltc.h>
#include <subdev/clock.h>
#include <subdev/clock/pll.h>
@@ -133,6 +133,7 @@ nvc0_ram_calc(struct nouveau_fb *pfb, u32 freq)
struct nouveau_bios *bios = nouveau_bios(pfb);
struct nvc0_ram *ram = (void *)pfb->ram;
struct nvc0_ramfuc *fuc = &ram->fuc;
+ struct nvbios_ramcfg cfg;
u8 ver, cnt, len, strap;
struct {
u32 data;
@@ -145,7 +146,7 @@ nvc0_ram_calc(struct nouveau_fb *pfb, u32 freq)
/* lookup memory config data relevant to the target frequency */
rammap.data = nvbios_rammapEm(bios, freq / 1000, &ver, &rammap.size,
- &cnt, &ramcfg.size);
+ &cnt, &ramcfg.size, &cfg);
if (!rammap.data || ver != 0x10 || rammap.size < 0x0e) {
nv_error(pfb, "invalid/missing rammap entry\n");
return -EINVAL;
@@ -425,7 +426,7 @@ extern const u8 nvc0_pte_storage_type_map[256];
void
nvc0_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem)
{
- struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb);
+ struct nouveau_ltc *ltc = nouveau_ltc(pfb);
struct nouveau_mem *mem = *pmem;
*pmem = NULL;
@@ -434,7 +435,7 @@ nvc0_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem)
mutex_lock(&pfb->base.mutex);
if (mem->tag)
- ltcg->tags_free(ltcg, &mem->tag);
+ ltc->tags_free(ltc, &mem->tag);
__nv50_ram_put(pfb, mem);
mutex_unlock(&pfb->base.mutex);
@@ -468,12 +469,12 @@ nvc0_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
mutex_lock(&pfb->base.mutex);
if (comp) {
- struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb);
+ struct nouveau_ltc *ltc = nouveau_ltc(pfb);
/* compression only works with lpages */
if (align == (1 << (17 - 12))) {
int n = size >> 5;
- ltcg->tags_alloc(ltcg, n, &mem->tag);
+ ltc->tags_alloc(ltc, n, &mem->tag);
}
if (unlikely(!mem->tag))
@@ -483,9 +484,9 @@ nvc0_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
do {
if (back)
- ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r);
+ ret = nouveau_mm_tail(mm, 0, 1, size, ncmin, align, &r);
else
- ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r);
+ ret = nouveau_mm_head(mm, 0, 1, size, ncmin, align, &r);
if (ret) {
mutex_unlock(&pfb->base.mutex);
pfb->ram->put(pfb, &mem);
@@ -554,15 +555,15 @@ nvc0_ram_create_(struct nouveau_object *parent, struct nouveau_object *engine,
} else {
/* otherwise, address lowest common amount from 0GiB */
ret = nouveau_mm_init(&pfb->vram, rsvd_head,
- (bsize << 8) * parts, 1);
+ (bsize << 8) * parts - rsvd_head, 1);
if (ret)
return ret;
/* and the rest starting from (8GiB + common_size) */
offset = (0x0200000000ULL >> 12) + (bsize << 8);
- length = (ram->size >> 12) - (bsize << 8) - rsvd_tail;
+ length = (ram->size >> 12) - ((bsize * parts) << 8) - rsvd_tail;
- ret = nouveau_mm_init(&pfb->vram, offset, length, 0);
+ ret = nouveau_mm_init(&pfb->vram, offset, length, 1);
if (ret)
nouveau_mm_fini(&pfb->vram);
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c
index c5b46e302319..6bae474abb44 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c
@@ -29,6 +29,8 @@
#include <subdev/bios/init.h>
#include <subdev/bios/rammap.h>
#include <subdev/bios/timing.h>
+#include <subdev/bios/M0205.h>
+#include <subdev/bios/M0209.h>
#include <subdev/clock.h>
#include <subdev/clock/pll.h>
@@ -41,14 +43,6 @@
#include "ramfuc.h"
-/* binary driver only executes this path if the condition (a) is true
- * for any configuration (combination of rammap+ramcfg+timing) that
- * can be reached on a given card. for now, we will execute the branch
- * unconditionally in the hope that a "false everywhere" in the bios
- * tables doesn't actually mean "don't touch this".
- */
-#define NOTE00(a) 1
-
struct nve0_ramfuc {
struct ramfuc base;
@@ -134,10 +128,12 @@ struct nve0_ram {
struct nouveau_ram base;
struct nve0_ramfuc fuc;
+ struct list_head cfg;
u32 parts;
u32 pmask;
u32 pnuts;
+ struct nvbios_ramcfg diff;
int from;
int mode;
int N1, fN1, M1, P1;
@@ -241,7 +237,7 @@ nve0_ram_nuts(struct nve0_ram *ram, struct ramfuc_reg *reg,
{
struct nve0_fb_priv *priv = (void *)nouveau_fb(ram);
struct ramfuc *fuc = &ram->fuc.base;
- u32 addr = 0x110000 + (reg->addr[0] & 0xfff);
+ u32 addr = 0x110000 + (reg->addr & 0xfff);
u32 mask = _mask | _copy;
u32 data = (_data & _mask) | (reg->data & _copy);
u32 i;
@@ -268,6 +264,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
u32 mask, data;
ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000);
+ ram_block(fuc);
ram_wr32(fuc, 0x62c000, 0x0f0f0000);
/* MR1: turn termination on early, for some reason.. */
@@ -478,7 +475,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x10f2e8, 0xffffffff, next->bios.timing[9]);
data = mask = 0x00000000;
- if (NOTE00(ramcfg_08_20)) {
+ if (ram->diff.ramcfg_11_08_20) {
if (next->bios.ramcfg_11_08_20)
data |= 0x01000000;
mask |= 0x01000000;
@@ -486,11 +483,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x10f200, mask, data);
data = mask = 0x00000000;
- if (NOTE00(ramcfg_02_03 != 0)) {
+ if (ram->diff.ramcfg_11_02_03) {
data |= next->bios.ramcfg_11_02_03 << 8;
mask |= 0x00000300;
}
- if (NOTE00(ramcfg_01_10)) {
+ if (ram->diff.ramcfg_11_01_10) {
if (next->bios.ramcfg_11_01_10)
data |= 0x70000000;
mask |= 0x70000000;
@@ -498,11 +495,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x10f604, mask, data);
data = mask = 0x00000000;
- if (NOTE00(timing_30_07 != 0)) {
+ if (ram->diff.timing_20_30_07) {
data |= next->bios.timing_20_30_07 << 28;
mask |= 0x70000000;
}
- if (NOTE00(ramcfg_01_01)) {
+ if (ram->diff.ramcfg_11_01_01) {
if (next->bios.ramcfg_11_01_01)
data |= 0x00000100;
mask |= 0x00000100;
@@ -510,11 +507,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x10f614, mask, data);
data = mask = 0x00000000;
- if (NOTE00(timing_30_07 != 0)) {
+ if (ram->diff.timing_20_30_07) {
data |= next->bios.timing_20_30_07 << 28;
mask |= 0x70000000;
}
- if (NOTE00(ramcfg_01_02)) {
+ if (ram->diff.ramcfg_11_01_02) {
if (next->bios.ramcfg_11_01_02)
data |= 0x00000100;
mask |= 0x00000100;
@@ -548,11 +545,11 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
ram_wr32(fuc, 0x10f870, 0x11111111 * next->bios.ramcfg_11_03_0f);
data = mask = 0x00000000;
- if (NOTE00(ramcfg_02_03 != 0)) {
+ if (ram->diff.ramcfg_11_02_03) {
data |= next->bios.ramcfg_11_02_03;
mask |= 0x00000003;
}
- if (NOTE00(ramcfg_01_10)) {
+ if (ram->diff.ramcfg_11_01_10) {
if (next->bios.ramcfg_11_01_10)
data |= 0x00000004;
mask |= 0x00000004;
@@ -666,6 +663,7 @@ nve0_ram_calc_gddr5(struct nouveau_fb *pfb, u32 freq)
if (next->bios.ramcfg_11_07_02)
nve0_ram_train(fuc, 0x80020000, 0x01000000);
+ ram_unblock(fuc);
ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
if (next->bios.rammap_11_08_01)
@@ -695,6 +693,7 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq)
u32 mask, data;
ram_mask(fuc, 0x10f808, 0x40000000, 0x40000000);
+ ram_block(fuc);
ram_wr32(fuc, 0x62c000, 0x0f0f0000);
if (vc == 1 && ram_have(fuc, gpio2E)) {
@@ -917,6 +916,7 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq)
ram_mask(fuc, 0x10f200, 0x80000000, 0x00000000);
ram_nsec(fuc, 1000);
+ ram_unblock(fuc);
ram_wr32(fuc, 0x62c000, 0x0f0f0f00);
if (next->bios.rammap_11_08_01)
@@ -932,58 +932,24 @@ nve0_ram_calc_sddr3(struct nouveau_fb *pfb, u32 freq)
******************************************************************************/
static int
-nve0_ram_calc_data(struct nouveau_fb *pfb, u32 freq,
+nve0_ram_calc_data(struct nouveau_fb *pfb, u32 khz,
struct nouveau_ram_data *data)
{
- struct nouveau_bios *bios = nouveau_bios(pfb);
struct nve0_ram *ram = (void *)pfb->ram;
- u8 strap, cnt, len;
-
- /* lookup memory config data relevant to the target frequency */
- ram->base.rammap.data = nvbios_rammapEp(bios, freq / 1000,
- &ram->base.rammap.version,
- &ram->base.rammap.size,
- &cnt, &len, &data->bios);
- if (!ram->base.rammap.data || ram->base.rammap.version != 0x11 ||
- ram->base.rammap.size < 0x09) {
- nv_error(pfb, "invalid/missing rammap entry\n");
- return -EINVAL;
- }
-
- /* locate specific data set for the attached memory */
- strap = nvbios_ramcfg_index(nv_subdev(pfb));
- ram->base.ramcfg.data = nvbios_rammapSp(bios, ram->base.rammap.data,
- ram->base.rammap.version,
- ram->base.rammap.size,
- cnt, len, strap,
- &ram->base.ramcfg.version,
- &ram->base.ramcfg.size,
- &data->bios);
- if (!ram->base.ramcfg.data || ram->base.ramcfg.version != 0x11 ||
- ram->base.ramcfg.size < 0x08) {
- nv_error(pfb, "invalid/missing ramcfg entry\n");
- return -EINVAL;
- }
-
- /* lookup memory timings, if bios says they're present */
- strap = nv_ro08(bios, ram->base.ramcfg.data + 0x00);
- if (strap != 0xff) {
- ram->base.timing.data =
- nvbios_timingEp(bios, strap, &ram->base.timing.version,
- &ram->base.timing.size, &cnt, &len,
- &data->bios);
- if (!ram->base.timing.data ||
- ram->base.timing.version != 0x20 ||
- ram->base.timing.size < 0x33) {
- nv_error(pfb, "invalid/missing timing entry\n");
- return -EINVAL;
+ struct nouveau_ram_data *cfg;
+ u32 mhz = khz / 1000;
+
+ list_for_each_entry(cfg, &ram->cfg, head) {
+ if (mhz >= cfg->bios.rammap_min &&
+ mhz <= cfg->bios.rammap_max) {
+ *data = *cfg;
+ data->freq = khz;
+ return 0;
}
- } else {
- ram->base.timing.data = 0;
}
- data->freq = freq;
- return 0;
+ nv_error(ram, "ramcfg data for %dMHz not found\n", mhz);
+ return -EINVAL;
}
static int
@@ -1106,13 +1072,99 @@ nve0_ram_calc(struct nouveau_fb *pfb, u32 freq)
return nve0_ram_calc_xits(pfb, ram->base.next);
}
+static void
+nve0_ram_prog_0(struct nouveau_fb *pfb, u32 freq)
+{
+ struct nve0_ram *ram = (void *)pfb->ram;
+ struct nouveau_ram_data *cfg;
+ u32 mhz = freq / 1000;
+ u32 mask, data;
+
+ list_for_each_entry(cfg, &ram->cfg, head) {
+ if (mhz >= cfg->bios.rammap_min &&
+ mhz <= cfg->bios.rammap_max)
+ break;
+ }
+
+ if (&cfg->head == &ram->cfg)
+ return;
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0a_03fe) {
+ data |= cfg->bios.rammap_11_0a_03fe << 12;
+ mask |= 0x001ff000;
+ }
+ if (ram->diff.rammap_11_09_01ff) {
+ data |= cfg->bios.rammap_11_09_01ff;
+ mask |= 0x000001ff;
+ }
+ nv_mask(pfb, 0x10f468, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0a_0400) {
+ data |= cfg->bios.rammap_11_0a_0400;
+ mask |= 0x00000001;
+ }
+ nv_mask(pfb, 0x10f420, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0a_0800) {
+ data |= cfg->bios.rammap_11_0a_0800;
+ mask |= 0x00000001;
+ }
+ nv_mask(pfb, 0x10f430, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0b_01f0) {
+ data |= cfg->bios.rammap_11_0b_01f0;
+ mask |= 0x0000001f;
+ }
+ nv_mask(pfb, 0x10f400, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0b_0200) {
+ data |= cfg->bios.rammap_11_0b_0200 << 9;
+ mask |= 0x00000200;
+ }
+ nv_mask(pfb, 0x10f410, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0d) {
+ data |= cfg->bios.rammap_11_0d << 16;
+ mask |= 0x00ff0000;
+ }
+ if (ram->diff.rammap_11_0f) {
+ data |= cfg->bios.rammap_11_0f << 8;
+ mask |= 0x0000ff00;
+ }
+ nv_mask(pfb, 0x10f440, mask, data);
+
+ if (mask = 0, data = 0, ram->diff.rammap_11_0e) {
+ data |= cfg->bios.rammap_11_0e << 8;
+ mask |= 0x0000ff00;
+ }
+ if (ram->diff.rammap_11_0b_0800) {
+ data |= cfg->bios.rammap_11_0b_0800 << 7;
+ mask |= 0x00000080;
+ }
+ if (ram->diff.rammap_11_0b_0400) {
+ data |= cfg->bios.rammap_11_0b_0400 << 5;
+ mask |= 0x00000020;
+ }
+ nv_mask(pfb, 0x10f444, mask, data);
+}
+
static int
nve0_ram_prog(struct nouveau_fb *pfb)
{
struct nouveau_device *device = nv_device(pfb);
struct nve0_ram *ram = (void *)pfb->ram;
struct nve0_ramfuc *fuc = &ram->fuc;
- ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true));
+ struct nouveau_ram_data *next = ram->base.next;
+
+ if (!nouveau_boolopt(device->cfgopt, "NvMemExec", true)) {
+ ram_exec(fuc, false);
+ return (ram->base.next == &ram->base.xition);
+ }
+
+ nve0_ram_prog_0(pfb, 1000);
+ ram_exec(fuc, true);
+ nve0_ram_prog_0(pfb, next->freq);
+
return (ram->base.next == &ram->base.xition);
}
@@ -1125,24 +1177,147 @@ nve0_ram_tidy(struct nouveau_fb *pfb)
ram_exec(fuc, false);
}
+struct nve0_ram_train {
+ u16 mask;
+ struct nvbios_M0209S remap;
+ struct nvbios_M0209S type00;
+ struct nvbios_M0209S type01;
+ struct nvbios_M0209S type04;
+ struct nvbios_M0209S type06;
+ struct nvbios_M0209S type07;
+ struct nvbios_M0209S type08;
+ struct nvbios_M0209S type09;
+};
+
+static int
+nve0_ram_train_type(struct nouveau_fb *pfb, int i, u8 ramcfg,
+ struct nve0_ram_train *train)
+{
+ struct nouveau_bios *bios = nouveau_bios(pfb);
+ struct nvbios_M0205E M0205E;
+ struct nvbios_M0205S M0205S;
+ struct nvbios_M0209E M0209E;
+ struct nvbios_M0209S *remap = &train->remap;
+ struct nvbios_M0209S *value;
+ u8 ver, hdr, cnt, len;
+ u32 data;
+
+ /* determine type of data for this index */
+ if (!(data = nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E)))
+ return -ENOENT;
+
+ switch (M0205E.type) {
+ case 0x00: value = &train->type00; break;
+ case 0x01: value = &train->type01; break;
+ case 0x04: value = &train->type04; break;
+ case 0x06: value = &train->type06; break;
+ case 0x07: value = &train->type07; break;
+ case 0x08: value = &train->type08; break;
+ case 0x09: value = &train->type09; break;
+ default:
+ return 0;
+ }
+
+ /* training data index determined by ramcfg strap */
+ if (!(data = nvbios_M0205Sp(bios, i, ramcfg, &ver, &hdr, &M0205S)))
+ return -EINVAL;
+ i = M0205S.data;
+
+ /* training data format information */
+ if (!(data = nvbios_M0209Ep(bios, i, &ver, &hdr, &cnt, &len, &M0209E)))
+ return -EINVAL;
+
+ /* ... and the raw data */
+ if (!(data = nvbios_M0209Sp(bios, i, 0, &ver, &hdr, value)))
+ return -EINVAL;
+
+ if (M0209E.v02_07 == 2) {
+ /* of course! why wouldn't we have a pointer to another entry
+ * in the same table, and use the first one as an array of
+ * remap indices...
+ */
+ if (!(data = nvbios_M0209Sp(bios, M0209E.v03, 0, &ver, &hdr,
+ remap)))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(value->data); i++)
+ value->data[i] = remap->data[value->data[i]];
+ } else
+ if (M0209E.v02_07 != 1)
+ return -EINVAL;
+
+ train->mask |= 1 << M0205E.type;
+ return 0;
+}
+
+static int
+nve0_ram_train_init_0(struct nouveau_fb *pfb, struct nve0_ram_train *train)
+{
+ int i, j;
+
+ if ((train->mask & 0x03d3) != 0x03d3) {
+ nv_warn(pfb, "missing link training data\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 0x30; i++) {
+ for (j = 0; j < 8; j += 4) {
+ nv_wr32(pfb, 0x10f968 + j, 0x00000000 | (i << 8));
+ nv_wr32(pfb, 0x10f920 + j, 0x00000000 |
+ train->type08.data[i] << 4 |
+ train->type06.data[i]);
+ nv_wr32(pfb, 0x10f918 + j, train->type00.data[i]);
+ nv_wr32(pfb, 0x10f920 + j, 0x00000100 |
+ train->type09.data[i] << 4 |
+ train->type07.data[i]);
+ nv_wr32(pfb, 0x10f918 + j, train->type01.data[i]);
+ }
+ }
+
+ for (j = 0; j < 8; j += 4) {
+ for (i = 0; i < 0x100; i++) {
+ nv_wr32(pfb, 0x10f968 + j, i);
+ nv_wr32(pfb, 0x10f900 + j, train->type04.data[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int
+nve0_ram_train_init(struct nouveau_fb *pfb)
+{
+ u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb));
+ struct nve0_ram_train *train;
+ int ret = -ENOMEM, i;
+
+ if ((train = kzalloc(sizeof(*train), GFP_KERNEL))) {
+ for (i = 0; i < 0x100; i++) {
+ ret = nve0_ram_train_type(pfb, i, ramcfg, train);
+ if (ret && ret != -ENOENT)
+ break;
+ }
+ }
+
+ switch (pfb->ram->type) {
+ case NV_MEM_TYPE_GDDR5:
+ ret = nve0_ram_train_init_0(pfb, train);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ kfree(train);
+ return ret;
+}
+
int
nve0_ram_init(struct nouveau_object *object)
{
struct nouveau_fb *pfb = (void *)object->parent;
struct nve0_ram *ram = (void *)object;
struct nouveau_bios *bios = nouveau_bios(pfb);
- static const u8 train0[] = {
- 0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
- 0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
- };
- static const u32 train1[] = {
- 0x00000000, 0xffffffff,
- 0x55555555, 0xaaaaaaaa,
- 0x33333333, 0xcccccccc,
- 0xf0f0f0f0, 0x0f0f0f0f,
- 0x00ff00ff, 0xff00ff00,
- 0x0000ffff, 0xffff0000,
- };
u8 ver, hdr, cnt, len, snr, ssz;
u32 data, save;
int ret, i;
@@ -1168,51 +1343,107 @@ nve0_ram_init(struct nouveau_object *object)
cnt = nv_ro08(bios, data + 0x14); /* guess at count */
data = nv_ro32(bios, data + 0x10); /* guess u32... */
- save = nv_rd32(pfb, 0x10f65c);
- for (i = 0; i < cnt; i++) {
- nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4);
- nvbios_exec(&(struct nvbios_init) {
- .subdev = nv_subdev(pfb),
- .bios = bios,
- .offset = nv_ro32(bios, data), /* guess u32 */
- .execute = 1,
- });
- data += 4;
- }
- nv_wr32(pfb, 0x10f65c, save);
+ save = nv_rd32(pfb, 0x10f65c) & 0x000000f0;
+ for (i = 0; i < cnt; i++, data += 4) {
+ if (i != save >> 4) {
+ nv_mask(pfb, 0x10f65c, 0x000000f0, i << 4);
+ nvbios_exec(&(struct nvbios_init) {
+ .subdev = nv_subdev(pfb),
+ .bios = bios,
+ .offset = nv_ro32(bios, data),
+ .execute = 1,
+ });
+ }
+ }
+ nv_mask(pfb, 0x10f65c, 0x000000f0, save);
nv_mask(pfb, 0x10f584, 0x11000000, 0x00000000);
+ nv_wr32(pfb, 0x10ecc0, 0xffffffff);
+ nv_mask(pfb, 0x10f160, 0x00000010, 0x00000010);
- switch (ram->base.type) {
- case NV_MEM_TYPE_GDDR5:
- for (i = 0; i < 0x30; i++) {
- nv_wr32(pfb, 0x10f968, 0x00000000 | (i << 8));
- nv_wr32(pfb, 0x10f920, 0x00000000 | train0[i % 12]);
- nv_wr32(pfb, 0x10f918, train1[i % 12]);
- nv_wr32(pfb, 0x10f920, 0x00000100 | train0[i % 12]);
- nv_wr32(pfb, 0x10f918, train1[i % 12]);
-
- nv_wr32(pfb, 0x10f96c, 0x00000000 | (i << 8));
- nv_wr32(pfb, 0x10f924, 0x00000000 | train0[i % 12]);
- nv_wr32(pfb, 0x10f91c, train1[i % 12]);
- nv_wr32(pfb, 0x10f924, 0x00000100 | train0[i % 12]);
- nv_wr32(pfb, 0x10f91c, train1[i % 12]);
- }
+ return nve0_ram_train_init(pfb);
+}
- for (i = 0; i < 0x100; i++) {
- nv_wr32(pfb, 0x10f968, i);
- nv_wr32(pfb, 0x10f900, train1[2 + (i & 1)]);
- }
+static int
+nve0_ram_ctor_data(struct nve0_ram *ram, u8 ramcfg, int i)
+{
+ struct nouveau_fb *pfb = (void *)nv_object(ram)->parent;
+ struct nouveau_bios *bios = nouveau_bios(pfb);
+ struct nouveau_ram_data *cfg;
+ struct nvbios_ramcfg *d = &ram->diff;
+ struct nvbios_ramcfg *p, *n;
+ u8 ver, hdr, cnt, len;
+ u32 data;
+ int ret;
- for (i = 0; i < 0x100; i++) {
- nv_wr32(pfb, 0x10f96c, i);
- nv_wr32(pfb, 0x10f900, train1[2 + (i & 1)]);
- }
- break;
- default:
- break;
+ if (!(cfg = kmalloc(sizeof(*cfg), GFP_KERNEL)))
+ return -ENOMEM;
+ p = &list_last_entry(&ram->cfg, typeof(*cfg), head)->bios;
+ n = &cfg->bios;
+
+ /* memory config data for a range of target frequencies */
+ data = nvbios_rammapEp(bios, i, &ver, &hdr, &cnt, &len, &cfg->bios);
+ if (ret = -ENOENT, !data)
+ goto done;
+ if (ret = -ENOSYS, ver != 0x11 || hdr < 0x12)
+ goto done;
+
+ /* ... and a portion specific to the attached memory */
+ data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, ramcfg,
+ &ver, &hdr, &cfg->bios);
+ if (ret = -EINVAL, !data)
+ goto done;
+ if (ret = -ENOSYS, ver != 0x11 || hdr < 0x0a)
+ goto done;
+
+ /* lookup memory timings, if bios says they're present */
+ if (cfg->bios.ramcfg_timing != 0xff) {
+ data = nvbios_timingEp(bios, cfg->bios.ramcfg_timing,
+ &ver, &hdr, &cnt, &len,
+ &cfg->bios);
+ if (ret = -EINVAL, !data)
+ goto done;
+ if (ret = -ENOSYS, ver != 0x20 || hdr < 0x33)
+ goto done;
}
- return 0;
+ list_add_tail(&cfg->head, &ram->cfg);
+ if (ret = 0, i == 0)
+ goto done;
+
+ d->rammap_11_0a_03fe |= p->rammap_11_0a_03fe != n->rammap_11_0a_03fe;
+ d->rammap_11_09_01ff |= p->rammap_11_09_01ff != n->rammap_11_09_01ff;
+ d->rammap_11_0a_0400 |= p->rammap_11_0a_0400 != n->rammap_11_0a_0400;
+ d->rammap_11_0a_0800 |= p->rammap_11_0a_0800 != n->rammap_11_0a_0800;
+ d->rammap_11_0b_01f0 |= p->rammap_11_0b_01f0 != n->rammap_11_0b_01f0;
+ d->rammap_11_0b_0200 |= p->rammap_11_0b_0200 != n->rammap_11_0b_0200;
+ d->rammap_11_0d |= p->rammap_11_0d != n->rammap_11_0d;
+ d->rammap_11_0f |= p->rammap_11_0f != n->rammap_11_0f;
+ d->rammap_11_0e |= p->rammap_11_0e != n->rammap_11_0e;
+ d->rammap_11_0b_0800 |= p->rammap_11_0b_0800 != n->rammap_11_0b_0800;
+ d->rammap_11_0b_0400 |= p->rammap_11_0b_0400 != n->rammap_11_0b_0400;
+ d->ramcfg_11_01_01 |= p->ramcfg_11_01_01 != n->ramcfg_11_01_01;
+ d->ramcfg_11_01_02 |= p->ramcfg_11_01_02 != n->ramcfg_11_01_02;
+ d->ramcfg_11_01_10 |= p->ramcfg_11_01_10 != n->ramcfg_11_01_10;
+ d->ramcfg_11_02_03 |= p->ramcfg_11_02_03 != n->ramcfg_11_02_03;
+ d->ramcfg_11_08_20 |= p->ramcfg_11_08_20 != n->ramcfg_11_08_20;
+ d->timing_20_30_07 |= p->timing_20_30_07 != n->timing_20_30_07;
+done:
+ if (ret)
+ kfree(cfg);
+ return ret;
+}
+
+static void
+nve0_ram_dtor(struct nouveau_object *object)
+{
+ struct nve0_ram *ram = (void *)object;
+ struct nouveau_ram_data *cfg, *tmp;
+
+ list_for_each_entry_safe(cfg, tmp, &ram->cfg, head) {
+ kfree(cfg);
+ }
+
+ nouveau_ram_destroy(&ram->base);
}
static int
@@ -1226,6 +1457,7 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct dcb_gpio_func func;
struct nve0_ram *ram;
int ret, i;
+ u8 ramcfg = nvbios_ramcfg_index(nv_subdev(pfb));
u32 tmp;
ret = nvc0_ram_create(parent, engine, oclass, 0x022554, &ram);
@@ -1233,6 +1465,8 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ INIT_LIST_HEAD(&ram->cfg);
+
switch (ram->base.type) {
case NV_MEM_TYPE_DDR3:
case NV_MEM_TYPE_GDDR5:
@@ -1264,7 +1498,26 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
}
}
- // parse bios data for both pll's
+ /* parse bios data for all rammap table entries up-front, and
+ * build information on whether certain fields differ between
+ * any of the entries.
+ *
+ * the binary driver appears to completely ignore some fields
+ * when all entries contain the same value. at first, it was
+ * hoped that these were mere optimisations and the bios init
+ * tables had configured as per the values here, but there is
+ * evidence now to suggest that this isn't the case and we do
+ * need to treat this condition as a "don't touch" indicator.
+ */
+ for (i = 0; !ret; i++) {
+ ret = nve0_ram_ctor_data(ram, ramcfg, i);
+ if (ret && ret != -ENOENT) {
+ nv_error(pfb, "failed to parse ramcfg data\n");
+ return ret;
+ }
+ }
+
+ /* parse bios data for both pll's */
ret = nvbios_pll_parse(bios, 0x0c, &ram->fuc.refpll);
if (ret) {
nv_error(pfb, "mclk refpll data not found\n");
@@ -1277,6 +1530,7 @@ nve0_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return ret;
}
+ /* lookup memory voltage gpios */
ret = gpio->find(gpio, 0, 0x18, DCB_GPIO_UNUSED, &func);
if (ret == 0) {
ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (func.line * 0x04));
@@ -1385,7 +1639,7 @@ nve0_ram_oclass = {
.handle = 0,
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nve0_ram_ctor,
- .dtor = _nouveau_ram_dtor,
+ .dtor = nve0_ram_dtor,
.init = nve0_ram_init,
.fini = _nouveau_ram_fini,
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/sddr2.c b/drivers/gpu/drm/nouveau/core/subdev/fb/sddr2.c
new file mode 100644
index 000000000000..bb1eb8f3e639
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/sddr2.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 Roy Spliet
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Roy Spliet <rspliet@eclipso.eu>
+ * Ben Skeggs
+ */
+
+#include "priv.h"
+
+struct ramxlat {
+ int id;
+ u8 enc;
+};
+
+static inline int
+ramxlat(const struct ramxlat *xlat, int id)
+{
+ while (xlat->id >= 0) {
+ if (xlat->id == id)
+ return xlat->enc;
+ xlat++;
+ }
+ return -EINVAL;
+}
+
+static const struct ramxlat
+ramddr2_cl[] = {
+ { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 },
+ /* The following are available in some, but not all DDR2 docs */
+ { 7, 7 },
+ { -1 }
+};
+
+static const struct ramxlat
+ramddr2_wr[] = {
+ { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 4 }, { 6, 5 },
+ /* The following are available in some, but not all DDR2 docs */
+ { 7, 6 },
+ { -1 }
+};
+
+int
+nouveau_sddr2_calc(struct nouveau_ram *ram)
+{
+ int CL, WR, DLL = 0, ODT = 0;
+
+ switch (ram->next->bios.timing_ver) {
+ case 0x10:
+ CL = ram->next->bios.timing_10_CL;
+ WR = ram->next->bios.timing_10_WR;
+ DLL = !ram->next->bios.ramcfg_10_02_40;
+ ODT = ram->next->bios.timing_10_ODT & 3;
+ break;
+ case 0x20:
+ CL = (ram->next->bios.timing[1] & 0x0000001f);
+ WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16;
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ CL = ramxlat(ramddr2_cl, CL);
+ WR = ramxlat(ramddr2_wr, WR);
+ if (CL < 0 || WR < 0)
+ return -EINVAL;
+
+ ram->mr[0] &= ~0xf70;
+ ram->mr[0] |= (WR & 0x07) << 9;
+ ram->mr[0] |= (CL & 0x07) << 4;
+
+ ram->mr[1] &= ~0x045;
+ ram->mr[1] |= (ODT & 0x1) << 2;
+ ram->mr[1] |= (ODT & 0x2) << 5;
+ ram->mr[1] |= !DLL;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c
index ebd4cd9c35d9..83949b11833a 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/sddr3.c
@@ -20,9 +20,9 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
+ * Roy Spliet <rspliet@eclipso.eu>
*/
-#include <subdev/bios.h>
#include "priv.h"
struct ramxlat {
@@ -69,31 +69,52 @@ ramddr3_cwl[] = {
int
nouveau_sddr3_calc(struct nouveau_ram *ram)
{
- struct nouveau_bios *bios = nouveau_bios(ram);
- int WL, CL, WR;
+ int CWL, CL, WR, DLL = 0, ODT = 0;
- switch (!!ram->timing.data * ram->timing.version) {
+ switch (ram->next->bios.timing_ver) {
+ case 0x10:
+ if (ram->next->bios.timing_hdr < 0x17) {
+ /* XXX: NV50: Get CWL from the timing register */
+ return -ENOSYS;
+ }
+ CWL = ram->next->bios.timing_10_CWL;
+ CL = ram->next->bios.timing_10_CL;
+ WR = ram->next->bios.timing_10_WR;
+ DLL = !ram->next->bios.ramcfg_10_02_40;
+ ODT = ram->next->bios.timing_10_ODT;
+ break;
case 0x20:
- WL = (nv_ro16(bios, ram->timing.data + 0x04) & 0x0f80) >> 7;
- CL = nv_ro08(bios, ram->timing.data + 0x04) & 0x1f;
- WR = nv_ro08(bios, ram->timing.data + 0x0a) & 0x7f;
+ CWL = (ram->next->bios.timing[1] & 0x00000f80) >> 7;
+ CL = (ram->next->bios.timing[1] & 0x0000001f) >> 0;
+ WR = (ram->next->bios.timing[2] & 0x007f0000) >> 16;
+ /* XXX: Get these values from the VBIOS instead */
+ DLL = !(ram->mr[1] & 0x1);
+ ODT = (ram->mr[1] & 0x004) >> 2 |
+ (ram->mr[1] & 0x040) >> 5 |
+ (ram->mr[1] & 0x200) >> 7;
break;
default:
return -ENOSYS;
}
- WL = ramxlat(ramddr3_cwl, WL);
- CL = ramxlat(ramddr3_cl, CL);
- WR = ramxlat(ramddr3_wr, WR);
- if (WL < 0 || CL < 0 || WR < 0)
+ CWL = ramxlat(ramddr3_cwl, CWL);
+ CL = ramxlat(ramddr3_cl, CL);
+ WR = ramxlat(ramddr3_wr, WR);
+ if (CL < 0 || CWL < 0 || WR < 0)
return -EINVAL;
- ram->mr[0] &= ~0xe74;
+ ram->mr[0] &= ~0xf74;
ram->mr[0] |= (WR & 0x07) << 9;
ram->mr[0] |= (CL & 0x0e) << 3;
ram->mr[0] |= (CL & 0x01) << 2;
+ ram->mr[1] &= ~0x245;
+ ram->mr[1] |= (ODT & 0x1) << 2;
+ ram->mr[1] |= (ODT & 0x2) << 5;
+ ram->mr[1] |= (ODT & 0x4) << 7;
+ ram->mr[1] |= !DLL;
+
ram->mr[2] &= ~0x038;
- ram->mr[2] |= (WL & 0x07) << 3;
+ ram->mr[2] |= (CWL & 0x07) << 3;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fuse/base.c b/drivers/gpu/drm/nouveau/core/subdev/fuse/base.c
new file mode 100644
index 000000000000..9e8e92127715
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/fuse/base.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Martin Peres
+ */
+
+#include <subdev/fuse.h>
+
+int
+_nouveau_fuse_init(struct nouveau_object *object)
+{
+ struct nouveau_fuse *fuse = (void *)object;
+ return nouveau_subdev_init(&fuse->base);
+}
+
+void
+_nouveau_fuse_dtor(struct nouveau_object *object)
+{
+ struct nouveau_fuse *fuse = (void *)object;
+ nouveau_subdev_destroy(&fuse->base);
+}
+
+int
+nouveau_fuse_create_(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, int length, void **pobject)
+{
+ struct nouveau_fuse *fuse;
+ int ret;
+
+ ret = nouveau_subdev_create_(parent, engine, oclass, 0, "FUSE",
+ "fuse", length, pobject);
+ fuse = *pobject;
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fuse/g80.c b/drivers/gpu/drm/nouveau/core/subdev/fuse/g80.c
new file mode 100644
index 000000000000..a374ade485be
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/fuse/g80.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Martin Peres
+ */
+
+#include "priv.h"
+
+struct g80_fuse_priv {
+ struct nouveau_fuse base;
+
+ spinlock_t fuse_enable_lock;
+};
+
+static u32
+g80_fuse_rd32(struct nouveau_object *object, u64 addr)
+{
+ struct g80_fuse_priv *priv = (void *)object;
+ unsigned long flags;
+ u32 fuse_enable, val;
+
+ spin_lock_irqsave(&priv->fuse_enable_lock, flags);
+
+ /* racy if another part of nouveau start writing to this reg */
+ fuse_enable = nv_mask(priv, 0x1084, 0x800, 0x800);
+ val = nv_rd32(priv, 0x21000 + addr);
+ nv_wr32(priv, 0x1084, fuse_enable);
+
+ spin_unlock_irqrestore(&priv->fuse_enable_lock, flags);
+
+ return val;
+}
+
+
+static int
+g80_fuse_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct g80_fuse_priv *priv;
+ int ret;
+
+ ret = nouveau_fuse_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&priv->fuse_enable_lock);
+
+ return 0;
+}
+
+struct nouveau_oclass
+g80_fuse_oclass = {
+ .handle = NV_SUBDEV(FUSE, 0x50),
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = g80_fuse_ctor,
+ .dtor = _nouveau_fuse_dtor,
+ .init = _nouveau_fuse_init,
+ .fini = _nouveau_fuse_fini,
+ .rd32 = g80_fuse_rd32,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fuse/gf100.c b/drivers/gpu/drm/nouveau/core/subdev/fuse/gf100.c
new file mode 100644
index 000000000000..5ed03f54b3d4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/fuse/gf100.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Martin Peres
+ */
+
+#include "priv.h"
+
+struct gf100_fuse_priv {
+ struct nouveau_fuse base;
+
+ spinlock_t fuse_enable_lock;
+};
+
+static u32
+gf100_fuse_rd32(struct nouveau_object *object, u64 addr)
+{
+ struct gf100_fuse_priv *priv = (void *)object;
+ unsigned long flags;
+ u32 fuse_enable, unk, val;
+
+ spin_lock_irqsave(&priv->fuse_enable_lock, flags);
+
+ /* racy if another part of nouveau start writing to these regs */
+ fuse_enable = nv_mask(priv, 0x22400, 0x800, 0x800);
+ unk = nv_mask(priv, 0x21000, 0x1, 0x1);
+ val = nv_rd32(priv, 0x21100 + addr);
+ nv_wr32(priv, 0x21000, unk);
+ nv_wr32(priv, 0x22400, fuse_enable);
+
+ spin_unlock_irqrestore(&priv->fuse_enable_lock, flags);
+
+ return val;
+}
+
+
+static int
+gf100_fuse_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct gf100_fuse_priv *priv;
+ int ret;
+
+ ret = nouveau_fuse_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&priv->fuse_enable_lock);
+
+ return 0;
+}
+
+struct nouveau_oclass
+gf100_fuse_oclass = {
+ .handle = NV_SUBDEV(FUSE, 0xC0),
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gf100_fuse_ctor,
+ .dtor = _nouveau_fuse_dtor,
+ .init = _nouveau_fuse_init,
+ .fini = _nouveau_fuse_fini,
+ .rd32 = gf100_fuse_rd32,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fuse/gm107.c b/drivers/gpu/drm/nouveau/core/subdev/fuse/gm107.c
new file mode 100644
index 000000000000..4f1a636c6538
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/fuse/gm107.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Martin Peres
+ */
+
+#include "priv.h"
+
+struct gm107_fuse_priv {
+ struct nouveau_fuse base;
+};
+
+static u32
+gm107_fuse_rd32(struct nouveau_object *object, u64 addr)
+{
+ struct gf100_fuse_priv *priv = (void *)object;
+
+ return nv_rd32(priv, 0x21100 + addr);
+}
+
+
+static int
+gm107_fuse_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct gm107_fuse_priv *priv;
+ int ret;
+
+ ret = nouveau_fuse_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nouveau_oclass
+gm107_fuse_oclass = {
+ .handle = NV_SUBDEV(FUSE, 0x117),
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gm107_fuse_ctor,
+ .dtor = _nouveau_fuse_dtor,
+ .init = _nouveau_fuse_init,
+ .fini = _nouveau_fuse_fini,
+ .rd32 = gm107_fuse_rd32,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fuse/priv.h b/drivers/gpu/drm/nouveau/core/subdev/fuse/priv.h
new file mode 100644
index 000000000000..d2085411a5cb
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/fuse/priv.h
@@ -0,0 +1,9 @@
+#ifndef __NVKM_FUSE_PRIV_H__
+#define __NVKM_FUSE_PRIV_H__
+
+#include <subdev/fuse.h>
+
+int _nouveau_fuse_init(struct nouveau_object *object);
+void _nouveau_fuse_dtor(struct nouveau_object *object);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
index 45e0202f3151..7ad99b763f4c 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
@@ -106,39 +106,60 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
}
static void
-nouveau_gpio_intr_disable(struct nouveau_event *event, int type, int index)
+nouveau_gpio_intr_fini(struct nvkm_event *event, int type, int index)
{
- struct nouveau_gpio *gpio = nouveau_gpio(event->priv);
+ struct nouveau_gpio *gpio = container_of(event, typeof(*gpio), event);
const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
impl->intr_mask(gpio, type, 1 << index, 0);
}
static void
-nouveau_gpio_intr_enable(struct nouveau_event *event, int type, int index)
+nouveau_gpio_intr_init(struct nvkm_event *event, int type, int index)
{
- struct nouveau_gpio *gpio = nouveau_gpio(event->priv);
+ struct nouveau_gpio *gpio = container_of(event, typeof(*gpio), event);
const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
impl->intr_mask(gpio, type, 1 << index, 1 << index);
}
+static int
+nouveau_gpio_intr_ctor(struct nouveau_object *object, void *data, u32 size,
+ struct nvkm_notify *notify)
+{
+ struct nvkm_gpio_ntfy_req *req = data;
+ if (!WARN_ON(size != sizeof(*req))) {
+ notify->size = sizeof(struct nvkm_gpio_ntfy_rep);
+ notify->types = req->mask;
+ notify->index = req->line;
+ return 0;
+ }
+ return -EINVAL;
+}
+
static void
nouveau_gpio_intr(struct nouveau_subdev *subdev)
{
struct nouveau_gpio *gpio = nouveau_gpio(subdev);
const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
- u32 hi, lo, e, i;
+ u32 hi, lo, i;
impl->intr_stat(gpio, &hi, &lo);
- for (i = 0; e = 0, (hi | lo) && i < impl->lines; i++) {
- if (hi & (1 << i))
- e |= NVKM_GPIO_HI;
- if (lo & (1 << i))
- e |= NVKM_GPIO_LO;
- nouveau_event_trigger(gpio->events, e, i);
+ for (i = 0; (hi | lo) && i < impl->lines; i++) {
+ struct nvkm_gpio_ntfy_rep rep = {
+ .mask = (NVKM_GPIO_HI * !!(hi & (1 << i))) |
+ (NVKM_GPIO_LO * !!(lo & (1 << i))),
+ };
+ nvkm_event_send(&gpio->event, rep.mask, i, &rep, sizeof(rep));
}
}
+static const struct nvkm_event_func
+nouveau_gpio_intr_func = {
+ .ctor = nouveau_gpio_intr_ctor,
+ .init = nouveau_gpio_intr_init,
+ .fini = nouveau_gpio_intr_fini,
+};
+
int
_nouveau_gpio_fini(struct nouveau_object *object, bool suspend)
{
@@ -183,7 +204,7 @@ void
_nouveau_gpio_dtor(struct nouveau_object *object)
{
struct nouveau_gpio *gpio = (void *)object;
- nouveau_event_destroy(&gpio->events);
+ nvkm_event_fini(&gpio->event);
nouveau_subdev_destroy(&gpio->base);
}
@@ -208,13 +229,11 @@ nouveau_gpio_create_(struct nouveau_object *parent,
gpio->get = nouveau_gpio_get;
gpio->reset = impl->reset;
- ret = nouveau_event_create(2, impl->lines, &gpio->events);
+ ret = nvkm_event_init(&nouveau_gpio_intr_func, 2, impl->lines,
+ &gpio->event);
if (ret)
return ret;
- gpio->events->priv = gpio;
- gpio->events->enable = nouveau_gpio_intr_enable;
- gpio->events->disable = nouveau_gpio_intr_disable;
nv_subdev(gpio)->intr = nouveau_gpio_intr;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv94.c
index 252083d376f5..cae404ccadac 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv94.c
@@ -25,7 +25,7 @@
#include "priv.h"
void
-nv92_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
+nv94_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
{
u32 intr0 = nv_rd32(gpio, 0x00e054);
u32 intr1 = nv_rd32(gpio, 0x00e074);
@@ -38,7 +38,7 @@ nv92_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
}
void
-nv92_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
+nv94_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
{
u32 inte0 = nv_rd32(gpio, 0x00e050);
u32 inte1 = nv_rd32(gpio, 0x00e070);
@@ -57,8 +57,8 @@ nv92_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
}
struct nouveau_oclass *
-nv92_gpio_oclass = &(struct nouveau_gpio_impl) {
- .base.handle = NV_SUBDEV(GPIO, 0x92),
+nv94_gpio_oclass = &(struct nouveau_gpio_impl) {
+ .base.handle = NV_SUBDEV(GPIO, 0x94),
.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = _nouveau_gpio_ctor,
.dtor = _nouveau_gpio_dtor,
@@ -66,8 +66,8 @@ nv92_gpio_oclass = &(struct nouveau_gpio_impl) {
.fini = _nouveau_gpio_fini,
},
.lines = 32,
- .intr_stat = nv92_gpio_intr_stat,
- .intr_mask = nv92_gpio_intr_mask,
+ .intr_stat = nv94_gpio_intr_stat,
+ .intr_mask = nv94_gpio_intr_mask,
.drive = nv50_gpio_drive,
.sense = nv50_gpio_sense,
.reset = nv50_gpio_reset,
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c
index a4682b0956ad..480d6d2af770 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c
@@ -77,8 +77,8 @@ nvd0_gpio_oclass = &(struct nouveau_gpio_impl) {
.fini = _nouveau_gpio_fini,
},
.lines = 32,
- .intr_stat = nv92_gpio_intr_stat,
- .intr_mask = nv92_gpio_intr_mask,
+ .intr_stat = nv94_gpio_intr_stat,
+ .intr_mask = nv94_gpio_intr_mask,
.drive = nvd0_gpio_drive,
.sense = nvd0_gpio_sense,
.reset = nvd0_gpio_reset,
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h
index e1724dfc86ae..bff98b86e2b5 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h
@@ -56,8 +56,8 @@ void nv50_gpio_reset(struct nouveau_gpio *, u8);
int nv50_gpio_drive(struct nouveau_gpio *, int, int, int);
int nv50_gpio_sense(struct nouveau_gpio *, int);
-void nv92_gpio_intr_stat(struct nouveau_gpio *, u32 *, u32 *);
-void nv92_gpio_intr_mask(struct nouveau_gpio *, u32, u32, u32);
+void nv94_gpio_intr_stat(struct nouveau_gpio *, u32 *, u32 *);
+void nv94_gpio_intr_mask(struct nouveau_gpio *, u32, u32, u32);
void nvd0_gpio_reset(struct nouveau_gpio *, u8);
int nvd0_gpio_drive(struct nouveau_gpio *, int, int, int);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
index 09ba2cc851cf..2b1bf545e488 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
@@ -23,6 +23,7 @@
*/
#include <core/option.h>
+#include <core/object.h>
#include <core/event.h>
#include <subdev/bios.h>
@@ -326,9 +327,9 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
}
static void
-nouveau_i2c_intr_disable(struct nouveau_event *event, int type, int index)
+nouveau_i2c_intr_fini(struct nvkm_event *event, int type, int index)
{
- struct nouveau_i2c *i2c = nouveau_i2c(event->priv);
+ struct nouveau_i2c *i2c = container_of(event, typeof(*i2c), event);
struct nouveau_i2c_port *port = i2c->find(i2c, index);
const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass;
if (port && port->aux >= 0)
@@ -336,15 +337,29 @@ nouveau_i2c_intr_disable(struct nouveau_event *event, int type, int index)
}
static void
-nouveau_i2c_intr_enable(struct nouveau_event *event, int type, int index)
+nouveau_i2c_intr_init(struct nvkm_event *event, int type, int index)
{
- struct nouveau_i2c *i2c = nouveau_i2c(event->priv);
+ struct nouveau_i2c *i2c = container_of(event, typeof(*i2c), event);
struct nouveau_i2c_port *port = i2c->find(i2c, index);
const struct nouveau_i2c_impl *impl = (void *)nv_object(i2c)->oclass;
if (port && port->aux >= 0)
impl->aux_mask(i2c, type, 1 << port->aux, 1 << port->aux);
}
+static int
+nouveau_i2c_intr_ctor(struct nouveau_object *object, void *data, u32 size,
+ struct nvkm_notify *notify)
+{
+ struct nvkm_i2c_ntfy_req *req = data;
+ if (!WARN_ON(size != sizeof(*req))) {
+ notify->size = sizeof(struct nvkm_i2c_ntfy_rep);
+ notify->types = req->mask;
+ notify->index = req->port;
+ return 0;
+ }
+ return -EINVAL;
+}
+
static void
nouveau_i2c_intr(struct nouveau_subdev *subdev)
{
@@ -364,13 +379,26 @@ nouveau_i2c_intr(struct nouveau_subdev *subdev)
if (lo & (1 << port->aux)) e |= NVKM_I2C_UNPLUG;
if (rq & (1 << port->aux)) e |= NVKM_I2C_IRQ;
if (tx & (1 << port->aux)) e |= NVKM_I2C_DONE;
-
- nouveau_event_trigger(i2c->ntfy, e, port->index);
+ if (e) {
+ struct nvkm_i2c_ntfy_rep rep = {
+ .mask = e,
+ };
+ nvkm_event_send(&i2c->event, rep.mask,
+ port->index, &rep,
+ sizeof(rep));
+ }
}
}
}
}
+static const struct nvkm_event_func
+nouveau_i2c_intr_func = {
+ .ctor = nouveau_i2c_intr_ctor,
+ .init = nouveau_i2c_intr_init,
+ .fini = nouveau_i2c_intr_fini,
+};
+
int
_nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
{
@@ -431,7 +459,7 @@ _nouveau_i2c_dtor(struct nouveau_object *object)
struct nouveau_i2c *i2c = (void *)object;
struct nouveau_i2c_port *port, *temp;
- nouveau_event_destroy(&i2c->ntfy);
+ nvkm_event_fini(&i2c->event);
list_for_each_entry_safe(port, temp, &i2c->ports, head) {
nouveau_object_ref(NULL, (struct nouveau_object **)&port);
@@ -547,13 +575,10 @@ nouveau_i2c_create_(struct nouveau_object *parent,
}
}
- ret = nouveau_event_create(4, index, &i2c->ntfy);
+ ret = nvkm_event_init(&nouveau_i2c_intr_func, 4, index, &i2c->event);
if (ret)
return ret;
- i2c->ntfy->priv = i2c;
- i2c->ntfy->enable = nouveau_i2c_intr_enable;
- i2c->ntfy->disable = nouveau_i2c_intr_disable;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c
index 7b64befee48f..e8b1401c59c0 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c
@@ -69,7 +69,7 @@ nv04_instobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
- ret = nouveau_mm_head(&priv->heap, 1, args->size, args->size,
+ ret = nouveau_mm_head(&priv->heap, 0, 1, args->size, args->size,
args->align, &node->mem);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltc/base.c b/drivers/gpu/drm/nouveau/core/subdev/ltc/base.c
new file mode 100644
index 000000000000..7fa331516f84
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/ltc/base.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "priv.h"
+
+static int
+nvkm_ltc_tags_alloc(struct nouveau_ltc *ltc, u32 n,
+ struct nouveau_mm_node **pnode)
+{
+ struct nvkm_ltc_priv *priv = (void *)ltc;
+ int ret;
+
+ ret = nouveau_mm_head(&priv->tags, 0, 1, n, n, 1, pnode);
+ if (ret)
+ *pnode = NULL;
+
+ return ret;
+}
+
+static void
+nvkm_ltc_tags_free(struct nouveau_ltc *ltc, struct nouveau_mm_node **pnode)
+{
+ struct nvkm_ltc_priv *priv = (void *)ltc;
+ nouveau_mm_free(&priv->tags, pnode);
+}
+
+static void
+nvkm_ltc_tags_clear(struct nouveau_ltc *ltc, u32 first, u32 count)
+{
+ const struct nvkm_ltc_impl *impl = (void *)nv_oclass(ltc);
+ struct nvkm_ltc_priv *priv = (void *)ltc;
+ const u32 limit = first + count - 1;
+
+ BUG_ON((first > limit) || (limit >= priv->num_tags));
+
+ impl->cbc_clear(priv, first, limit);
+ impl->cbc_wait(priv);
+}
+
+static int
+nvkm_ltc_zbc_color_get(struct nouveau_ltc *ltc, int index, const u32 color[4])
+{
+ const struct nvkm_ltc_impl *impl = (void *)nv_oclass(ltc);
+ struct nvkm_ltc_priv *priv = (void *)ltc;
+ memcpy(priv->zbc_color[index], color, sizeof(priv->zbc_color[index]));
+ impl->zbc_clear_color(priv, index, color);
+ return index;
+}
+
+static int
+nvkm_ltc_zbc_depth_get(struct nouveau_ltc *ltc, int index, const u32 depth)
+{
+ const struct nvkm_ltc_impl *impl = (void *)nv_oclass(ltc);
+ struct nvkm_ltc_priv *priv = (void *)ltc;
+ priv->zbc_depth[index] = depth;
+ impl->zbc_clear_depth(priv, index, depth);
+ return index;
+}
+
+int
+_nvkm_ltc_init(struct nouveau_object *object)
+{
+ const struct nvkm_ltc_impl *impl = (void *)nv_oclass(object);
+ struct nvkm_ltc_priv *priv = (void *)object;
+ int ret, i;
+
+ ret = nouveau_subdev_init(&priv->base.base);
+ if (ret)
+ return ret;
+
+ for (i = priv->base.zbc_min; i <= priv->base.zbc_max; i++) {
+ impl->zbc_clear_color(priv, i, priv->zbc_color[i]);
+ impl->zbc_clear_depth(priv, i, priv->zbc_depth[i]);
+ }
+
+ return 0;
+}
+
+int
+nvkm_ltc_create_(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, int length, void **pobject)
+{
+ const struct nvkm_ltc_impl *impl = (void *)oclass;
+ struct nvkm_ltc_priv *priv;
+ int ret;
+
+ ret = nouveau_subdev_create_(parent, engine, oclass, 0, "PLTCG",
+ "l2c", length, pobject);
+ priv = *pobject;
+ if (ret)
+ return ret;
+
+ memset(priv->zbc_color, 0x00, sizeof(priv->zbc_color));
+ memset(priv->zbc_depth, 0x00, sizeof(priv->zbc_depth));
+
+ priv->base.base.intr = impl->intr;
+ priv->base.tags_alloc = nvkm_ltc_tags_alloc;
+ priv->base.tags_free = nvkm_ltc_tags_free;
+ priv->base.tags_clear = nvkm_ltc_tags_clear;
+ priv->base.zbc_min = 1; /* reserve 0 for disabled */
+ priv->base.zbc_max = min(impl->zbc, NOUVEAU_LTC_MAX_ZBC_CNT) - 1;
+ priv->base.zbc_color_get = nvkm_ltc_zbc_color_get;
+ priv->base.zbc_depth_get = nvkm_ltc_zbc_depth_get;
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.c b/drivers/gpu/drm/nouveau/core/subdev/ltc/gf100.c
index f2f3338a967a..2db0977284f8 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/ltc/gf100.c
@@ -25,86 +25,126 @@
#include <subdev/fb.h>
#include <subdev/timer.h>
-#include "gf100.h"
+#include "priv.h"
+
+void
+gf100_ltc_cbc_clear(struct nvkm_ltc_priv *priv, u32 start, u32 limit)
+{
+ nv_wr32(priv, 0x17e8cc, start);
+ nv_wr32(priv, 0x17e8d0, limit);
+ nv_wr32(priv, 0x17e8c8, 0x00000004);
+}
+
+void
+gf100_ltc_cbc_wait(struct nvkm_ltc_priv *priv)
+{
+ int c, s;
+ for (c = 0; c < priv->ltc_nr; c++) {
+ for (s = 0; s < priv->lts_nr; s++)
+ nv_wait(priv, 0x1410c8 + c * 0x2000 + s * 0x400, ~0, 0);
+ }
+}
+
+void
+gf100_ltc_zbc_clear_color(struct nvkm_ltc_priv *priv, int i, const u32 color[4])
+{
+ nv_mask(priv, 0x17ea44, 0x0000000f, i);
+ nv_wr32(priv, 0x17ea48, color[0]);
+ nv_wr32(priv, 0x17ea4c, color[1]);
+ nv_wr32(priv, 0x17ea50, color[2]);
+ nv_wr32(priv, 0x17ea54, color[3]);
+}
+
+void
+gf100_ltc_zbc_clear_depth(struct nvkm_ltc_priv *priv, int i, const u32 depth)
+{
+ nv_mask(priv, 0x17ea44, 0x0000000f, i);
+ nv_wr32(priv, 0x17ea58, depth);
+}
+
+static const struct nouveau_bitfield
+gf100_ltc_lts_intr_name[] = {
+ { 0x00000001, "IDLE_ERROR_IQ" },
+ { 0x00000002, "IDLE_ERROR_CBC" },
+ { 0x00000004, "IDLE_ERROR_TSTG" },
+ { 0x00000008, "IDLE_ERROR_DSTG" },
+ { 0x00000010, "EVICTED_CB" },
+ { 0x00000020, "ILLEGAL_COMPSTAT" },
+ { 0x00000040, "BLOCKLINEAR_CB" },
+ { 0x00000100, "ECC_SEC_ERROR" },
+ { 0x00000200, "ECC_DED_ERROR" },
+ { 0x00000400, "DEBUG" },
+ { 0x00000800, "ATOMIC_TO_Z" },
+ { 0x00001000, "ILLEGAL_ATOMIC" },
+ { 0x00002000, "BLKACTIVITY_ERR" },
+ {}
+};
static void
-gf100_ltcg_lts_isr(struct gf100_ltcg_priv *priv, int ltc, int lts)
+gf100_ltc_lts_intr(struct nvkm_ltc_priv *priv, int ltc, int lts)
{
u32 base = 0x141000 + (ltc * 0x2000) + (lts * 0x400);
- u32 stat = nv_rd32(priv, base + 0x020);
+ u32 intr = nv_rd32(priv, base + 0x020);
+ u32 stat = intr & 0x0000ffff;
if (stat) {
- nv_info(priv, "LTC%d_LTS%d: 0x%08x\n", ltc, lts, stat);
- nv_wr32(priv, base + 0x020, stat);
+ nv_info(priv, "LTC%d_LTS%d:", ltc, lts);
+ nouveau_bitfield_print(gf100_ltc_lts_intr_name, stat);
+ pr_cont("\n");
}
+
+ nv_wr32(priv, base + 0x020, intr);
}
-static void
-gf100_ltcg_intr(struct nouveau_subdev *subdev)
+void
+gf100_ltc_intr(struct nouveau_subdev *subdev)
{
- struct gf100_ltcg_priv *priv = (void *)subdev;
+ struct nvkm_ltc_priv *priv = (void *)subdev;
u32 mask;
mask = nv_rd32(priv, 0x00017c);
while (mask) {
u32 lts, ltc = __ffs(mask);
for (lts = 0; lts < priv->lts_nr; lts++)
- gf100_ltcg_lts_isr(priv, ltc, lts);
+ gf100_ltc_lts_intr(priv, ltc, lts);
mask &= ~(1 << ltc);
}
-
- /* we do something horribly wrong and upset PMFB a lot, so mask off
- * interrupts from it after the first one until it's fixed
- */
- nv_mask(priv, 0x000640, 0x02000000, 0x00000000);
}
-int
-gf100_ltcg_tags_alloc(struct nouveau_ltcg *ltcg, u32 n,
- struct nouveau_mm_node **pnode)
+static int
+gf100_ltc_init(struct nouveau_object *object)
{
- struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
+ struct nvkm_ltc_priv *priv = (void *)object;
+ u32 lpg128 = !(nv_rd32(priv, 0x100c80) & 0x00000001);
int ret;
- ret = nouveau_mm_head(&priv->tags, 1, n, n, 1, pnode);
+ ret = nvkm_ltc_init(priv);
if (ret)
- *pnode = NULL;
+ return ret;
- return ret;
+ nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */
+ nv_wr32(priv, 0x17e8d8, priv->ltc_nr);
+ nv_wr32(priv, 0x17e8d4, priv->tag_base);
+ nv_mask(priv, 0x17e8c0, 0x00000002, lpg128 ? 0x00000002 : 0x00000000);
+ return 0;
}
void
-gf100_ltcg_tags_free(struct nouveau_ltcg *ltcg, struct nouveau_mm_node **pnode)
-{
- struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
-
- nouveau_mm_free(&priv->tags, pnode);
-}
-
-static void
-gf100_ltcg_tags_clear(struct nouveau_ltcg *ltcg, u32 first, u32 count)
+gf100_ltc_dtor(struct nouveau_object *object)
{
- struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
- u32 last = first + count - 1;
- int p, i;
+ struct nouveau_fb *pfb = nouveau_fb(object);
+ struct nvkm_ltc_priv *priv = (void *)object;
- BUG_ON((first > last) || (last >= priv->num_tags));
-
- nv_wr32(priv, 0x17e8cc, first);
- nv_wr32(priv, 0x17e8d0, last);
- nv_wr32(priv, 0x17e8c8, 0x4); /* trigger clear */
+ nouveau_mm_fini(&priv->tags);
+ nouveau_mm_free(&pfb->vram, &priv->tag_ram);
- /* wait until it's finished with clearing */
- for (p = 0; p < priv->ltc_nr; ++p) {
- for (i = 0; i < priv->lts_nr; ++i)
- nv_wait(priv, 0x1410c8 + p * 0x2000 + i * 0x400, ~0, 0);
- }
+ nvkm_ltc_destroy(priv);
}
/* TODO: Figure out tag memory details and drop the over-cautious allocation.
*/
int
-gf100_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct gf100_ltcg_priv *priv)
+gf100_ltc_init_tag_ram(struct nouveau_fb *pfb, struct nvkm_ltc_priv *priv)
{
u32 tag_size, tag_margin, tag_align;
int ret;
@@ -130,34 +170,34 @@ gf100_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct gf100_ltcg_priv *priv)
tag_size += tag_align;
tag_size = (tag_size + 0xfff) >> 12; /* round up */
- ret = nouveau_mm_tail(&pfb->vram, 1, tag_size, tag_size, 1,
+ ret = nouveau_mm_tail(&pfb->vram, 1, 1, tag_size, tag_size, 1,
&priv->tag_ram);
if (ret) {
priv->num_tags = 0;
} else {
- u64 tag_base = (priv->tag_ram->offset << 12) + tag_margin;
+ u64 tag_base = ((u64)priv->tag_ram->offset << 12) + tag_margin;
tag_base += tag_align - 1;
ret = do_div(tag_base, tag_align);
priv->tag_base = tag_base;
}
- ret = nouveau_mm_init(&priv->tags, 0, priv->num_tags, 1);
+ ret = nouveau_mm_init(&priv->tags, 0, priv->num_tags, 1);
return ret;
}
-static int
-gf100_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+int
+gf100_ltc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
- struct gf100_ltcg_priv *priv;
struct nouveau_fb *pfb = nouveau_fb(parent);
+ struct nvkm_ltc_priv *priv;
u32 parts, mask;
int ret, i;
- ret = nouveau_ltcg_create(parent, engine, oclass, &priv);
+ ret = nvkm_ltc_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
@@ -170,57 +210,27 @@ gf100_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
}
priv->lts_nr = nv_rd32(priv, 0x17e8dc) >> 28;
- ret = gf100_ltcg_init_tag_ram(pfb, priv);
- if (ret)
- return ret;
-
- priv->base.tags_alloc = gf100_ltcg_tags_alloc;
- priv->base.tags_free = gf100_ltcg_tags_free;
- priv->base.tags_clear = gf100_ltcg_tags_clear;
-
- nv_subdev(priv)->intr = gf100_ltcg_intr;
- return 0;
-}
-
-void
-gf100_ltcg_dtor(struct nouveau_object *object)
-{
- struct nouveau_ltcg *ltcg = (struct nouveau_ltcg *)object;
- struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
- struct nouveau_fb *pfb = nouveau_fb(ltcg->base.base.parent);
-
- nouveau_mm_fini(&priv->tags);
- nouveau_mm_free(&pfb->vram, &priv->tag_ram);
-
- nouveau_ltcg_destroy(ltcg);
-}
-
-static int
-gf100_ltcg_init(struct nouveau_object *object)
-{
- struct nouveau_ltcg *ltcg = (struct nouveau_ltcg *)object;
- struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
- int ret;
-
- ret = nouveau_ltcg_init(ltcg);
+ ret = gf100_ltc_init_tag_ram(pfb, priv);
if (ret)
return ret;
- nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */
- nv_wr32(priv, 0x17e8d8, priv->ltc_nr);
- if (nv_device(ltcg)->card_type >= NV_E0)
- nv_wr32(priv, 0x17e000, priv->ltc_nr);
- nv_wr32(priv, 0x17e8d4, priv->tag_base);
+ nv_subdev(priv)->intr = gf100_ltc_intr;
return 0;
}
struct nouveau_oclass *
-gf100_ltcg_oclass = &(struct nouveau_oclass) {
- .handle = NV_SUBDEV(LTCG, 0xc0),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = gf100_ltcg_ctor,
- .dtor = gf100_ltcg_dtor,
- .init = gf100_ltcg_init,
- .fini = _nouveau_ltcg_fini,
+gf100_ltc_oclass = &(struct nvkm_ltc_impl) {
+ .base.handle = NV_SUBDEV(LTC, 0xc0),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gf100_ltc_ctor,
+ .dtor = gf100_ltc_dtor,
+ .init = gf100_ltc_init,
+ .fini = _nvkm_ltc_fini,
},
-};
+ .intr = gf100_ltc_intr,
+ .cbc_clear = gf100_ltc_cbc_clear,
+ .cbc_wait = gf100_ltc_cbc_wait,
+ .zbc = 16,
+ .zbc_clear_color = gf100_ltc_zbc_clear_color,
+ .zbc_clear_depth = gf100_ltc_zbc_clear_depth,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltc/gk104.c b/drivers/gpu/drm/nouveau/core/subdev/ltc/gk104.c
new file mode 100644
index 000000000000..b39b5d0eb8f9
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/ltc/gk104.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "priv.h"
+
+static int
+gk104_ltc_init(struct nouveau_object *object)
+{
+ struct nvkm_ltc_priv *priv = (void *)object;
+ u32 lpg128 = !(nv_rd32(priv, 0x100c80) & 0x00000001);
+ int ret;
+
+ ret = nvkm_ltc_init(priv);
+ if (ret)
+ return ret;
+
+ nv_wr32(priv, 0x17e8d8, priv->ltc_nr);
+ nv_wr32(priv, 0x17e000, priv->ltc_nr);
+ nv_wr32(priv, 0x17e8d4, priv->tag_base);
+ nv_mask(priv, 0x17e8c0, 0x00000002, lpg128 ? 0x00000002 : 0x00000000);
+ return 0;
+}
+
+struct nouveau_oclass *
+gk104_ltc_oclass = &(struct nvkm_ltc_impl) {
+ .base.handle = NV_SUBDEV(LTC, 0xe4),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gf100_ltc_ctor,
+ .dtor = gf100_ltc_dtor,
+ .init = gk104_ltc_init,
+ .fini = _nvkm_ltc_fini,
+ },
+ .intr = gf100_ltc_intr,
+ .cbc_clear = gf100_ltc_cbc_clear,
+ .cbc_wait = gf100_ltc_cbc_wait,
+ .zbc = 16,
+ .zbc_clear_color = gf100_ltc_zbc_clear_color,
+ .zbc_clear_depth = gf100_ltc_zbc_clear_depth,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltcg/gm107.c b/drivers/gpu/drm/nouveau/core/subdev/ltc/gm107.c
index e79d0e81de40..89fc4238f50c 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/ltcg/gm107.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/ltc/gm107.c
@@ -25,10 +25,45 @@
#include <subdev/fb.h>
#include <subdev/timer.h>
-#include "gf100.h"
+#include "priv.h"
static void
-gm107_ltcg_lts_isr(struct gf100_ltcg_priv *priv, int ltc, int lts)
+gm107_ltc_cbc_clear(struct nvkm_ltc_priv *priv, u32 start, u32 limit)
+{
+ nv_wr32(priv, 0x17e270, start);
+ nv_wr32(priv, 0x17e274, limit);
+ nv_wr32(priv, 0x17e26c, 0x00000004);
+}
+
+static void
+gm107_ltc_cbc_wait(struct nvkm_ltc_priv *priv)
+{
+ int c, s;
+ for (c = 0; c < priv->ltc_nr; c++) {
+ for (s = 0; s < priv->lts_nr; s++)
+ nv_wait(priv, 0x14046c + c * 0x2000 + s * 0x200, ~0, 0);
+ }
+}
+
+static void
+gm107_ltc_zbc_clear_color(struct nvkm_ltc_priv *priv, int i, const u32 color[4])
+{
+ nv_mask(priv, 0x17e338, 0x0000000f, i);
+ nv_wr32(priv, 0x17e33c, color[0]);
+ nv_wr32(priv, 0x17e340, color[1]);
+ nv_wr32(priv, 0x17e344, color[2]);
+ nv_wr32(priv, 0x17e348, color[3]);
+}
+
+static void
+gm107_ltc_zbc_clear_depth(struct nvkm_ltc_priv *priv, int i, const u32 depth)
+{
+ nv_mask(priv, 0x17e338, 0x0000000f, i);
+ nv_wr32(priv, 0x17e34c, depth);
+}
+
+static void
+gm107_ltc_lts_isr(struct nvkm_ltc_priv *priv, int ltc, int lts)
{
u32 base = 0x140000 + (ltc * 0x2000) + (lts * 0x400);
u32 stat = nv_rd32(priv, base + 0x00c);
@@ -40,56 +75,48 @@ gm107_ltcg_lts_isr(struct gf100_ltcg_priv *priv, int ltc, int lts)
}
static void
-gm107_ltcg_intr(struct nouveau_subdev *subdev)
+gm107_ltc_intr(struct nouveau_subdev *subdev)
{
- struct gf100_ltcg_priv *priv = (void *)subdev;
+ struct nvkm_ltc_priv *priv = (void *)subdev;
u32 mask;
mask = nv_rd32(priv, 0x00017c);
while (mask) {
u32 lts, ltc = __ffs(mask);
for (lts = 0; lts < priv->lts_nr; lts++)
- gm107_ltcg_lts_isr(priv, ltc, lts);
+ gm107_ltc_lts_isr(priv, ltc, lts);
mask &= ~(1 << ltc);
}
-
- /* we do something horribly wrong and upset PMFB a lot, so mask off
- * interrupts from it after the first one until it's fixed
- */
- nv_mask(priv, 0x000640, 0x02000000, 0x00000000);
}
-static void
-gm107_ltcg_tags_clear(struct nouveau_ltcg *ltcg, u32 first, u32 count)
+static int
+gm107_ltc_init(struct nouveau_object *object)
{
- struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
- u32 last = first + count - 1;
- int p, i;
-
- BUG_ON((first > last) || (last >= priv->num_tags));
+ struct nvkm_ltc_priv *priv = (void *)object;
+ u32 lpg128 = !(nv_rd32(priv, 0x100c80) & 0x00000001);
+ int ret;
- nv_wr32(priv, 0x17e270, first);
- nv_wr32(priv, 0x17e274, last);
- nv_wr32(priv, 0x17e26c, 0x4); /* trigger clear */
+ ret = nvkm_ltc_init(priv);
+ if (ret)
+ return ret;
- /* wait until it's finished with clearing */
- for (p = 0; p < priv->ltc_nr; ++p) {
- for (i = 0; i < priv->lts_nr; ++i)
- nv_wait(priv, 0x14046c + p * 0x2000 + i * 0x200, ~0, 0);
- }
+ nv_wr32(priv, 0x17e27c, priv->ltc_nr);
+ nv_wr32(priv, 0x17e278, priv->tag_base);
+ nv_mask(priv, 0x17e264, 0x00000002, lpg128 ? 0x00000002 : 0x00000000);
+ return 0;
}
static int
-gm107_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+gm107_ltc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
struct nouveau_object **pobject)
{
- struct gf100_ltcg_priv *priv;
struct nouveau_fb *pfb = nouveau_fb(parent);
+ struct nvkm_ltc_priv *priv;
u32 parts, mask;
int ret, i;
- ret = nouveau_ltcg_create(parent, engine, oclass, &priv);
+ ret = nvkm_ltc_create(parent, engine, oclass, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
@@ -102,41 +129,26 @@ gm107_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
}
priv->lts_nr = nv_rd32(priv, 0x17e280) >> 28;
- ret = gf100_ltcg_init_tag_ram(pfb, priv);
- if (ret)
- return ret;
-
- priv->base.tags_alloc = gf100_ltcg_tags_alloc;
- priv->base.tags_free = gf100_ltcg_tags_free;
- priv->base.tags_clear = gm107_ltcg_tags_clear;
-
- nv_subdev(priv)->intr = gm107_ltcg_intr;
- return 0;
-}
-
-static int
-gm107_ltcg_init(struct nouveau_object *object)
-{
- struct nouveau_ltcg *ltcg = (struct nouveau_ltcg *)object;
- struct gf100_ltcg_priv *priv = (struct gf100_ltcg_priv *)ltcg;
- int ret;
-
- ret = nouveau_ltcg_init(ltcg);
+ ret = gf100_ltc_init_tag_ram(pfb, priv);
if (ret)
return ret;
- nv_wr32(priv, 0x17e27c, priv->ltc_nr);
- nv_wr32(priv, 0x17e278, priv->tag_base);
return 0;
}
struct nouveau_oclass *
-gm107_ltcg_oclass = &(struct nouveau_oclass) {
- .handle = NV_SUBDEV(LTCG, 0xff),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = gm107_ltcg_ctor,
- .dtor = gf100_ltcg_dtor,
- .init = gm107_ltcg_init,
- .fini = _nouveau_ltcg_fini,
+gm107_ltc_oclass = &(struct nvkm_ltc_impl) {
+ .base.handle = NV_SUBDEV(LTC, 0xff),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gm107_ltc_ctor,
+ .dtor = gf100_ltc_dtor,
+ .init = gm107_ltc_init,
+ .fini = _nvkm_ltc_fini,
},
-};
+ .intr = gm107_ltc_intr,
+ .cbc_clear = gm107_ltc_cbc_clear,
+ .cbc_wait = gm107_ltc_cbc_wait,
+ .zbc = 16,
+ .zbc_clear_color = gm107_ltc_zbc_clear_color,
+ .zbc_clear_depth = gm107_ltc_zbc_clear_depth,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltc/priv.h b/drivers/gpu/drm/nouveau/core/subdev/ltc/priv.h
new file mode 100644
index 000000000000..41f179d93da6
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/ltc/priv.h
@@ -0,0 +1,71 @@
+#ifndef __NVKM_LTC_PRIV_H__
+#define __NVKM_LTC_PRIV_H__
+
+#include <subdev/ltc.h>
+#include <subdev/fb.h>
+
+#include <core/enum.h>
+
+struct nvkm_ltc_priv {
+ struct nouveau_ltc base;
+ u32 ltc_nr;
+ u32 lts_nr;
+
+ u32 num_tags;
+ u32 tag_base;
+ struct nouveau_mm tags;
+ struct nouveau_mm_node *tag_ram;
+
+ u32 zbc_color[NOUVEAU_LTC_MAX_ZBC_CNT][4];
+ u32 zbc_depth[NOUVEAU_LTC_MAX_ZBC_CNT];
+};
+
+#define nvkm_ltc_create(p,e,o,d) \
+ nvkm_ltc_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nvkm_ltc_destroy(p) ({ \
+ struct nvkm_ltc_priv *_priv = (p); \
+ _nvkm_ltc_dtor(nv_object(_priv)); \
+})
+#define nvkm_ltc_init(p) ({ \
+ struct nvkm_ltc_priv *_priv = (p); \
+ _nvkm_ltc_init(nv_object(_priv)); \
+})
+#define nvkm_ltc_fini(p,s) ({ \
+ struct nvkm_ltc_priv *_priv = (p); \
+ _nvkm_ltc_fini(nv_object(_priv), (s)); \
+})
+
+int nvkm_ltc_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, int, void **);
+
+#define _nvkm_ltc_dtor _nouveau_subdev_dtor
+int _nvkm_ltc_init(struct nouveau_object *);
+#define _nvkm_ltc_fini _nouveau_subdev_fini
+
+int gf100_ltc_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+void gf100_ltc_dtor(struct nouveau_object *);
+int gf100_ltc_init_tag_ram(struct nouveau_fb *, struct nvkm_ltc_priv *);
+int gf100_ltc_tags_alloc(struct nouveau_ltc *, u32, struct nouveau_mm_node **);
+void gf100_ltc_tags_free(struct nouveau_ltc *, struct nouveau_mm_node **);
+
+struct nvkm_ltc_impl {
+ struct nouveau_oclass base;
+ void (*intr)(struct nouveau_subdev *);
+
+ void (*cbc_clear)(struct nvkm_ltc_priv *, u32 start, u32 limit);
+ void (*cbc_wait)(struct nvkm_ltc_priv *);
+
+ int zbc;
+ void (*zbc_clear_color)(struct nvkm_ltc_priv *, int, const u32[4]);
+ void (*zbc_clear_depth)(struct nvkm_ltc_priv *, int, const u32);
+};
+
+void gf100_ltc_intr(struct nouveau_subdev *);
+void gf100_ltc_cbc_clear(struct nvkm_ltc_priv *, u32, u32);
+void gf100_ltc_cbc_wait(struct nvkm_ltc_priv *);
+void gf100_ltc_zbc_clear_color(struct nvkm_ltc_priv *, int, const u32[4]);
+void gf100_ltc_zbc_clear_depth(struct nvkm_ltc_priv *, int, const u32);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.h b/drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.h
deleted file mode 100644
index 87b10b8412ea..000000000000
--- a/drivers/gpu/drm/nouveau/core/subdev/ltcg/gf100.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef __NVKM_LTCG_PRIV_GF100_H__
-#define __NVKM_LTCG_PRIV_GF100_H__
-
-#include <subdev/ltcg.h>
-
-struct gf100_ltcg_priv {
- struct nouveau_ltcg base;
- u32 ltc_nr;
- u32 lts_nr;
- u32 num_tags;
- u32 tag_base;
- struct nouveau_mm tags;
- struct nouveau_mm_node *tag_ram;
-};
-
-void gf100_ltcg_dtor(struct nouveau_object *);
-int gf100_ltcg_init_tag_ram(struct nouveau_fb *, struct gf100_ltcg_priv *);
-int gf100_ltcg_tags_alloc(struct nouveau_ltcg *, u32, struct nouveau_mm_node **);
-void gf100_ltcg_tags_free(struct nouveau_ltcg *, struct nouveau_mm_node **);
-
-#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c
index 8a5555192fa5..ca7cee3a314a 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c
@@ -22,9 +22,17 @@
* Authors: Ben Skeggs
*/
-#include <subdev/mc.h>
+#include "priv.h"
#include <core/option.h>
+static inline void
+nouveau_mc_unk260(struct nouveau_mc *pmc, u32 data)
+{
+ const struct nouveau_mc_oclass *impl = (void *)nv_oclass(pmc);
+ if (impl->unk260)
+ impl->unk260(pmc, data);
+}
+
static inline u32
nouveau_mc_intr_mask(struct nouveau_mc *pmc)
{
@@ -114,6 +122,8 @@ nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ pmc->unk260 = nouveau_mc_unk260;
+
if (nv_device_is_pci(device))
switch (device->pdev->device & 0x0ff0) {
case 0x00f0:
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/gk20a.c b/drivers/gpu/drm/nouveau/core/subdev/mc/gk20a.c
new file mode 100644
index 000000000000..b8d6cb435d0a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/gk20a.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "nv04.h"
+
+struct nouveau_oclass *
+gk20a_mc_oclass = &(struct nouveau_mc_oclass) {
+ .base.handle = NV_SUBDEV(MC, 0xea),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nv04_mc_ctor,
+ .dtor = _nouveau_mc_dtor,
+ .init = nv50_mc_init,
+ .fini = _nouveau_mc_fini,
+ },
+ .intr = nvc0_mc_intr,
+ .msi_rearm = nv40_mc_msi_rearm,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.h b/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.h
index 81a408e7d034..4d9ea46c47c2 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.h
@@ -1,7 +1,7 @@
#ifndef __NVKM_MC_NV04_H__
#define __NVKM_MC_NV04_H__
-#include <subdev/mc.h>
+#include "priv.h"
struct nv04_mc_priv {
struct nouveau_mc base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
index f9c6a678b47d..15d41dc176ff 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
@@ -41,7 +41,7 @@ nvc0_mc_intr[] = {
{ 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */
{ 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */
{ 0x01000000, NVDEV_SUBDEV_PWR },
- { 0x02000000, NVDEV_SUBDEV_LTCG },
+ { 0x02000000, NVDEV_SUBDEV_LTC },
{ 0x08000000, NVDEV_SUBDEV_FB },
{ 0x10000000, NVDEV_SUBDEV_BUS },
{ 0x40000000, NVDEV_SUBDEV_IBUS },
@@ -56,6 +56,12 @@ nvc0_mc_msi_rearm(struct nouveau_mc *pmc)
nv_wr32(priv, 0x088704, 0x00000000);
}
+void
+nvc0_mc_unk260(struct nouveau_mc *pmc, u32 data)
+{
+ nv_wr32(pmc, 0x000260, data);
+}
+
struct nouveau_oclass *
nvc0_mc_oclass = &(struct nouveau_mc_oclass) {
.base.handle = NV_SUBDEV(MC, 0xc0),
@@ -67,4 +73,5 @@ nvc0_mc_oclass = &(struct nouveau_mc_oclass) {
},
.intr = nvc0_mc_intr,
.msi_rearm = nvc0_mc_msi_rearm,
+ .unk260 = nvc0_mc_unk260,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc3.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc3.c
index 837e545aeb9f..68b5f61aadb5 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc3.c
@@ -35,4 +35,5 @@ nvc3_mc_oclass = &(struct nouveau_mc_oclass) {
},
.intr = nvc0_mc_intr,
.msi_rearm = nv40_mc_msi_rearm,
+ .unk260 = nvc0_mc_unk260,
}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/priv.h b/drivers/gpu/drm/nouveau/core/subdev/mc/priv.h
new file mode 100644
index 000000000000..911e66392587
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/priv.h
@@ -0,0 +1,38 @@
+#ifndef __NVKM_MC_PRIV_H__
+#define __NVKM_MC_PRIV_H__
+
+#include <subdev/mc.h>
+
+#define nouveau_mc_create(p,e,o,d) \
+ nouveau_mc_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_mc_destroy(p) ({ \
+ struct nouveau_mc *pmc = (p); _nouveau_mc_dtor(nv_object(pmc)); \
+})
+#define nouveau_mc_init(p) ({ \
+ struct nouveau_mc *pmc = (p); _nouveau_mc_init(nv_object(pmc)); \
+})
+#define nouveau_mc_fini(p,s) ({ \
+ struct nouveau_mc *pmc = (p); _nouveau_mc_fini(nv_object(pmc), (s)); \
+})
+
+int nouveau_mc_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, int, void **);
+void _nouveau_mc_dtor(struct nouveau_object *);
+int _nouveau_mc_init(struct nouveau_object *);
+int _nouveau_mc_fini(struct nouveau_object *, bool);
+
+struct nouveau_mc_intr {
+ u32 stat;
+ u32 unit;
+};
+
+struct nouveau_mc_oclass {
+ struct nouveau_oclass base;
+ const struct nouveau_mc_intr *intr;
+ void (*msi_rearm)(struct nouveau_mc *);
+ void (*unk260)(struct nouveau_mc *, u32);
+};
+
+void nvc0_mc_unk260(struct nouveau_mc *, u32);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/base.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/base.c
index d4fd3bc9c66f..0ab55f27ec45 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/base.c
@@ -22,9 +22,18 @@
* Authors: Ben Skeggs
*/
-#include <subdev/pwr.h>
#include <subdev/timer.h>
+#include "priv.h"
+
+static void
+nouveau_pwr_pgob(struct nouveau_pwr *ppwr, bool enable)
+{
+ const struct nvkm_pwr_impl *impl = (void *)nv_oclass(ppwr);
+ if (impl->pgob)
+ impl->pgob(ppwr, enable);
+}
+
static int
nouveau_pwr_send(struct nouveau_pwr *ppwr, u32 reply[2],
u32 process, u32 message, u32 data0, u32 data1)
@@ -177,6 +186,7 @@ _nouveau_pwr_fini(struct nouveau_object *object, bool suspend)
int
_nouveau_pwr_init(struct nouveau_object *object)
{
+ const struct nvkm_pwr_impl *impl = (void *)object->oclass;
struct nouveau_pwr *ppwr = (void *)object;
int ret, i;
@@ -186,24 +196,27 @@ _nouveau_pwr_init(struct nouveau_object *object)
nv_subdev(ppwr)->intr = nouveau_pwr_intr;
ppwr->message = nouveau_pwr_send;
+ ppwr->pgob = nouveau_pwr_pgob;
/* prevent previous ucode from running, wait for idle, reset */
nv_wr32(ppwr, 0x10a014, 0x0000ffff); /* INTR_EN_CLR = ALL */
nv_wait(ppwr, 0x10a04c, 0xffffffff, 0x00000000);
nv_mask(ppwr, 0x000200, 0x00002000, 0x00000000);
nv_mask(ppwr, 0x000200, 0x00002000, 0x00002000);
+ nv_rd32(ppwr, 0x000200);
+ nv_wait(ppwr, 0x10a10c, 0x00000006, 0x00000000);
/* upload data segment */
nv_wr32(ppwr, 0x10a1c0, 0x01000000);
- for (i = 0; i < ppwr->data.size / 4; i++)
- nv_wr32(ppwr, 0x10a1c4, ppwr->data.data[i]);
+ for (i = 0; i < impl->data.size / 4; i++)
+ nv_wr32(ppwr, 0x10a1c4, impl->data.data[i]);
/* upload code segment */
nv_wr32(ppwr, 0x10a180, 0x01000000);
- for (i = 0; i < ppwr->code.size / 4; i++) {
+ for (i = 0; i < impl->code.size / 4; i++) {
if ((i & 0x3f) == 0)
nv_wr32(ppwr, 0x10a188, i >> 6);
- nv_wr32(ppwr, 0x10a184, ppwr->code.data[i]);
+ nv_wr32(ppwr, 0x10a184, impl->code.data[i]);
}
/* start it running */
@@ -245,3 +258,15 @@ nouveau_pwr_create_(struct nouveau_object *parent,
init_waitqueue_head(&ppwr->recv.wait);
return 0;
}
+
+int
+_nouveau_pwr_ctor(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct nouveau_pwr *ppwr;
+ int ret = nouveau_pwr_create(parent, engine, oclass, &ppwr);
+ *pobject = nv_object(ppwr);
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/arith.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/arith.fuc
new file mode 100644
index 000000000000..214a6d9e088d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/arith.fuc
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 Martin Peres <martin.peres@free.fr>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the folloing conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Martin Peres
+ */
+
+/******************************************************************************
+ * arith data segment
+ *****************************************************************************/
+#ifdef INCLUDE_PROC
+#endif
+
+#ifdef INCLUDE_DATA
+#endif
+
+/******************************************************************************
+ * arith code segment
+ *****************************************************************************/
+#ifdef INCLUDE_CODE
+
+// does a 32x32 -> 64 multiplication
+//
+// A * B = A_lo * B_lo
+// + ( A_hi * B_lo ) << 16
+// + ( A_lo * B_hi ) << 16
+// + ( A_hi * B_hi ) << 32
+//
+// $r15 - current
+// $r14 - A
+// $r13 - B
+// $r12 - mul_lo (return)
+// $r11 - mul_hi (return)
+// $r0 - zero
+mulu32_32_64:
+ push $r1 // A_hi
+ push $r2 // B_hi
+ push $r3 // tmp0
+ push $r4 // tmp1
+
+ shr b32 $r1 $r14 16
+ shr b32 $r2 $r13 16
+
+ clear b32 $r12
+ clear b32 $r11
+
+ // A_lo * B_lo
+ mulu $r12 $r14 $r13
+
+ // ( A_hi * B_lo ) << 16
+ mulu $r3 $r1 $r13 // tmp0 = A_hi * B_lo
+ mov b32 $r4 $r3
+ and $r3 0xffff // tmp0 = tmp0_lo
+ shl b32 $r3 16
+ shr b32 $r4 16 // tmp1 = tmp0_hi
+ add b32 $r12 $r3
+ adc b32 $r11 $r4
+
+ // ( A_lo * B_hi ) << 16
+ mulu $r3 $r14 $r2 // tmp0 = A_lo * B_hi
+ mov b32 $r4 $r3
+ and $r3 0xffff // tmp0 = tmp0_lo
+ shl b32 $r3 16
+ shr b32 $r4 16 // tmp1 = tmp0_hi
+ add b32 $r12 $r3
+ adc b32 $r11 $r4
+
+ // ( A_hi * B_hi ) << 32
+ mulu $r3 $r1 $r2 // tmp0 = A_hi * B_hi
+ add b32 $r11 $r3
+
+ pop $r4
+ pop $r3
+ pop $r2
+ pop $r1
+ ret
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc
index 8f29badd785f..5cf5be63cbef 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/kernel.fuc
@@ -98,12 +98,16 @@ wr32:
// $r14 - ns
// $r0 - zero
nsec:
+ push $r9
+ push $r8
nv_iord($r8, NV_PPWR_TIMER_LOW)
nsec_loop:
nv_iord($r9, NV_PPWR_TIMER_LOW)
sub b32 $r9 $r8
cmp b32 $r9 $r14
bra l #nsec_loop
+ pop $r8
+ pop $r9
ret
// busy-wait for a period of time
@@ -115,6 +119,8 @@ nsec:
// $r11 - timeout (ns)
// $r0 - zero
wait:
+ push $r9
+ push $r8
nv_iord($r8, NV_PPWR_TIMER_LOW)
wait_loop:
nv_rd32($r10, $r14)
@@ -126,6 +132,8 @@ wait:
cmp b32 $r9 $r11
bra l #wait_loop
wait_done:
+ pop $r8
+ pop $r9
ret
// $r15 - current (kern)
@@ -242,12 +250,89 @@ intr:
bclr $flags $p0
iret
-// request the current process be sent a message after a timeout expires
+// calculate the number of ticks in the specified nanoseconds delay
+//
+// $r15 - current
+// $r14 - ns
+// $r14 - ticks (return)
+// $r0 - zero
+ticks_from_ns:
+ push $r12
+ push $r11
+
+ /* try not losing precision (multiply then divide) */
+ imm32($r13, HW_TICKS_PER_US)
+ call #mulu32_32_64
+
+ /* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */
+ div $r12 $r12 1000
+
+ /* check if there wasn't any overflow */
+ cmpu b32 $r11 0
+ bra e #ticks_from_ns_quit
+
+ /* let's divide then multiply, too bad for the precision! */
+ div $r14 $r14 1000
+ imm32($r13, HW_TICKS_PER_US)
+ call #mulu32_32_64
+
+ /* this cannot overflow as long as HW_TICKS_PER_US < 1000 */
+
+ticks_from_ns_quit:
+ mov b32 $r14 $r12
+ pop $r11
+ pop $r12
+ ret
+
+// calculate the number of ticks in the specified microsecond delay
+//
+// $r15 - current
+// $r14 - us
+// $r14 - ticks (return)
+// $r0 - zero
+ticks_from_us:
+ push $r12
+ push $r11
+
+ /* simply multiply $us by HW_TICKS_PER_US */
+ imm32($r13, HW_TICKS_PER_US)
+ call #mulu32_32_64
+ mov b32 $r14 $r12
+
+ /* check if there wasn't any overflow */
+ cmpu b32 $r11 0
+ bra e #ticks_from_us_quit
+
+ /* Overflow! */
+ clear b32 $r14
+
+ticks_from_us_quit:
+ pop $r11
+ pop $r12
+ ret
+
+// calculate the number of ticks in the specified microsecond delay
//
// $r15 - current
// $r14 - ticks
+// $r14 - us (return)
+// $r0 - zero
+ticks_to_us:
+ /* simply divide $ticks by HW_TICKS_PER_US */
+ imm32($r13, HW_TICKS_PER_US)
+ div $r14 $r14 $r13
+
+ ret
+
+// request the current process be sent a message after a timeout expires
+//
+// $r15 - current
+// $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow)
// $r0 - zero
timer:
+ push $r9
+ push $r8
+
// interrupts off to prevent racing with timer isr
bclr $flags ie0
@@ -255,13 +340,22 @@ timer:
ld b32 $r8 D[$r15 + #proc_time]
cmp b32 $r8 0
bra g #timer_done
- st b32 D[$r15 + #proc_time] $r14
- // halt watchdog timer temporarily and check for a pending
- // interrupt. if there's one already pending, we can just
- // bail since the timer isr will queue the next soonest
- // right after it's done
+ // halt watchdog timer temporarily
+ clear b32 $r8
nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
+
+ // find out how much time elapsed since the last update
+ // of the watchdog and add this time to the wanted ticks
+ nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
+ ld b32 $r9 D[$r0 + #time_prev]
+ sub b32 $r9 $r8
+ add b32 $r14 $r9
+ st b32 D[$r15 + #proc_time] $r14
+
+ // check for a pending interrupt. if there's one already
+ // pending, we can just bail since the timer isr will
+ // queue the next soonest right after it's done
nv_iord($r8, NV_PPWR_INTR)
and $r8 NV_PPWR_INTR_WATCHDOG
bra nz #timer_enable
@@ -272,10 +366,10 @@ timer:
cmp b32 $r14 $r0
bra e #timer_reset
cmp b32 $r14 $r8
- bra l #timer_done
- timer_reset:
- nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14)
- st b32 D[$r0 + #time_prev] $r14
+ bra g #timer_enable
+ timer_reset:
+ nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14)
+ st b32 D[$r0 + #time_prev] $r14
// re-enable the watchdog timer
timer_enable:
@@ -285,6 +379,9 @@ timer:
// interrupts back on
timer_done:
bset $flags ie0
+
+ pop $r8
+ pop $r9
ret
// send message to another process
@@ -371,6 +468,9 @@ send:
// $r14 - process
// $r0 - zero
recv:
+ push $r9
+ push $r8
+
ld b32 $r8 D[$r14 + #proc_qget]
ld b32 $r9 D[$r14 + #proc_qput]
bclr $flags $p1
@@ -403,6 +503,8 @@ recv:
bset $flags $p1
pop $r15
recv_done:
+ pop $r8
+ pop $r9
ret
init:
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc
index e2a63ac5422b..96fc984dafdc 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/macros.fuc
@@ -242,7 +242,7 @@
*/ push reg /*
*/ pop $r13 /*
*/ pop $r14 /*
-*/ call(wr32) /*
+*/ call(wr32)
#else
#define nv_wr32(addr,reg) /*
*/ sethi $r0 0x14000000 /*
@@ -250,3 +250,23 @@
*/ st b32 D[$r0] reg /*
*/ clear b32 $r0
#endif
+
+#define st(size, addr, reg) /*
+*/ movw $r0 addr /*
+*/ st size D[$r0] reg /*
+*/ clear b32 $r0
+
+#define ld(size, reg, addr) /*
+*/ movw $r0 addr /*
+*/ ld size reg D[$r0] /*
+*/ clear b32 $r0
+
+// does a 64+64 -> 64 unsigned addition (C = A + B)
+#define addu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /*
+*/ add b32 reg_a_c_lo b_lo /*
+*/ adc b32 reg_a_c_hi b_hi
+
+// does a 64+64 -> 64 substraction (C = A - B)
+#define subu64(reg_a_c_hi, reg_a_c_lo, b_hi, b_lo) /*
+*/ sub b32 reg_a_c_lo b_lo /*
+*/ sbb b32 reg_a_c_hi b_hi
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc
index d43741eccb11..e89789a53b80 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/memx.fuc
@@ -43,17 +43,23 @@ process(PROC_MEMX, #memx_init, #memx_recv)
*/ .b32 func
memx_func_head:
-handler(ENTER , 0x0001, 0x0000, #memx_func_enter)
+handler(ENTER , 0x0000, 0x0000, #memx_func_enter)
memx_func_next:
handler(LEAVE , 0x0000, 0x0000, #memx_func_leave)
handler(WR32 , 0x0000, 0x0002, #memx_func_wr32)
handler(WAIT , 0x0004, 0x0000, #memx_func_wait)
handler(DELAY , 0x0001, 0x0000, #memx_func_delay)
+handler(VBLANK, 0x0001, 0x0000, #memx_func_wait_vblank)
memx_func_tail:
.equ #memx_func_size #memx_func_next - #memx_func_head
.equ #memx_func_num (#memx_func_tail - #memx_func_head) / #memx_func_size
+memx_ts_start:
+.b32 0
+memx_ts_end:
+.b32 0
+
memx_data_head:
.skip 0x0800
memx_data_tail:
@@ -67,19 +73,44 @@ memx_data_tail:
//
// $r15 - current (memx)
// $r4 - packet length
-// +00: bitmask of heads to wait for vblank on
// $r3 - opcode desciption
// $r0 - zero
memx_func_enter:
+#if NVKM_PPWR_CHIPSET == GT215
+ movw $r8 0x1610
+ nv_rd32($r7, $r8)
+ imm32($r6, 0xfffffffc)
+ and $r7 $r6
+ movw $r6 0x2
+ or $r7 $r6
+ nv_wr32($r8, $r7)
+#else
+ movw $r6 0x001620
+ imm32($r7, ~0x00000aa2);
+ nv_rd32($r8, $r6)
+ and $r8 $r7
+ nv_wr32($r6, $r8)
+
+ imm32($r7, ~0x00000001)
+ nv_rd32($r8, $r6)
+ and $r8 $r7
+ nv_wr32($r6, $r8)
+
+ movw $r6 0x0026f0
+ nv_rd32($r8, $r6)
+ and $r8 $r7
+ nv_wr32($r6, $r8)
+#endif
+
mov $r6 NV_PPWR_OUTPUT_SET_FB_PAUSE
nv_iowr(NV_PPWR_OUTPUT_SET, $r6)
memx_func_enter_wait:
nv_iord($r6, NV_PPWR_OUTPUT)
and $r6 NV_PPWR_OUTPUT_FB_PAUSE
bra z #memx_func_enter_wait
- //XXX: TODO
- ld b32 $r6 D[$r1 + 0x00]
- add b32 $r1 0x04
+
+ nv_iord($r6, NV_PPWR_TIMER_LOW)
+ st b32 D[$r0 + #memx_ts_start] $r6
ret
// description
@@ -89,14 +120,93 @@ memx_func_enter:
// $r3 - opcode desciption
// $r0 - zero
memx_func_leave:
+ nv_iord($r6, NV_PPWR_TIMER_LOW)
+ st b32 D[$r0 + #memx_ts_end] $r6
+
mov $r6 NV_PPWR_OUTPUT_CLR_FB_PAUSE
nv_iowr(NV_PPWR_OUTPUT_CLR, $r6)
memx_func_leave_wait:
nv_iord($r6, NV_PPWR_OUTPUT)
and $r6 NV_PPWR_OUTPUT_FB_PAUSE
bra nz #memx_func_leave_wait
+
+#if NVKM_PPWR_CHIPSET == GT215
+ movw $r8 0x1610
+ nv_rd32($r7, $r8)
+ imm32($r6, 0xffffffcc)
+ and $r7 $r6
+ nv_wr32($r8, $r7)
+#else
+ movw $r6 0x0026f0
+ imm32($r7, 0x00000001)
+ nv_rd32($r8, $r6)
+ or $r8 $r7
+ nv_wr32($r6, $r8)
+
+ movw $r6 0x001620
+ nv_rd32($r8, $r6)
+ or $r8 $r7
+ nv_wr32($r6, $r8)
+
+ imm32($r7, 0x00000aa2);
+ nv_rd32($r8, $r6)
+ or $r8 $r7
+ nv_wr32($r6, $r8)
+#endif
+ ret
+
+#if NVKM_PPWR_CHIPSET < GF119
+// description
+//
+// $r15 - current (memx)
+// $r4 - packet length
+// +00: head to wait for vblank on
+// $r3 - opcode desciption
+// $r0 - zero
+memx_func_wait_vblank:
+ ld b32 $r6 D[$r1 + 0x00]
+ cmp b32 $r6 0x0
+ bra z #memx_func_wait_vblank_head0
+ cmp b32 $r6 0x1
+ bra z #memx_func_wait_vblank_head1
+ bra #memx_func_wait_vblank_fini
+
+ memx_func_wait_vblank_head1:
+ movw $r7 0x20
+ bra #memx_func_wait_vblank_0
+
+ memx_func_wait_vblank_head0:
+ movw $r7 0x8
+
+ memx_func_wait_vblank_0:
+ nv_iord($r6, NV_PPWR_INPUT)
+ and $r6 $r7
+ bra nz #memx_func_wait_vblank_0
+
+ memx_func_wait_vblank_1:
+ nv_iord($r6, NV_PPWR_INPUT)
+ and $r6 $r7
+ bra z #memx_func_wait_vblank_1
+
+ memx_func_wait_vblank_fini:
+ add b32 $r1 0x4
+ ret
+
+#else
+
+// XXX: currently no-op
+//
+// $r15 - current (memx)
+// $r4 - packet length
+// +00: head to wait for vblank on
+// $r3 - opcode desciption
+// $r0 - zero
+memx_func_wait_vblank:
+ add b32 $r1 0x4
ret
+#endif
+
// description
//
// $r15 - current (memx)
@@ -160,14 +270,17 @@ memx_exec:
push $r13
mov b32 $r1 $r12
mov b32 $r2 $r11
+
memx_exec_next:
- // fetch the packet header, and locate opcode info
+ // fetch the packet header
ld b32 $r3 D[$r1]
add b32 $r1 4
- shr b32 $r4 $r3 16
- mulu $r3 #memx_func_size
+ extr $r4 $r3 16:31
+ extr $r3 $r3 0:15
// execute the opcode handler
+ sub b32 $r3 1
+ mulu $r3 #memx_func_size
ld b32 $r5 D[$r3 + #memx_func_head + #memx_func]
call $r5
@@ -176,6 +289,10 @@ memx_exec:
bra l #memx_exec_next
// send completion reply
+ ld b32 $r11 D[$r0 + #memx_ts_start]
+ ld b32 $r12 D[$r0 + #memx_ts_end]
+ sub b32 $r12 $r11
+ nv_iord($r11, NV_PPWR_INPUT)
pop $r13
pop $r14
call(send)
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc
index 17a8a383d91a..b439519ec866 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc
@@ -23,6 +23,7 @@
*/
#define NVKM_PPWR_CHIPSET GK208
+#define HW_TICKS_PER_US 324
#define NVKM_FALCON_PC24
#define NVKM_FALCON_UNSHIFTED_IO
@@ -34,6 +35,7 @@
.section #nv108_pwr_data
#define INCLUDE_PROC
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
@@ -44,6 +46,7 @@
#define INCLUDE_DATA
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
@@ -56,6 +59,7 @@
.section #nv108_pwr_code
#define INCLUDE_CODE
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h
index 39a5dc150a05..4d278a96b2bb 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nv108.fuc.h
@@ -24,8 +24,8 @@ uint32_t nv108_pwr_data[] = {
0x00000000,
/* 0x0058: proc_list_head */
0x54534f48,
- 0x00000379,
- 0x0000032a,
+ 0x00000453,
+ 0x00000404,
0x00000000,
0x00000000,
0x00000000,
@@ -46,8 +46,8 @@ uint32_t nv108_pwr_data[] = {
0x00000000,
0x00000000,
0x584d454d,
- 0x0000046f,
- 0x00000461,
+ 0x0000061c,
+ 0x0000060e,
0x00000000,
0x00000000,
0x00000000,
@@ -68,8 +68,8 @@ uint32_t nv108_pwr_data[] = {
0x00000000,
0x00000000,
0x46524550,
- 0x00000473,
- 0x00000471,
+ 0x00000620,
+ 0x0000061e,
0x00000000,
0x00000000,
0x00000000,
@@ -90,8 +90,8 @@ uint32_t nv108_pwr_data[] = {
0x00000000,
0x00000000,
0x5f433249,
- 0x00000877,
- 0x0000071e,
+ 0x00000a24,
+ 0x000008cb,
0x00000000,
0x00000000,
0x00000000,
@@ -112,8 +112,8 @@ uint32_t nv108_pwr_data[] = {
0x00000000,
0x00000000,
0x54534554,
- 0x00000898,
- 0x00000879,
+ 0x00000a45,
+ 0x00000a26,
0x00000000,
0x00000000,
0x00000000,
@@ -134,8 +134,8 @@ uint32_t nv108_pwr_data[] = {
0x00000000,
0x00000000,
0x454c4449,
- 0x000008a3,
- 0x000008a1,
+ 0x00000a50,
+ 0x00000a4e,
0x00000000,
0x00000000,
0x00000000,
@@ -227,24 +227,31 @@ uint32_t nv108_pwr_data[] = {
0x00000000,
0x00000000,
/* 0x0370: memx_func_head */
- 0x00010000,
- 0x00000000,
- 0x000003a9,
-/* 0x037c: memx_func_next */
0x00000001,
0x00000000,
- 0x000003c7,
+ 0x00000483,
+/* 0x037c: memx_func_next */
0x00000002,
+ 0x00000000,
+ 0x00000500,
+ 0x00000003,
0x00000002,
- 0x000003df,
- 0x00040003,
+ 0x00000580,
+ 0x00040004,
+ 0x00000000,
+ 0x0000059d,
+ 0x00010005,
+ 0x00000000,
+ 0x000005b7,
+ 0x00010006,
+ 0x00000000,
+ 0x0000057b,
+/* 0x03b8: memx_func_tail */
+/* 0x03b8: memx_ts_start */
0x00000000,
- 0x00000407,
- 0x00010004,
+/* 0x03bc: memx_ts_end */
0x00000000,
- 0x00000421,
-/* 0x03ac: memx_func_tail */
-/* 0x03ac: memx_data_head */
+/* 0x03c0: memx_data_head */
0x00000000,
0x00000000,
0x00000000,
@@ -757,8 +764,8 @@ uint32_t nv108_pwr_data[] = {
0x00000000,
0x00000000,
0x00000000,
-/* 0x0bac: memx_data_tail */
-/* 0x0bac: i2c_scl_map */
+/* 0x0bc0: memx_data_tail */
+/* 0x0bc0: i2c_scl_map */
0x00000400,
0x00000800,
0x00001000,
@@ -769,7 +776,7 @@ uint32_t nv108_pwr_data[] = {
0x00020000,
0x00040000,
0x00080000,
-/* 0x0bd4: i2c_sda_map */
+/* 0x0be8: i2c_sda_map */
0x00100000,
0x00200000,
0x00400000,
@@ -781,10 +788,69 @@ uint32_t nv108_pwr_data[] = {
0x10000000,
0x20000000,
0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
};
uint32_t nv108_pwr_code[] = {
- 0x02910ef5,
+ 0x031c0ef5,
/* 0x0004: rd32 */
0xf607a040,
0x04bd000e,
@@ -812,15 +878,18 @@ uint32_t nv108_pwr_code[] = {
0x7000d4f1,
0xf8f61bf4,
/* 0x005d: nsec */
- 0xcf2c0800,
-/* 0x0062: nsec_loop */
+ 0xf990f900,
+ 0xcf2c0880,
+/* 0x0066: nsec_loop */
0x2c090088,
0xbb0099cf,
0x9ea60298,
- 0xf8f61ef4,
-/* 0x0071: wait */
- 0xcf2c0800,
-/* 0x0076: wait_loop */
+ 0xfcf61ef4,
+ 0xf890fc80,
+/* 0x0079: wait */
+ 0xf990f900,
+ 0xcf2c0880,
+/* 0x0082: wait_loop */
0xeeb20088,
0x0000047e,
0xadfddab2,
@@ -828,28 +897,29 @@ uint32_t nv108_pwr_code[] = {
0x2c09100b,
0xbb0099cf,
0x9ba60298,
-/* 0x0093: wait_done */
- 0xf8e61ef4,
-/* 0x0095: intr_watchdog */
+/* 0x009f: wait_done */
+ 0xfce61ef4,
+ 0xf890fc80,
+/* 0x00a5: intr_watchdog */
0x03e99800,
0xf40096b0,
0x0a98280b,
0x029abb9a,
0x0d0e1cf4,
- 0x01de7e01,
+ 0x02617e01,
0xf494bd00,
-/* 0x00b2: intr_watchdog_next_time */
+/* 0x00c2: intr_watchdog_next_time */
0x0a98140e,
0x00a6b09b,
0xa6080bf4,
0x061cf49a,
-/* 0x00c0: intr_watchdog_next_time_set */
-/* 0x00c3: intr_watchdog_next_proc */
+/* 0x00d0: intr_watchdog_next_time_set */
+/* 0x00d3: intr_watchdog_next_proc */
0xb59b09b5,
0xe0b603e9,
0x68e6b158,
0xc81bf402,
-/* 0x00d2: intr */
+/* 0x00e2: intr */
0x00f900f8,
0x80f904bd,
0xa0f990f9,
@@ -865,13 +935,13 @@ uint32_t nv108_pwr_code[] = {
0xc40088cf,
0x0bf40289,
0x9b00b51f,
- 0x957e580e,
+ 0xa57e580e,
0x09980000,
0x0096b09b,
0x000d0bf4,
0x0009f634,
0x09b504bd,
-/* 0x0125: intr_skip_watchdog */
+/* 0x0135: intr_skip_watchdog */
0x0089e49a,
0x360bf408,
0xcf068849,
@@ -881,20 +951,20 @@ uint32_t nv108_pwr_code[] = {
0xc0f900cc,
0xf14f484e,
0x0d5453e3,
- 0x023f7e00,
+ 0x02c27e00,
0x40c0fc00,
0x0cf604c0,
-/* 0x0157: intr_subintr_skip_fifo */
+/* 0x0167: intr_subintr_skip_fifo */
0x4004bd00,
0x09f60688,
-/* 0x015f: intr_skip_subintr */
+/* 0x016f: intr_skip_subintr */
0xc404bd00,
0x0bf42089,
0xbfa4f107,
-/* 0x0169: intr_skip_pause */
+/* 0x0179: intr_skip_pause */
0x4089c4ff,
0xf1070bf4,
-/* 0x0173: intr_skip_user0 */
+/* 0x0183: intr_skip_user0 */
0x00ffbfa4,
0x0008f604,
0x80fc04bd,
@@ -904,551 +974,682 @@ uint32_t nv108_pwr_code[] = {
0xfca0fcb0,
0xfc80fc90,
0x0032f400,
-/* 0x0196: timer */
- 0x32f401f8,
- 0x03f89810,
- 0xf40086b0,
- 0xfeb53a1c,
- 0xf6380003,
+/* 0x01a6: ticks_from_ns */
+ 0xc0f901f8,
+ 0xd7f1b0f9,
+ 0xd3f00144,
+ 0x7721f500,
+ 0xe8ccec03,
+ 0x00b4b003,
+ 0xec120bf4,
+ 0xf103e8ee,
+ 0xf00144d7,
+ 0x21f500d3,
+/* 0x01ce: ticks_from_ns_quit */
+ 0xceb20377,
+ 0xc0fcb0fc,
+/* 0x01d6: ticks_from_us */
+ 0xc0f900f8,
+ 0xd7f1b0f9,
+ 0xd3f00144,
+ 0x7721f500,
+ 0xb0ceb203,
+ 0x0bf400b4,
+/* 0x01ef: ticks_from_us_quit */
+ 0xfce4bd05,
+ 0xf8c0fcb0,
+/* 0x01f5: ticks_to_us */
+ 0x44d7f100,
+ 0x00d3f001,
+ 0xf8ecedff,
+/* 0x0201: timer */
+ 0xf990f900,
+ 0x1032f480,
+ 0xb003f898,
+ 0x1cf40086,
+ 0x0084bd4a,
+ 0x0008f638,
+ 0x340804bd,
+ 0x980088cf,
+ 0x98bb9a09,
+ 0x00e9bb02,
+ 0x0803feb5,
+ 0x0088cf08,
+ 0xf40284f0,
+ 0x34081c1b,
+ 0xa60088cf,
+ 0x080bf4e0,
+ 0x1cf4e8a6,
+/* 0x0245: timer_reset */
+ 0xf634000d,
+ 0x04bd000e,
+/* 0x024f: timer_enable */
+ 0x089a0eb5,
+ 0xf6380001,
0x04bd0008,
- 0x88cf0808,
- 0x0284f000,
- 0x081c1bf4,
- 0x0088cf34,
- 0x0bf4e0a6,
- 0xf4e8a608,
-/* 0x01c6: timer_reset */
- 0x3400161e,
- 0xbd000ef6,
- 0x9a0eb504,
-/* 0x01d0: timer_enable */
- 0x38000108,
- 0xbd0008f6,
-/* 0x01d9: timer_done */
- 0x1031f404,
-/* 0x01de: send_proc */
- 0x80f900f8,
- 0xe89890f9,
- 0x04e99805,
- 0xa60486f0,
- 0x2a0bf489,
- 0x940398c4,
- 0x80b60488,
- 0x008ebb18,
- 0xb500fa98,
- 0x8db5008a,
- 0x028cb501,
- 0xb6038bb5,
- 0x94f00190,
- 0x04e9b507,
-/* 0x0217: send_done */
- 0xfc0231f4,
- 0xf880fc90,
-/* 0x021d: find */
- 0x0880f900,
- 0x0131f458,
-/* 0x0224: find_loop */
- 0xa6008a98,
- 0x100bf4ae,
- 0xb15880b6,
- 0xf4026886,
- 0x32f4f11b,
-/* 0x0239: find_done */
- 0xfc8eb201,
-/* 0x023f: send */
- 0x7e00f880,
- 0xf400021d,
- 0x00f89b01,
-/* 0x0248: recv */
- 0x9805e898,
- 0x32f404e9,
- 0xf489a601,
- 0x89c43c0b,
- 0x0180b603,
- 0xb50784f0,
- 0xea9805e8,
- 0xfef0f902,
- 0xf0f9018f,
- 0x9994efb2,
- 0x00e9bb04,
- 0x9818e0b6,
- 0xec9803eb,
- 0x01ed9802,
- 0xf900ee98,
- 0xfef0fca5,
- 0x31f400f8,
-/* 0x028f: recv_done */
- 0xf8f0fc01,
-/* 0x0291: init */
- 0x01084100,
- 0xe70011cf,
- 0xb6010911,
- 0x14fe0814,
- 0x00e04100,
- 0x000013f0,
- 0x0001f61c,
- 0xff0104bd,
- 0x01f61400,
- 0x0104bd00,
- 0x0015f102,
- 0xf6100008,
- 0x04bd0001,
- 0xf000d241,
- 0x10fe0013,
- 0x1031f400,
- 0x38000101,
+/* 0x0258: timer_done */
+ 0xfc1031f4,
+ 0xf890fc80,
+/* 0x0261: send_proc */
+ 0xf980f900,
+ 0x05e89890,
+ 0xf004e998,
+ 0x89a60486,
+ 0xc42a0bf4,
+ 0x88940398,
+ 0x1880b604,
+ 0x98008ebb,
+ 0x8ab500fa,
+ 0x018db500,
+ 0xb5028cb5,
+ 0x90b6038b,
+ 0x0794f001,
+ 0xf404e9b5,
+/* 0x029a: send_done */
+ 0x90fc0231,
+ 0x00f880fc,
+/* 0x02a0: find */
+ 0x580880f9,
+/* 0x02a7: find_loop */
+ 0x980131f4,
+ 0xaea6008a,
+ 0xb6100bf4,
+ 0x86b15880,
+ 0x1bf40268,
+ 0x0132f4f1,
+/* 0x02bc: find_done */
+ 0x80fc8eb2,
+/* 0x02c2: send */
+ 0xa07e00f8,
+ 0x01f40002,
+/* 0x02cb: recv */
+ 0xf900f89b,
+ 0x9880f990,
+ 0xe99805e8,
+ 0x0132f404,
+ 0x0bf489a6,
+ 0x0389c43c,
+ 0xf00180b6,
+ 0xe8b50784,
+ 0x02ea9805,
+ 0x8ffef0f9,
+ 0xb2f0f901,
+ 0x049994ef,
+ 0xb600e9bb,
+ 0xeb9818e0,
+ 0x02ec9803,
+ 0x9801ed98,
+ 0xa5f900ee,
+ 0xf8fef0fc,
+ 0x0131f400,
+/* 0x0316: recv_done */
+ 0x80fcf0fc,
+ 0x00f890fc,
+/* 0x031c: init */
+ 0xcf010841,
+ 0x11e70011,
+ 0x14b60109,
+ 0x0014fe08,
+ 0xf000e041,
+ 0x1c000013,
0xbd0001f6,
-/* 0x02db: init_proc */
- 0x98580f04,
- 0x16b001f1,
- 0xfa0bf400,
- 0xf0b615f9,
- 0xf20ef458,
-/* 0x02ec: host_send */
- 0xcf04b041,
- 0xa0420011,
- 0x0022cf04,
- 0x0bf412a6,
- 0x071ec42e,
- 0xb704ee94,
- 0x980270e0,
- 0xec9803eb,
- 0x01ed9802,
- 0x7e00ee98,
- 0xb600023f,
- 0x1ec40110,
- 0x04b0400f,
- 0xbd000ef6,
- 0xc70ef404,
-/* 0x0328: host_send_done */
-/* 0x032a: host_recv */
- 0x494100f8,
- 0x5413f14e,
- 0xf4e1a652,
-/* 0x0336: host_recv_wait */
- 0xcc41b90b,
- 0x0011cf04,
- 0xcf04c842,
- 0x16f00022,
- 0xf412a608,
- 0x23c4ef0b,
- 0x0434b607,
- 0x02f030b7,
- 0xb5033bb5,
- 0x3db5023c,
- 0x003eb501,
- 0xf00120b6,
- 0xc8400f24,
- 0x0002f604,
- 0x400204bd,
- 0x02f60000,
- 0xf804bd00,
-/* 0x0379: host_init */
- 0x00804100,
- 0xf11014b6,
- 0x40027015,
- 0x01f604d0,
+ 0x00ff0104,
+ 0x0001f614,
+ 0x020104bd,
+ 0x080015f1,
+ 0x01f61000,
0x4104bd00,
+ 0x13f000e2,
+ 0x0010fe00,
+ 0x011031f4,
+ 0xf6380001,
+ 0x04bd0001,
+/* 0x0366: init_proc */
+ 0xf198580f,
+ 0x0016b001,
+ 0xf9fa0bf4,
+ 0x58f0b615,
+/* 0x0377: mulu32_32_64 */
+ 0xf9f20ef4,
+ 0xf920f910,
+ 0x9540f930,
+ 0xd29510e1,
+ 0xbdc4bd10,
+ 0xc0edffb4,
+ 0xb2301dff,
+ 0xff34f134,
+ 0x1034b6ff,
+ 0xbb1045b6,
+ 0xb4bb00c3,
+ 0x30e2ff01,
+ 0x34f134b2,
+ 0x34b6ffff,
+ 0x1045b610,
+ 0xbb00c3bb,
+ 0x12ff01b4,
+ 0x00b3bb30,
+ 0x30fc40fc,
+ 0x10fc20fc,
+/* 0x03c6: host_send */
+ 0xb04100f8,
+ 0x0011cf04,
+ 0xcf04a042,
+ 0x12a60022,
+ 0xc42e0bf4,
+ 0xee94071e,
+ 0x70e0b704,
+ 0x03eb9802,
+ 0x9802ec98,
+ 0xee9801ed,
+ 0x02c27e00,
+ 0x0110b600,
+ 0x400f1ec4,
+ 0x0ef604b0,
+ 0xf404bd00,
+/* 0x0402: host_send_done */
+ 0x00f8c70e,
+/* 0x0404: host_recv */
+ 0xf14e4941,
+ 0xa6525413,
+ 0xb90bf4e1,
+/* 0x0410: host_recv_wait */
+ 0xcf04cc41,
+ 0xc8420011,
+ 0x0022cf04,
+ 0xa60816f0,
+ 0xef0bf412,
+ 0xb60723c4,
+ 0x30b70434,
+ 0x3bb502f0,
+ 0x023cb503,
+ 0xb5013db5,
+ 0x20b6003e,
+ 0x0f24f001,
+ 0xf604c840,
+ 0x04bd0002,
+ 0x00004002,
+ 0xbd0002f6,
+/* 0x0453: host_init */
+ 0x4100f804,
0x14b60080,
- 0xf015f110,
- 0x04dc4002,
+ 0x7015f110,
+ 0x04d04002,
+ 0xbd0001f6,
+ 0x00804104,
+ 0xf11014b6,
+ 0x4002f015,
+ 0x01f604dc,
+ 0x0104bd00,
+ 0x04c44001,
0xbd0001f6,
- 0x40010104,
- 0x01f604c4,
- 0xf804bd00,
-/* 0x03a9: memx_func_enter */
- 0x40040600,
- 0x06f607e0,
-/* 0x03b3: memx_func_enter_wait */
- 0x4604bd00,
- 0x66cf07c0,
- 0x0464f000,
- 0x98f70bf4,
- 0x10b60016,
-/* 0x03c7: memx_func_leave */
- 0x0600f804,
- 0x07e44004,
- 0xbd0006f6,
-/* 0x03d1: memx_func_leave_wait */
- 0x07c04604,
- 0xf00066cf,
- 0x1bf40464,
-/* 0x03df: memx_func_wr32 */
- 0x9800f8f7,
- 0x15980016,
- 0x0810b601,
- 0x50f960f9,
+/* 0x0483: memx_func_enter */
+ 0xf100f804,
+ 0xf1162067,
+ 0xf1f55d77,
+ 0xb2ffff73,
+ 0x00047e6e,
+ 0xfdd8b200,
+ 0x60f90487,
+ 0xd0fc80f9,
+ 0x2e7ee0fc,
+ 0x77f10000,
+ 0x73f1fffe,
+ 0x6eb2ffff,
+ 0x0000047e,
+ 0x87fdd8b2,
+ 0xf960f904,
+ 0xfcd0fc80,
+ 0x002e7ee0,
+ 0xf067f100,
+ 0x7e6eb226,
+ 0xb2000004,
+ 0x0487fdd8,
+ 0x80f960f9,
0xe0fcd0fc,
0x00002e7e,
- 0x140003f1,
- 0xa00506fd,
- 0xb604bd05,
- 0x1bf40242,
-/* 0x0407: memx_func_wait */
- 0x0800f8dd,
- 0x0088cf2c,
- 0x98001e98,
- 0x1c98011d,
- 0x031b9802,
- 0x7e1010b6,
- 0xf8000071,
-/* 0x0421: memx_func_delay */
- 0x001e9800,
- 0x7e0410b6,
- 0xf800005d,
-/* 0x042d: memx_exec */
- 0xf9e0f900,
- 0xb2c1b2d0,
-/* 0x0435: memx_exec_next */
- 0x001398b2,
- 0x950410b6,
- 0x30f01034,
- 0xde35980c,
- 0x12a655f9,
- 0xfced1ef4,
+ 0xe0400406,
+ 0x0006f607,
+/* 0x04ea: memx_func_enter_wait */
+ 0xc04604bd,
+ 0x0066cf07,
+ 0xf40464f0,
+ 0x2c06f70b,
+ 0xb50066cf,
+ 0x00f8ee06,
+/* 0x0500: memx_func_leave */
+ 0x66cf2c06,
+ 0xef06b500,
+ 0xe4400406,
+ 0x0006f607,
+/* 0x0512: memx_func_leave_wait */
+ 0xc04604bd,
+ 0x0066cf07,
+ 0xf40464f0,
+ 0x67f1f71b,
+ 0x77f126f0,
+ 0x73f00001,
+ 0x7e6eb200,
+ 0xb2000004,
+ 0x0587fdd8,
+ 0x80f960f9,
+ 0xe0fcd0fc,
+ 0x00002e7e,
+ 0x162067f1,
+ 0x047e6eb2,
+ 0xd8b20000,
+ 0xf90587fd,
+ 0xfc80f960,
0x7ee0fcd0,
- 0xf800023f,
-/* 0x0455: memx_info */
- 0x03ac4c00,
- 0x7e08004b,
- 0xf800023f,
-/* 0x0461: memx_recv */
- 0x01d6b000,
- 0xb0c90bf4,
- 0x0bf400d6,
-/* 0x046f: memx_init */
- 0xf800f8eb,
-/* 0x0471: perf_recv */
-/* 0x0473: perf_init */
- 0xf800f800,
-/* 0x0475: i2c_drive_scl */
- 0x0036b000,
- 0x400d0bf4,
- 0x01f607e0,
- 0xf804bd00,
-/* 0x0485: i2c_drive_scl_lo */
- 0x07e44000,
- 0xbd0001f6,
-/* 0x048f: i2c_drive_sda */
- 0xb000f804,
- 0x0bf40036,
- 0x07e0400d,
- 0xbd0002f6,
-/* 0x049f: i2c_drive_sda_lo */
- 0x4000f804,
- 0x02f607e4,
- 0xf804bd00,
-/* 0x04a9: i2c_sense_scl */
- 0x0132f400,
- 0xcf07c443,
- 0x31fd0033,
- 0x060bf404,
-/* 0x04bb: i2c_sense_scl_done */
- 0xf80131f4,
-/* 0x04bd: i2c_sense_sda */
- 0x0132f400,
- 0xcf07c443,
- 0x32fd0033,
- 0x060bf404,
-/* 0x04cf: i2c_sense_sda_done */
- 0xf80131f4,
-/* 0x04d1: i2c_raise_scl */
- 0x4440f900,
- 0x01030898,
- 0x0004757e,
-/* 0x04dc: i2c_raise_scl_wait */
- 0x7e03e84e,
- 0x7e00005d,
- 0xf40004a9,
- 0x42b60901,
- 0xef1bf401,
-/* 0x04f0: i2c_raise_scl_done */
- 0x00f840fc,
-/* 0x04f4: i2c_start */
- 0x0004a97e,
- 0x7e0d11f4,
- 0xf40004bd,
- 0x0ef40611,
-/* 0x0505: i2c_start_rep */
- 0x7e00032e,
- 0x03000475,
- 0x048f7e01,
- 0x0076bb00,
- 0xf90465b6,
- 0x04659450,
- 0xbd0256bb,
- 0x0475fd50,
- 0xd17e50fc,
- 0x64b60004,
- 0x1d11f404,
-/* 0x0530: i2c_start_send */
- 0x8f7e0003,
- 0x884e0004,
- 0x005d7e13,
- 0x7e000300,
- 0x4e000475,
- 0x5d7e1388,
-/* 0x054a: i2c_start_out */
- 0x00f80000,
-/* 0x054c: i2c_stop */
- 0x757e0003,
- 0x00030004,
- 0x00048f7e,
- 0x7e03e84e,
- 0x0300005d,
- 0x04757e01,
- 0x13884e00,
+ 0xf100002e,
+ 0xf00aa277,
+ 0x6eb20073,
+ 0x0000047e,
+ 0x87fdd8b2,
+ 0xf960f905,
+ 0xfcd0fc80,
+ 0x002e7ee0,
+/* 0x057b: memx_func_wait_vblank */
+ 0xb600f800,
+ 0x00f80410,
+/* 0x0580: memx_func_wr32 */
+ 0x98001698,
+ 0x10b60115,
+ 0xf960f908,
+ 0xfcd0fc50,
+ 0x002e7ee0,
+ 0x0242b600,
+ 0xf8e81bf4,
+/* 0x059d: memx_func_wait */
+ 0xcf2c0800,
+ 0x1e980088,
+ 0x011d9800,
+ 0x98021c98,
+ 0x10b6031b,
+ 0x00797e10,
+/* 0x05b7: memx_func_delay */
+ 0x9800f800,
+ 0x10b6001e,
+ 0x005d7e04,
+/* 0x05c3: memx_exec */
+ 0xf900f800,
+ 0xb2d0f9e0,
+/* 0x05cb: memx_exec_next */
+ 0x98b2b2c1,
+ 0x10b60013,
+ 0xf034e704,
+ 0xe033e701,
+ 0x0132b601,
+ 0x980c30f0,
+ 0x55f9de35,
+ 0x1ef412a6,
+ 0xee0b98e5,
+ 0xbbef0c98,
+ 0xc44b02cb,
+ 0x00bbcf07,
+ 0xe0fcd0fc,
+ 0x0002c27e,
+/* 0x0602: memx_info */
+ 0xc04c00f8,
+ 0x08004b03,
+ 0x0002c27e,
+/* 0x060e: memx_recv */
+ 0xd6b000f8,
+ 0xb20bf401,
+ 0xf400d6b0,
+ 0x00f8eb0b,
+/* 0x061c: memx_init */
+/* 0x061e: perf_recv */
+ 0x00f800f8,
+/* 0x0620: perf_init */
+/* 0x0622: i2c_drive_scl */
+ 0x36b000f8,
+ 0x0d0bf400,
+ 0xf607e040,
+ 0x04bd0001,
+/* 0x0632: i2c_drive_scl_lo */
+ 0xe44000f8,
+ 0x0001f607,
+ 0x00f804bd,
+/* 0x063c: i2c_drive_sda */
+ 0xf40036b0,
+ 0xe0400d0b,
+ 0x0002f607,
+ 0x00f804bd,
+/* 0x064c: i2c_drive_sda_lo */
+ 0xf607e440,
+ 0x04bd0002,
+/* 0x0656: i2c_sense_scl */
+ 0x32f400f8,
+ 0x07c44301,
+ 0xfd0033cf,
+ 0x0bf40431,
+ 0x0131f406,
+/* 0x0668: i2c_sense_scl_done */
+/* 0x066a: i2c_sense_sda */
+ 0x32f400f8,
+ 0x07c44301,
+ 0xfd0033cf,
+ 0x0bf40432,
+ 0x0131f406,
+/* 0x067c: i2c_sense_sda_done */
+/* 0x067e: i2c_raise_scl */
+ 0x40f900f8,
+ 0x03089844,
+ 0x06227e01,
+/* 0x0689: i2c_raise_scl_wait */
+ 0x03e84e00,
0x00005d7e,
- 0x8f7e0103,
- 0x884e0004,
- 0x005d7e13,
-/* 0x057b: i2c_bitw */
- 0x7e00f800,
- 0x4e00048f,
- 0x5d7e03e8,
- 0x76bb0000,
+ 0x0006567e,
+ 0xb60901f4,
+ 0x1bf40142,
+/* 0x069d: i2c_raise_scl_done */
+ 0xf840fcef,
+/* 0x06a1: i2c_start */
+ 0x06567e00,
+ 0x0d11f400,
+ 0x00066a7e,
+ 0xf40611f4,
+/* 0x06b2: i2c_start_rep */
+ 0x00032e0e,
+ 0x0006227e,
+ 0x3c7e0103,
+ 0x76bb0006,
0x0465b600,
0x659450f9,
0x0256bb04,
0x75fd50bd,
0x7e50fc04,
- 0xb60004d1,
+ 0xb600067e,
0x11f40464,
- 0x13884e17,
- 0x00005d7e,
- 0x757e0003,
- 0x884e0004,
- 0x005d7e13,
-/* 0x05b9: i2c_bitw_out */
-/* 0x05bb: i2c_bitr */
- 0x0300f800,
- 0x048f7e01,
+/* 0x06dd: i2c_start_send */
+ 0x7e00031d,
+ 0x4e00063c,
+ 0x5d7e1388,
+ 0x00030000,
+ 0x0006227e,
+ 0x7e13884e,
+/* 0x06f7: i2c_start_out */
+ 0xf800005d,
+/* 0x06f9: i2c_stop */
+ 0x7e000300,
+ 0x03000622,
+ 0x063c7e00,
0x03e84e00,
0x00005d7e,
- 0xb60076bb,
- 0x50f90465,
- 0xbb046594,
- 0x50bd0256,
- 0xfc0475fd,
- 0x04d17e50,
- 0x0464b600,
- 0x7e1a11f4,
- 0x030004bd,
- 0x04757e00,
- 0x13884e00,
- 0x00005d7e,
- 0xf4013cf0,
-/* 0x05fe: i2c_bitr_done */
- 0x00f80131,
-/* 0x0600: i2c_get_byte */
- 0x08040005,
-/* 0x0604: i2c_get_byte_next */
- 0xbb0154b6,
+ 0x227e0103,
+ 0x884e0006,
+ 0x005d7e13,
+ 0x7e010300,
+ 0x4e00063c,
+ 0x5d7e1388,
+ 0x00f80000,
+/* 0x0728: i2c_bitw */
+ 0x00063c7e,
+ 0x7e03e84e,
+ 0xbb00005d,
0x65b60076,
0x9450f904,
0x56bb0465,
0xfd50bd02,
0x50fc0475,
- 0x0005bb7e,
+ 0x00067e7e,
0xf40464b6,
- 0x53fd2a11,
- 0x0142b605,
- 0x03d81bf4,
- 0x0076bb01,
+ 0x884e1711,
+ 0x005d7e13,
+ 0x7e000300,
+ 0x4e000622,
+ 0x5d7e1388,
+/* 0x0766: i2c_bitw_out */
+ 0x00f80000,
+/* 0x0768: i2c_bitr */
+ 0x3c7e0103,
+ 0xe84e0006,
+ 0x005d7e03,
+ 0x0076bb00,
0xf90465b6,
0x04659450,
0xbd0256bb,
0x0475fd50,
- 0x7b7e50fc,
- 0x64b60005,
-/* 0x064d: i2c_get_byte_done */
-/* 0x064f: i2c_put_byte */
- 0x0400f804,
-/* 0x0651: i2c_put_byte_next */
- 0x0142b608,
- 0xbb3854ff,
- 0x65b60076,
- 0x9450f904,
- 0x56bb0465,
- 0xfd50bd02,
- 0x50fc0475,
- 0x00057b7e,
- 0xf40464b6,
- 0x46b03411,
- 0xd81bf400,
+ 0x7e7e50fc,
+ 0x64b60006,
+ 0x1a11f404,
+ 0x00066a7e,
+ 0x227e0003,
+ 0x884e0006,
+ 0x005d7e13,
+ 0x013cf000,
+/* 0x07ab: i2c_bitr_done */
+ 0xf80131f4,
+/* 0x07ad: i2c_get_byte */
+ 0x04000500,
+/* 0x07b1: i2c_get_byte_next */
+ 0x0154b608,
0xb60076bb,
0x50f90465,
0xbb046594,
0x50bd0256,
0xfc0475fd,
- 0x05bb7e50,
+ 0x07687e50,
0x0464b600,
- 0xbb0f11f4,
- 0x36b00076,
- 0x061bf401,
-/* 0x06a7: i2c_put_byte_done */
- 0xf80132f4,
-/* 0x06a9: i2c_addr */
- 0x0076bb00,
- 0xf90465b6,
- 0x04659450,
- 0xbd0256bb,
- 0x0475fd50,
- 0xf47e50fc,
- 0x64b60004,
- 0x2911f404,
- 0x012ec3e7,
- 0xfd0134b6,
- 0x76bb0553,
+ 0xfd2a11f4,
+ 0x42b60553,
+ 0xd81bf401,
+ 0x76bb0103,
0x0465b600,
0x659450f9,
0x0256bb04,
0x75fd50bd,
0x7e50fc04,
- 0xb600064f,
-/* 0x06ee: i2c_addr_done */
+ 0xb6000728,
+/* 0x07fa: i2c_get_byte_done */
0x00f80464,
-/* 0x06f0: i2c_acquire_addr */
- 0xb6f8cec7,
- 0xe0b705e4,
- 0x00f8d014,
-/* 0x06fc: i2c_acquire */
- 0x0006f07e,
- 0x0000047e,
- 0x7e03d9f0,
- 0xf800002e,
-/* 0x070d: i2c_release */
- 0x06f07e00,
- 0x00047e00,
- 0x03daf000,
- 0x00002e7e,
-/* 0x071e: i2c_recv */
- 0x32f400f8,
- 0xf8c1c701,
- 0xb00214b6,
- 0x1ff52816,
- 0x13b80137,
- 0x98000bd4,
- 0x13b80032,
- 0x98000bac,
- 0x31f40031,
- 0xf9d0f902,
- 0xf1d0f9e0,
- 0xf1000067,
- 0x92100063,
- 0x76bb0167,
- 0x0465b600,
- 0x659450f9,
- 0x0256bb04,
- 0x75fd50bd,
- 0x7e50fc04,
- 0xb60006fc,
- 0xd0fc0464,
- 0xf500d6b0,
- 0x0500b01b,
- 0x0076bb00,
- 0xf90465b6,
- 0x04659450,
- 0xbd0256bb,
- 0x0475fd50,
- 0xa97e50fc,
- 0x64b60006,
- 0xcc11f504,
- 0xe0c5c700,
+/* 0x07fc: i2c_put_byte */
+/* 0x07fe: i2c_put_byte_next */
+ 0x42b60804,
+ 0x3854ff01,
0xb60076bb,
0x50f90465,
0xbb046594,
0x50bd0256,
0xfc0475fd,
- 0x064f7e50,
+ 0x07287e50,
0x0464b600,
- 0x00a911f5,
- 0x76bb0105,
+ 0xb03411f4,
+ 0x1bf40046,
+ 0x0076bbd8,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x687e50fc,
+ 0x64b60007,
+ 0x0f11f404,
+ 0xb00076bb,
+ 0x1bf40136,
+ 0x0132f406,
+/* 0x0854: i2c_put_byte_done */
+/* 0x0856: i2c_addr */
+ 0x76bb00f8,
0x0465b600,
0x659450f9,
0x0256bb04,
0x75fd50bd,
0x7e50fc04,
- 0xb60006a9,
- 0x11f50464,
- 0x76bb0087,
+ 0xb60006a1,
+ 0x11f40464,
+ 0x2ec3e729,
+ 0x0134b601,
+ 0xbb0553fd,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x0007fc7e,
+/* 0x089b: i2c_addr_done */
+ 0xf80464b6,
+/* 0x089d: i2c_acquire_addr */
+ 0xf8cec700,
+ 0xb705e4b6,
+ 0xf8d014e0,
+/* 0x08a9: i2c_acquire */
+ 0x089d7e00,
+ 0x00047e00,
+ 0x03d9f000,
+ 0x00002e7e,
+/* 0x08ba: i2c_release */
+ 0x9d7e00f8,
+ 0x047e0008,
+ 0xdaf00000,
+ 0x002e7e03,
+/* 0x08cb: i2c_recv */
+ 0xf400f800,
+ 0xc1c70132,
+ 0x0214b6f8,
+ 0xf52816b0,
+ 0xb801371f,
+ 0x000be813,
+ 0xb8003298,
+ 0x000bc013,
+ 0xf4003198,
+ 0xd0f90231,
+ 0xd0f9e0f9,
+ 0x000067f1,
+ 0x100063f1,
+ 0xbb016792,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x0008a97e,
+ 0xfc0464b6,
+ 0x00d6b0d0,
+ 0x00b01bf5,
+ 0x76bb0005,
0x0465b600,
0x659450f9,
0x0256bb04,
0x75fd50bd,
0x7e50fc04,
- 0xb6000600,
- 0x11f40464,
- 0xe05bcb67,
- 0xb60076bb,
- 0x50f90465,
- 0xbb046594,
- 0x50bd0256,
- 0xfc0475fd,
- 0x054c7e50,
- 0x0464b600,
- 0x74bd5bb2,
-/* 0x0823: i2c_recv_not_rd08 */
- 0xb0410ef4,
- 0x1bf401d6,
- 0x7e00053b,
- 0xf40006a9,
- 0xc5c73211,
- 0x064f7ee0,
- 0x2811f400,
- 0xa97e0005,
- 0x11f40006,
- 0xe0b5c71f,
- 0x00064f7e,
- 0x7e1511f4,
- 0xbd00054c,
- 0x08c5c774,
- 0xf4091bf4,
- 0x0ef40232,
-/* 0x0861: i2c_recv_not_wr08 */
-/* 0x0861: i2c_recv_done */
- 0xf8cec703,
- 0x00070d7e,
- 0xd0fce0fc,
- 0xb20912f4,
- 0x023f7e7c,
-/* 0x0875: i2c_recv_exit */
-/* 0x0877: i2c_init */
- 0xf800f800,
-/* 0x0879: test_recv */
- 0x04584100,
- 0xb60011cf,
- 0x58400110,
- 0x0001f604,
- 0xe7f104bd,
- 0xe3f1d900,
- 0x967e134f,
- 0x00f80001,
-/* 0x0898: test_init */
- 0x7e08004e,
- 0xf8000196,
-/* 0x08a1: idle_recv */
-/* 0x08a3: idle */
- 0xf400f800,
- 0x54410031,
+ 0xb6000856,
+ 0x11f50464,
+ 0xc5c700cc,
+ 0x0076bbe0,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0xfc7e50fc,
+ 0x64b60007,
+ 0xa911f504,
+ 0xbb010500,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x0008567e,
+ 0xf50464b6,
+ 0xbb008711,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x0007ad7e,
+ 0xf40464b6,
+ 0x5bcb6711,
+ 0x0076bbe0,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0xf97e50fc,
+ 0x64b60006,
+ 0xbd5bb204,
+ 0x410ef474,
+/* 0x09d0: i2c_recv_not_rd08 */
+ 0xf401d6b0,
+ 0x00053b1b,
+ 0x0008567e,
+ 0xc73211f4,
+ 0xfc7ee0c5,
+ 0x11f40007,
+ 0x7e000528,
+ 0xf4000856,
+ 0xb5c71f11,
+ 0x07fc7ee0,
+ 0x1511f400,
+ 0x0006f97e,
+ 0xc5c774bd,
+ 0x091bf408,
+ 0xf40232f4,
+/* 0x0a0e: i2c_recv_not_wr08 */
+/* 0x0a0e: i2c_recv_done */
+ 0xcec7030e,
+ 0x08ba7ef8,
+ 0xfce0fc00,
+ 0x0912f4d0,
+ 0xc27e7cb2,
+/* 0x0a22: i2c_recv_exit */
+ 0x00f80002,
+/* 0x0a24: i2c_init */
+/* 0x0a26: test_recv */
+ 0x584100f8,
0x0011cf04,
0x400110b6,
- 0x01f60454,
-/* 0x08b7: idle_loop */
- 0x0104bd00,
- 0x0232f458,
-/* 0x08bc: idle_proc */
-/* 0x08bc: idle_proc_exec */
- 0x1eb210f9,
- 0x0002487e,
- 0x11f410fc,
- 0x0231f409,
-/* 0x08cf: idle_proc_next */
- 0xb6f00ef4,
- 0x1fa65810,
- 0xf4e81bf4,
- 0x28f4e002,
- 0xc60ef400,
+ 0x01f60458,
+ 0xf104bd00,
+ 0xf1d900e7,
+ 0x7e134fe3,
+ 0xf8000201,
+/* 0x0a45: test_init */
+ 0x08004e00,
+ 0x0002017e,
+/* 0x0a4e: idle_recv */
+ 0x00f800f8,
+/* 0x0a50: idle */
+ 0x410031f4,
+ 0x11cf0454,
+ 0x0110b600,
+ 0xf6045440,
+ 0x04bd0001,
+/* 0x0a64: idle_loop */
+ 0x32f45801,
+/* 0x0a69: idle_proc */
+/* 0x0a69: idle_proc_exec */
+ 0xb210f902,
+ 0x02cb7e1e,
+ 0xf410fc00,
+ 0x31f40911,
+ 0xf00ef402,
+/* 0x0a7c: idle_proc_next */
+ 0xa65810b6,
+ 0xe81bf41f,
+ 0xf4e002f4,
+ 0x0ef40028,
+ 0x000000c6,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
0x00000000,
0x00000000,
0x00000000,
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc
index 6744fcc06151..daa06c1c655e 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc
@@ -23,6 +23,7 @@
*/
#define NVKM_PPWR_CHIPSET GT215
+#define HW_TICKS_PER_US 203 // should be 202.5
//#define NVKM_FALCON_PC24
//#define NVKM_FALCON_UNSHIFTED_IO
@@ -34,6 +35,7 @@
.section #nva3_pwr_data
#define INCLUDE_PROC
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
@@ -44,6 +46,7 @@
#define INCLUDE_DATA
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
@@ -56,6 +59,7 @@
.section #nva3_pwr_code
#define INCLUDE_CODE
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h
index 254205cd5166..64e97baabc3c 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nva3.fuc.h
@@ -24,8 +24,8 @@ uint32_t nva3_pwr_data[] = {
0x00000000,
/* 0x0058: proc_list_head */
0x54534f48,
- 0x00000430,
- 0x000003cd,
+ 0x00000512,
+ 0x000004af,
0x00000000,
0x00000000,
0x00000000,
@@ -46,8 +46,8 @@ uint32_t nva3_pwr_data[] = {
0x00000000,
0x00000000,
0x584d454d,
- 0x0000054e,
- 0x00000540,
+ 0x000006e0,
+ 0x000006d2,
0x00000000,
0x00000000,
0x00000000,
@@ -68,8 +68,8 @@ uint32_t nva3_pwr_data[] = {
0x00000000,
0x00000000,
0x46524550,
- 0x00000552,
- 0x00000550,
+ 0x000006e4,
+ 0x000006e2,
0x00000000,
0x00000000,
0x00000000,
@@ -90,8 +90,8 @@ uint32_t nva3_pwr_data[] = {
0x00000000,
0x00000000,
0x5f433249,
- 0x00000982,
- 0x00000825,
+ 0x00000b14,
+ 0x000009b7,
0x00000000,
0x00000000,
0x00000000,
@@ -112,8 +112,8 @@ uint32_t nva3_pwr_data[] = {
0x00000000,
0x00000000,
0x54534554,
- 0x000009ab,
- 0x00000984,
+ 0x00000b3d,
+ 0x00000b16,
0x00000000,
0x00000000,
0x00000000,
@@ -134,8 +134,8 @@ uint32_t nva3_pwr_data[] = {
0x00000000,
0x00000000,
0x454c4449,
- 0x000009b7,
- 0x000009b5,
+ 0x00000b49,
+ 0x00000b47,
0x00000000,
0x00000000,
0x00000000,
@@ -227,27 +227,31 @@ uint32_t nva3_pwr_data[] = {
0x00000000,
0x00000000,
/* 0x0370: memx_func_head */
- 0x00010000,
- 0x00000000,
- 0x0000046f,
-/* 0x037c: memx_func_next */
0x00000001,
0x00000000,
- 0x00000496,
+ 0x00000551,
+/* 0x037c: memx_func_next */
0x00000002,
+ 0x00000000,
+ 0x000005a8,
+ 0x00000003,
0x00000002,
- 0x000004b7,
- 0x00040003,
+ 0x0000063a,
+ 0x00040004,
0x00000000,
- 0x000004df,
- 0x00010004,
+ 0x00000656,
+ 0x00010005,
0x00000000,
- 0x000004fc,
-/* 0x03ac: memx_func_tail */
-/* 0x03ac: memx_data_head */
+ 0x00000673,
+ 0x00010006,
0x00000000,
+ 0x000005f8,
+/* 0x03b8: memx_func_tail */
+/* 0x03b8: memx_ts_start */
0x00000000,
+/* 0x03bc: memx_ts_end */
0x00000000,
+/* 0x03c0: memx_data_head */
0x00000000,
0x00000000,
0x00000000,
@@ -757,8 +761,11 @@ uint32_t nva3_pwr_data[] = {
0x00000000,
0x00000000,
0x00000000,
-/* 0x0bac: memx_data_tail */
-/* 0x0bac: i2c_scl_map */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+/* 0x0bc0: memx_data_tail */
+/* 0x0bc0: i2c_scl_map */
0x00001000,
0x00004000,
0x00010000,
@@ -769,7 +776,7 @@ uint32_t nva3_pwr_data[] = {
0x01000000,
0x04000000,
0x10000000,
-/* 0x0bd4: i2c_sda_map */
+/* 0x0be8: i2c_sda_map */
0x00002000,
0x00008000,
0x00020000,
@@ -780,7 +787,7 @@ uint32_t nva3_pwr_data[] = {
0x02000000,
0x08000000,
0x20000000,
-/* 0x0bfc: i2c_ctrl */
+/* 0x0c10: i2c_ctrl */
0x0000e138,
0x0000e150,
0x0000e168,
@@ -841,15 +848,10 @@ uint32_t nva3_pwr_data[] = {
0x00000000,
0x00000000,
0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
};
uint32_t nva3_pwr_code[] = {
- 0x030d0ef5,
+ 0x039e0ef5,
/* 0x0004: rd32 */
0x07a007f1,
0xd00604b6,
@@ -885,19 +887,22 @@ uint32_t nva3_pwr_code[] = {
0xd4f100dd,
0x1bf47000,
/* 0x007f: nsec */
- 0xf000f8f2,
+ 0xf900f8f2,
+ 0xf080f990,
0x84b62c87,
0x0088cf06,
-/* 0x0088: nsec_loop */
+/* 0x008c: nsec_loop */
0xb62c97f0,
0x99cf0694,
0x0298bb00,
0xf4069eb8,
- 0x00f8f11e,
-/* 0x009c: wait */
+ 0x80fcf11e,
+ 0x00f890fc,
+/* 0x00a4: wait */
+ 0x80f990f9,
0xb62c87f0,
0x88cf0684,
-/* 0x00a5: wait_loop */
+/* 0x00b1: wait_loop */
0x02eeb900,
0xb90421f4,
0xadfd02da,
@@ -907,28 +912,29 @@ uint32_t nva3_pwr_code[] = {
0x0099cf06,
0xb80298bb,
0x1ef4069b,
-/* 0x00c9: wait_done */
-/* 0x00cb: intr_watchdog */
- 0x9800f8df,
+/* 0x00d5: wait_done */
+ 0xfc80fcdf,
+/* 0x00db: intr_watchdog */
+ 0x9800f890,
0x96b003e9,
0x2a0bf400,
0xbb9a0a98,
0x1cf4029a,
0x01d7f00f,
- 0x025421f5,
+ 0x02dd21f5,
0x0ef494bd,
-/* 0x00e9: intr_watchdog_next_time */
+/* 0x00f9: intr_watchdog_next_time */
0x9b0a9815,
0xf400a6b0,
0x9ab8090b,
0x061cf406,
-/* 0x00f8: intr_watchdog_next_time_set */
-/* 0x00fb: intr_watchdog_next_proc */
+/* 0x0108: intr_watchdog_next_time_set */
+/* 0x010b: intr_watchdog_next_proc */
0x809b0980,
0xe0b603e9,
0x68e6b158,
0xc61bf402,
-/* 0x010a: intr */
+/* 0x011a: intr */
0x00f900f8,
0x80f904bd,
0xa0f990f9,
@@ -948,13 +954,13 @@ uint32_t nva3_pwr_code[] = {
0xf40289c4,
0x0080230b,
0x58e7f09b,
- 0x98cb21f4,
+ 0x98db21f4,
0x96b09b09,
0x110bf400,
0xb63407f0,
0x09d00604,
0x8004bd00,
-/* 0x016e: intr_skip_watchdog */
+/* 0x017e: intr_skip_watchdog */
0x89e49a09,
0x0bf40800,
0x8897f148,
@@ -967,22 +973,22 @@ uint32_t nva3_pwr_code[] = {
0x48e7f1c0,
0x53e3f14f,
0x00d7f054,
- 0x02b921f5,
+ 0x034221f5,
0x07f1c0fc,
0x04b604c0,
0x000cd006,
-/* 0x01ae: intr_subintr_skip_fifo */
+/* 0x01be: intr_subintr_skip_fifo */
0x07f104bd,
0x04b60688,
0x0009d006,
-/* 0x01ba: intr_skip_subintr */
+/* 0x01ca: intr_skip_subintr */
0x89c404bd,
0x070bf420,
0xffbfa4f1,
-/* 0x01c4: intr_skip_pause */
+/* 0x01d4: intr_skip_pause */
0xf44089c4,
0xa4f1070b,
-/* 0x01ce: intr_skip_user0 */
+/* 0x01de: intr_skip_user0 */
0x07f0ffbf,
0x0604b604,
0xbd0008d0,
@@ -993,597 +999,733 @@ uint32_t nva3_pwr_code[] = {
0x90fca0fc,
0x00fc80fc,
0xf80032f4,
-/* 0x01f5: timer */
- 0x1032f401,
- 0xb003f898,
- 0x1cf40086,
- 0x03fe8051,
+/* 0x0205: ticks_from_ns */
+ 0xf9c0f901,
+ 0xcbd7f1b0,
+ 0x00d3f000,
+ 0x041321f5,
+ 0x03e8ccec,
+ 0xf400b4b0,
+ 0xeeec120b,
+ 0xd7f103e8,
+ 0xd3f000cb,
+ 0x1321f500,
+/* 0x022d: ticks_from_ns_quit */
+ 0x02ceb904,
+ 0xc0fcb0fc,
+/* 0x0236: ticks_from_us */
+ 0xc0f900f8,
+ 0xd7f1b0f9,
+ 0xd3f000cb,
+ 0x1321f500,
+ 0x02ceb904,
+ 0xf400b4b0,
+ 0xe4bd050b,
+/* 0x0250: ticks_from_us_quit */
+ 0xc0fcb0fc,
+/* 0x0256: ticks_to_us */
+ 0xd7f100f8,
+ 0xd3f000cb,
+ 0xecedff00,
+/* 0x0262: timer */
+ 0x90f900f8,
+ 0x32f480f9,
+ 0x03f89810,
+ 0xf40086b0,
+ 0x84bd651c,
0xb63807f0,
0x08d00604,
0xf004bd00,
- 0x84b60887,
+ 0x84b63487,
0x0088cf06,
- 0xf40284f0,
- 0x87f0261b,
- 0x0684b634,
- 0xb80088cf,
- 0x0bf406e0,
- 0x06e8b809,
-/* 0x0233: timer_reset */
- 0xf01f1ef4,
- 0x04b63407,
- 0x000ed006,
- 0x0e8004bd,
-/* 0x0241: timer_enable */
- 0x0187f09a,
+ 0xbb9a0998,
+ 0xe9bb0298,
+ 0x03fe8000,
+ 0xb60887f0,
+ 0x88cf0684,
+ 0x0284f000,
+ 0xf0261bf4,
+ 0x84b63487,
+ 0x0088cf06,
+ 0xf406e0b8,
+ 0xe8b8090b,
+ 0x111cf406,
+/* 0x02b8: timer_reset */
+ 0xb63407f0,
+ 0x0ed00604,
+ 0x8004bd00,
+/* 0x02c6: timer_enable */
+ 0x87f09a0e,
+ 0x3807f001,
+ 0xd00604b6,
+ 0x04bd0008,
+/* 0x02d4: timer_done */
+ 0xfc1031f4,
+ 0xf890fc80,
+/* 0x02dd: send_proc */
+ 0xf980f900,
+ 0x05e89890,
+ 0xf004e998,
+ 0x89b80486,
+ 0x2a0bf406,
+ 0x940398c4,
+ 0x80b60488,
+ 0x008ebb18,
+ 0x8000fa98,
+ 0x8d80008a,
+ 0x028c8001,
+ 0xb6038b80,
+ 0x94f00190,
+ 0x04e98007,
+/* 0x0317: send_done */
+ 0xfc0231f4,
+ 0xf880fc90,
+/* 0x031d: find */
+ 0xf080f900,
+ 0x31f45887,
+/* 0x0325: find_loop */
+ 0x008a9801,
+ 0xf406aeb8,
+ 0x80b6100b,
+ 0x6886b158,
+ 0xf01bf402,
+/* 0x033b: find_done */
+ 0xb90132f4,
+ 0x80fc028e,
+/* 0x0342: send */
+ 0x21f500f8,
+ 0x01f4031d,
+/* 0x034b: recv */
+ 0xf900f897,
+ 0x9880f990,
+ 0xe99805e8,
+ 0x0132f404,
+ 0xf40689b8,
+ 0x89c43d0b,
+ 0x0180b603,
+ 0x800784f0,
+ 0xea9805e8,
+ 0xfef0f902,
+ 0xf0f9018f,
+ 0x9402efb9,
+ 0xe9bb0499,
+ 0x18e0b600,
+ 0x9803eb98,
+ 0xed9802ec,
+ 0x00ee9801,
+ 0xf0fca5f9,
+ 0xf400f8fe,
+ 0xf0fc0131,
+/* 0x0398: recv_done */
+ 0x90fc80fc,
+/* 0x039e: init */
+ 0x17f100f8,
+ 0x14b60108,
+ 0x0011cf06,
+ 0x010911e7,
+ 0xfe0814b6,
+ 0x17f10014,
+ 0x13f000e0,
+ 0x1c07f000,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0xf0ff17f0,
+ 0x04b61407,
+ 0x0001d006,
+ 0x17f004bd,
+ 0x0015f102,
+ 0x1007f008,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0x011a17f1,
+ 0xfe0013f0,
+ 0x31f40010,
+ 0x0117f010,
0xb63807f0,
- 0x08d00604,
-/* 0x024f: timer_done */
- 0xf404bd00,
- 0x00f81031,
-/* 0x0254: send_proc */
- 0x90f980f9,
- 0x9805e898,
- 0x86f004e9,
- 0x0689b804,
- 0xc42a0bf4,
- 0x88940398,
- 0x1880b604,
- 0x98008ebb,
- 0x8a8000fa,
- 0x018d8000,
- 0x80028c80,
- 0x90b6038b,
- 0x0794f001,
- 0xf404e980,
-/* 0x028e: send_done */
- 0x90fc0231,
- 0x00f880fc,
-/* 0x0294: find */
- 0x87f080f9,
- 0x0131f458,
-/* 0x029c: find_loop */
- 0xb8008a98,
- 0x0bf406ae,
- 0x5880b610,
- 0x026886b1,
- 0xf4f01bf4,
-/* 0x02b2: find_done */
- 0x8eb90132,
- 0xf880fc02,
-/* 0x02b9: send */
- 0x9421f500,
- 0x9701f402,
-/* 0x02c2: recv */
- 0xe89800f8,
- 0x04e99805,
- 0xb80132f4,
- 0x0bf40689,
- 0x0389c43d,
- 0xf00180b6,
- 0xe8800784,
- 0x02ea9805,
- 0x8ffef0f9,
- 0xb9f0f901,
- 0x999402ef,
- 0x00e9bb04,
- 0x9818e0b6,
- 0xec9803eb,
- 0x01ed9802,
- 0xf900ee98,
- 0xfef0fca5,
- 0x31f400f8,
-/* 0x030b: recv_done */
- 0xf8f0fc01,
-/* 0x030d: init */
- 0x0817f100,
- 0x0614b601,
- 0xe70011cf,
- 0xb6010911,
- 0x14fe0814,
- 0xe017f100,
- 0x0013f000,
- 0xb61c07f0,
0x01d00604,
0xf004bd00,
- 0x07f0ff17,
- 0x0604b614,
- 0xbd0001d0,
- 0x0217f004,
- 0x080015f1,
- 0xb61007f0,
- 0x01d00604,
- 0xf104bd00,
- 0xf0010a17,
- 0x10fe0013,
- 0x1031f400,
- 0xf00117f0,
- 0x04b63807,
- 0x0001d006,
- 0xf7f004bd,
-/* 0x0371: init_proc */
- 0x01f19858,
- 0xf40016b0,
- 0x15f9fa0b,
- 0xf458f0b6,
-/* 0x0382: host_send */
- 0x17f1f20e,
- 0x14b604b0,
- 0x0011cf06,
- 0x04a027f1,
- 0xcf0624b6,
- 0x12b80022,
- 0x320bf406,
- 0x94071ec4,
- 0xe0b704ee,
- 0xeb980270,
- 0x02ec9803,
- 0x9801ed98,
- 0x21f500ee,
- 0x10b602b9,
- 0x0f1ec401,
- 0x04b007f1,
- 0xd00604b6,
- 0x04bd000e,
-/* 0x03cb: host_send_done */
- 0xf8ba0ef4,
-/* 0x03cd: host_recv */
- 0x4917f100,
- 0x5413f14e,
- 0x06e1b852,
-/* 0x03db: host_recv_wait */
- 0xf1aa0bf4,
- 0xb604cc17,
- 0x11cf0614,
- 0xc827f100,
- 0x0624b604,
- 0xf00022cf,
- 0x12b80816,
- 0xe60bf406,
- 0xb60723c4,
- 0x30b70434,
- 0x3b8002f0,
- 0x023c8003,
- 0x80013d80,
- 0x20b6003e,
- 0x0f24f001,
- 0x04c807f1,
+/* 0x0402: init_proc */
+ 0xf19858f7,
+ 0x0016b001,
+ 0xf9fa0bf4,
+ 0x58f0b615,
+/* 0x0413: mulu32_32_64 */
+ 0xf9f20ef4,
+ 0xf920f910,
+ 0x9540f930,
+ 0xd29510e1,
+ 0xbdc4bd10,
+ 0xc0edffb4,
+ 0xb9301dff,
+ 0x34f10234,
+ 0x34b6ffff,
+ 0x1045b610,
+ 0xbb00c3bb,
+ 0xe2ff01b4,
+ 0x0234b930,
+ 0xffff34f1,
+ 0xb61034b6,
+ 0xc3bb1045,
+ 0x01b4bb00,
+ 0xbb3012ff,
+ 0x40fc00b3,
+ 0x20fc30fc,
+ 0x00f810fc,
+/* 0x0464: host_send */
+ 0x04b017f1,
+ 0xcf0614b6,
+ 0x27f10011,
+ 0x24b604a0,
+ 0x0022cf06,
+ 0xf40612b8,
+ 0x1ec4320b,
+ 0x04ee9407,
+ 0x0270e0b7,
+ 0x9803eb98,
+ 0xed9802ec,
+ 0x00ee9801,
+ 0x034221f5,
+ 0xc40110b6,
+ 0x07f10f1e,
+ 0x04b604b0,
+ 0x000ed006,
+ 0x0ef404bd,
+/* 0x04ad: host_send_done */
+/* 0x04af: host_recv */
+ 0xf100f8ba,
+ 0xf14e4917,
+ 0xb8525413,
+ 0x0bf406e1,
+/* 0x04bd: host_recv_wait */
+ 0xcc17f1aa,
+ 0x0614b604,
+ 0xf10011cf,
+ 0xb604c827,
+ 0x22cf0624,
+ 0x0816f000,
+ 0xf40612b8,
+ 0x23c4e60b,
+ 0x0434b607,
+ 0x02f030b7,
+ 0x80033b80,
+ 0x3d80023c,
+ 0x003e8001,
+ 0xf00120b6,
+ 0x07f10f24,
+ 0x04b604c8,
+ 0x0002d006,
+ 0x27f004bd,
+ 0x0007f040,
0xd00604b6,
0x04bd0002,
- 0xf04027f0,
- 0x04b60007,
- 0x0002d006,
- 0x00f804bd,
-/* 0x0430: host_init */
- 0x008017f1,
- 0xf11014b6,
- 0xf1027015,
- 0xb604d007,
- 0x01d00604,
- 0xf104bd00,
- 0xb6008017,
- 0x15f11014,
- 0x07f102f0,
- 0x04b604dc,
- 0x0001d006,
- 0x17f004bd,
- 0xc407f101,
+/* 0x0512: host_init */
+ 0x17f100f8,
+ 0x14b60080,
+ 0x7015f110,
+ 0xd007f102,
0x0604b604,
0xbd0001d0,
-/* 0x046f: memx_func_enter */
- 0xf000f804,
+ 0x8017f104,
+ 0x1014b600,
+ 0x02f015f1,
+ 0x04dc07f1,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0xf10117f0,
+ 0xb604c407,
+ 0x01d00604,
+ 0xf804bd00,
+/* 0x0551: memx_func_enter */
+ 0x1087f100,
+ 0x028eb916,
+ 0xb90421f4,
+ 0x67f102d7,
+ 0x63f1fffc,
+ 0x76fdffff,
+ 0x0267f104,
+ 0x0576fd00,
+ 0x70f980f9,
+ 0xe0fcd0fc,
+ 0xf03f21f4,
0x07f10467,
0x04b607e0,
0x0006d006,
-/* 0x047e: memx_func_enter_wait */
+/* 0x058a: memx_func_enter_wait */
0x67f104bd,
0x64b607c0,
0x0066cf06,
0xf40464f0,
- 0x1698f30b,
- 0x0410b600,
-/* 0x0496: memx_func_leave */
- 0x67f000f8,
- 0xe407f104,
- 0x0604b607,
- 0xbd0006d0,
-/* 0x04a5: memx_func_leave_wait */
- 0xc067f104,
+ 0x67f0f30b,
+ 0x0664b62c,
+ 0x800066cf,
+ 0x00f8ee06,
+/* 0x05a8: memx_func_leave */
+ 0xb62c67f0,
+ 0x66cf0664,
+ 0xef068000,
+ 0xf10467f0,
+ 0xb607e407,
+ 0x06d00604,
+/* 0x05c3: memx_func_leave_wait */
+ 0xf104bd00,
+ 0xb607c067,
+ 0x66cf0664,
+ 0x0464f000,
+ 0xf1f31bf4,
+ 0xb9161087,
+ 0x21f4028e,
+ 0x02d7b904,
+ 0xffcc67f1,
+ 0xffff63f1,
+ 0xf90476fd,
+ 0xfc70f980,
+ 0xf4e0fcd0,
+ 0x00f83f21,
+/* 0x05f8: memx_func_wait_vblank */
+ 0xb0001698,
+ 0x0bf40066,
+ 0x0166b013,
+ 0xf4060bf4,
+/* 0x060a: memx_func_wait_vblank_head1 */
+ 0x77f12e0e,
+ 0x0ef40020,
+/* 0x0611: memx_func_wait_vblank_head0 */
+ 0x0877f107,
+/* 0x0615: memx_func_wait_vblank_0 */
+ 0xc467f100,
0x0664b607,
- 0xf00066cf,
- 0x1bf40464,
-/* 0x04b7: memx_func_wr32 */
- 0x9800f8f3,
- 0x15980016,
- 0x0810b601,
- 0x50f960f9,
- 0xe0fcd0fc,
- 0xf13f21f4,
- 0xfd140003,
- 0x05800506,
- 0xb604bd00,
- 0x1bf40242,
-/* 0x04df: memx_func_wait */
- 0xf000f8dd,
- 0x84b62c87,
- 0x0088cf06,
- 0x98001e98,
- 0x1c98011d,
- 0x031b9802,
- 0xf41010b6,
- 0x00f89c21,
-/* 0x04fc: memx_func_delay */
- 0xb6001e98,
- 0x21f40410,
-/* 0x0507: memx_exec */
- 0xf900f87f,
- 0xb9d0f9e0,
- 0xb2b902c1,
-/* 0x0511: memx_exec_next */
- 0x00139802,
- 0x950410b6,
- 0x30f01034,
- 0xde35980c,
- 0x12b855f9,
- 0xec1ef406,
- 0xe0fcd0fc,
- 0x02b921f5,
-/* 0x0532: memx_info */
- 0xc7f100f8,
- 0xb7f103ac,
- 0x21f50800,
- 0x00f802b9,
-/* 0x0540: memx_recv */
- 0xf401d6b0,
- 0xd6b0c40b,
- 0xe90bf400,
-/* 0x054e: memx_init */
- 0x00f800f8,
-/* 0x0550: perf_recv */
-/* 0x0552: perf_init */
+ 0xfd0066cf,
+ 0x1bf40467,
+/* 0x0625: memx_func_wait_vblank_1 */
+ 0xc467f1f3,
+ 0x0664b607,
+ 0xfd0066cf,
+ 0x0bf40467,
+/* 0x0635: memx_func_wait_vblank_fini */
+ 0x0410b6f3,
+/* 0x063a: memx_func_wr32 */
+ 0x169800f8,
+ 0x01159800,
+ 0xf90810b6,
+ 0xfc50f960,
+ 0xf4e0fcd0,
+ 0x42b63f21,
+ 0xe91bf402,
+/* 0x0656: memx_func_wait */
+ 0x87f000f8,
+ 0x0684b62c,
+ 0x980088cf,
+ 0x1d98001e,
+ 0x021c9801,
+ 0xb6031b98,
+ 0x21f41010,
+/* 0x0673: memx_func_delay */
+ 0x9800f8a4,
+ 0x10b6001e,
+ 0x7f21f404,
+/* 0x067e: memx_exec */
+ 0xe0f900f8,
+ 0xc1b9d0f9,
+ 0x02b2b902,
+/* 0x0688: memx_exec_next */
+ 0xb6001398,
+ 0x34e70410,
+ 0x33e701f0,
+ 0x32b601e0,
+ 0x0c30f001,
+ 0xf9de3598,
+ 0x0612b855,
+ 0x98e41ef4,
+ 0x0c98ee0b,
+ 0x02cbbbef,
+ 0x07c4b7f1,
+ 0xcf06b4b6,
+ 0xd0fc00bb,
+ 0x21f5e0fc,
+ 0x00f80342,
+/* 0x06c4: memx_info */
+ 0x03c0c7f1,
+ 0x0800b7f1,
+ 0x034221f5,
+/* 0x06d2: memx_recv */
+ 0xd6b000f8,
+ 0xa90bf401,
+ 0xf400d6b0,
+ 0x00f8e90b,
+/* 0x06e0: memx_init */
+/* 0x06e2: perf_recv */
0x00f800f8,
-/* 0x0554: i2c_drive_scl */
- 0xf40036b0,
- 0x07f1110b,
- 0x04b607e0,
- 0x0001d006,
- 0x00f804bd,
-/* 0x0568: i2c_drive_scl_lo */
- 0x07e407f1,
- 0xd00604b6,
- 0x04bd0001,
-/* 0x0576: i2c_drive_sda */
+/* 0x06e4: perf_init */
+/* 0x06e6: i2c_drive_scl */
0x36b000f8,
0x110bf400,
0x07e007f1,
0xd00604b6,
- 0x04bd0002,
-/* 0x058a: i2c_drive_sda_lo */
+ 0x04bd0001,
+/* 0x06fa: i2c_drive_scl_lo */
0x07f100f8,
0x04b607e4,
+ 0x0001d006,
+ 0x00f804bd,
+/* 0x0708: i2c_drive_sda */
+ 0xf40036b0,
+ 0x07f1110b,
+ 0x04b607e0,
0x0002d006,
0x00f804bd,
-/* 0x0598: i2c_sense_scl */
- 0xf10132f4,
- 0xb607c437,
- 0x33cf0634,
- 0x0431fd00,
- 0xf4060bf4,
-/* 0x05ae: i2c_sense_scl_done */
- 0x00f80131,
-/* 0x05b0: i2c_sense_sda */
- 0xf10132f4,
- 0xb607c437,
- 0x33cf0634,
- 0x0432fd00,
- 0xf4060bf4,
-/* 0x05c6: i2c_sense_sda_done */
- 0x00f80131,
-/* 0x05c8: i2c_raise_scl */
- 0x47f140f9,
- 0x37f00898,
- 0x5421f501,
-/* 0x05d5: i2c_raise_scl_wait */
- 0xe8e7f105,
- 0x7f21f403,
- 0x059821f5,
- 0xb60901f4,
- 0x1bf40142,
-/* 0x05e9: i2c_raise_scl_done */
- 0xf840fcef,
-/* 0x05ed: i2c_start */
- 0x9821f500,
- 0x0d11f405,
- 0x05b021f5,
- 0xf40611f4,
-/* 0x05fe: i2c_start_rep */
- 0x37f0300e,
- 0x5421f500,
- 0x0137f005,
- 0x057621f5,
- 0xb60076bb,
- 0x50f90465,
- 0xbb046594,
- 0x50bd0256,
- 0xfc0475fd,
- 0xc821f550,
- 0x0464b605,
-/* 0x062b: i2c_start_send */
- 0xf01f11f4,
+/* 0x071c: i2c_drive_sda_lo */
+ 0x07e407f1,
+ 0xd00604b6,
+ 0x04bd0002,
+/* 0x072a: i2c_sense_scl */
+ 0x32f400f8,
+ 0xc437f101,
+ 0x0634b607,
+ 0xfd0033cf,
+ 0x0bf40431,
+ 0x0131f406,
+/* 0x0740: i2c_sense_scl_done */
+/* 0x0742: i2c_sense_sda */
+ 0x32f400f8,
+ 0xc437f101,
+ 0x0634b607,
+ 0xfd0033cf,
+ 0x0bf40432,
+ 0x0131f406,
+/* 0x0758: i2c_sense_sda_done */
+/* 0x075a: i2c_raise_scl */
+ 0x40f900f8,
+ 0x089847f1,
+ 0xf50137f0,
+/* 0x0767: i2c_raise_scl_wait */
+ 0xf106e621,
+ 0xf403e8e7,
+ 0x21f57f21,
+ 0x01f4072a,
+ 0x0142b609,
+/* 0x077b: i2c_raise_scl_done */
+ 0xfcef1bf4,
+/* 0x077f: i2c_start */
+ 0xf500f840,
+ 0xf4072a21,
+ 0x21f50d11,
+ 0x11f40742,
+ 0x300ef406,
+/* 0x0790: i2c_start_rep */
+ 0xf50037f0,
+ 0xf006e621,
+ 0x21f50137,
+ 0x76bb0708,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb6075a21,
+ 0x11f40464,
+/* 0x07bd: i2c_start_send */
+ 0x0037f01f,
+ 0x070821f5,
+ 0x1388e7f1,
+ 0xf07f21f4,
0x21f50037,
- 0xe7f10576,
+ 0xe7f106e6,
0x21f41388,
- 0x0037f07f,
- 0x055421f5,
- 0x1388e7f1,
-/* 0x0647: i2c_start_out */
- 0xf87f21f4,
-/* 0x0649: i2c_stop */
- 0x0037f000,
- 0x055421f5,
- 0xf50037f0,
- 0xf1057621,
- 0xf403e8e7,
- 0x37f07f21,
- 0x5421f501,
- 0x88e7f105,
- 0x7f21f413,
+/* 0x07d9: i2c_start_out */
+/* 0x07db: i2c_stop */
+ 0xf000f87f,
+ 0x21f50037,
+ 0x37f006e6,
+ 0x0821f500,
+ 0xe8e7f107,
+ 0x7f21f403,
0xf50137f0,
- 0xf1057621,
+ 0xf106e621,
0xf41388e7,
- 0x00f87f21,
-/* 0x067c: i2c_bitw */
- 0x057621f5,
- 0x03e8e7f1,
- 0xbb7f21f4,
- 0x65b60076,
- 0x9450f904,
- 0x56bb0465,
- 0xfd50bd02,
- 0x50fc0475,
- 0x05c821f5,
- 0xf40464b6,
- 0xe7f11811,
- 0x21f41388,
- 0x0037f07f,
- 0x055421f5,
- 0x1388e7f1,
-/* 0x06bb: i2c_bitw_out */
- 0xf87f21f4,
-/* 0x06bd: i2c_bitr */
- 0x0137f000,
- 0x057621f5,
- 0x03e8e7f1,
- 0xbb7f21f4,
- 0x65b60076,
- 0x9450f904,
- 0x56bb0465,
- 0xfd50bd02,
- 0x50fc0475,
- 0x05c821f5,
- 0xf40464b6,
- 0x21f51b11,
- 0x37f005b0,
- 0x5421f500,
- 0x88e7f105,
+ 0x37f07f21,
+ 0x0821f501,
+ 0x88e7f107,
0x7f21f413,
- 0xf4013cf0,
-/* 0x0702: i2c_bitr_done */
- 0x00f80131,
-/* 0x0704: i2c_get_byte */
- 0xf00057f0,
-/* 0x070a: i2c_get_byte_next */
- 0x54b60847,
- 0x0076bb01,
+/* 0x080e: i2c_bitw */
+ 0x21f500f8,
+ 0xe7f10708,
+ 0x21f403e8,
+ 0x0076bb7f,
0xf90465b6,
0x04659450,
0xbd0256bb,
0x0475fd50,
0x21f550fc,
- 0x64b606bd,
- 0x2b11f404,
- 0xb60553fd,
- 0x1bf40142,
- 0x0137f0d8,
- 0xb60076bb,
- 0x50f90465,
- 0xbb046594,
- 0x50bd0256,
- 0xfc0475fd,
- 0x7c21f550,
- 0x0464b606,
-/* 0x0754: i2c_get_byte_done */
-/* 0x0756: i2c_put_byte */
- 0x47f000f8,
-/* 0x0759: i2c_put_byte_next */
- 0x0142b608,
- 0xbb3854ff,
+ 0x64b6075a,
+ 0x1811f404,
+ 0x1388e7f1,
+ 0xf07f21f4,
+ 0x21f50037,
+ 0xe7f106e6,
+ 0x21f41388,
+/* 0x084d: i2c_bitw_out */
+/* 0x084f: i2c_bitr */
+ 0xf000f87f,
+ 0x21f50137,
+ 0xe7f10708,
+ 0x21f403e8,
+ 0x0076bb7f,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b6075a,
+ 0x1b11f404,
+ 0x074221f5,
+ 0xf50037f0,
+ 0xf106e621,
+ 0xf41388e7,
+ 0x3cf07f21,
+ 0x0131f401,
+/* 0x0894: i2c_bitr_done */
+/* 0x0896: i2c_get_byte */
+ 0x57f000f8,
+ 0x0847f000,
+/* 0x089c: i2c_get_byte_next */
+ 0xbb0154b6,
0x65b60076,
0x9450f904,
0x56bb0465,
0xfd50bd02,
0x50fc0475,
- 0x067c21f5,
+ 0x084f21f5,
0xf40464b6,
- 0x46b03411,
- 0xd81bf400,
- 0xb60076bb,
- 0x50f90465,
- 0xbb046594,
- 0x50bd0256,
- 0xfc0475fd,
- 0xbd21f550,
- 0x0464b606,
- 0xbb0f11f4,
- 0x36b00076,
- 0x061bf401,
-/* 0x07af: i2c_put_byte_done */
- 0xf80132f4,
-/* 0x07b1: i2c_addr */
- 0x0076bb00,
+ 0x53fd2b11,
+ 0x0142b605,
+ 0xf0d81bf4,
+ 0x76bb0137,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb6080e21,
+/* 0x08e6: i2c_get_byte_done */
+ 0x00f80464,
+/* 0x08e8: i2c_put_byte */
+/* 0x08eb: i2c_put_byte_next */
+ 0xb60847f0,
+ 0x54ff0142,
+ 0x0076bb38,
0xf90465b6,
0x04659450,
0xbd0256bb,
0x0475fd50,
0x21f550fc,
- 0x64b605ed,
- 0x2911f404,
- 0x012ec3e7,
- 0xfd0134b6,
- 0x76bb0553,
+ 0x64b6080e,
+ 0x3411f404,
+ 0xf40046b0,
+ 0x76bbd81b,
0x0465b600,
0x659450f9,
0x0256bb04,
0x75fd50bd,
0xf550fc04,
- 0xb6075621,
-/* 0x07f6: i2c_addr_done */
- 0x00f80464,
-/* 0x07f8: i2c_acquire_addr */
- 0xb6f8cec7,
- 0xe0b702e4,
- 0xee980bfc,
-/* 0x0807: i2c_acquire */
- 0xf500f800,
- 0xf407f821,
- 0xd9f00421,
- 0x3f21f403,
-/* 0x0816: i2c_release */
- 0x21f500f8,
- 0x21f407f8,
- 0x03daf004,
- 0xf83f21f4,
-/* 0x0825: i2c_recv */
- 0x0132f400,
- 0xb6f8c1c7,
- 0x16b00214,
- 0x3a1ff528,
- 0xd413a001,
- 0x0032980b,
- 0x0bac13a0,
- 0xf4003198,
- 0xd0f90231,
- 0xd0f9e0f9,
- 0x000067f1,
- 0x100063f1,
- 0xbb016792,
+ 0xb6084f21,
+ 0x11f40464,
+ 0x0076bb0f,
+ 0xf40136b0,
+ 0x32f4061b,
+/* 0x0941: i2c_put_byte_done */
+/* 0x0943: i2c_addr */
+ 0xbb00f801,
0x65b60076,
0x9450f904,
0x56bb0465,
0xfd50bd02,
0x50fc0475,
- 0x080721f5,
- 0xfc0464b6,
- 0x00d6b0d0,
- 0x00b31bf5,
- 0xbb0057f0,
- 0x65b60076,
- 0x9450f904,
- 0x56bb0465,
- 0xfd50bd02,
- 0x50fc0475,
- 0x07b121f5,
- 0xf50464b6,
- 0xc700d011,
- 0x76bbe0c5,
- 0x0465b600,
- 0x659450f9,
- 0x0256bb04,
- 0x75fd50bd,
- 0xf550fc04,
- 0xb6075621,
- 0x11f50464,
- 0x57f000ad,
+ 0x077f21f5,
+ 0xf40464b6,
+ 0xc3e72911,
+ 0x34b6012e,
+ 0x0553fd01,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0xe821f550,
+ 0x0464b608,
+/* 0x0988: i2c_addr_done */
+/* 0x098a: i2c_acquire_addr */
+ 0xcec700f8,
+ 0x02e4b6f8,
+ 0x0c10e0b7,
+ 0xf800ee98,
+/* 0x0999: i2c_acquire */
+ 0x8a21f500,
+ 0x0421f409,
+ 0xf403d9f0,
+ 0x00f83f21,
+/* 0x09a8: i2c_release */
+ 0x098a21f5,
+ 0xf00421f4,
+ 0x21f403da,
+/* 0x09b7: i2c_recv */
+ 0xf400f83f,
+ 0xc1c70132,
+ 0x0214b6f8,
+ 0xf52816b0,
+ 0xa0013a1f,
+ 0x980be813,
+ 0x13a00032,
+ 0x31980bc0,
+ 0x0231f400,
+ 0xe0f9d0f9,
+ 0x67f1d0f9,
+ 0x63f10000,
+ 0x67921000,
0x0076bb01,
0xf90465b6,
0x04659450,
0xbd0256bb,
0x0475fd50,
0x21f550fc,
- 0x64b607b1,
- 0x8a11f504,
+ 0x64b60999,
+ 0xb0d0fc04,
+ 0x1bf500d6,
+ 0x57f000b3,
0x0076bb00,
0xf90465b6,
0x04659450,
0xbd0256bb,
0x0475fd50,
0x21f550fc,
- 0x64b60704,
- 0x6a11f404,
- 0xbbe05bcb,
+ 0x64b60943,
+ 0xd011f504,
+ 0xe0c5c700,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0xe821f550,
+ 0x0464b608,
+ 0x00ad11f5,
+ 0xbb0157f0,
0x65b60076,
0x9450f904,
0x56bb0465,
0xfd50bd02,
0x50fc0475,
- 0x064921f5,
- 0xb90464b6,
- 0x74bd025b,
-/* 0x092b: i2c_recv_not_rd08 */
- 0xb0430ef4,
- 0x1bf401d6,
- 0x0057f03d,
- 0x07b121f5,
- 0xc73311f4,
- 0x21f5e0c5,
- 0x11f40756,
- 0x0057f029,
- 0x07b121f5,
- 0xc71f11f4,
- 0x21f5e0b5,
- 0x11f40756,
- 0x4921f515,
- 0xc774bd06,
- 0x1bf408c5,
- 0x0232f409,
-/* 0x096b: i2c_recv_not_wr08 */
-/* 0x096b: i2c_recv_done */
- 0xc7030ef4,
- 0x21f5f8ce,
- 0xe0fc0816,
- 0x12f4d0fc,
- 0x027cb90a,
- 0x02b921f5,
-/* 0x0980: i2c_recv_exit */
-/* 0x0982: i2c_init */
- 0x00f800f8,
-/* 0x0984: test_recv */
- 0x05d817f1,
+ 0x094321f5,
+ 0xf50464b6,
+ 0xbb008a11,
+ 0x65b60076,
+ 0x9450f904,
+ 0x56bb0465,
+ 0xfd50bd02,
+ 0x50fc0475,
+ 0x089621f5,
+ 0xf40464b6,
+ 0x5bcb6a11,
+ 0x0076bbe0,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b607db,
+ 0x025bb904,
+ 0x0ef474bd,
+/* 0x0abd: i2c_recv_not_rd08 */
+ 0x01d6b043,
+ 0xf03d1bf4,
+ 0x21f50057,
+ 0x11f40943,
+ 0xe0c5c733,
+ 0x08e821f5,
+ 0xf02911f4,
+ 0x21f50057,
+ 0x11f40943,
+ 0xe0b5c71f,
+ 0x08e821f5,
+ 0xf51511f4,
+ 0xbd07db21,
+ 0x08c5c774,
+ 0xf4091bf4,
+ 0x0ef40232,
+/* 0x0afd: i2c_recv_not_wr08 */
+/* 0x0afd: i2c_recv_done */
+ 0xf8cec703,
+ 0x09a821f5,
+ 0xd0fce0fc,
+ 0xb90a12f4,
+ 0x21f5027c,
+/* 0x0b12: i2c_recv_exit */
+ 0x00f80342,
+/* 0x0b14: i2c_init */
+/* 0x0b16: test_recv */
+ 0x17f100f8,
+ 0x14b605d8,
+ 0x0011cf06,
+ 0xf10110b6,
+ 0xb605d807,
+ 0x01d00604,
+ 0xf104bd00,
+ 0xf1d900e7,
+ 0xf5134fe3,
+ 0xf8026221,
+/* 0x0b3d: test_init */
+ 0x00e7f100,
+ 0x6221f508,
+/* 0x0b47: idle_recv */
+ 0xf800f802,
+/* 0x0b49: idle */
+ 0x0031f400,
+ 0x05d417f1,
0xcf0614b6,
0x10b60011,
- 0xd807f101,
+ 0xd407f101,
0x0604b605,
0xbd0001d0,
- 0x00e7f104,
- 0x4fe3f1d9,
- 0xf521f513,
-/* 0x09ab: test_init */
- 0xf100f801,
- 0xf50800e7,
- 0xf801f521,
-/* 0x09b5: idle_recv */
-/* 0x09b7: idle */
- 0xf400f800,
- 0x17f10031,
- 0x14b605d4,
- 0x0011cf06,
- 0xf10110b6,
- 0xb605d407,
- 0x01d00604,
-/* 0x09d3: idle_loop */
- 0xf004bd00,
- 0x32f45817,
-/* 0x09d9: idle_proc */
-/* 0x09d9: idle_proc_exec */
- 0xb910f902,
- 0x21f5021e,
- 0x10fc02c2,
- 0xf40911f4,
- 0x0ef40231,
-/* 0x09ed: idle_proc_next */
- 0x5810b6ef,
- 0xf4061fb8,
- 0x02f4e61b,
- 0x0028f4dd,
- 0x00bb0ef4,
+/* 0x0b65: idle_loop */
+ 0x5817f004,
+/* 0x0b6b: idle_proc */
+/* 0x0b6b: idle_proc_exec */
+ 0xf90232f4,
+ 0x021eb910,
+ 0x034b21f5,
+ 0x11f410fc,
+ 0x0231f409,
+/* 0x0b7f: idle_proc_next */
+ 0xb6ef0ef4,
+ 0x1fb85810,
+ 0xe61bf406,
+ 0xf4dd02f4,
+ 0x0ef40028,
+ 0x000000bb,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc
index 48f79434a449..21bf8cc7618f 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc
@@ -23,6 +23,7 @@
*/
#define NVKM_PPWR_CHIPSET GF100
+#define HW_TICKS_PER_US 203 // should be 202.5
//#define NVKM_FALCON_PC24
//#define NVKM_FALCON_UNSHIFTED_IO
@@ -34,6 +35,7 @@
.section #nvc0_pwr_data
#define INCLUDE_PROC
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
@@ -44,6 +46,7 @@
#define INCLUDE_DATA
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
@@ -56,6 +59,7 @@
.section #nvc0_pwr_code
#define INCLUDE_CODE
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h
index 7ac87405d01b..ca30fa4011b5 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvc0.fuc.h
@@ -24,8 +24,8 @@ uint32_t nvc0_pwr_data[] = {
0x00000000,
/* 0x0058: proc_list_head */
0x54534f48,
- 0x00000430,
- 0x000003cd,
+ 0x00000512,
+ 0x000004af,
0x00000000,
0x00000000,
0x00000000,
@@ -46,8 +46,8 @@ uint32_t nvc0_pwr_data[] = {
0x00000000,
0x00000000,
0x584d454d,
- 0x0000054e,
- 0x00000540,
+ 0x0000074b,
+ 0x0000073d,
0x00000000,
0x00000000,
0x00000000,
@@ -68,8 +68,8 @@ uint32_t nvc0_pwr_data[] = {
0x00000000,
0x00000000,
0x46524550,
- 0x00000552,
- 0x00000550,
+ 0x0000074f,
+ 0x0000074d,
0x00000000,
0x00000000,
0x00000000,
@@ -90,8 +90,8 @@ uint32_t nvc0_pwr_data[] = {
0x00000000,
0x00000000,
0x5f433249,
- 0x00000982,
- 0x00000825,
+ 0x00000b7f,
+ 0x00000a22,
0x00000000,
0x00000000,
0x00000000,
@@ -112,8 +112,8 @@ uint32_t nvc0_pwr_data[] = {
0x00000000,
0x00000000,
0x54534554,
- 0x000009ab,
- 0x00000984,
+ 0x00000ba8,
+ 0x00000b81,
0x00000000,
0x00000000,
0x00000000,
@@ -134,8 +134,8 @@ uint32_t nvc0_pwr_data[] = {
0x00000000,
0x00000000,
0x454c4449,
- 0x000009b7,
- 0x000009b5,
+ 0x00000bb4,
+ 0x00000bb2,
0x00000000,
0x00000000,
0x00000000,
@@ -227,25 +227,31 @@ uint32_t nvc0_pwr_data[] = {
0x00000000,
0x00000000,
/* 0x0370: memx_func_head */
- 0x00010000,
- 0x00000000,
- 0x0000046f,
-/* 0x037c: memx_func_next */
0x00000001,
0x00000000,
- 0x00000496,
+ 0x00000551,
+/* 0x037c: memx_func_next */
0x00000002,
+ 0x00000000,
+ 0x000005db,
+ 0x00000003,
0x00000002,
- 0x000004b7,
- 0x00040003,
+ 0x000006a5,
+ 0x00040004,
+ 0x00000000,
+ 0x000006c1,
+ 0x00010005,
+ 0x00000000,
+ 0x000006de,
+ 0x00010006,
0x00000000,
- 0x000004df,
- 0x00010004,
+ 0x00000663,
+/* 0x03b8: memx_func_tail */
+/* 0x03b8: memx_ts_start */
0x00000000,
- 0x000004fc,
-/* 0x03ac: memx_func_tail */
-/* 0x03ac: memx_data_head */
+/* 0x03bc: memx_ts_end */
0x00000000,
+/* 0x03c0: memx_data_head */
0x00000000,
0x00000000,
0x00000000,
@@ -757,8 +763,9 @@ uint32_t nvc0_pwr_data[] = {
0x00000000,
0x00000000,
0x00000000,
-/* 0x0bac: memx_data_tail */
-/* 0x0bac: i2c_scl_map */
+ 0x00000000,
+/* 0x0bc0: memx_data_tail */
+/* 0x0bc0: i2c_scl_map */
0x00001000,
0x00004000,
0x00010000,
@@ -769,7 +776,7 @@ uint32_t nvc0_pwr_data[] = {
0x01000000,
0x04000000,
0x10000000,
-/* 0x0bd4: i2c_sda_map */
+/* 0x0be8: i2c_sda_map */
0x00002000,
0x00008000,
0x00020000,
@@ -780,7 +787,7 @@ uint32_t nvc0_pwr_data[] = {
0x02000000,
0x08000000,
0x20000000,
-/* 0x0bfc: i2c_ctrl */
+/* 0x0c10: i2c_ctrl */
0x0000e138,
0x0000e150,
0x0000e168,
@@ -841,15 +848,10 @@ uint32_t nvc0_pwr_data[] = {
0x00000000,
0x00000000,
0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
- 0x00000000,
};
uint32_t nvc0_pwr_code[] = {
- 0x030d0ef5,
+ 0x039e0ef5,
/* 0x0004: rd32 */
0x07a007f1,
0xd00604b6,
@@ -885,19 +887,22 @@ uint32_t nvc0_pwr_code[] = {
0xd4f100dd,
0x1bf47000,
/* 0x007f: nsec */
- 0xf000f8f2,
+ 0xf900f8f2,
+ 0xf080f990,
0x84b62c87,
0x0088cf06,
-/* 0x0088: nsec_loop */
+/* 0x008c: nsec_loop */
0xb62c97f0,
0x99cf0694,
0x0298bb00,
0xf4069eb8,
- 0x00f8f11e,
-/* 0x009c: wait */
+ 0x80fcf11e,
+ 0x00f890fc,
+/* 0x00a4: wait */
+ 0x80f990f9,
0xb62c87f0,
0x88cf0684,
-/* 0x00a5: wait_loop */
+/* 0x00b1: wait_loop */
0x02eeb900,
0xb90421f4,
0xadfd02da,
@@ -907,28 +912,29 @@ uint32_t nvc0_pwr_code[] = {
0x0099cf06,
0xb80298bb,
0x1ef4069b,
-/* 0x00c9: wait_done */
-/* 0x00cb: intr_watchdog */
- 0x9800f8df,
+/* 0x00d5: wait_done */
+ 0xfc80fcdf,
+/* 0x00db: intr_watchdog */
+ 0x9800f890,
0x96b003e9,
0x2a0bf400,
0xbb9a0a98,
0x1cf4029a,
0x01d7f00f,
- 0x025421f5,
+ 0x02dd21f5,
0x0ef494bd,
-/* 0x00e9: intr_watchdog_next_time */
+/* 0x00f9: intr_watchdog_next_time */
0x9b0a9815,
0xf400a6b0,
0x9ab8090b,
0x061cf406,
-/* 0x00f8: intr_watchdog_next_time_set */
-/* 0x00fb: intr_watchdog_next_proc */
+/* 0x0108: intr_watchdog_next_time_set */
+/* 0x010b: intr_watchdog_next_proc */
0x809b0980,
0xe0b603e9,
0x68e6b158,
0xc61bf402,
-/* 0x010a: intr */
+/* 0x011a: intr */
0x00f900f8,
0x80f904bd,
0xa0f990f9,
@@ -948,13 +954,13 @@ uint32_t nvc0_pwr_code[] = {
0xf40289c4,
0x0080230b,
0x58e7f09b,
- 0x98cb21f4,
+ 0x98db21f4,
0x96b09b09,
0x110bf400,
0xb63407f0,
0x09d00604,
0x8004bd00,
-/* 0x016e: intr_skip_watchdog */
+/* 0x017e: intr_skip_watchdog */
0x89e49a09,
0x0bf40800,
0x8897f148,
@@ -967,22 +973,22 @@ uint32_t nvc0_pwr_code[] = {
0x48e7f1c0,
0x53e3f14f,
0x00d7f054,
- 0x02b921f5,
+ 0x034221f5,
0x07f1c0fc,
0x04b604c0,
0x000cd006,
-/* 0x01ae: intr_subintr_skip_fifo */
+/* 0x01be: intr_subintr_skip_fifo */
0x07f104bd,
0x04b60688,
0x0009d006,
-/* 0x01ba: intr_skip_subintr */
+/* 0x01ca: intr_skip_subintr */
0x89c404bd,
0x070bf420,
0xffbfa4f1,
-/* 0x01c4: intr_skip_pause */
+/* 0x01d4: intr_skip_pause */
0xf44089c4,
0xa4f1070b,
-/* 0x01ce: intr_skip_user0 */
+/* 0x01de: intr_skip_user0 */
0x07f0ffbf,
0x0604b604,
0xbd0008d0,
@@ -993,597 +999,733 @@ uint32_t nvc0_pwr_code[] = {
0x90fca0fc,
0x00fc80fc,
0xf80032f4,
-/* 0x01f5: timer */
- 0x1032f401,
- 0xb003f898,
- 0x1cf40086,
- 0x03fe8051,
+/* 0x0205: ticks_from_ns */
+ 0xf9c0f901,
+ 0xcbd7f1b0,
+ 0x00d3f000,
+ 0x041321f5,
+ 0x03e8ccec,
+ 0xf400b4b0,
+ 0xeeec120b,
+ 0xd7f103e8,
+ 0xd3f000cb,
+ 0x1321f500,
+/* 0x022d: ticks_from_ns_quit */
+ 0x02ceb904,
+ 0xc0fcb0fc,
+/* 0x0236: ticks_from_us */
+ 0xc0f900f8,
+ 0xd7f1b0f9,
+ 0xd3f000cb,
+ 0x1321f500,
+ 0x02ceb904,
+ 0xf400b4b0,
+ 0xe4bd050b,
+/* 0x0250: ticks_from_us_quit */
+ 0xc0fcb0fc,
+/* 0x0256: ticks_to_us */
+ 0xd7f100f8,
+ 0xd3f000cb,
+ 0xecedff00,
+/* 0x0262: timer */
+ 0x90f900f8,
+ 0x32f480f9,
+ 0x03f89810,
+ 0xf40086b0,
+ 0x84bd651c,
0xb63807f0,
0x08d00604,
0xf004bd00,
- 0x84b60887,
+ 0x84b63487,
0x0088cf06,
- 0xf40284f0,
- 0x87f0261b,
- 0x0684b634,
- 0xb80088cf,
- 0x0bf406e0,
- 0x06e8b809,
-/* 0x0233: timer_reset */
- 0xf01f1ef4,
- 0x04b63407,
- 0x000ed006,
- 0x0e8004bd,
-/* 0x0241: timer_enable */
- 0x0187f09a,
+ 0xbb9a0998,
+ 0xe9bb0298,
+ 0x03fe8000,
+ 0xb60887f0,
+ 0x88cf0684,
+ 0x0284f000,
+ 0xf0261bf4,
+ 0x84b63487,
+ 0x0088cf06,
+ 0xf406e0b8,
+ 0xe8b8090b,
+ 0x111cf406,
+/* 0x02b8: timer_reset */
+ 0xb63407f0,
+ 0x0ed00604,
+ 0x8004bd00,
+/* 0x02c6: timer_enable */
+ 0x87f09a0e,
+ 0x3807f001,
+ 0xd00604b6,
+ 0x04bd0008,
+/* 0x02d4: timer_done */
+ 0xfc1031f4,
+ 0xf890fc80,
+/* 0x02dd: send_proc */
+ 0xf980f900,
+ 0x05e89890,
+ 0xf004e998,
+ 0x89b80486,
+ 0x2a0bf406,
+ 0x940398c4,
+ 0x80b60488,
+ 0x008ebb18,
+ 0x8000fa98,
+ 0x8d80008a,
+ 0x028c8001,
+ 0xb6038b80,
+ 0x94f00190,
+ 0x04e98007,
+/* 0x0317: send_done */
+ 0xfc0231f4,
+ 0xf880fc90,
+/* 0x031d: find */
+ 0xf080f900,
+ 0x31f45887,
+/* 0x0325: find_loop */
+ 0x008a9801,
+ 0xf406aeb8,
+ 0x80b6100b,
+ 0x6886b158,
+ 0xf01bf402,
+/* 0x033b: find_done */
+ 0xb90132f4,
+ 0x80fc028e,
+/* 0x0342: send */
+ 0x21f500f8,
+ 0x01f4031d,
+/* 0x034b: recv */
+ 0xf900f897,
+ 0x9880f990,
+ 0xe99805e8,
+ 0x0132f404,
+ 0xf40689b8,
+ 0x89c43d0b,
+ 0x0180b603,
+ 0x800784f0,
+ 0xea9805e8,
+ 0xfef0f902,
+ 0xf0f9018f,
+ 0x9402efb9,
+ 0xe9bb0499,
+ 0x18e0b600,
+ 0x9803eb98,
+ 0xed9802ec,
+ 0x00ee9801,
+ 0xf0fca5f9,
+ 0xf400f8fe,
+ 0xf0fc0131,
+/* 0x0398: recv_done */
+ 0x90fc80fc,
+/* 0x039e: init */
+ 0x17f100f8,
+ 0x14b60108,
+ 0x0011cf06,
+ 0x010911e7,
+ 0xfe0814b6,
+ 0x17f10014,
+ 0x13f000e0,
+ 0x1c07f000,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0xf0ff17f0,
+ 0x04b61407,
+ 0x0001d006,
+ 0x17f004bd,
+ 0x0015f102,
+ 0x1007f008,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0x011a17f1,
+ 0xfe0013f0,
+ 0x31f40010,
+ 0x0117f010,
0xb63807f0,
- 0x08d00604,
-/* 0x024f: timer_done */
- 0xf404bd00,
- 0x00f81031,
-/* 0x0254: send_proc */
- 0x90f980f9,
- 0x9805e898,
- 0x86f004e9,
- 0x0689b804,
- 0xc42a0bf4,
- 0x88940398,
- 0x1880b604,
- 0x98008ebb,
- 0x8a8000fa,
- 0x018d8000,
- 0x80028c80,
- 0x90b6038b,
- 0x0794f001,
- 0xf404e980,
-/* 0x028e: send_done */
- 0x90fc0231,
- 0x00f880fc,
-/* 0x0294: find */
- 0x87f080f9,
- 0x0131f458,
-/* 0x029c: find_loop */
- 0xb8008a98,
- 0x0bf406ae,
- 0x5880b610,
- 0x026886b1,
- 0xf4f01bf4,
-/* 0x02b2: find_done */
- 0x8eb90132,
- 0xf880fc02,
-/* 0x02b9: send */
- 0x9421f500,
- 0x9701f402,
-/* 0x02c2: recv */
- 0xe89800f8,
- 0x04e99805,
- 0xb80132f4,
- 0x0bf40689,
- 0x0389c43d,
- 0xf00180b6,
- 0xe8800784,
- 0x02ea9805,
- 0x8ffef0f9,
- 0xb9f0f901,
- 0x999402ef,
- 0x00e9bb04,
- 0x9818e0b6,
- 0xec9803eb,
- 0x01ed9802,
- 0xf900ee98,
- 0xfef0fca5,
- 0x31f400f8,
-/* 0x030b: recv_done */
- 0xf8f0fc01,
-/* 0x030d: init */
- 0x0817f100,
- 0x0614b601,
- 0xe70011cf,
- 0xb6010911,
- 0x14fe0814,
- 0xe017f100,
- 0x0013f000,
- 0xb61c07f0,
0x01d00604,
0xf004bd00,
- 0x07f0ff17,
- 0x0604b614,
- 0xbd0001d0,
- 0x0217f004,
- 0x080015f1,
- 0xb61007f0,
- 0x01d00604,
- 0xf104bd00,
- 0xf0010a17,
- 0x10fe0013,
- 0x1031f400,
- 0xf00117f0,
- 0x04b63807,
- 0x0001d006,
- 0xf7f004bd,
-/* 0x0371: init_proc */
- 0x01f19858,
- 0xf40016b0,
- 0x15f9fa0b,
- 0xf458f0b6,
-/* 0x0382: host_send */
- 0x17f1f20e,
- 0x14b604b0,
- 0x0011cf06,
- 0x04a027f1,
- 0xcf0624b6,
- 0x12b80022,
- 0x320bf406,
- 0x94071ec4,
- 0xe0b704ee,
- 0xeb980270,
- 0x02ec9803,
- 0x9801ed98,
- 0x21f500ee,
- 0x10b602b9,
- 0x0f1ec401,
- 0x04b007f1,
- 0xd00604b6,
- 0x04bd000e,
-/* 0x03cb: host_send_done */
- 0xf8ba0ef4,
-/* 0x03cd: host_recv */
- 0x4917f100,
- 0x5413f14e,
- 0x06e1b852,
-/* 0x03db: host_recv_wait */
- 0xf1aa0bf4,
- 0xb604cc17,
- 0x11cf0614,
- 0xc827f100,
- 0x0624b604,
- 0xf00022cf,
- 0x12b80816,
- 0xe60bf406,
- 0xb60723c4,
- 0x30b70434,
- 0x3b8002f0,
- 0x023c8003,
- 0x80013d80,
- 0x20b6003e,
- 0x0f24f001,
- 0x04c807f1,
+/* 0x0402: init_proc */
+ 0xf19858f7,
+ 0x0016b001,
+ 0xf9fa0bf4,
+ 0x58f0b615,
+/* 0x0413: mulu32_32_64 */
+ 0xf9f20ef4,
+ 0xf920f910,
+ 0x9540f930,
+ 0xd29510e1,
+ 0xbdc4bd10,
+ 0xc0edffb4,
+ 0xb9301dff,
+ 0x34f10234,
+ 0x34b6ffff,
+ 0x1045b610,
+ 0xbb00c3bb,
+ 0xe2ff01b4,
+ 0x0234b930,
+ 0xffff34f1,
+ 0xb61034b6,
+ 0xc3bb1045,
+ 0x01b4bb00,
+ 0xbb3012ff,
+ 0x40fc00b3,
+ 0x20fc30fc,
+ 0x00f810fc,
+/* 0x0464: host_send */
+ 0x04b017f1,
+ 0xcf0614b6,
+ 0x27f10011,
+ 0x24b604a0,
+ 0x0022cf06,
+ 0xf40612b8,
+ 0x1ec4320b,
+ 0x04ee9407,
+ 0x0270e0b7,
+ 0x9803eb98,
+ 0xed9802ec,
+ 0x00ee9801,
+ 0x034221f5,
+ 0xc40110b6,
+ 0x07f10f1e,
+ 0x04b604b0,
+ 0x000ed006,
+ 0x0ef404bd,
+/* 0x04ad: host_send_done */
+/* 0x04af: host_recv */
+ 0xf100f8ba,
+ 0xf14e4917,
+ 0xb8525413,
+ 0x0bf406e1,
+/* 0x04bd: host_recv_wait */
+ 0xcc17f1aa,
+ 0x0614b604,
+ 0xf10011cf,
+ 0xb604c827,
+ 0x22cf0624,
+ 0x0816f000,
+ 0xf40612b8,
+ 0x23c4e60b,
+ 0x0434b607,
+ 0x02f030b7,
+ 0x80033b80,
+ 0x3d80023c,
+ 0x003e8001,
+ 0xf00120b6,
+ 0x07f10f24,
+ 0x04b604c8,
+ 0x0002d006,
+ 0x27f004bd,
+ 0x0007f040,
0xd00604b6,
0x04bd0002,
- 0xf04027f0,
- 0x04b60007,
- 0x0002d006,
- 0x00f804bd,
-/* 0x0430: host_init */
- 0x008017f1,
- 0xf11014b6,
- 0xf1027015,
- 0xb604d007,
- 0x01d00604,
- 0xf104bd00,
- 0xb6008017,
- 0x15f11014,
- 0x07f102f0,
- 0x04b604dc,
- 0x0001d006,
- 0x17f004bd,
- 0xc407f101,
+/* 0x0512: host_init */
+ 0x17f100f8,
+ 0x14b60080,
+ 0x7015f110,
+ 0xd007f102,
0x0604b604,
0xbd0001d0,
-/* 0x046f: memx_func_enter */
- 0xf000f804,
+ 0x8017f104,
+ 0x1014b600,
+ 0x02f015f1,
+ 0x04dc07f1,
+ 0xd00604b6,
+ 0x04bd0001,
+ 0xf10117f0,
+ 0xb604c407,
+ 0x01d00604,
+ 0xf804bd00,
+/* 0x0551: memx_func_enter */
+ 0x2067f100,
+ 0x5d77f116,
+ 0xff73f1f5,
+ 0x026eb9ff,
+ 0xb90421f4,
+ 0x87fd02d8,
+ 0xf960f904,
+ 0xfcd0fc80,
+ 0x3f21f4e0,
+ 0xfffe77f1,
+ 0xffff73f1,
+ 0xf4026eb9,
+ 0xd8b90421,
+ 0x0487fd02,
+ 0x80f960f9,
+ 0xe0fcd0fc,
+ 0xf13f21f4,
+ 0xb926f067,
+ 0x21f4026e,
+ 0x02d8b904,
+ 0xf90487fd,
+ 0xfc80f960,
+ 0xf4e0fcd0,
+ 0x67f03f21,
+ 0xe007f104,
+ 0x0604b607,
+ 0xbd0006d0,
+/* 0x05bd: memx_func_enter_wait */
+ 0xc067f104,
+ 0x0664b607,
+ 0xf00066cf,
+ 0x0bf40464,
+ 0x2c67f0f3,
+ 0xcf0664b6,
+ 0x06800066,
+/* 0x05db: memx_func_leave */
+ 0xf000f8ee,
+ 0x64b62c67,
+ 0x0066cf06,
+ 0xf0ef0680,
0x07f10467,
- 0x04b607e0,
+ 0x04b607e4,
0x0006d006,
-/* 0x047e: memx_func_enter_wait */
+/* 0x05f6: memx_func_leave_wait */
0x67f104bd,
0x64b607c0,
0x0066cf06,
0xf40464f0,
- 0x1698f30b,
- 0x0410b600,
-/* 0x0496: memx_func_leave */
- 0x67f000f8,
- 0xe407f104,
- 0x0604b607,
- 0xbd0006d0,
-/* 0x04a5: memx_func_leave_wait */
- 0xc067f104,
- 0x0664b607,
- 0xf00066cf,
- 0x1bf40464,
-/* 0x04b7: memx_func_wr32 */
- 0x9800f8f3,
- 0x15980016,
- 0x0810b601,
- 0x50f960f9,
+ 0x67f1f31b,
+ 0x77f126f0,
+ 0x73f00001,
+ 0x026eb900,
+ 0xb90421f4,
+ 0x87fd02d8,
+ 0xf960f905,
+ 0xfcd0fc80,
+ 0x3f21f4e0,
+ 0x162067f1,
+ 0xf4026eb9,
+ 0xd8b90421,
+ 0x0587fd02,
+ 0x80f960f9,
0xe0fcd0fc,
0xf13f21f4,
- 0xfd140003,
- 0x05800506,
- 0xb604bd00,
- 0x1bf40242,
-/* 0x04df: memx_func_wait */
- 0xf000f8dd,
- 0x84b62c87,
- 0x0088cf06,
- 0x98001e98,
- 0x1c98011d,
- 0x031b9802,
- 0xf41010b6,
- 0x00f89c21,
-/* 0x04fc: memx_func_delay */
- 0xb6001e98,
- 0x21f40410,
-/* 0x0507: memx_exec */
- 0xf900f87f,
- 0xb9d0f9e0,
- 0xb2b902c1,
-/* 0x0511: memx_exec_next */
- 0x00139802,
- 0x950410b6,
- 0x30f01034,
- 0xde35980c,
- 0x12b855f9,
- 0xec1ef406,
- 0xe0fcd0fc,
- 0x02b921f5,
-/* 0x0532: memx_info */
- 0xc7f100f8,
- 0xb7f103ac,
- 0x21f50800,
- 0x00f802b9,
-/* 0x0540: memx_recv */
- 0xf401d6b0,
- 0xd6b0c40b,
- 0xe90bf400,
-/* 0x054e: memx_init */
- 0x00f800f8,
-/* 0x0550: perf_recv */
-/* 0x0552: perf_init */
- 0x00f800f8,
-/* 0x0554: i2c_drive_scl */
- 0xf40036b0,
- 0x07f1110b,
- 0x04b607e0,
- 0x0001d006,
- 0x00f804bd,
-/* 0x0568: i2c_drive_scl_lo */
- 0x07e407f1,
- 0xd00604b6,
- 0x04bd0001,
-/* 0x0576: i2c_drive_sda */
- 0x36b000f8,
- 0x110bf400,
- 0x07e007f1,
- 0xd00604b6,
- 0x04bd0002,
-/* 0x058a: i2c_drive_sda_lo */
- 0x07f100f8,
- 0x04b607e4,
- 0x0002d006,
- 0x00f804bd,
-/* 0x0598: i2c_sense_scl */
- 0xf10132f4,
- 0xb607c437,
- 0x33cf0634,
- 0x0431fd00,
- 0xf4060bf4,
-/* 0x05ae: i2c_sense_scl_done */
- 0x00f80131,
-/* 0x05b0: i2c_sense_sda */
- 0xf10132f4,
- 0xb607c437,
- 0x33cf0634,
- 0x0432fd00,
- 0xf4060bf4,
-/* 0x05c6: i2c_sense_sda_done */
- 0x00f80131,
-/* 0x05c8: i2c_raise_scl */
- 0x47f140f9,
- 0x37f00898,
- 0x5421f501,
-/* 0x05d5: i2c_raise_scl_wait */
- 0xe8e7f105,
- 0x7f21f403,
- 0x059821f5,
- 0xb60901f4,
- 0x1bf40142,
-/* 0x05e9: i2c_raise_scl_done */
- 0xf840fcef,
-/* 0x05ed: i2c_start */
- 0x9821f500,
- 0x0d11f405,
- 0x05b021f5,
- 0xf40611f4,
-/* 0x05fe: i2c_start_rep */
- 0x37f0300e,
- 0x5421f500,
- 0x0137f005,
- 0x057621f5,
- 0xb60076bb,
- 0x50f90465,
- 0xbb046594,
- 0x50bd0256,
- 0xfc0475fd,
- 0xc821f550,
- 0x0464b605,
-/* 0x062b: i2c_start_send */
- 0xf01f11f4,
- 0x21f50037,
- 0xe7f10576,
- 0x21f41388,
- 0x0037f07f,
- 0x055421f5,
- 0x1388e7f1,
-/* 0x0647: i2c_start_out */
+ 0xf00aa277,
+ 0x6eb90073,
+ 0x0421f402,
+ 0xfd02d8b9,
+ 0x60f90587,
+ 0xd0fc80f9,
+ 0x21f4e0fc,
+/* 0x0663: memx_func_wait_vblank */
+ 0x9800f83f,
+ 0x66b00016,
+ 0x130bf400,
+ 0xf40166b0,
+ 0x0ef4060b,
+/* 0x0675: memx_func_wait_vblank_head1 */
+ 0x2077f12e,
+ 0x070ef400,
+/* 0x067c: memx_func_wait_vblank_head0 */
+ 0x000877f1,
+/* 0x0680: memx_func_wait_vblank_0 */
+ 0x07c467f1,
+ 0xcf0664b6,
+ 0x67fd0066,
+ 0xf31bf404,
+/* 0x0690: memx_func_wait_vblank_1 */
+ 0x07c467f1,
+ 0xcf0664b6,
+ 0x67fd0066,
+ 0xf30bf404,
+/* 0x06a0: memx_func_wait_vblank_fini */
+ 0xf80410b6,
+/* 0x06a5: memx_func_wr32 */
+ 0x00169800,
+ 0xb6011598,
+ 0x60f90810,
+ 0xd0fc50f9,
+ 0x21f4e0fc,
+ 0x0242b63f,
+ 0xf8e91bf4,
+/* 0x06c1: memx_func_wait */
+ 0x2c87f000,
+ 0xcf0684b6,
+ 0x1e980088,
+ 0x011d9800,
+ 0x98021c98,
+ 0x10b6031b,
+ 0xa421f410,
+/* 0x06de: memx_func_delay */
+ 0x1e9800f8,
+ 0x0410b600,
0xf87f21f4,
-/* 0x0649: i2c_stop */
- 0x0037f000,
- 0x055421f5,
+/* 0x06e9: memx_exec */
+ 0xf9e0f900,
+ 0x02c1b9d0,
+/* 0x06f3: memx_exec_next */
+ 0x9802b2b9,
+ 0x10b60013,
+ 0xf034e704,
+ 0xe033e701,
+ 0x0132b601,
+ 0x980c30f0,
+ 0x55f9de35,
+ 0xf40612b8,
+ 0x0b98e41e,
+ 0xef0c98ee,
+ 0xf102cbbb,
+ 0xb607c4b7,
+ 0xbbcf06b4,
+ 0xfcd0fc00,
+ 0x4221f5e0,
+/* 0x072f: memx_info */
+ 0xf100f803,
+ 0xf103c0c7,
+ 0xf50800b7,
+ 0xf8034221,
+/* 0x073d: memx_recv */
+ 0x01d6b000,
+ 0xb0a90bf4,
+ 0x0bf400d6,
+/* 0x074b: memx_init */
+ 0xf800f8e9,
+/* 0x074d: perf_recv */
+/* 0x074f: perf_init */
+ 0xf800f800,
+/* 0x0751: i2c_drive_scl */
+ 0x0036b000,
+ 0xf1110bf4,
+ 0xb607e007,
+ 0x01d00604,
+ 0xf804bd00,
+/* 0x0765: i2c_drive_scl_lo */
+ 0xe407f100,
+ 0x0604b607,
+ 0xbd0001d0,
+/* 0x0773: i2c_drive_sda */
+ 0xb000f804,
+ 0x0bf40036,
+ 0xe007f111,
+ 0x0604b607,
+ 0xbd0002d0,
+/* 0x0787: i2c_drive_sda_lo */
+ 0xf100f804,
+ 0xb607e407,
+ 0x02d00604,
+ 0xf804bd00,
+/* 0x0795: i2c_sense_scl */
+ 0x0132f400,
+ 0x07c437f1,
+ 0xcf0634b6,
+ 0x31fd0033,
+ 0x060bf404,
+/* 0x07ab: i2c_sense_scl_done */
+ 0xf80131f4,
+/* 0x07ad: i2c_sense_sda */
+ 0x0132f400,
+ 0x07c437f1,
+ 0xcf0634b6,
+ 0x32fd0033,
+ 0x060bf404,
+/* 0x07c3: i2c_sense_sda_done */
+ 0xf80131f4,
+/* 0x07c5: i2c_raise_scl */
+ 0xf140f900,
+ 0xf0089847,
+ 0x21f50137,
+/* 0x07d2: i2c_raise_scl_wait */
+ 0xe7f10751,
+ 0x21f403e8,
+ 0x9521f57f,
+ 0x0901f407,
+ 0xf40142b6,
+/* 0x07e6: i2c_raise_scl_done */
+ 0x40fcef1b,
+/* 0x07ea: i2c_start */
+ 0x21f500f8,
+ 0x11f40795,
+ 0xad21f50d,
+ 0x0611f407,
+/* 0x07fb: i2c_start_rep */
+ 0xf0300ef4,
+ 0x21f50037,
+ 0x37f00751,
+ 0x7321f501,
+ 0x0076bb07,
+ 0xf90465b6,
+ 0x04659450,
+ 0xbd0256bb,
+ 0x0475fd50,
+ 0x21f550fc,
+ 0x64b607c5,
+ 0x1f11f404,
+/* 0x0828: i2c_start_send */
0xf50037f0,
- 0xf1057621,
- 0xf403e8e7,
+ 0xf1077321,
+ 0xf41388e7,
0x37f07f21,
- 0x5421f501,
- 0x88e7f105,
+ 0x5121f500,
+ 0x88e7f107,
0x7f21f413,
- 0xf50137f0,
- 0xf1057621,
- 0xf41388e7,
- 0x00f87f21,
-/* 0x067c: i2c_bitw */
- 0x057621f5,
+/* 0x0844: i2c_start_out */
+/* 0x0846: i2c_stop */
+ 0x37f000f8,
+ 0x5121f500,
+ 0x0037f007,
+ 0x077321f5,
0x03e8e7f1,
- 0xbb7f21f4,
- 0x65b60076,
- 0x9450f904,
- 0x56bb0465,
- 0xfd50bd02,
- 0x50fc0475,
- 0x05c821f5,
- 0xf40464b6,
- 0xe7f11811,
+ 0xf07f21f4,
+ 0x21f50137,
+ 0xe7f10751,
0x21f41388,
- 0x0037f07f,
- 0x055421f5,
+ 0x0137f07f,
+ 0x077321f5,
0x1388e7f1,
-/* 0x06bb: i2c_bitw_out */
0xf87f21f4,
-/* 0x06bd: i2c_bitr */
- 0x0137f000,
- 0x057621f5,
- 0x03e8e7f1,
- 0xbb7f21f4,
- 0x65b60076,
- 0x9450f904,
- 0x56bb0465,
- 0xfd50bd02,
- 0x50fc0475,
- 0x05c821f5,
- 0xf40464b6,
- 0x21f51b11,
- 0x37f005b0,
- 0x5421f500,
- 0x88e7f105,
+/* 0x0879: i2c_bitw */
+ 0x7321f500,
+ 0xe8e7f107,
+ 0x7f21f403,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0xc521f550,
+ 0x0464b607,
+ 0xf11811f4,
+ 0xf41388e7,
+ 0x37f07f21,
+ 0x5121f500,
+ 0x88e7f107,
0x7f21f413,
- 0xf4013cf0,
-/* 0x0702: i2c_bitr_done */
- 0x00f80131,
-/* 0x0704: i2c_get_byte */
- 0xf00057f0,
-/* 0x070a: i2c_get_byte_next */
- 0x54b60847,
+/* 0x08b8: i2c_bitw_out */
+/* 0x08ba: i2c_bitr */
+ 0x37f000f8,
+ 0x7321f501,
+ 0xe8e7f107,
+ 0x7f21f403,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0xc521f550,
+ 0x0464b607,
+ 0xf51b11f4,
+ 0xf007ad21,
+ 0x21f50037,
+ 0xe7f10751,
+ 0x21f41388,
+ 0x013cf07f,
+/* 0x08ff: i2c_bitr_done */
+ 0xf80131f4,
+/* 0x0901: i2c_get_byte */
+ 0x0057f000,
+/* 0x0907: i2c_get_byte_next */
+ 0xb60847f0,
+ 0x76bb0154,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb608ba21,
+ 0x11f40464,
+ 0x0553fd2b,
+ 0xf40142b6,
+ 0x37f0d81b,
0x0076bb01,
0xf90465b6,
0x04659450,
0xbd0256bb,
0x0475fd50,
0x21f550fc,
- 0x64b606bd,
- 0x2b11f404,
- 0xb60553fd,
- 0x1bf40142,
- 0x0137f0d8,
- 0xb60076bb,
- 0x50f90465,
- 0xbb046594,
- 0x50bd0256,
- 0xfc0475fd,
- 0x7c21f550,
- 0x0464b606,
-/* 0x0754: i2c_get_byte_done */
-/* 0x0756: i2c_put_byte */
- 0x47f000f8,
-/* 0x0759: i2c_put_byte_next */
- 0x0142b608,
- 0xbb3854ff,
- 0x65b60076,
- 0x9450f904,
- 0x56bb0465,
- 0xfd50bd02,
- 0x50fc0475,
- 0x067c21f5,
- 0xf40464b6,
- 0x46b03411,
- 0xd81bf400,
+ 0x64b60879,
+/* 0x0951: i2c_get_byte_done */
+/* 0x0953: i2c_put_byte */
+ 0xf000f804,
+/* 0x0956: i2c_put_byte_next */
+ 0x42b60847,
+ 0x3854ff01,
0xb60076bb,
0x50f90465,
0xbb046594,
0x50bd0256,
0xfc0475fd,
- 0xbd21f550,
- 0x0464b606,
- 0xbb0f11f4,
- 0x36b00076,
- 0x061bf401,
-/* 0x07af: i2c_put_byte_done */
- 0xf80132f4,
-/* 0x07b1: i2c_addr */
- 0x0076bb00,
+ 0x7921f550,
+ 0x0464b608,
+ 0xb03411f4,
+ 0x1bf40046,
+ 0x0076bbd8,
0xf90465b6,
0x04659450,
0xbd0256bb,
0x0475fd50,
0x21f550fc,
- 0x64b605ed,
- 0x2911f404,
- 0x012ec3e7,
- 0xfd0134b6,
- 0x76bb0553,
+ 0x64b608ba,
+ 0x0f11f404,
+ 0xb00076bb,
+ 0x1bf40136,
+ 0x0132f406,
+/* 0x09ac: i2c_put_byte_done */
+/* 0x09ae: i2c_addr */
+ 0x76bb00f8,
0x0465b600,
0x659450f9,
0x0256bb04,
0x75fd50bd,
0xf550fc04,
- 0xb6075621,
-/* 0x07f6: i2c_addr_done */
- 0x00f80464,
-/* 0x07f8: i2c_acquire_addr */
- 0xb6f8cec7,
- 0xe0b702e4,
- 0xee980bfc,
-/* 0x0807: i2c_acquire */
- 0xf500f800,
- 0xf407f821,
- 0xd9f00421,
- 0x3f21f403,
-/* 0x0816: i2c_release */
- 0x21f500f8,
- 0x21f407f8,
- 0x03daf004,
- 0xf83f21f4,
-/* 0x0825: i2c_recv */
- 0x0132f400,
- 0xb6f8c1c7,
- 0x16b00214,
- 0x3a1ff528,
- 0xd413a001,
- 0x0032980b,
- 0x0bac13a0,
- 0xf4003198,
- 0xd0f90231,
- 0xd0f9e0f9,
- 0x000067f1,
- 0x100063f1,
- 0xbb016792,
+ 0xb607ea21,
+ 0x11f40464,
+ 0x2ec3e729,
+ 0x0134b601,
+ 0xbb0553fd,
0x65b60076,
0x9450f904,
0x56bb0465,
0xfd50bd02,
0x50fc0475,
- 0x080721f5,
- 0xfc0464b6,
- 0x00d6b0d0,
- 0x00b31bf5,
- 0xbb0057f0,
+ 0x095321f5,
+/* 0x09f3: i2c_addr_done */
+ 0xf80464b6,
+/* 0x09f5: i2c_acquire_addr */
+ 0xf8cec700,
+ 0xb702e4b6,
+ 0x980c10e0,
+ 0x00f800ee,
+/* 0x0a04: i2c_acquire */
+ 0x09f521f5,
+ 0xf00421f4,
+ 0x21f403d9,
+/* 0x0a13: i2c_release */
+ 0xf500f83f,
+ 0xf409f521,
+ 0xdaf00421,
+ 0x3f21f403,
+/* 0x0a22: i2c_recv */
+ 0x32f400f8,
+ 0xf8c1c701,
+ 0xb00214b6,
+ 0x1ff52816,
+ 0x13a0013a,
+ 0x32980be8,
+ 0xc013a000,
+ 0x0031980b,
+ 0xf90231f4,
+ 0xf9e0f9d0,
+ 0x0067f1d0,
+ 0x0063f100,
+ 0x01679210,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0x0421f550,
+ 0x0464b60a,
+ 0xd6b0d0fc,
+ 0xb31bf500,
+ 0x0057f000,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0xae21f550,
+ 0x0464b609,
+ 0x00d011f5,
+ 0xbbe0c5c7,
0x65b60076,
0x9450f904,
0x56bb0465,
0xfd50bd02,
0x50fc0475,
- 0x07b121f5,
+ 0x095321f5,
0xf50464b6,
- 0xc700d011,
- 0x76bbe0c5,
+ 0xf000ad11,
+ 0x76bb0157,
0x0465b600,
0x659450f9,
0x0256bb04,
0x75fd50bd,
0xf550fc04,
- 0xb6075621,
+ 0xb609ae21,
0x11f50464,
- 0x57f000ad,
- 0x0076bb01,
- 0xf90465b6,
- 0x04659450,
- 0xbd0256bb,
- 0x0475fd50,
- 0x21f550fc,
- 0x64b607b1,
- 0x8a11f504,
- 0x0076bb00,
- 0xf90465b6,
- 0x04659450,
- 0xbd0256bb,
- 0x0475fd50,
- 0x21f550fc,
- 0x64b60704,
- 0x6a11f404,
- 0xbbe05bcb,
- 0x65b60076,
- 0x9450f904,
- 0x56bb0465,
- 0xfd50bd02,
- 0x50fc0475,
- 0x064921f5,
- 0xb90464b6,
- 0x74bd025b,
-/* 0x092b: i2c_recv_not_rd08 */
- 0xb0430ef4,
- 0x1bf401d6,
- 0x0057f03d,
- 0x07b121f5,
- 0xc73311f4,
- 0x21f5e0c5,
- 0x11f40756,
- 0x0057f029,
- 0x07b121f5,
- 0xc71f11f4,
- 0x21f5e0b5,
- 0x11f40756,
- 0x4921f515,
- 0xc774bd06,
- 0x1bf408c5,
- 0x0232f409,
-/* 0x096b: i2c_recv_not_wr08 */
-/* 0x096b: i2c_recv_done */
- 0xc7030ef4,
- 0x21f5f8ce,
- 0xe0fc0816,
- 0x12f4d0fc,
- 0x027cb90a,
- 0x02b921f5,
-/* 0x0980: i2c_recv_exit */
-/* 0x0982: i2c_init */
+ 0x76bb008a,
+ 0x0465b600,
+ 0x659450f9,
+ 0x0256bb04,
+ 0x75fd50bd,
+ 0xf550fc04,
+ 0xb6090121,
+ 0x11f40464,
+ 0xe05bcb6a,
+ 0xb60076bb,
+ 0x50f90465,
+ 0xbb046594,
+ 0x50bd0256,
+ 0xfc0475fd,
+ 0x4621f550,
+ 0x0464b608,
+ 0xbd025bb9,
+ 0x430ef474,
+/* 0x0b28: i2c_recv_not_rd08 */
+ 0xf401d6b0,
+ 0x57f03d1b,
+ 0xae21f500,
+ 0x3311f409,
+ 0xf5e0c5c7,
+ 0xf4095321,
+ 0x57f02911,
+ 0xae21f500,
+ 0x1f11f409,
+ 0xf5e0b5c7,
+ 0xf4095321,
+ 0x21f51511,
+ 0x74bd0846,
+ 0xf408c5c7,
+ 0x32f4091b,
+ 0x030ef402,
+/* 0x0b68: i2c_recv_not_wr08 */
+/* 0x0b68: i2c_recv_done */
+ 0xf5f8cec7,
+ 0xfc0a1321,
+ 0xf4d0fce0,
+ 0x7cb90a12,
+ 0x4221f502,
+/* 0x0b7d: i2c_recv_exit */
+/* 0x0b7f: i2c_init */
+ 0xf800f803,
+/* 0x0b81: test_recv */
+ 0xd817f100,
+ 0x0614b605,
+ 0xb60011cf,
+ 0x07f10110,
+ 0x04b605d8,
+ 0x0001d006,
+ 0xe7f104bd,
+ 0xe3f1d900,
+ 0x21f5134f,
+ 0x00f80262,
+/* 0x0ba8: test_init */
+ 0x0800e7f1,
+ 0x026221f5,
+/* 0x0bb2: idle_recv */
0x00f800f8,
-/* 0x0984: test_recv */
- 0x05d817f1,
- 0xcf0614b6,
- 0x10b60011,
- 0xd807f101,
- 0x0604b605,
- 0xbd0001d0,
- 0x00e7f104,
- 0x4fe3f1d9,
- 0xf521f513,
-/* 0x09ab: test_init */
- 0xf100f801,
- 0xf50800e7,
- 0xf801f521,
-/* 0x09b5: idle_recv */
-/* 0x09b7: idle */
- 0xf400f800,
- 0x17f10031,
- 0x14b605d4,
- 0x0011cf06,
- 0xf10110b6,
- 0xb605d407,
- 0x01d00604,
-/* 0x09d3: idle_loop */
- 0xf004bd00,
- 0x32f45817,
-/* 0x09d9: idle_proc */
-/* 0x09d9: idle_proc_exec */
- 0xb910f902,
- 0x21f5021e,
- 0x10fc02c2,
- 0xf40911f4,
- 0x0ef40231,
-/* 0x09ed: idle_proc_next */
- 0x5810b6ef,
- 0xf4061fb8,
- 0x02f4e61b,
- 0x0028f4dd,
- 0x00bb0ef4,
+/* 0x0bb4: idle */
+ 0xf10031f4,
+ 0xb605d417,
+ 0x11cf0614,
+ 0x0110b600,
+ 0x05d407f1,
+ 0xd00604b6,
+ 0x04bd0001,
+/* 0x0bd0: idle_loop */
+ 0xf45817f0,
+/* 0x0bd6: idle_proc */
+/* 0x0bd6: idle_proc_exec */
+ 0x10f90232,
+ 0xf5021eb9,
+ 0xfc034b21,
+ 0x0911f410,
+ 0xf40231f4,
+/* 0x0bea: idle_proc_next */
+ 0x10b6ef0e,
+ 0x061fb858,
+ 0xf4e61bf4,
+ 0x28f4dd02,
+ 0xbb0ef400,
+ 0x00000000,
};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc
index 8a89dfe41ce1..b85443261569 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc
@@ -23,6 +23,7 @@
*/
#define NVKM_PPWR_CHIPSET GF119
+#define HW_TICKS_PER_US 324
//#define NVKM_FALCON_PC24
#define NVKM_FALCON_UNSHIFTED_IO
@@ -34,6 +35,7 @@
.section #nvd0_pwr_data
#define INCLUDE_PROC
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
@@ -44,6 +46,7 @@
#define INCLUDE_DATA
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
@@ -56,6 +59,7 @@
.section #nvd0_pwr_code
#define INCLUDE_CODE
#include "kernel.fuc"
+#include "arith.fuc"
#include "host.fuc"
#include "memx.fuc"
#include "perf.fuc"
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h
index cd9ff1a73284..12d86f72ad10 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/nvd0.fuc.h
@@ -24,8 +24,8 @@ uint32_t nvd0_pwr_data[] = {
0x00000000,
/* 0x0058: proc_list_head */
0x54534f48,
- 0x000003be,
- 0x00000367,
+ 0x0000049d,
+ 0x00000446,
0x00000000,
0x00000000,
0x00000000,
@@ -46,8 +46,8 @@ uint32_t nvd0_pwr_data[] = {
0x00000000,
0x00000000,
0x584d454d,
- 0x000004c4,
- 0x000004b6,
+ 0x00000678,
+ 0x0000066a,
0x00000000,
0x00000000,
0x00000000,
@@ -68,8 +68,8 @@ uint32_t nvd0_pwr_data[] = {
0x00000000,
0x00000000,
0x46524550,
- 0x000004c8,
- 0x000004c6,
+ 0x0000067c,
+ 0x0000067a,
0x00000000,
0x00000000,
0x00000000,
@@ -90,8 +90,8 @@ uint32_t nvd0_pwr_data[] = {
0x00000000,
0x00000000,
0x5f433249,
- 0x000008e3,
- 0x00000786,
+ 0x00000a97,
+ 0x0000093a,
0x00000000,
0x00000000,
0x00000000,
@@ -112,8 +112,8 @@ uint32_t nvd0_pwr_data[] = {
0x00000000,
0x00000000,
0x54534554,
- 0x00000906,
- 0x000008e5,
+ 0x00000aba,
+ 0x00000a99,
0x00000000,
0x00000000,
0x00000000,
@@ -134,8 +134,8 @@ uint32_t nvd0_pwr_data[] = {
0x00000000,
0x00000000,
0x454c4449,
- 0x00000912,
- 0x00000910,
+ 0x00000ac6,
+ 0x00000ac4,
0x00000000,
0x00000000,
0x00000000,
@@ -227,24 +227,31 @@ uint32_t nvd0_pwr_data[] = {
0x00000000,
0x00000000,
/* 0x0370: memx_func_head */
- 0x00010000,
- 0x00000000,
- 0x000003f4,
-/* 0x037c: memx_func_next */
0x00000001,
0x00000000,
- 0x00000415,
+ 0x000004d3,
+/* 0x037c: memx_func_next */
0x00000002,
+ 0x00000000,
+ 0x00000554,
+ 0x00000003,
0x00000002,
- 0x00000430,
- 0x00040003,
+ 0x000005d8,
+ 0x00040004,
+ 0x00000000,
+ 0x000005f4,
+ 0x00010005,
+ 0x00000000,
+ 0x0000060e,
+ 0x00010006,
+ 0x00000000,
+ 0x000005d3,
+/* 0x03b8: memx_func_tail */
+/* 0x03b8: memx_ts_start */
0x00000000,
- 0x00000458,
- 0x00010004,
+/* 0x03bc: memx_ts_end */
0x00000000,
- 0x00000472,
-/* 0x03ac: memx_func_tail */
-/* 0x03ac: memx_data_head */
+/* 0x03c0: memx_data_head */
0x00000000,
0x00000000,
0x00000000,
@@ -757,8 +764,8 @@ uint32_t nvd0_pwr_data[] = {
0x00000000,
0x00000000,
0x00000000,
-/* 0x0bac: memx_data_tail */
-/* 0x0bac: i2c_scl_map */
+/* 0x0bc0: memx_data_tail */
+/* 0x0bc0: i2c_scl_map */
0x00000400,
0x00000800,
0x00001000,
@@ -769,7 +776,7 @@ uint32_t nvd0_pwr_data[] = {
0x00020000,
0x00040000,
0x00080000,
-/* 0x0bd4: i2c_sda_map */
+/* 0x0be8: i2c_sda_map */
0x00100000,
0x00200000,
0x00400000,
@@ -781,10 +788,69 @@ uint32_t nvd0_pwr_data[] = {
0x10000000,
0x20000000,
0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
};
uint32_t nvd0_pwr_code[] = {
- 0x02bf0ef5,
+ 0x034d0ef5,
/* 0x0004: rd32 */
0x07a007f1,
0xbd000ed0,
@@ -814,17 +880,20 @@ uint32_t nvd0_pwr_code[] = {
0xd4f100dd,
0x1bf47000,
/* 0x0067: nsec */
- 0xf000f8f5,
+ 0xf900f8f5,
+ 0xf080f990,
0x88cf2c87,
-/* 0x006d: nsec_loop */
+/* 0x0071: nsec_loop */
0x2c97f000,
0xbb0099cf,
0x9eb80298,
0xf41ef406,
-/* 0x007e: wait */
- 0x87f000f8,
+ 0x90fc80fc,
+/* 0x0086: wait */
+ 0x90f900f8,
+ 0x87f080f9,
0x0088cf2c,
-/* 0x0084: wait_loop */
+/* 0x0090: wait_loop */
0xf402eeb9,
0xdab90421,
0x04adfd02,
@@ -833,28 +902,29 @@ uint32_t nvd0_pwr_code[] = {
0x0099cf2c,
0xb80298bb,
0x1ef4069b,
-/* 0x00a5: wait_done */
-/* 0x00a7: intr_watchdog */
- 0x9800f8e2,
+/* 0x00b1: wait_done */
+ 0xfc80fce2,
+/* 0x00b7: intr_watchdog */
+ 0x9800f890,
0x96b003e9,
0x2a0bf400,
0xbb9a0a98,
0x1cf4029a,
0x01d7f00f,
- 0x020621f5,
+ 0x028c21f5,
0x0ef494bd,
-/* 0x00c5: intr_watchdog_next_time */
+/* 0x00d5: intr_watchdog_next_time */
0x9b0a9815,
0xf400a6b0,
0x9ab8090b,
0x061cf406,
-/* 0x00d4: intr_watchdog_next_time_set */
-/* 0x00d7: intr_watchdog_next_proc */
+/* 0x00e4: intr_watchdog_next_time_set */
+/* 0x00e7: intr_watchdog_next_proc */
0x809b0980,
0xe0b603e9,
0x68e6b158,
0xc61bf402,
-/* 0x00e6: intr */
+/* 0x00f6: intr */
0x00f900f8,
0x80f904bd,
0xa0f990f9,
@@ -872,12 +942,12 @@ uint32_t nvd0_pwr_code[] = {
0x0bf40289,
0x9b008020,
0xf458e7f0,
- 0x0998a721,
+ 0x0998b721,
0x0096b09b,
0xf00e0bf4,
0x09d03407,
0x8004bd00,
-/* 0x013e: intr_skip_watchdog */
+/* 0x014e: intr_skip_watchdog */
0x89e49a09,
0x0bf40800,
0x8897f13c,
@@ -889,20 +959,20 @@ uint32_t nvd0_pwr_code[] = {
0xf14f48e7,
0xf05453e3,
0x21f500d7,
- 0xc0fc026b,
+ 0xc0fc02f1,
0x04c007f1,
0xbd000cd0,
-/* 0x0175: intr_subintr_skip_fifo */
+/* 0x0185: intr_subintr_skip_fifo */
0x8807f104,
0x0009d006,
-/* 0x017e: intr_skip_subintr */
+/* 0x018e: intr_skip_subintr */
0x89c404bd,
0x070bf420,
0xffbfa4f1,
-/* 0x0188: intr_skip_pause */
+/* 0x0198: intr_skip_pause */
0xf44089c4,
0xa4f1070b,
-/* 0x0192: intr_skip_user0 */
+/* 0x01a2: intr_skip_user0 */
0x07f0ffbf,
0x0008d004,
0x80fc04bd,
@@ -912,324 +982,436 @@ uint32_t nvd0_pwr_code[] = {
0xfca0fcb0,
0xfc80fc90,
0x0032f400,
-/* 0x01b6: timer */
- 0x32f401f8,
- 0x03f89810,
- 0xf40086b0,
- 0xfe80421c,
- 0x3807f003,
+/* 0x01c6: ticks_from_ns */
+ 0xc0f901f8,
+ 0xd7f1b0f9,
+ 0xd3f00144,
+ 0xb321f500,
+ 0xe8ccec03,
+ 0x00b4b003,
+ 0xec120bf4,
+ 0xf103e8ee,
+ 0xf00144d7,
+ 0x21f500d3,
+/* 0x01ee: ticks_from_ns_quit */
+ 0xceb903b3,
+ 0xfcb0fc02,
+/* 0x01f7: ticks_from_us */
+ 0xf900f8c0,
+ 0xf1b0f9c0,
+ 0xf00144d7,
+ 0x21f500d3,
+ 0xceb903b3,
+ 0x00b4b002,
+ 0xbd050bf4,
+/* 0x0211: ticks_from_us_quit */
+ 0xfcb0fce4,
+/* 0x0217: ticks_to_us */
+ 0xf100f8c0,
+ 0xf00144d7,
+ 0xedff00d3,
+/* 0x0223: timer */
+ 0xf900f8ec,
+ 0xf480f990,
+ 0xf8981032,
+ 0x0086b003,
+ 0xbd531cf4,
+ 0x3807f084,
0xbd0008d0,
- 0x0887f004,
- 0xf00088cf,
- 0x1bf40284,
- 0x3487f020,
- 0xb80088cf,
- 0x0bf406e0,
- 0x06e8b809,
-/* 0x01eb: timer_reset */
- 0xf0191ef4,
- 0x0ed03407,
- 0x8004bd00,
-/* 0x01f6: timer_enable */
- 0x87f09a0e,
- 0x3807f001,
- 0xbd0008d0,
-/* 0x0201: timer_done */
- 0x1031f404,
-/* 0x0206: send_proc */
- 0x80f900f8,
- 0xe89890f9,
+ 0x3487f004,
+ 0x980088cf,
+ 0x98bb9a09,
+ 0x00e9bb02,
+ 0xf003fe80,
+ 0x88cf0887,
+ 0x0284f000,
+ 0xf0201bf4,
+ 0x88cf3487,
+ 0x06e0b800,
+ 0xb8090bf4,
+ 0x1cf406e8,
+/* 0x026d: timer_reset */
+ 0x3407f00e,
+ 0xbd000ed0,
+ 0x9a0e8004,
+/* 0x0278: timer_enable */
+ 0xf00187f0,
+ 0x08d03807,
+/* 0x0283: timer_done */
+ 0xf404bd00,
+ 0x80fc1031,
+ 0x00f890fc,
+/* 0x028c: send_proc */
+ 0x90f980f9,
+ 0x9805e898,
+ 0x86f004e9,
+ 0x0689b804,
+ 0xc42a0bf4,
+ 0x88940398,
+ 0x1880b604,
+ 0x98008ebb,
+ 0x8a8000fa,
+ 0x018d8000,
+ 0x80028c80,
+ 0x90b6038b,
+ 0x0794f001,
+ 0xf404e980,
+/* 0x02c6: send_done */
+ 0x90fc0231,
+ 0x00f880fc,
+/* 0x02cc: find */
+ 0x87f080f9,
+ 0x0131f458,
+/* 0x02d4: find_loop */
+ 0xb8008a98,
+ 0x0bf406ae,
+ 0x5880b610,
+ 0x026886b1,
+ 0xf4f01bf4,
+/* 0x02ea: find_done */
+ 0x8eb90132,
+ 0xf880fc02,
+/* 0x02f1: send */
+ 0xcc21f500,
+ 0x9701f402,
+/* 0x02fa: recv */
+ 0x90f900f8,
+ 0xe89880f9,
0x04e99805,
- 0xb80486f0,
+ 0xb80132f4,
0x0bf40689,
- 0x0398c42a,
- 0xb6048894,
- 0x8ebb1880,
- 0x00fa9800,
- 0x80008a80,
- 0x8c80018d,
- 0x038b8002,
- 0xf00190b6,
- 0xe9800794,
- 0x0231f404,
-/* 0x0240: send_done */
- 0x80fc90fc,
-/* 0x0246: find */
- 0x80f900f8,
- 0xf45887f0,
-/* 0x024e: find_loop */
- 0x8a980131,
- 0x06aeb800,
- 0xb6100bf4,
- 0x86b15880,
- 0x1bf40268,
- 0x0132f4f0,
-/* 0x0264: find_done */
- 0xfc028eb9,
-/* 0x026b: send */
- 0xf500f880,
- 0xf4024621,
- 0x00f89701,
-/* 0x0274: recv */
- 0x9805e898,
- 0x32f404e9,
- 0x0689b801,
- 0xc43d0bf4,
- 0x80b60389,
- 0x0784f001,
- 0x9805e880,
- 0xf0f902ea,
- 0xf9018ffe,
- 0x02efb9f0,
- 0xbb049994,
- 0xe0b600e9,
- 0x03eb9818,
- 0x9802ec98,
- 0xee9801ed,
- 0xfca5f900,
- 0x00f8fef0,
- 0xfc0131f4,
-/* 0x02bd: recv_done */
-/* 0x02bf: init */
- 0xf100f8f0,
- 0xcf010817,
- 0x11e70011,
- 0x14b60109,
- 0x0014fe08,
- 0x00e017f1,
- 0xf00013f0,
- 0x01d01c07,
- 0xf004bd00,
- 0x07f0ff17,
- 0x0001d014,
- 0x17f004bd,
- 0x0015f102,
- 0x1007f008,
- 0xbd0001d0,
- 0xe617f104,
- 0x0013f000,
- 0xf40010fe,
- 0x17f01031,
- 0x3807f001,
- 0xbd0001d0,
- 0x58f7f004,
-/* 0x0314: init_proc */
- 0xb001f198,
- 0x0bf40016,
- 0xb615f9fa,
- 0x0ef458f0,
-/* 0x0325: host_send */
- 0xb017f1f2,
- 0x0011cf04,
- 0x04a027f1,
- 0xb80022cf,
- 0x0bf40612,
- 0x071ec42f,
- 0xb704ee94,
- 0x980270e0,
+ 0x0389c43d,
+ 0xf00180b6,
+ 0xe8800784,
+ 0x02ea9805,
+ 0x8ffef0f9,
+ 0xb9f0f901,
+ 0x999402ef,
+ 0x00e9bb04,
+ 0x9818e0b6,
0xec9803eb,
0x01ed9802,
- 0xf500ee98,
- 0xb6026b21,
- 0x1ec40110,
- 0xb007f10f,
- 0x000ed004,
- 0x0ef404bd,
-/* 0x0365: host_send_done */
-/* 0x0367: host_recv */
- 0xf100f8c3,
- 0xf14e4917,
- 0xb8525413,
- 0x0bf406e1,
-/* 0x0375: host_recv_wait */
- 0xcc17f1b3,
- 0x0011cf04,
- 0x04c827f1,
- 0xf00022cf,
- 0x12b80816,
- 0xec0bf406,
- 0xb60723c4,
- 0x30b70434,
- 0x3b8002f0,
- 0x023c8003,
- 0x80013d80,
- 0x20b6003e,
- 0x0f24f001,
- 0x04c807f1,
- 0xbd0002d0,
- 0x4027f004,
- 0xd00007f0,
- 0x04bd0002,
-/* 0x03be: host_init */
+ 0xf900ee98,
+ 0xfef0fca5,
+ 0x31f400f8,
+/* 0x0347: recv_done */
+ 0xfcf0fc01,
+ 0xf890fc80,
+/* 0x034d: init */
+ 0x0817f100,
+ 0x0011cf01,
+ 0x010911e7,
+ 0xfe0814b6,
+ 0x17f10014,
+ 0x13f000e0,
+ 0x1c07f000,
+ 0xbd0001d0,
+ 0xff17f004,
+ 0xd01407f0,
+ 0x04bd0001,
+ 0xf10217f0,
+ 0xf0080015,
+ 0x01d01007,
+ 0xf104bd00,
+ 0xf000f617,
+ 0x10fe0013,
+ 0x1031f400,
+ 0xf00117f0,
+ 0x01d03807,
+ 0xf004bd00,
+/* 0x03a2: init_proc */
+ 0xf19858f7,
+ 0x0016b001,
+ 0xf9fa0bf4,
+ 0x58f0b615,
+/* 0x03b3: mulu32_32_64 */
+ 0xf9f20ef4,
+ 0xf920f910,
+ 0x9540f930,
+ 0xd29510e1,
+ 0xbdc4bd10,
+ 0xc0edffb4,
+ 0xb9301dff,
+ 0x34f10234,
+ 0x34b6ffff,
+ 0x1045b610,
+ 0xbb00c3bb,
+ 0xe2ff01b4,
+ 0x0234b930,
+ 0xffff34f1,
+ 0xb61034b6,
+ 0xc3bb1045,
+ 0x01b4bb00,
+ 0xbb3012ff,
+ 0x40fc00b3,
+ 0x20fc30fc,
+ 0x00f810fc,
+/* 0x0404: host_send */
+ 0x04b017f1,
+ 0xf10011cf,
+ 0xcf04a027,
+ 0x12b80022,
+ 0x2f0bf406,
+ 0x94071ec4,
+ 0xe0b704ee,
+ 0xeb980270,
+ 0x02ec9803,
+ 0x9801ed98,
+ 0x21f500ee,
+ 0x10b602f1,
+ 0x0f1ec401,
+ 0x04b007f1,
+ 0xbd000ed0,
+ 0xc30ef404,
+/* 0x0444: host_send_done */
+/* 0x0446: host_recv */
0x17f100f8,
- 0x14b60080,
- 0x7015f110,
- 0xd007f102,
- 0x0001d004,
- 0x17f104bd,
- 0x14b60080,
- 0xf015f110,
- 0xdc07f102,
- 0x0001d004,
- 0x17f004bd,
- 0xc407f101,
- 0x0001d004,
- 0x00f804bd,
-/* 0x03f4: memx_func_enter */
+ 0x13f14e49,
+ 0xe1b85254,
+ 0xb30bf406,
+/* 0x0454: host_recv_wait */
+ 0x04cc17f1,
+ 0xf10011cf,
+ 0xcf04c827,
+ 0x16f00022,
+ 0x0612b808,
+ 0xc4ec0bf4,
+ 0x34b60723,
+ 0xf030b704,
+ 0x033b8002,
+ 0x80023c80,
+ 0x3e80013d,
+ 0x0120b600,
+ 0xf10f24f0,
+ 0xd004c807,
+ 0x04bd0002,
+ 0xf04027f0,
+ 0x02d00007,
+ 0xf804bd00,
+/* 0x049d: host_init */
+ 0x8017f100,
+ 0x1014b600,
+ 0x027015f1,
+ 0x04d007f1,
+ 0xbd0001d0,
+ 0x8017f104,
+ 0x1014b600,
+ 0x02f015f1,
+ 0x04dc07f1,
+ 0xbd0001d0,
+ 0x0117f004,
+ 0x04c407f1,
+ 0xbd0001d0,
+/* 0x04d3: memx_func_enter */
+ 0xf100f804,
+ 0xf1162067,
+ 0xf1f55d77,
+ 0xb9ffff73,
+ 0x21f4026e,
+ 0x02d8b904,
+ 0xf90487fd,
+ 0xfc80f960,
+ 0xf4e0fcd0,
+ 0x77f13321,
+ 0x73f1fffe,
+ 0x6eb9ffff,
+ 0x0421f402,
+ 0xfd02d8b9,
+ 0x60f90487,
+ 0xd0fc80f9,
+ 0x21f4e0fc,
+ 0xf067f133,
+ 0x026eb926,
+ 0xb90421f4,
+ 0x87fd02d8,
+ 0xf960f904,
+ 0xfcd0fc80,
+ 0x3321f4e0,
0xf10467f0,
0xd007e007,
0x04bd0006,
-/* 0x0400: memx_func_enter_wait */
+/* 0x053c: memx_func_enter_wait */
0x07c067f1,
0xf00066cf,
0x0bf40464,
- 0x001698f6,
- 0xf80410b6,
-/* 0x0415: memx_func_leave */
- 0x0467f000,
+ 0x2c67f0f6,
+ 0x800066cf,
+ 0x00f8ee06,
+/* 0x0554: memx_func_leave */
+ 0xcf2c67f0,
+ 0x06800066,
+ 0x0467f0ef,
0x07e407f1,
0xbd0006d0,
-/* 0x0421: memx_func_leave_wait */
+/* 0x0569: memx_func_leave_wait */
0xc067f104,
0x0066cf07,
0xf40464f0,
- 0x00f8f61b,
-/* 0x0430: memx_func_wr32 */
+ 0x67f1f61b,
+ 0x77f126f0,
+ 0x73f00001,
+ 0x026eb900,
+ 0xb90421f4,
+ 0x87fd02d8,
+ 0xf960f905,
+ 0xfcd0fc80,
+ 0x3321f4e0,
+ 0x162067f1,
+ 0xf4026eb9,
+ 0xd8b90421,
+ 0x0587fd02,
+ 0x80f960f9,
+ 0xe0fcd0fc,
+ 0xf13321f4,
+ 0xf00aa277,
+ 0x6eb90073,
+ 0x0421f402,
+ 0xfd02d8b9,
+ 0x60f90587,
+ 0xd0fc80f9,
+ 0x21f4e0fc,
+/* 0x05d3: memx_func_wait_vblank */
+ 0xb600f833,
+ 0x00f80410,
+/* 0x05d8: memx_func_wr32 */
0x98001698,
0x10b60115,
0xf960f908,
0xfcd0fc50,
0x3321f4e0,
- 0x140003f1,
- 0x800506fd,
- 0x04bd0005,
0xf40242b6,
- 0x00f8dd1b,
-/* 0x0458: memx_func_wait */
+ 0x00f8e91b,
+/* 0x05f4: memx_func_wait */
0xcf2c87f0,
0x1e980088,
0x011d9800,
0x98021c98,
0x10b6031b,
- 0x7e21f410,
-/* 0x0472: memx_func_delay */
+ 0x8621f410,
+/* 0x060e: memx_func_delay */
0x1e9800f8,
0x0410b600,
0xf86721f4,
-/* 0x047d: memx_exec */
+/* 0x0619: memx_exec */
0xf9e0f900,
0x02c1b9d0,
-/* 0x0487: memx_exec_next */
+/* 0x0623: memx_exec_next */
0x9802b2b9,
0x10b60013,
- 0x10349504,
+ 0xf034e704,
+ 0xe033e701,
+ 0x0132b601,
0x980c30f0,
0x55f9de35,
0xf40612b8,
- 0xd0fcec1e,
+ 0x0b98e41e,
+ 0xef0c98ee,
+ 0xf102cbbb,
+ 0xcf07c4b7,
+ 0xd0fc00bb,
0x21f5e0fc,
- 0x00f8026b,
-/* 0x04a8: memx_info */
- 0x03acc7f1,
+ 0x00f802f1,
+/* 0x065c: memx_info */
+ 0x03c0c7f1,
0x0800b7f1,
- 0x026b21f5,
-/* 0x04b6: memx_recv */
+ 0x02f121f5,
+/* 0x066a: memx_recv */
0xd6b000f8,
- 0xc40bf401,
+ 0xac0bf401,
0xf400d6b0,
0x00f8e90b,
-/* 0x04c4: memx_init */
-/* 0x04c6: perf_recv */
+/* 0x0678: memx_init */
+/* 0x067a: perf_recv */
0x00f800f8,
-/* 0x04c8: perf_init */
-/* 0x04ca: i2c_drive_scl */
+/* 0x067c: perf_init */
+/* 0x067e: i2c_drive_scl */
0x36b000f8,
0x0e0bf400,
0x07e007f1,
0xbd0001d0,
-/* 0x04db: i2c_drive_scl_lo */
+/* 0x068f: i2c_drive_scl_lo */
0xf100f804,
0xd007e407,
0x04bd0001,
-/* 0x04e6: i2c_drive_sda */
+/* 0x069a: i2c_drive_sda */
0x36b000f8,
0x0e0bf400,
0x07e007f1,
0xbd0002d0,
-/* 0x04f7: i2c_drive_sda_lo */
+/* 0x06ab: i2c_drive_sda_lo */
0xf100f804,
0xd007e407,
0x04bd0002,
-/* 0x0502: i2c_sense_scl */
+/* 0x06b6: i2c_sense_scl */
0x32f400f8,
0xc437f101,
0x0033cf07,
0xf40431fd,
0x31f4060b,
-/* 0x0515: i2c_sense_scl_done */
-/* 0x0517: i2c_sense_sda */
+/* 0x06c9: i2c_sense_scl_done */
+/* 0x06cb: i2c_sense_sda */
0xf400f801,
0x37f10132,
0x33cf07c4,
0x0432fd00,
0xf4060bf4,
-/* 0x052a: i2c_sense_sda_done */
+/* 0x06de: i2c_sense_sda_done */
0x00f80131,
-/* 0x052c: i2c_raise_scl */
+/* 0x06e0: i2c_raise_scl */
0x47f140f9,
0x37f00898,
- 0xca21f501,
-/* 0x0539: i2c_raise_scl_wait */
- 0xe8e7f104,
+ 0x7e21f501,
+/* 0x06ed: i2c_raise_scl_wait */
+ 0xe8e7f106,
0x6721f403,
- 0x050221f5,
+ 0x06b621f5,
0xb60901f4,
0x1bf40142,
-/* 0x054d: i2c_raise_scl_done */
+/* 0x0701: i2c_raise_scl_done */
0xf840fcef,
-/* 0x0551: i2c_start */
- 0x0221f500,
- 0x0d11f405,
- 0x051721f5,
+/* 0x0705: i2c_start */
+ 0xb621f500,
+ 0x0d11f406,
+ 0x06cb21f5,
0xf40611f4,
-/* 0x0562: i2c_start_rep */
+/* 0x0716: i2c_start_rep */
0x37f0300e,
- 0xca21f500,
- 0x0137f004,
- 0x04e621f5,
+ 0x7e21f500,
+ 0x0137f006,
+ 0x069a21f5,
0xb60076bb,
0x50f90465,
0xbb046594,
0x50bd0256,
0xfc0475fd,
- 0x2c21f550,
- 0x0464b605,
-/* 0x058f: i2c_start_send */
+ 0xe021f550,
+ 0x0464b606,
+/* 0x0743: i2c_start_send */
0xf01f11f4,
0x21f50037,
- 0xe7f104e6,
+ 0xe7f1069a,
0x21f41388,
0x0037f067,
- 0x04ca21f5,
+ 0x067e21f5,
0x1388e7f1,
-/* 0x05ab: i2c_start_out */
+/* 0x075f: i2c_start_out */
0xf86721f4,
-/* 0x05ad: i2c_stop */
+/* 0x0761: i2c_stop */
0x0037f000,
- 0x04ca21f5,
+ 0x067e21f5,
0xf50037f0,
- 0xf104e621,
+ 0xf1069a21,
0xf403e8e7,
0x37f06721,
- 0xca21f501,
- 0x88e7f104,
+ 0x7e21f501,
+ 0x88e7f106,
0x6721f413,
0xf50137f0,
- 0xf104e621,
+ 0xf1069a21,
0xf41388e7,
0x00f86721,
-/* 0x05e0: i2c_bitw */
- 0x04e621f5,
+/* 0x0794: i2c_bitw */
+ 0x069a21f5,
0x03e8e7f1,
0xbb6721f4,
0x65b60076,
@@ -1237,18 +1419,18 @@ uint32_t nvd0_pwr_code[] = {
0x56bb0465,
0xfd50bd02,
0x50fc0475,
- 0x052c21f5,
+ 0x06e021f5,
0xf40464b6,
0xe7f11811,
0x21f41388,
0x0037f067,
- 0x04ca21f5,
+ 0x067e21f5,
0x1388e7f1,
-/* 0x061f: i2c_bitw_out */
+/* 0x07d3: i2c_bitw_out */
0xf86721f4,
-/* 0x0621: i2c_bitr */
+/* 0x07d5: i2c_bitr */
0x0137f000,
- 0x04e621f5,
+ 0x069a21f5,
0x03e8e7f1,
0xbb6721f4,
0x65b60076,
@@ -1256,19 +1438,19 @@ uint32_t nvd0_pwr_code[] = {
0x56bb0465,
0xfd50bd02,
0x50fc0475,
- 0x052c21f5,
+ 0x06e021f5,
0xf40464b6,
0x21f51b11,
- 0x37f00517,
- 0xca21f500,
- 0x88e7f104,
+ 0x37f006cb,
+ 0x7e21f500,
+ 0x88e7f106,
0x6721f413,
0xf4013cf0,
-/* 0x0666: i2c_bitr_done */
+/* 0x081a: i2c_bitr_done */
0x00f80131,
-/* 0x0668: i2c_get_byte */
+/* 0x081c: i2c_get_byte */
0xf00057f0,
-/* 0x066e: i2c_get_byte_next */
+/* 0x0822: i2c_get_byte_next */
0x54b60847,
0x0076bb01,
0xf90465b6,
@@ -1276,7 +1458,7 @@ uint32_t nvd0_pwr_code[] = {
0xbd0256bb,
0x0475fd50,
0x21f550fc,
- 0x64b60621,
+ 0x64b607d5,
0x2b11f404,
0xb60553fd,
0x1bf40142,
@@ -1286,12 +1468,12 @@ uint32_t nvd0_pwr_code[] = {
0xbb046594,
0x50bd0256,
0xfc0475fd,
- 0xe021f550,
- 0x0464b605,
-/* 0x06b8: i2c_get_byte_done */
-/* 0x06ba: i2c_put_byte */
+ 0x9421f550,
+ 0x0464b607,
+/* 0x086c: i2c_get_byte_done */
+/* 0x086e: i2c_put_byte */
0x47f000f8,
-/* 0x06bd: i2c_put_byte_next */
+/* 0x0871: i2c_put_byte_next */
0x0142b608,
0xbb3854ff,
0x65b60076,
@@ -1299,7 +1481,7 @@ uint32_t nvd0_pwr_code[] = {
0x56bb0465,
0xfd50bd02,
0x50fc0475,
- 0x05e021f5,
+ 0x079421f5,
0xf40464b6,
0x46b03411,
0xd81bf400,
@@ -1308,21 +1490,21 @@ uint32_t nvd0_pwr_code[] = {
0xbb046594,
0x50bd0256,
0xfc0475fd,
- 0x2121f550,
- 0x0464b606,
+ 0xd521f550,
+ 0x0464b607,
0xbb0f11f4,
0x36b00076,
0x061bf401,
-/* 0x0713: i2c_put_byte_done */
+/* 0x08c7: i2c_put_byte_done */
0xf80132f4,
-/* 0x0715: i2c_addr */
+/* 0x08c9: i2c_addr */
0x0076bb00,
0xf90465b6,
0x04659450,
0xbd0256bb,
0x0475fd50,
0x21f550fc,
- 0x64b60551,
+ 0x64b60705,
0x2911f404,
0x012ec3e7,
0xfd0134b6,
@@ -1332,30 +1514,30 @@ uint32_t nvd0_pwr_code[] = {
0x0256bb04,
0x75fd50bd,
0xf550fc04,
- 0xb606ba21,
-/* 0x075a: i2c_addr_done */
+ 0xb6086e21,
+/* 0x090e: i2c_addr_done */
0x00f80464,
-/* 0x075c: i2c_acquire_addr */
+/* 0x0910: i2c_acquire_addr */
0xb6f8cec7,
0xe0b705e4,
0x00f8d014,
-/* 0x0768: i2c_acquire */
- 0x075c21f5,
+/* 0x091c: i2c_acquire */
+ 0x091021f5,
0xf00421f4,
0x21f403d9,
-/* 0x0777: i2c_release */
+/* 0x092b: i2c_release */
0xf500f833,
- 0xf4075c21,
+ 0xf4091021,
0xdaf00421,
0x3321f403,
-/* 0x0786: i2c_recv */
+/* 0x093a: i2c_recv */
0x32f400f8,
0xf8c1c701,
0xb00214b6,
0x1ff52816,
0x13a0013a,
- 0x32980bd4,
- 0xac13a000,
+ 0x32980be8,
+ 0xc013a000,
0x0031980b,
0xf90231f4,
0xf9e0f9d0,
@@ -1367,8 +1549,8 @@ uint32_t nvd0_pwr_code[] = {
0xbb046594,
0x50bd0256,
0xfc0475fd,
- 0x6821f550,
- 0x0464b607,
+ 0x1c21f550,
+ 0x0464b609,
0xd6b0d0fc,
0xb31bf500,
0x0057f000,
@@ -1377,8 +1559,8 @@ uint32_t nvd0_pwr_code[] = {
0xbb046594,
0x50bd0256,
0xfc0475fd,
- 0x1521f550,
- 0x0464b607,
+ 0xc921f550,
+ 0x0464b608,
0x00d011f5,
0xbbe0c5c7,
0x65b60076,
@@ -1386,7 +1568,7 @@ uint32_t nvd0_pwr_code[] = {
0x56bb0465,
0xfd50bd02,
0x50fc0475,
- 0x06ba21f5,
+ 0x086e21f5,
0xf50464b6,
0xf000ad11,
0x76bb0157,
@@ -1395,7 +1577,7 @@ uint32_t nvd0_pwr_code[] = {
0x0256bb04,
0x75fd50bd,
0xf550fc04,
- 0xb6071521,
+ 0xb608c921,
0x11f50464,
0x76bb008a,
0x0465b600,
@@ -1403,7 +1585,7 @@ uint32_t nvd0_pwr_code[] = {
0x0256bb04,
0x75fd50bd,
0xf550fc04,
- 0xb6066821,
+ 0xb6081c21,
0x11f40464,
0xe05bcb6a,
0xb60076bb,
@@ -1411,38 +1593,38 @@ uint32_t nvd0_pwr_code[] = {
0xbb046594,
0x50bd0256,
0xfc0475fd,
- 0xad21f550,
- 0x0464b605,
+ 0x6121f550,
+ 0x0464b607,
0xbd025bb9,
0x430ef474,
-/* 0x088c: i2c_recv_not_rd08 */
+/* 0x0a40: i2c_recv_not_rd08 */
0xf401d6b0,
0x57f03d1b,
- 0x1521f500,
- 0x3311f407,
+ 0xc921f500,
+ 0x3311f408,
0xf5e0c5c7,
- 0xf406ba21,
+ 0xf4086e21,
0x57f02911,
- 0x1521f500,
- 0x1f11f407,
+ 0xc921f500,
+ 0x1f11f408,
0xf5e0b5c7,
- 0xf406ba21,
+ 0xf4086e21,
0x21f51511,
- 0x74bd05ad,
+ 0x74bd0761,
0xf408c5c7,
0x32f4091b,
0x030ef402,
-/* 0x08cc: i2c_recv_not_wr08 */
-/* 0x08cc: i2c_recv_done */
+/* 0x0a80: i2c_recv_not_wr08 */
+/* 0x0a80: i2c_recv_done */
0xf5f8cec7,
- 0xfc077721,
+ 0xfc092b21,
0xf4d0fce0,
0x7cb90a12,
- 0x6b21f502,
-/* 0x08e1: i2c_recv_exit */
-/* 0x08e3: i2c_init */
+ 0xf121f502,
+/* 0x0a95: i2c_recv_exit */
+/* 0x0a97: i2c_init */
0xf800f802,
-/* 0x08e5: test_recv */
+/* 0x0a99: test_recv */
0xd817f100,
0x0011cf05,
0xf10110b6,
@@ -1450,29 +1632,29 @@ uint32_t nvd0_pwr_code[] = {
0x04bd0001,
0xd900e7f1,
0x134fe3f1,
- 0x01b621f5,
-/* 0x0906: test_init */
+ 0x022321f5,
+/* 0x0aba: test_init */
0xe7f100f8,
0x21f50800,
- 0x00f801b6,
-/* 0x0910: idle_recv */
-/* 0x0912: idle */
+ 0x00f80223,
+/* 0x0ac4: idle_recv */
+/* 0x0ac6: idle */
0x31f400f8,
0xd417f100,
0x0011cf05,
0xf10110b6,
0xd005d407,
0x04bd0001,
-/* 0x0928: idle_loop */
+/* 0x0adc: idle_loop */
0xf45817f0,
-/* 0x092e: idle_proc */
-/* 0x092e: idle_proc_exec */
+/* 0x0ae2: idle_proc */
+/* 0x0ae2: idle_proc_exec */
0x10f90232,
0xf5021eb9,
- 0xfc027421,
+ 0xfc02fa21,
0x0911f410,
0xf40231f4,
-/* 0x0942: idle_proc_next */
+/* 0x0af6: idle_proc_next */
0x10b6ef0e,
0x061fb858,
0xf4e61bf4,
@@ -1521,4 +1703,23 @@ uint32_t nvd0_pwr_code[] = {
0x00000000,
0x00000000,
0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h
index 574acfa44c8c..522e3079f824 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/fuc/os.h
@@ -19,11 +19,12 @@
#define MEMX_MSG_EXEC 1
/* MEMX: script opcode definitions */
-#define MEMX_ENTER 0
-#define MEMX_LEAVE 1
-#define MEMX_WR32 2
-#define MEMX_WAIT 3
-#define MEMX_DELAY 4
+#define MEMX_ENTER 1
+#define MEMX_LEAVE 2
+#define MEMX_WR32 3
+#define MEMX_WAIT 4
+#define MEMX_DELAY 5
+#define MEMX_VBLANK 6
/* I2C_: message identifiers */
#define I2C__MSG_RD08 0
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/gk104.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/gk104.c
new file mode 100644
index 000000000000..d76612999b9f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/gk104.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "priv.h"
+
+#define nvd0_pwr_code gk104_pwr_code
+#define nvd0_pwr_data gk104_pwr_data
+#include "fuc/nvd0.fuc.h"
+
+static void
+gk104_pwr_pgob(struct nouveau_pwr *ppwr, bool enable)
+{
+ nv_mask(ppwr, 0x000200, 0x00001000, 0x00000000);
+ nv_rd32(ppwr, 0x000200);
+ nv_mask(ppwr, 0x000200, 0x08000000, 0x08000000);
+ msleep(50);
+
+ nv_mask(ppwr, 0x10a78c, 0x00000002, 0x00000002);
+ nv_mask(ppwr, 0x10a78c, 0x00000001, 0x00000001);
+ nv_mask(ppwr, 0x10a78c, 0x00000001, 0x00000000);
+
+ nv_mask(ppwr, 0x020004, 0xc0000000, enable ? 0xc0000000 : 0x40000000);
+ msleep(50);
+
+ nv_mask(ppwr, 0x10a78c, 0x00000002, 0x00000000);
+ nv_mask(ppwr, 0x10a78c, 0x00000001, 0x00000001);
+ nv_mask(ppwr, 0x10a78c, 0x00000001, 0x00000000);
+
+ nv_mask(ppwr, 0x000200, 0x08000000, 0x00000000);
+ nv_mask(ppwr, 0x000200, 0x00001000, 0x00001000);
+ nv_rd32(ppwr, 0x000200);
+}
+
+struct nouveau_oclass *
+gk104_pwr_oclass = &(struct nvkm_pwr_impl) {
+ .base.handle = NV_SUBDEV(PWR, 0xe4),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_pwr_ctor,
+ .dtor = _nouveau_pwr_dtor,
+ .init = _nouveau_pwr_init,
+ .fini = _nouveau_pwr_fini,
+ },
+ .code.data = gk104_pwr_code,
+ .code.size = sizeof(gk104_pwr_code),
+ .data.data = gk104_pwr_data,
+ .data.size = sizeof(gk104_pwr_data),
+ .pgob = gk104_pwr_pgob,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c
index 03de3107d29f..65eaa2546cad 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/memx.c
@@ -1,8 +1,7 @@
#ifndef __NVKM_PWR_MEMX_H__
#define __NVKM_PWR_MEMX_H__
-#include <subdev/pwr.h>
-#include <subdev/pwr/fuc/os.h>
+#include "priv.h"
struct nouveau_memx {
struct nouveau_pwr *ppwr;
@@ -21,10 +20,11 @@ memx_out(struct nouveau_memx *memx)
struct nouveau_pwr *ppwr = memx->ppwr;
int i;
- if (memx->c.size) {
+ if (memx->c.mthd) {
nv_wr32(ppwr, 0x10a1c4, (memx->c.size << 16) | memx->c.mthd);
for (i = 0; i < memx->c.size; i++)
nv_wr32(ppwr, 0x10a1c4, memx->c.data[i]);
+ memx->c.mthd = 0;
memx->c.size = 0;
}
}
@@ -33,7 +33,7 @@ static void
memx_cmd(struct nouveau_memx *memx, u32 mthd, u32 size, u32 data[])
{
if ((memx->c.size + size >= ARRAY_SIZE(memx->c.data)) ||
- (memx->c.size && memx->c.mthd != mthd))
+ (memx->c.mthd && memx->c.mthd != mthd))
memx_out(memx);
memcpy(&memx->c.data[memx->c.size], data, size * sizeof(data[0]));
memx->c.size += size;
@@ -63,8 +63,7 @@ nouveau_memx_init(struct nouveau_pwr *ppwr, struct nouveau_memx **pmemx)
nv_wr32(ppwr, 0x10a580, 0x00000003);
} while (nv_rd32(ppwr, 0x10a580) != 0x00000003);
nv_wr32(ppwr, 0x10a1c0, 0x01000000 | memx->base);
- nv_wr32(ppwr, 0x10a1c4, 0x00010000 | MEMX_ENTER);
- nv_wr32(ppwr, 0x10a1c4, 0x00000000);
+
return 0;
}
@@ -79,7 +78,6 @@ nouveau_memx_fini(struct nouveau_memx **pmemx, bool exec)
memx_out(memx);
/* release data segment access */
- nv_wr32(ppwr, 0x10a1c4, 0x00000000 | MEMX_LEAVE);
finish = nv_rd32(ppwr, 0x10a1c0) & 0x00ffffff;
nv_wr32(ppwr, 0x10a580, 0x00000000);
@@ -89,6 +87,8 @@ nouveau_memx_fini(struct nouveau_memx **pmemx, bool exec)
memx->base, finish);
}
+ nv_debug(memx->ppwr, "Exec took %uns, PPWR_IN %08x\n",
+ reply[0], reply[1]);
kfree(memx);
return 0;
}
@@ -118,4 +118,51 @@ nouveau_memx_nsec(struct nouveau_memx *memx, u32 nsec)
memx_out(memx); /* fuc can't handle multiple */
}
+void
+nouveau_memx_wait_vblank(struct nouveau_memx *memx)
+{
+ struct nouveau_pwr *ppwr = memx->ppwr;
+ u32 heads, x, y, px = 0;
+ int i, head_sync;
+
+ if (nv_device(ppwr)->chipset < 0xd0) {
+ heads = nv_rd32(ppwr, 0x610050);
+ for (i = 0; i < 2; i++) {
+ /* Heuristic: sync to head with biggest resolution */
+ if (heads & (2 << (i << 3))) {
+ x = nv_rd32(ppwr, 0x610b40 + (0x540 * i));
+ y = (x & 0xffff0000) >> 16;
+ x &= 0x0000ffff;
+ if ((x * y) > px) {
+ px = (x * y);
+ head_sync = i;
+ }
+ }
+ }
+ }
+
+ if (px == 0) {
+ nv_debug(memx->ppwr, "WAIT VBLANK !NO ACTIVE HEAD\n");
+ return;
+ }
+
+ nv_debug(memx->ppwr, "WAIT VBLANK HEAD%d\n", head_sync);
+ memx_cmd(memx, MEMX_VBLANK, 1, (u32[]){ head_sync });
+ memx_out(memx); /* fuc can't handle multiple */
+}
+
+void
+nouveau_memx_block(struct nouveau_memx *memx)
+{
+ nv_debug(memx->ppwr, " HOST BLOCKED\n");
+ memx_cmd(memx, MEMX_ENTER, 0, NULL);
+}
+
+void
+nouveau_memx_unblock(struct nouveau_memx *memx)
+{
+ nv_debug(memx->ppwr, " HOST UNBLOCKED\n");
+ memx_cmd(memx, MEMX_LEAVE, 0, NULL);
+}
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/nv108.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/nv108.c
index 52c85414866a..04ff7c3c34e9 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/nv108.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/nv108.c
@@ -22,41 +22,20 @@
* Authors: Ben Skeggs
*/
-#include <subdev/pwr.h>
-
+#include "priv.h"
#include "fuc/nv108.fuc.h"
-struct nv108_pwr_priv {
- struct nouveau_pwr base;
-};
-
-static int
-nv108_pwr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv108_pwr_priv *priv;
- int ret;
-
- ret = nouveau_pwr_create(parent, engine, oclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- priv->base.code.data = nv108_pwr_code;
- priv->base.code.size = sizeof(nv108_pwr_code);
- priv->base.data.data = nv108_pwr_data;
- priv->base.data.size = sizeof(nv108_pwr_data);
- return 0;
-}
-
-struct nouveau_oclass
-nv108_pwr_oclass = {
- .handle = NV_SUBDEV(PWR, 0x00),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nv108_pwr_ctor,
+struct nouveau_oclass *
+nv108_pwr_oclass = &(struct nvkm_pwr_impl) {
+ .base.handle = NV_SUBDEV(PWR, 0x00),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_pwr_ctor,
.dtor = _nouveau_pwr_dtor,
.init = _nouveau_pwr_init,
.fini = _nouveau_pwr_fini,
},
-};
+ .code.data = nv108_pwr_code,
+ .code.size = sizeof(nv108_pwr_code),
+ .data.data = nv108_pwr_data,
+ .data.size = sizeof(nv108_pwr_data),
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/nva3.c
index c132b7ca9747..998d53076b8b 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/nva3.c
@@ -22,50 +22,29 @@
* Authors: Ben Skeggs
*/
-#include <subdev/pwr.h>
-
+#include "priv.h"
#include "fuc/nva3.fuc.h"
-struct nva3_pwr_priv {
- struct nouveau_pwr base;
-};
-
static int
nva3_pwr_init(struct nouveau_object *object)
{
- struct nva3_pwr_priv *priv = (void *)object;
- nv_mask(priv, 0x022210, 0x00000001, 0x00000000);
- nv_mask(priv, 0x022210, 0x00000001, 0x00000001);
- return nouveau_pwr_init(&priv->base);
-}
-
-static int
-nva3_pwr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nva3_pwr_priv *priv;
- int ret;
-
- ret = nouveau_pwr_create(parent, engine, oclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- priv->base.code.data = nva3_pwr_code;
- priv->base.code.size = sizeof(nva3_pwr_code);
- priv->base.data.data = nva3_pwr_data;
- priv->base.data.size = sizeof(nva3_pwr_data);
- return 0;
+ struct nouveau_pwr *ppwr = (void *)object;
+ nv_mask(ppwr, 0x022210, 0x00000001, 0x00000000);
+ nv_mask(ppwr, 0x022210, 0x00000001, 0x00000001);
+ return nouveau_pwr_init(ppwr);
}
-struct nouveau_oclass
-nva3_pwr_oclass = {
- .handle = NV_SUBDEV(PWR, 0xa3),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nva3_pwr_ctor,
+struct nouveau_oclass *
+nva3_pwr_oclass = &(struct nvkm_pwr_impl) {
+ .base.handle = NV_SUBDEV(PWR, 0xa3),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_pwr_ctor,
.dtor = _nouveau_pwr_dtor,
.init = nva3_pwr_init,
.fini = _nouveau_pwr_fini,
},
-};
+ .code.data = nva3_pwr_code,
+ .code.size = sizeof(nva3_pwr_code),
+ .data.data = nva3_pwr_data,
+ .data.size = sizeof(nva3_pwr_data),
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/nvc0.c
index 495f6857428d..9a773e66efa4 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/nvc0.c
@@ -22,41 +22,20 @@
* Authors: Ben Skeggs
*/
-#include <subdev/pwr.h>
-
+#include "priv.h"
#include "fuc/nvc0.fuc.h"
-struct nvc0_pwr_priv {
- struct nouveau_pwr base;
-};
-
-static int
-nvc0_pwr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nvc0_pwr_priv *priv;
- int ret;
-
- ret = nouveau_pwr_create(parent, engine, oclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- priv->base.code.data = nvc0_pwr_code;
- priv->base.code.size = sizeof(nvc0_pwr_code);
- priv->base.data.data = nvc0_pwr_data;
- priv->base.data.size = sizeof(nvc0_pwr_data);
- return 0;
-}
-
-struct nouveau_oclass
-nvc0_pwr_oclass = {
- .handle = NV_SUBDEV(PWR, 0xc0),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nvc0_pwr_ctor,
+struct nouveau_oclass *
+nvc0_pwr_oclass = &(struct nvkm_pwr_impl) {
+ .base.handle = NV_SUBDEV(PWR, 0xc0),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_pwr_ctor,
.dtor = _nouveau_pwr_dtor,
.init = _nouveau_pwr_init,
.fini = _nouveau_pwr_fini,
},
-};
+ .code.data = nvc0_pwr_code,
+ .code.size = sizeof(nvc0_pwr_code),
+ .data.data = nvc0_pwr_data,
+ .data.size = sizeof(nvc0_pwr_data),
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/pwr/nvd0.c
index 043aa142fe82..2b29be5d08ac 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/pwr/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/nvd0.c
@@ -22,41 +22,20 @@
* Authors: Ben Skeggs
*/
-#include <subdev/pwr.h>
-
+#include "priv.h"
#include "fuc/nvd0.fuc.h"
-struct nvd0_pwr_priv {
- struct nouveau_pwr base;
-};
-
-static int
-nvd0_pwr_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nvd0_pwr_priv *priv;
- int ret;
-
- ret = nouveau_pwr_create(parent, engine, oclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- priv->base.code.data = nvd0_pwr_code;
- priv->base.code.size = sizeof(nvd0_pwr_code);
- priv->base.data.data = nvd0_pwr_data;
- priv->base.data.size = sizeof(nvd0_pwr_data);
- return 0;
-}
-
-struct nouveau_oclass
-nvd0_pwr_oclass = {
- .handle = NV_SUBDEV(PWR, 0xd0),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nvd0_pwr_ctor,
+struct nouveau_oclass *
+nvd0_pwr_oclass = &(struct nvkm_pwr_impl) {
+ .base.handle = NV_SUBDEV(PWR, 0xd0),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_pwr_ctor,
.dtor = _nouveau_pwr_dtor,
.init = _nouveau_pwr_init,
.fini = _nouveau_pwr_fini,
},
-};
+ .code.data = nvd0_pwr_code,
+ .code.size = sizeof(nvd0_pwr_code),
+ .data.data = nvd0_pwr_data,
+ .data.size = sizeof(nvd0_pwr_data),
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/pwr/priv.h b/drivers/gpu/drm/nouveau/core/subdev/pwr/priv.h
new file mode 100644
index 000000000000..3814a341db32
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/pwr/priv.h
@@ -0,0 +1,44 @@
+#ifndef __NVKM_PWR_PRIV_H__
+#define __NVKM_PWR_PRIV_H__
+
+#include <subdev/pwr.h>
+#include <subdev/pwr/fuc/os.h>
+
+#define nouveau_pwr_create(p, e, o, d) \
+ nouveau_pwr_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_pwr_destroy(p) \
+ nouveau_subdev_destroy(&(p)->base)
+#define nouveau_pwr_init(p) ({ \
+ struct nouveau_pwr *_ppwr = (p); \
+ _nouveau_pwr_init(nv_object(_ppwr)); \
+})
+#define nouveau_pwr_fini(p,s) ({ \
+ struct nouveau_pwr *_ppwr = (p); \
+ _nouveau_pwr_fini(nv_object(_ppwr), (s)); \
+})
+
+int nouveau_pwr_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, int, void **);
+
+int _nouveau_pwr_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+#define _nouveau_pwr_dtor _nouveau_subdev_dtor
+int _nouveau_pwr_init(struct nouveau_object *);
+int _nouveau_pwr_fini(struct nouveau_object *, bool);
+
+struct nvkm_pwr_impl {
+ struct nouveau_oclass base;
+ struct {
+ u32 *data;
+ u32 size;
+ } code;
+ struct {
+ u32 *data;
+ u32 size;
+ } data;
+
+ void (*pgob)(struct nouveau_pwr *, bool);
+};
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c
index 016990a8252c..3656d605168f 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c
@@ -31,6 +31,8 @@
#include <subdev/gpio.h>
#include <subdev/timer.h>
+#include <subdev/bios/fan.h>
+
static int
nouveau_fan_update(struct nouveau_fan *fan, bool immediate, int target)
{
@@ -275,8 +277,11 @@ nouveau_therm_fan_ctor(struct nouveau_therm *therm)
/* other random init... */
nouveau_therm_fan_set_defaults(therm);
nvbios_perf_fan_parse(bios, &priv->fan->perf);
- if (nvbios_therm_fan_parse(bios, &priv->fan->bios))
- nv_error(therm, "parsing the thermal table failed\n");
+ if (!nvbios_fan_parse(bios, &priv->fan->bios)) {
+ nv_debug(therm, "parsing the fan table failed\n");
+ if (nvbios_therm_fan_parse(bios, &priv->fan->bios))
+ nv_error(therm, "parsing both fan tables failed\n");
+ }
nouveau_therm_fan_safety_checks(therm);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c
index 9a5c07340263..c629d7f2a6a4 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fanpwm.c
@@ -25,6 +25,8 @@
#include <core/option.h>
#include <subdev/gpio.h>
+#include <subdev/bios.h>
+#include <subdev/bios/fan.h>
#include "priv.h"
@@ -86,11 +88,15 @@ nouveau_fanpwm_create(struct nouveau_therm *therm, struct dcb_gpio_func *func)
{
struct nouveau_device *device = nv_device(therm);
struct nouveau_therm_priv *tpriv = (void *)therm;
+ struct nouveau_bios *bios = nouveau_bios(therm);
struct nouveau_fanpwm_priv *priv;
+ struct nvbios_therm_fan fan;
u32 divs, duty;
+ nvbios_fan_parse(bios, &fan);
+
if (!nouveau_boolopt(device->cfgopt, "NvFanPWM", func->param) ||
- !therm->pwm_ctrl ||
+ !therm->pwm_ctrl || fan.type == NVBIOS_THERM_FAN_TOGGLE ||
therm->pwm_get(therm, func->line, &divs, &duty) == -ENODEV)
return -ENODEV;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/gm107.c b/drivers/gpu/drm/nouveau/core/subdev/therm/gm107.c
new file mode 100644
index 000000000000..668cf3322285
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/gm107.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 Martin Peres
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Martin Peres
+ */
+
+#include "priv.h"
+
+struct gm107_therm_priv {
+ struct nouveau_therm_priv base;
+};
+
+static int
+gm107_fan_pwm_ctrl(struct nouveau_therm *therm, int line, bool enable)
+{
+ /* nothing to do, it seems hardwired */
+ return 0;
+}
+
+static int
+gm107_fan_pwm_get(struct nouveau_therm *therm, int line, u32 *divs, u32 *duty)
+{
+ *divs = nv_rd32(therm, 0x10eb20) & 0x1fff;
+ *duty = nv_rd32(therm, 0x10eb24) & 0x1fff;
+ return 0;
+}
+
+static int
+gm107_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
+{
+ nv_mask(therm, 0x10eb10, 0x1fff, divs); /* keep the high bits */
+ nv_wr32(therm, 0x10eb14, duty | 0x80000000);
+ return 0;
+}
+
+static int
+gm107_fan_pwm_clock(struct nouveau_therm *therm, int line)
+{
+ return nv_device(therm)->crystal * 1000;
+}
+
+static int
+gm107_therm_ctor(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct gm107_therm_priv *priv;
+ int ret;
+
+ ret = nouveau_therm_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ priv->base.base.pwm_ctrl = gm107_fan_pwm_ctrl;
+ priv->base.base.pwm_get = gm107_fan_pwm_get;
+ priv->base.base.pwm_set = gm107_fan_pwm_set;
+ priv->base.base.pwm_clock = gm107_fan_pwm_clock;
+ priv->base.base.temp_get = nv84_temp_get;
+ priv->base.base.fan_sense = nva3_therm_fan_sense;
+ priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling;
+ return nouveau_therm_preinit(&priv->base.base);
+}
+
+struct nouveau_oclass
+gm107_therm_oclass = {
+ .handle = NV_SUBDEV(THERM, 0x117),
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gm107_therm_ctor,
+ .dtor = _nouveau_therm_dtor,
+ .init = nvd0_therm_init,
+ .fini = nv84_therm_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nv84.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nv84.c
index 1d15c52fad0c..14e2e09bfc24 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nv84.c
@@ -24,6 +24,7 @@
*/
#include "priv.h"
+#include <subdev/fuse.h>
struct nv84_therm_priv {
struct nouveau_therm_priv base;
@@ -32,7 +33,25 @@ struct nv84_therm_priv {
int
nv84_temp_get(struct nouveau_therm *therm)
{
- return nv_rd32(therm, 0x20400);
+ struct nouveau_fuse *fuse = nouveau_fuse(therm);
+
+ if (nv_ro32(fuse, 0x1a8) == 1)
+ return nv_rd32(therm, 0x20400);
+ else
+ return -ENODEV;
+}
+
+void
+nv84_sensor_setup(struct nouveau_therm *therm)
+{
+ struct nouveau_fuse *fuse = nouveau_fuse(therm);
+
+ /* enable temperature reading for cards with insane defaults */
+ if (nv_ro32(fuse, 0x1a8) == 1) {
+ nv_mask(therm, 0x20008, 0x80008000, 0x80000000);
+ nv_mask(therm, 0x2000c, 0x80000003, 0x00000000);
+ mdelay(20); /* wait for the temperature to stabilize */
+ }
}
static void
@@ -171,6 +190,21 @@ nv84_therm_intr(struct nouveau_subdev *subdev)
}
static int
+nv84_therm_init(struct nouveau_object *object)
+{
+ struct nv84_therm_priv *priv = (void *)object;
+ int ret;
+
+ ret = nouveau_therm_init(&priv->base.base);
+ if (ret)
+ return ret;
+
+ nv84_sensor_setup(&priv->base.base);
+
+ return 0;
+}
+
+static int
nv84_therm_ctor(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
@@ -228,7 +262,7 @@ nv84_therm_oclass = {
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nv84_therm_ctor,
.dtor = _nouveau_therm_dtor,
- .init = _nouveau_therm_init,
+ .init = nv84_therm_init,
.fini = nv84_therm_fini,
},
};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
index 0478b2e3fb1d..7893357a7e9f 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
@@ -51,6 +51,8 @@ nva3_therm_init(struct nouveau_object *object)
if (ret)
return ret;
+ nv84_sensor_setup(&priv->base.base);
+
/* enable fan tach, count revolutions per-second */
nv_mask(priv, 0x00e720, 0x00000003, 0x00000002);
if (tach->func != DCB_GPIO_UNUSED) {
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
index bbf117be572f..b70f7cc649b8 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
@@ -114,7 +114,7 @@ nvd0_fan_pwm_clock(struct nouveau_therm *therm, int line)
return nv_device(therm)->crystal * 1000 / 10;
}
-static int
+int
nvd0_therm_init(struct nouveau_object *object)
{
struct nvd0_therm_priv *priv = (void *)object;
@@ -150,6 +150,8 @@ nvd0_therm_ctor(struct nouveau_object *parent,
if (ret)
return ret;
+ nv84_sensor_setup(&priv->base.base);
+
priv->base.base.pwm_ctrl = nvd0_fan_pwm_ctrl;
priv->base.base.pwm_get = nvd0_fan_pwm_get;
priv->base.base.pwm_set = nvd0_fan_pwm_set;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
index 916fca5c7816..7dba8c281a0b 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
@@ -145,10 +145,13 @@ int nv50_fan_pwm_get(struct nouveau_therm *, int, u32 *, u32 *);
int nv50_fan_pwm_set(struct nouveau_therm *, int, u32, u32);
int nv50_fan_pwm_clock(struct nouveau_therm *, int);
int nv84_temp_get(struct nouveau_therm *therm);
+void nv84_sensor_setup(struct nouveau_therm *therm);
int nv84_therm_fini(struct nouveau_object *object, bool suspend);
int nva3_therm_fan_sense(struct nouveau_therm *);
+int nvd0_therm_init(struct nouveau_object *object);
+
int nouveau_fanpwm_create(struct nouveau_therm *, struct dcb_gpio_func *);
int nouveau_fantog_create(struct nouveau_therm *, struct dcb_gpio_func *);
int nouveau_fannil_create(struct nouveau_therm *);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c
index 7dd680ff2f6f..f75a683bd47a 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c
@@ -296,7 +296,7 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,
int ret;
mutex_lock(&nv_subdev(vmm)->mutex);
- ret = nouveau_mm_head(&vm->mm, page_shift, msize, msize, align,
+ ret = nouveau_mm_head(&vm->mm, 0, page_shift, msize, msize, align,
&vma->node);
if (unlikely(ret != 0)) {
mutex_unlock(&nv_subdev(vmm)->mutex);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c
index 668cf964e4a9..2d0988755530 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c
@@ -28,7 +28,7 @@
#include <subdev/timer.h>
#include <subdev/fb.h>
#include <subdev/vm.h>
-#include <subdev/ltcg.h>
+#include <subdev/ltc.h>
#include <subdev/bar.h>
struct nvc0_vmmgr_priv {
@@ -116,12 +116,12 @@ nvc0_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt,
pte <<= 3;
if (mem->tag) {
- struct nouveau_ltcg *ltcg =
- nouveau_ltcg(vma->vm->vmm->base.base.parent);
+ struct nouveau_ltc *ltc =
+ nouveau_ltc(vma->vm->vmm->base.base.parent);
u32 tag = mem->tag->offset + (delta >> 17);
phys |= (u64)tag << (32 + 12);
next |= (u64)1 << (32 + 12);
- ltcg->tags_clear(ltcg, tag, cnt);
+ ltc->tags_clear(ltc, tag, cnt);
}
while (cnt--) {
diff --git a/drivers/gpu/drm/nouveau/dispnv04/arb.c b/drivers/gpu/drm/nouveau/dispnv04/arb.c
index 2a15b98b4d2b..c6361422a0b2 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/arb.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/arb.c
@@ -198,12 +198,12 @@ nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
int *burst, int *lwm)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
struct nv_fifo_info fifo_data;
struct nv_sim_state sim_data;
int MClk = nouveau_hw_get_clock(dev, PLL_MEMORY);
int NVClk = nouveau_hw_get_clock(dev, PLL_CORE);
- uint32_t cfg1 = nv_rd32(device, NV04_PFB_CFG1);
+ uint32_t cfg1 = nvif_rd32(device, NV04_PFB_CFG1);
sim_data.pclk_khz = VClk;
sim_data.mclk_khz = MClk;
@@ -221,13 +221,13 @@ nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
sim_data.mem_latency = 3;
sim_data.mem_page_miss = 10;
} else {
- sim_data.memory_type = nv_rd32(device, NV04_PFB_CFG0) & 0x1;
- sim_data.memory_width = (nv_rd32(device, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64;
+ sim_data.memory_type = nvif_rd32(device, NV04_PFB_CFG0) & 0x1;
+ sim_data.memory_width = (nvif_rd32(device, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64;
sim_data.mem_latency = cfg1 & 0xf;
sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1);
}
- if (nv_device(drm->device)->card_type == NV_04)
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_TNT)
nv04_calc_arb(&fifo_data, &sim_data);
else
nv10_calc_arb(&fifo_data, &sim_data);
@@ -254,7 +254,7 @@ nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm
{
struct nouveau_drm *drm = nouveau_drm(dev);
- if (nv_device(drm->device)->card_type < NV_20)
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_KELVIN)
nv04_update_arb(dev, vclk, bpp, burst, lwm);
else if ((dev->pdev->device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ ||
(dev->pdev->device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) {
diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
index 41be3424c906..fca6a1f9c20c 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
@@ -111,8 +111,8 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod
{
struct drm_device *dev = crtc->dev;
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_bios *bios = nouveau_bios(drm->device);
- struct nouveau_clock *clk = nouveau_clock(drm->device);
+ struct nouveau_bios *bios = nvkm_bios(&drm->device);
+ struct nouveau_clock *clk = nvkm_clock(&drm->device);
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct nv04_mode_state *state = &nv04_display(dev)->mode_reg;
struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index];
@@ -136,7 +136,7 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod
* has yet been observed in allowing the use a single stage pll on all
* nv43 however. the behaviour of single stage use is untested on nv40
*/
- if (nv_device(drm->device)->chipset > 0x40 && dot_clock <= (pll_lim.vco1.max_freq / 2))
+ if (drm->device.info.chipset > 0x40 && dot_clock <= (pll_lim.vco1.max_freq / 2))
memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2));
@@ -146,10 +146,10 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mod
state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK;
/* The blob uses this always, so let's do the same */
- if (nv_device(drm->device)->card_type == NV_40)
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE)
state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE;
/* again nv40 and some nv43 act more like nv3x as described above */
- if (nv_device(drm->device)->chipset < 0x41)
+ if (drm->device.info.chipset < 0x41)
state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL |
NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL;
state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK;
@@ -275,7 +275,7 @@ nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode)
horizEnd = horizTotal - 2;
horizBlankEnd = horizTotal + 4;
#if 0
- if (dev->overlayAdaptor && nv_device(drm->device)->card_type >= NV_10)
+ if (dev->overlayAdaptor && drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS)
/* This reportedly works around some video overlay bandwidth problems */
horizTotal += 2;
#endif
@@ -509,7 +509,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
regp->cursor_cfg = NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 |
NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 |
NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM;
- if (nv_device(drm->device)->chipset >= 0x11)
+ if (drm->device.info.chipset >= 0x11)
regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE;
@@ -550,26 +550,26 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
* 1 << 30 on 0x60.830), for no apparent reason */
regp->CRTC[NV_CIO_CRE_59] = off_chip_digital;
- if (nv_device(drm->device)->card_type >= NV_30)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE)
regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1;
regp->crtc_830 = mode->crtc_vdisplay - 3;
regp->crtc_834 = mode->crtc_vdisplay - 1;
- if (nv_device(drm->device)->card_type == NV_40)
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE)
/* This is what the blob does */
regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850);
- if (nv_device(drm->device)->card_type >= NV_30)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE)
regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT);
- if (nv_device(drm->device)->card_type >= NV_10)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS)
regp->crtc_cfg = NV10_PCRTC_CONFIG_START_ADDRESS_HSYNC;
else
regp->crtc_cfg = NV04_PCRTC_CONFIG_START_ADDRESS_HSYNC;
/* Some misc regs */
- if (nv_device(drm->device)->card_type == NV_40) {
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) {
regp->CRTC[NV_CIO_CRE_85] = 0xFF;
regp->CRTC[NV_CIO_CRE_86] = 0x1;
}
@@ -581,7 +581,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
/* Generic PRAMDAC regs */
- if (nv_device(drm->device)->card_type >= NV_10)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS)
/* Only bit that bios and blob set. */
regp->nv10_cursync = (1 << 25);
@@ -590,7 +590,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON;
if (crtc->primary->fb->depth == 16)
regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
- if (nv_device(drm->device)->chipset >= 0x11)
+ if (drm->device.info.chipset >= 0x11)
regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG;
regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */
@@ -653,7 +653,7 @@ nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
nv_crtc_mode_set_vga(crtc, adjusted_mode);
/* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */
- if (nv_device(drm->device)->card_type == NV_40)
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, nv04_display(dev)->mode_reg.sel_clk);
nv_crtc_mode_set_regs(crtc, adjusted_mode);
nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock);
@@ -714,7 +714,7 @@ static void nv_crtc_prepare(struct drm_crtc *crtc)
/* Some more preparation. */
NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_CONFIG, NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA);
- if (nv_device(drm->device)->card_type == NV_40) {
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) {
uint32_t reg900 = NVReadRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900);
NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900, reg900 & ~0x10000);
}
@@ -888,7 +888,7 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX);
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX);
- if (nv_device(drm->device)->card_type >= NV_20) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_KELVIN) {
regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8;
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47);
}
@@ -915,9 +915,9 @@ nv04_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
struct drm_device *dev = drm->dev;
if (state == ENTER_ATOMIC_MODE_SET)
- nouveau_fbcon_save_disable_accel(dev);
+ nouveau_fbcon_accel_save_disable(dev);
else
- nouveau_fbcon_restore_accel(dev);
+ nouveau_fbcon_accel_restore(dev);
return nv04_crtc_do_mode_set_base(crtc, fb, x, y, true);
}
@@ -969,7 +969,7 @@ static void nv11_cursor_upload(struct drm_device *dev, struct nouveau_bo *src,
{
struct nouveau_drm *drm = nouveau_drm(dev);
- if (nv_device(drm->device)->chipset == 0x11) {
+ if (drm->device.info.chipset == 0x11) {
pixel = ((pixel & 0x000000ff) << 24) |
((pixel & 0x0000ff00) << 8) |
((pixel & 0x00ff0000) >> 8) |
@@ -1010,7 +1010,7 @@ nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
if (ret)
goto out;
- if (nv_device(drm->device)->chipset >= 0x11)
+ if (drm->device.info.chipset >= 0x11)
nv11_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo);
else
nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo);
@@ -1127,7 +1127,7 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num)
drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
- 0, 0x0000, NULL, &nv_crtc->cursor.nvbo);
+ 0, 0x0000, NULL, NULL, &nv_crtc->cursor.nvbo);
if (!ret) {
ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
if (!ret) {
diff --git a/drivers/gpu/drm/nouveau/dispnv04/cursor.c b/drivers/gpu/drm/nouveau/dispnv04/cursor.c
index a810303169de..4e61173c3353 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/cursor.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/cursor.c
@@ -55,7 +55,7 @@ nv04_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
- if (nv_device(drm->device)->card_type == NV_40)
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE)
nv_fix_nv40_hw_cursor(dev, nv_crtc->index);
}
diff --git a/drivers/gpu/drm/nouveau/dispnv04/dac.c b/drivers/gpu/drm/nouveau/dispnv04/dac.c
index a96dda48718e..2d8056cde996 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/dac.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/dac.c
@@ -65,8 +65,8 @@ int nv04_dac_output_offset(struct drm_encoder *encoder)
static int sample_load_twice(struct drm_device *dev, bool sense[2])
{
- struct nouveau_device *device = nouveau_dev(dev);
- struct nouveau_timer *ptimer = nouveau_timer(device);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
+ struct nouveau_timer *ptimer = nvkm_timer(device);
int i;
for (i = 0; i < 2; i++) {
@@ -95,15 +95,15 @@ static int sample_load_twice(struct drm_device *dev, bool sense[2])
udelay(100);
/* when level triggers, sense is _LO_ */
- sense_a = nv_rd08(device, NV_PRMCIO_INP0) & 0x10;
+ sense_a = nvif_rd08(device, NV_PRMCIO_INP0) & 0x10;
/* take another reading until it agrees with sense_a... */
do {
udelay(100);
- sense_b = nv_rd08(device, NV_PRMCIO_INP0) & 0x10;
+ sense_b = nvif_rd08(device, NV_PRMCIO_INP0) & 0x10;
if (sense_a != sense_b) {
sense_b_prime =
- nv_rd08(device, NV_PRMCIO_INP0) & 0x10;
+ nvif_rd08(device, NV_PRMCIO_INP0) & 0x10;
if (sense_b == sense_b_prime) {
/* ... unless two consecutive subsequent
* samples agree; sense_a is replaced */
@@ -128,7 +128,7 @@ static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
struct drm_connector *connector)
{
struct drm_device *dev = encoder->dev;
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
struct nouveau_drm *drm = nouveau_drm(dev);
uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode;
uint8_t saved_palette0[3], saved_palette_mask;
@@ -164,11 +164,11 @@ static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX);
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0);
- nv_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS, 0x0);
+ nvif_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS, 0x0);
for (i = 0; i < 3; i++)
- saved_palette0[i] = nv_rd08(device, NV_PRMDIO_PALETTE_DATA);
- saved_palette_mask = nv_rd08(device, NV_PRMDIO_PIXEL_MASK);
- nv_wr08(device, NV_PRMDIO_PIXEL_MASK, 0);
+ saved_palette0[i] = nvif_rd08(device, NV_PRMDIO_PALETTE_DATA);
+ saved_palette_mask = nvif_rd08(device, NV_PRMDIO_PIXEL_MASK);
+ nvif_wr08(device, NV_PRMDIO_PIXEL_MASK, 0);
saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL,
@@ -181,11 +181,11 @@ static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
do {
bool sense_pair[2];
- nv_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
- nv_wr08(device, NV_PRMDIO_PALETTE_DATA, 0);
- nv_wr08(device, NV_PRMDIO_PALETTE_DATA, 0);
+ nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
+ nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, 0);
+ nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, 0);
/* testing blue won't find monochrome monitors. I don't care */
- nv_wr08(device, NV_PRMDIO_PALETTE_DATA, blue);
+ nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, blue);
i = 0;
/* take sample pairs until both samples in the pair agree */
@@ -208,11 +208,11 @@ static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
} while (++blue < 0x18 && sense);
out:
- nv_wr08(device, NV_PRMDIO_PIXEL_MASK, saved_palette_mask);
+ nvif_wr08(device, NV_PRMDIO_PIXEL_MASK, saved_palette_mask);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl);
- nv_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
+ nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
for (i = 0; i < 3; i++)
- nv_wr08(device, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]);
+ nvif_wr08(device, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl);
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
@@ -231,8 +231,8 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nouveau_dev(dev);
- struct nouveau_gpio *gpio = nouveau_gpio(device);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
+ struct nouveau_gpio *gpio = nvkm_gpio(device);
struct dcb_output *dcb = nouveau_encoder(encoder)->dcb;
uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder);
uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput,
@@ -256,12 +256,12 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset,
saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
- saved_powerctrl_2 = nv_rd32(device, NV_PBUS_POWERCTRL_2);
+ saved_powerctrl_2 = nvif_rd32(device, NV_PBUS_POWERCTRL_2);
- nv_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff);
+ nvif_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff);
if (regoffset == 0x68) {
- saved_powerctrl_4 = nv_rd32(device, NV_PBUS_POWERCTRL_4);
- nv_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
+ saved_powerctrl_4 = nvif_rd32(device, NV_PBUS_POWERCTRL_4);
+ nvif_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
}
if (gpio) {
@@ -283,7 +283,7 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
/* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
routput = (saved_routput & 0xfffffece) | head << 8;
- if (nv_device(drm->device)->card_type >= NV_40) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_CURIE) {
if (dcb->type == DCB_OUTPUT_TV)
routput |= 0x1a << 16;
else
@@ -316,8 +316,8 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl);
if (regoffset == 0x68)
- nv_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
- nv_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
+ nvif_wr32(device, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
+ nvif_wr32(device, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
if (gpio) {
gpio->set(gpio, 0, DCB_GPIO_TVDAC1, 0xff, saved_gpio1);
@@ -398,7 +398,7 @@ static void nv04_dac_mode_set(struct drm_encoder *encoder,
}
/* This could use refinement for flatpanels, but it should work this way */
- if (nv_device(drm->device)->chipset < 0x44)
+ if (drm->device.info.chipset < 0x44)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
else
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
diff --git a/drivers/gpu/drm/nouveau/dispnv04/dfp.c b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
index e57babb206d3..42a5435259f7 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/dfp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
@@ -281,7 +281,7 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct nv04_crtc_reg *regp = &nv04_display(dev)->mode_reg.crtc_reg[nv_crtc->index];
@@ -335,7 +335,7 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder,
regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE;
else /* gpu needs to scale */
regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE;
- if (nv_rd32(device, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT)
+ if (nvif_rd32(device, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT)
regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP &&
output_mode->clock > 165000)
@@ -416,7 +416,7 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder,
if ((nv_connector->dithering_mode == DITHERING_MODE_ON) ||
(nv_connector->dithering_mode == DITHERING_MODE_AUTO &&
encoder->crtc->primary->fb->depth > connector->display_info.bpc * 3)) {
- if (nv_device(drm->device)->chipset == 0x11)
+ if (drm->device.info.chipset == 0x11)
regp->dither = savep->dither | 0x00010000;
else {
int i;
@@ -427,7 +427,7 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder,
}
}
} else {
- if (nv_device(drm->device)->chipset != 0x11) {
+ if (drm->device.info.chipset != 0x11) {
/* reset them */
int i;
for (i = 0; i < 3; i++) {
@@ -463,7 +463,7 @@ static void nv04_dfp_commit(struct drm_encoder *encoder)
NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
/* This could use refinement for flatpanels, but it should work this way */
- if (nv_device(drm->device)->chipset < 0x44)
+ if (drm->device.info.chipset < 0x44)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
else
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
@@ -485,7 +485,7 @@ static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode)
{
#ifdef __powerpc__
struct drm_device *dev = encoder->dev;
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
/* BIOS scripts usually take care of the backlight, thanks
* Apple for your consistency.
@@ -623,7 +623,7 @@ static void nv04_tmds_slave_init(struct drm_encoder *encoder)
struct drm_device *dev = encoder->dev;
struct dcb_output *dcb = nouveau_encoder(encoder)->dcb;
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
+ struct nouveau_i2c *i2c = nvkm_i2c(&drm->device);
struct nouveau_i2c_port *port = i2c->find(i2c, 2);
struct nouveau_i2c_board_info info[] = {
{
diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c
index 4342fdaee707..3d0afa1c6cff 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c
@@ -22,9 +22,6 @@
* Author: Ben Skeggs
*/
-#include <core/object.h>
-#include <core/class.h>
-
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
@@ -34,8 +31,6 @@
#include "nouveau_encoder.h"
#include "nouveau_connector.h"
-#include <subdev/i2c.h>
-
int
nv04_display_early_init(struct drm_device *dev)
{
@@ -58,7 +53,7 @@ int
nv04_display_create(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
+ struct nouveau_i2c *i2c = nvkm_i2c(&drm->device);
struct dcb_table *dcb = &drm->vbios.dcb;
struct drm_connector *connector, *ct;
struct drm_encoder *encoder;
@@ -70,6 +65,8 @@ nv04_display_create(struct drm_device *dev)
if (!disp)
return -ENOMEM;
+ nvif_object_map(nvif_object(&drm->device));
+
nouveau_display(dev)->priv = disp;
nouveau_display(dev)->dtor = nv04_display_destroy;
nouveau_display(dev)->init = nv04_display_init;
@@ -144,6 +141,7 @@ void
nv04_display_destroy(struct drm_device *dev)
{
struct nv04_display *disp = nv04_display(dev);
+ struct nouveau_drm *drm = nouveau_drm(dev);
struct drm_encoder *encoder;
struct drm_crtc *crtc;
@@ -170,6 +168,8 @@ nv04_display_destroy(struct drm_device *dev)
nouveau_display(dev)->priv = NULL;
kfree(disp);
+
+ nvif_object_unmap(nvif_object(&drm->device));
}
int
diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.h b/drivers/gpu/drm/nouveau/dispnv04/disp.h
index 4245fc3dab70..17b899d9aba3 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/disp.h
+++ b/drivers/gpu/drm/nouveau/dispnv04/disp.h
@@ -131,7 +131,7 @@ nv_two_heads(struct drm_device *dev)
struct nouveau_drm *drm = nouveau_drm(dev);
const int impl = dev->pdev->device & 0x0ff0;
- if (nv_device(drm->device)->card_type >= NV_10 && impl != 0x0100 &&
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS && impl != 0x0100 &&
impl != 0x0150 && impl != 0x01a0 && impl != 0x0200)
return true;
@@ -150,7 +150,7 @@ nv_two_reg_pll(struct drm_device *dev)
struct nouveau_drm *drm = nouveau_drm(dev);
const int impl = dev->pdev->device & 0x0ff0;
- if (impl == 0x0310 || impl == 0x0340 || nv_device(drm->device)->card_type >= NV_40)
+ if (impl == 0x0310 || impl == 0x0340 || drm->device.info.family >= NV_DEVICE_INFO_V0_CURIE)
return true;
return false;
}
@@ -171,8 +171,8 @@ static inline void
nouveau_bios_run_init_table(struct drm_device *dev, u16 table,
struct dcb_output *outp, int crtc)
{
- struct nouveau_device *device = nouveau_dev(dev);
- struct nouveau_bios *bios = nouveau_bios(device);
+ struct nouveau_drm *drm = nouveau_drm(dev);
+ struct nouveau_bios *bios = nvkm_bios(&drm->device);
struct nvbios_init init = {
.subdev = nv_subdev(bios),
.bios = bios,
diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.c b/drivers/gpu/drm/nouveau/dispnv04/hw.c
index aca76af115b3..3d4c19300768 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/hw.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/hw.c
@@ -27,9 +27,6 @@
#include "hw.h"
#include <subdev/bios/pll.h>
-#include <subdev/fb.h>
-#include <subdev/clock.h>
-#include <subdev/timer.h>
#define CHIPSET_NFORCE 0x01a0
#define CHIPSET_NFORCE2 0x01f0
@@ -92,7 +89,7 @@ NVSetOwner(struct drm_device *dev, int owner)
if (owner == 1)
owner *= 3;
- if (nv_device(drm->device)->chipset == 0x11) {
+ if (drm->device.info.chipset == 0x11) {
/* This might seem stupid, but the blob does it and
* omitting it often locks the system up.
*/
@@ -103,7 +100,7 @@ NVSetOwner(struct drm_device *dev, int owner)
/* CR44 is always changed on CRTC0 */
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, owner);
- if (nv_device(drm->device)->chipset == 0x11) { /* set me harder */
+ if (drm->device.info.chipset == 0x11) { /* set me harder */
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
}
@@ -152,7 +149,7 @@ nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1,
pllvals->NM1 = pll1 & 0xffff;
if (nv_two_reg_pll(dev) && pll2 & NV31_RAMDAC_ENABLE_VCO2)
pllvals->NM2 = pll2 & 0xffff;
- else if (nv_device(drm->device)->chipset == 0x30 || nv_device(drm->device)->chipset == 0x35) {
+ else if (drm->device.info.chipset == 0x30 || drm->device.info.chipset == 0x35) {
pllvals->M1 &= 0xf; /* only 4 bits */
if (pll1 & NV30_RAMDAC_ENABLE_VCO2) {
pllvals->M2 = (pll1 >> 4) & 0x7;
@@ -168,8 +165,8 @@ nouveau_hw_get_pllvals(struct drm_device *dev, enum nvbios_pll_type plltype,
struct nouveau_pll_vals *pllvals)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
- struct nouveau_bios *bios = nouveau_bios(device);
+ struct nvif_device *device = &drm->device;
+ struct nouveau_bios *bios = nvkm_bios(device);
uint32_t reg1, pll1, pll2 = 0;
struct nvbios_pll pll_lim;
int ret;
@@ -178,16 +175,16 @@ nouveau_hw_get_pllvals(struct drm_device *dev, enum nvbios_pll_type plltype,
if (ret || !(reg1 = pll_lim.reg))
return -ENOENT;
- pll1 = nv_rd32(device, reg1);
+ pll1 = nvif_rd32(device, reg1);
if (reg1 <= 0x405c)
- pll2 = nv_rd32(device, reg1 + 4);
+ pll2 = nvif_rd32(device, reg1 + 4);
else if (nv_two_reg_pll(dev)) {
uint32_t reg2 = reg1 + (reg1 == NV_RAMDAC_VPLL2 ? 0x5c : 0x70);
- pll2 = nv_rd32(device, reg2);
+ pll2 = nvif_rd32(device, reg2);
}
- if (nv_device(drm->device)->card_type == 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) {
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CELSIUS && reg1 >= NV_PRAMDAC_VPLL_COEFF) {
uint32_t ramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580);
/* check whether vpll has been forced into single stage mode */
@@ -255,9 +252,9 @@ nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head)
*/
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
- struct nouveau_clock *clk = nouveau_clock(device);
- struct nouveau_bios *bios = nouveau_bios(device);
+ struct nvif_device *device = &drm->device;
+ struct nouveau_clock *clk = nvkm_clock(device);
+ struct nouveau_bios *bios = nvkm_bios(device);
struct nvbios_pll pll_lim;
struct nouveau_pll_vals pv;
enum nvbios_pll_type pll = head ? PLL_VPLL1 : PLL_VPLL0;
@@ -394,21 +391,21 @@ nv_save_state_ramdac(struct drm_device *dev, int head,
struct nv04_crtc_reg *regp = &state->crtc_reg[head];
int i;
- if (nv_device(drm->device)->card_type >= NV_10)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS)
regp->nv10_cursync = NVReadRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC);
nouveau_hw_get_pllvals(dev, head ? PLL_VPLL1 : PLL_VPLL0, &regp->pllvals);
state->pllsel = NVReadRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT);
if (nv_two_heads(dev))
state->sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
- if (nv_device(drm->device)->chipset == 0x11)
+ if (drm->device.info.chipset == 0x11)
regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11);
regp->ramdac_gen_ctrl = NVReadRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL);
if (nv_gf4_disp_arch(dev))
regp->ramdac_630 = NVReadRAMDAC(dev, head, NV_PRAMDAC_630);
- if (nv_device(drm->device)->chipset >= 0x30)
+ if (drm->device.info.chipset >= 0x30)
regp->ramdac_634 = NVReadRAMDAC(dev, head, NV_PRAMDAC_634);
regp->tv_setup = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP);
@@ -450,7 +447,7 @@ nv_save_state_ramdac(struct drm_device *dev, int head,
if (nv_gf4_disp_arch(dev))
regp->ramdac_8c0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_8C0);
- if (nv_device(drm->device)->card_type == NV_40) {
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) {
regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20);
regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24);
regp->ramdac_a34 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A34);
@@ -466,26 +463,26 @@ nv_load_state_ramdac(struct drm_device *dev, int head,
struct nv04_mode_state *state)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_clock *clk = nouveau_clock(drm->device);
+ struct nouveau_clock *clk = nvkm_clock(&drm->device);
struct nv04_crtc_reg *regp = &state->crtc_reg[head];
uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF;
int i;
- if (nv_device(drm->device)->card_type >= NV_10)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS)
NVWriteRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC, regp->nv10_cursync);
clk->pll_prog(clk, pllreg, &regp->pllvals);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
if (nv_two_heads(dev))
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, state->sel_clk);
- if (nv_device(drm->device)->chipset == 0x11)
+ if (drm->device.info.chipset == 0x11)
NVWriteRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11, regp->dither);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL, regp->ramdac_gen_ctrl);
if (nv_gf4_disp_arch(dev))
NVWriteRAMDAC(dev, head, NV_PRAMDAC_630, regp->ramdac_630);
- if (nv_device(drm->device)->chipset >= 0x30)
+ if (drm->device.info.chipset >= 0x30)
NVWriteRAMDAC(dev, head, NV_PRAMDAC_634, regp->ramdac_634);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, regp->tv_setup);
@@ -522,7 +519,7 @@ nv_load_state_ramdac(struct drm_device *dev, int head,
if (nv_gf4_disp_arch(dev))
NVWriteRAMDAC(dev, head, NV_PRAMDAC_8C0, regp->ramdac_8c0);
- if (nv_device(drm->device)->card_type == NV_40) {
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) {
NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_A34, regp->ramdac_a34);
@@ -603,10 +600,10 @@ nv_save_state_ext(struct drm_device *dev, int head,
rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_21);
- if (nv_device(drm->device)->card_type >= NV_20)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_KELVIN)
rd_cio_state(dev, head, regp, NV_CIO_CRE_47);
- if (nv_device(drm->device)->card_type >= NV_30)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE)
rd_cio_state(dev, head, regp, 0x9f);
rd_cio_state(dev, head, regp, NV_CIO_CRE_49);
@@ -615,14 +612,14 @@ nv_save_state_ext(struct drm_device *dev, int head,
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX);
- if (nv_device(drm->device)->card_type >= NV_10) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) {
regp->crtc_830 = NVReadCRTC(dev, head, NV_PCRTC_830);
regp->crtc_834 = NVReadCRTC(dev, head, NV_PCRTC_834);
- if (nv_device(drm->device)->card_type >= NV_30)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE)
regp->gpio_ext = NVReadCRTC(dev, head, NV_PCRTC_GPIO_EXT);
- if (nv_device(drm->device)->card_type == NV_40)
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE)
regp->crtc_850 = NVReadCRTC(dev, head, NV_PCRTC_850);
if (nv_two_heads(dev))
@@ -634,7 +631,7 @@ nv_save_state_ext(struct drm_device *dev, int head,
rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX);
- if (nv_device(drm->device)->card_type >= NV_10) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) {
rd_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_CSB);
rd_cio_state(dev, head, regp, NV_CIO_CRE_4B);
@@ -663,14 +660,13 @@ nv_load_state_ext(struct drm_device *dev, int head,
struct nv04_mode_state *state)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
- struct nouveau_timer *ptimer = nouveau_timer(device);
- struct nouveau_fb *pfb = nouveau_fb(device);
+ struct nvif_device *device = &drm->device;
+ struct nouveau_timer *ptimer = nvkm_timer(device);
struct nv04_crtc_reg *regp = &state->crtc_reg[head];
uint32_t reg900;
int i;
- if (nv_device(drm->device)->card_type >= NV_10) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) {
if (nv_two_heads(dev))
/* setting ENGINE_CTRL (EC) *must* come before
* CIO_CRE_LCD, as writing CRE_LCD sets bits 16 & 17 in
@@ -678,24 +674,24 @@ nv_load_state_ext(struct drm_device *dev, int head,
*/
NVWriteCRTC(dev, head, NV_PCRTC_ENGINE_CTRL, regp->crtc_eng_ctrl);
- nv_wr32(device, NV_PVIDEO_STOP, 1);
- nv_wr32(device, NV_PVIDEO_INTR_EN, 0);
- nv_wr32(device, NV_PVIDEO_OFFSET_BUFF(0), 0);
- nv_wr32(device, NV_PVIDEO_OFFSET_BUFF(1), 0);
- nv_wr32(device, NV_PVIDEO_LIMIT(0), pfb->ram->size - 1);
- nv_wr32(device, NV_PVIDEO_LIMIT(1), pfb->ram->size - 1);
- nv_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(0), pfb->ram->size - 1);
- nv_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(1), pfb->ram->size - 1);
- nv_wr32(device, NV_PBUS_POWERCTRL_2, 0);
+ nvif_wr32(device, NV_PVIDEO_STOP, 1);
+ nvif_wr32(device, NV_PVIDEO_INTR_EN, 0);
+ nvif_wr32(device, NV_PVIDEO_OFFSET_BUFF(0), 0);
+ nvif_wr32(device, NV_PVIDEO_OFFSET_BUFF(1), 0);
+ nvif_wr32(device, NV_PVIDEO_LIMIT(0), device->info.ram_size - 1);
+ nvif_wr32(device, NV_PVIDEO_LIMIT(1), device->info.ram_size - 1);
+ nvif_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(0), device->info.ram_size - 1);
+ nvif_wr32(device, NV_PVIDEO_UVPLANE_LIMIT(1), device->info.ram_size - 1);
+ nvif_wr32(device, NV_PBUS_POWERCTRL_2, 0);
NVWriteCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG, regp->cursor_cfg);
NVWriteCRTC(dev, head, NV_PCRTC_830, regp->crtc_830);
NVWriteCRTC(dev, head, NV_PCRTC_834, regp->crtc_834);
- if (nv_device(drm->device)->card_type >= NV_30)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE)
NVWriteCRTC(dev, head, NV_PCRTC_GPIO_EXT, regp->gpio_ext);
- if (nv_device(drm->device)->card_type == NV_40) {
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE) {
NVWriteCRTC(dev, head, NV_PCRTC_850, regp->crtc_850);
reg900 = NVReadRAMDAC(dev, head, NV_PRAMDAC_900);
@@ -718,23 +714,23 @@ nv_load_state_ext(struct drm_device *dev, int head,
wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
- if (nv_device(drm->device)->card_type >= NV_20)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_KELVIN)
wr_cio_state(dev, head, regp, NV_CIO_CRE_47);
- if (nv_device(drm->device)->card_type >= NV_30)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE)
wr_cio_state(dev, head, regp, 0x9f);
wr_cio_state(dev, head, regp, NV_CIO_CRE_49);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
- if (nv_device(drm->device)->card_type == NV_40)
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE)
nv_fix_nv40_hw_cursor(dev, head);
wr_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX);
- if (nv_device(drm->device)->card_type >= NV_10) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) {
wr_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_CSB);
wr_cio_state(dev, head, regp, NV_CIO_CRE_4B);
@@ -742,7 +738,7 @@ nv_load_state_ext(struct drm_device *dev, int head,
}
/* NV11 and NV20 stop at 0x52. */
if (nv_gf4_disp_arch(dev)) {
- if (nv_device(drm->device)->card_type < NV_20) {
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_KELVIN) {
/* Not waiting for vertical retrace before modifying
CRE_53/CRE_54 causes lockups. */
nouveau_timer_wait_eq(ptimer, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x8);
@@ -769,15 +765,15 @@ static void
nv_save_state_palette(struct drm_device *dev, int head,
struct nv04_mode_state *state)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
int head_offset = head * NV_PRMDIO_SIZE, i;
- nv_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset,
+ nvif_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset,
NV_PRMDIO_PIXEL_MASK_MASK);
- nv_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS + head_offset, 0x0);
+ nvif_wr08(device, NV_PRMDIO_READ_MODE_ADDRESS + head_offset, 0x0);
for (i = 0; i < 768; i++) {
- state->crtc_reg[head].DAC[i] = nv_rd08(device,
+ state->crtc_reg[head].DAC[i] = nvif_rd08(device,
NV_PRMDIO_PALETTE_DATA + head_offset);
}
@@ -788,15 +784,15 @@ void
nouveau_hw_load_state_palette(struct drm_device *dev, int head,
struct nv04_mode_state *state)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
int head_offset = head * NV_PRMDIO_SIZE, i;
- nv_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset,
+ nvif_wr08(device, NV_PRMDIO_PIXEL_MASK + head_offset,
NV_PRMDIO_PIXEL_MASK_MASK);
- nv_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS + head_offset, 0x0);
+ nvif_wr08(device, NV_PRMDIO_WRITE_MODE_ADDRESS + head_offset, 0x0);
for (i = 0; i < 768; i++) {
- nv_wr08(device, NV_PRMDIO_PALETTE_DATA + head_offset,
+ nvif_wr08(device, NV_PRMDIO_PALETTE_DATA + head_offset,
state->crtc_reg[head].DAC[i]);
}
@@ -808,7 +804,7 @@ void nouveau_hw_save_state(struct drm_device *dev, int head,
{
struct nouveau_drm *drm = nouveau_drm(dev);
- if (nv_device(drm->device)->chipset == 0x11)
+ if (drm->device.info.chipset == 0x11)
/* NB: no attempt is made to restore the bad pll later on */
nouveau_hw_fix_bad_vpll(dev, head);
nv_save_state_ramdac(dev, head, state);
diff --git a/drivers/gpu/drm/nouveau/dispnv04/hw.h b/drivers/gpu/drm/nouveau/dispnv04/hw.h
index eeb70d912d99..7f53c571f31f 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/hw.h
+++ b/drivers/gpu/drm/nouveau/dispnv04/hw.h
@@ -60,41 +60,41 @@ extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp,
static inline uint32_t NVReadCRTC(struct drm_device *dev,
int head, uint32_t reg)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
uint32_t val;
if (head)
reg += NV_PCRTC0_SIZE;
- val = nv_rd32(device, reg);
+ val = nvif_rd32(device, reg);
return val;
}
static inline void NVWriteCRTC(struct drm_device *dev,
int head, uint32_t reg, uint32_t val)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
if (head)
reg += NV_PCRTC0_SIZE;
- nv_wr32(device, reg, val);
+ nvif_wr32(device, reg, val);
}
static inline uint32_t NVReadRAMDAC(struct drm_device *dev,
int head, uint32_t reg)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
uint32_t val;
if (head)
reg += NV_PRAMDAC0_SIZE;
- val = nv_rd32(device, reg);
+ val = nvif_rd32(device, reg);
return val;
}
static inline void NVWriteRAMDAC(struct drm_device *dev,
int head, uint32_t reg, uint32_t val)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
if (head)
reg += NV_PRAMDAC0_SIZE;
- nv_wr32(device, reg, val);
+ nvif_wr32(device, reg, val);
}
static inline uint8_t nv_read_tmds(struct drm_device *dev,
@@ -120,18 +120,18 @@ static inline void nv_write_tmds(struct drm_device *dev,
static inline void NVWriteVgaCrtc(struct drm_device *dev,
int head, uint8_t index, uint8_t value)
{
- struct nouveau_device *device = nouveau_dev(dev);
- nv_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
- nv_wr08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
+ nvif_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
+ nvif_wr08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value);
}
static inline uint8_t NVReadVgaCrtc(struct drm_device *dev,
int head, uint8_t index)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
uint8_t val;
- nv_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
- val = nv_rd08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE);
+ nvif_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
+ val = nvif_rd08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE);
return val;
}
@@ -165,74 +165,74 @@ static inline uint8_t NVReadVgaCrtc5758(struct drm_device *dev, int head, uint8_
static inline uint8_t NVReadPRMVIO(struct drm_device *dev,
int head, uint32_t reg)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
struct nouveau_drm *drm = nouveau_drm(dev);
uint8_t val;
/* Only NV4x have two pvio ranges; other twoHeads cards MUST call
* NVSetOwner for the relevant head to be programmed */
- if (head && nv_device(drm->device)->card_type == NV_40)
+ if (head && drm->device.info.family == NV_DEVICE_INFO_V0_CURIE)
reg += NV_PRMVIO_SIZE;
- val = nv_rd08(device, reg);
+ val = nvif_rd08(device, reg);
return val;
}
static inline void NVWritePRMVIO(struct drm_device *dev,
int head, uint32_t reg, uint8_t value)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
struct nouveau_drm *drm = nouveau_drm(dev);
/* Only NV4x have two pvio ranges; other twoHeads cards MUST call
* NVSetOwner for the relevant head to be programmed */
- if (head && nv_device(drm->device)->card_type == NV_40)
+ if (head && drm->device.info.family == NV_DEVICE_INFO_V0_CURIE)
reg += NV_PRMVIO_SIZE;
- nv_wr08(device, reg, value);
+ nvif_wr08(device, reg, value);
}
static inline void NVSetEnablePalette(struct drm_device *dev, int head, bool enable)
{
- struct nouveau_device *device = nouveau_dev(dev);
- nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
- nv_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
+ nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
+ nvif_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20);
}
static inline bool NVGetEnablePalette(struct drm_device *dev, int head)
{
- struct nouveau_device *device = nouveau_dev(dev);
- nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
- return !(nv_rd08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
+ nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
+ return !(nvif_rd08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20);
}
static inline void NVWriteVgaAttr(struct drm_device *dev,
int head, uint8_t index, uint8_t value)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
if (NVGetEnablePalette(dev, head))
index &= ~0x20;
else
index |= 0x20;
- nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
- nv_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
- nv_wr08(device, NV_PRMCIO_AR__WRITE + head * NV_PRMCIO_SIZE, value);
+ nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
+ nvif_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
+ nvif_wr08(device, NV_PRMCIO_AR__WRITE + head * NV_PRMCIO_SIZE, value);
}
static inline uint8_t NVReadVgaAttr(struct drm_device *dev,
int head, uint8_t index)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
uint8_t val;
if (NVGetEnablePalette(dev, head))
index &= ~0x20;
else
index |= 0x20;
- nv_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
- nv_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
- val = nv_rd08(device, NV_PRMCIO_AR__READ + head * NV_PRMCIO_SIZE);
+ nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
+ nvif_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
+ val = nvif_rd08(device, NV_PRMCIO_AR__READ + head * NV_PRMCIO_SIZE);
return val;
}
@@ -259,11 +259,11 @@ static inline void NVVgaProtect(struct drm_device *dev, int head, bool protect)
static inline bool
nv_heads_tied(struct drm_device *dev)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
struct nouveau_drm *drm = nouveau_drm(dev);
- if (nv_device(drm->device)->chipset == 0x11)
- return !!(nv_rd32(device, NV_PBUS_DEBUG_1) & (1 << 28));
+ if (drm->device.info.chipset == 0x11)
+ return !!(nvif_rd32(device, NV_PBUS_DEBUG_1) & (1 << 28));
return NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44) & 0x4;
}
@@ -318,7 +318,7 @@ NVLockVgaCrtcs(struct drm_device *dev, bool lock)
NVWriteVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX,
lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE);
/* NV11 has independently lockable extended crtcs, except when tied */
- if (nv_device(drm->device)->chipset == 0x11 && !nv_heads_tied(dev))
+ if (drm->device.info.chipset == 0x11 && !nv_heads_tied(dev))
NVWriteVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX,
lock ? NV_CIO_SR_LOCK_VALUE :
NV_CIO_SR_UNLOCK_RW_VALUE);
@@ -335,7 +335,7 @@ static inline int nv_cursor_width(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- return nv_device(drm->device)->card_type >= NV_10 ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE;
+ return drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE;
}
static inline void
@@ -357,7 +357,7 @@ nv_set_crtc_base(struct drm_device *dev, int head, uint32_t offset)
NVWriteCRTC(dev, head, NV_PCRTC_START, offset);
- if (nv_device(drm->device)->card_type == NV_04) {
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_TNT) {
/*
* Hilarious, the 24th bit doesn't want to stick to
* PCRTC_START...
@@ -382,7 +382,7 @@ nv_show_cursor(struct drm_device *dev, int head, bool show)
*curctl1 &= ~MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE);
NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HCUR_ADDR1_INDEX, *curctl1);
- if (nv_device(drm->device)->card_type == NV_40)
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE)
nv_fix_nv40_hw_cursor(dev, head);
}
@@ -398,7 +398,7 @@ nv_pitch_align(struct drm_device *dev, uint32_t width, int bpp)
bpp = 8;
/* Alignment requirements taken from the Haiku driver */
- if (nv_device(drm->device)->card_type == NV_04)
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_TNT)
mask = 128 / bpp - 1;
else
mask = 512 / bpp - 1;
diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
index ab03f7719d2d..1e9056a8df94 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
@@ -96,8 +96,9 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{
- struct nouveau_device *dev = nouveau_dev(plane->dev);
- struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
+ struct nvif_device *dev = &nouveau_drm(plane->dev)->device;
+ struct nouveau_plane *nv_plane =
+ container_of(plane, struct nouveau_plane, base);
struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb);
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct nouveau_bo *cur = nv_plane->cur;
@@ -117,7 +118,7 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
if (format > 0xffff)
return -ERANGE;
- if (dev->chipset >= 0x30) {
+ if (dev->info.chipset >= 0x30) {
if (crtc_w < (src_w >> 1) || crtc_h < (src_h >> 1))
return -ERANGE;
} else {
@@ -131,17 +132,17 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
nv_plane->cur = nv_fb->nvbo;
- nv_mask(dev, NV_PCRTC_ENGINE_CTRL + soff, NV_CRTC_FSEL_OVERLAY, NV_CRTC_FSEL_OVERLAY);
- nv_mask(dev, NV_PCRTC_ENGINE_CTRL + soff2, NV_CRTC_FSEL_OVERLAY, 0);
+ nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff, NV_CRTC_FSEL_OVERLAY, NV_CRTC_FSEL_OVERLAY);
+ nvif_mask(dev, NV_PCRTC_ENGINE_CTRL + soff2, NV_CRTC_FSEL_OVERLAY, 0);
- nv_wr32(dev, NV_PVIDEO_BASE(flip), 0);
- nv_wr32(dev, NV_PVIDEO_OFFSET_BUFF(flip), nv_fb->nvbo->bo.offset);
- nv_wr32(dev, NV_PVIDEO_SIZE_IN(flip), src_h << 16 | src_w);
- nv_wr32(dev, NV_PVIDEO_POINT_IN(flip), src_y << 16 | src_x);
- nv_wr32(dev, NV_PVIDEO_DS_DX(flip), (src_w << 20) / crtc_w);
- nv_wr32(dev, NV_PVIDEO_DT_DY(flip), (src_h << 20) / crtc_h);
- nv_wr32(dev, NV_PVIDEO_POINT_OUT(flip), crtc_y << 16 | crtc_x);
- nv_wr32(dev, NV_PVIDEO_SIZE_OUT(flip), crtc_h << 16 | crtc_w);
+ nvif_wr32(dev, NV_PVIDEO_BASE(flip), 0);
+ nvif_wr32(dev, NV_PVIDEO_OFFSET_BUFF(flip), nv_fb->nvbo->bo.offset);
+ nvif_wr32(dev, NV_PVIDEO_SIZE_IN(flip), src_h << 16 | src_w);
+ nvif_wr32(dev, NV_PVIDEO_POINT_IN(flip), src_y << 16 | src_x);
+ nvif_wr32(dev, NV_PVIDEO_DS_DX(flip), (src_w << 20) / crtc_w);
+ nvif_wr32(dev, NV_PVIDEO_DT_DY(flip), (src_h << 20) / crtc_h);
+ nvif_wr32(dev, NV_PVIDEO_POINT_OUT(flip), crtc_y << 16 | crtc_x);
+ nvif_wr32(dev, NV_PVIDEO_SIZE_OUT(flip), crtc_h << 16 | crtc_w);
if (fb->pixel_format != DRM_FORMAT_UYVY)
format |= NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8;
@@ -153,14 +154,14 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY;
if (fb->pixel_format == DRM_FORMAT_NV12) {
- nv_wr32(dev, NV_PVIDEO_UVPLANE_BASE(flip), 0);
- nv_wr32(dev, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip),
+ nvif_wr32(dev, NV_PVIDEO_UVPLANE_BASE(flip), 0);
+ nvif_wr32(dev, NV_PVIDEO_UVPLANE_OFFSET_BUFF(flip),
nv_fb->nvbo->bo.offset + fb->offsets[1]);
}
- nv_wr32(dev, NV_PVIDEO_FORMAT(flip), format);
- nv_wr32(dev, NV_PVIDEO_STOP, 0);
+ nvif_wr32(dev, NV_PVIDEO_FORMAT(flip), format);
+ nvif_wr32(dev, NV_PVIDEO_STOP, 0);
/* TODO: wait for vblank? */
- nv_wr32(dev, NV_PVIDEO_BUFFER, flip ? 0x10 : 0x1);
+ nvif_wr32(dev, NV_PVIDEO_BUFFER, flip ? 0x10 : 0x1);
nv_plane->flip = !flip;
if (cur)
@@ -172,10 +173,11 @@ nv10_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
static int
nv10_disable_plane(struct drm_plane *plane)
{
- struct nouveau_device *dev = nouveau_dev(plane->dev);
- struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
+ struct nvif_device *dev = &nouveau_drm(plane->dev)->device;
+ struct nouveau_plane *nv_plane =
+ container_of(plane, struct nouveau_plane, base);
- nv_wr32(dev, NV_PVIDEO_STOP, 1);
+ nvif_wr32(dev, NV_PVIDEO_STOP, 1);
if (nv_plane->cur) {
nouveau_bo_unpin(nv_plane->cur);
nv_plane->cur = NULL;
@@ -195,24 +197,24 @@ nv_destroy_plane(struct drm_plane *plane)
static void
nv10_set_params(struct nouveau_plane *plane)
{
- struct nouveau_device *dev = nouveau_dev(plane->base.dev);
+ struct nvif_device *dev = &nouveau_drm(plane->base.dev)->device;
u32 luma = (plane->brightness - 512) << 16 | plane->contrast;
u32 chroma = ((sin_mul(plane->hue, plane->saturation) & 0xffff) << 16) |
(cos_mul(plane->hue, plane->saturation) & 0xffff);
u32 format = 0;
- nv_wr32(dev, NV_PVIDEO_LUMINANCE(0), luma);
- nv_wr32(dev, NV_PVIDEO_LUMINANCE(1), luma);
- nv_wr32(dev, NV_PVIDEO_CHROMINANCE(0), chroma);
- nv_wr32(dev, NV_PVIDEO_CHROMINANCE(1), chroma);
- nv_wr32(dev, NV_PVIDEO_COLOR_KEY, plane->colorkey & 0xffffff);
+ nvif_wr32(dev, NV_PVIDEO_LUMINANCE(0), luma);
+ nvif_wr32(dev, NV_PVIDEO_LUMINANCE(1), luma);
+ nvif_wr32(dev, NV_PVIDEO_CHROMINANCE(0), chroma);
+ nvif_wr32(dev, NV_PVIDEO_CHROMINANCE(1), chroma);
+ nvif_wr32(dev, NV_PVIDEO_COLOR_KEY, plane->colorkey & 0xffffff);
if (plane->cur) {
if (plane->iturbt_709)
format |= NV_PVIDEO_FORMAT_MATRIX_ITURBT709;
if (plane->colorkey & (1 << 24))
format |= NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY;
- nv_mask(dev, NV_PVIDEO_FORMAT(plane->flip),
+ nvif_mask(dev, NV_PVIDEO_FORMAT(plane->flip),
NV_PVIDEO_FORMAT_MATRIX_ITURBT709 |
NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY,
format);
@@ -224,7 +226,8 @@ nv_set_property(struct drm_plane *plane,
struct drm_property *property,
uint64_t value)
{
- struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
+ struct nouveau_plane *nv_plane =
+ container_of(plane, struct nouveau_plane, base);
if (property == nv_plane->props.colorkey)
nv_plane->colorkey = value;
@@ -256,7 +259,7 @@ static const struct drm_plane_funcs nv10_plane_funcs = {
static void
nv10_overlay_init(struct drm_device *device)
{
- struct nouveau_device *dev = nouveau_dev(device);
+ struct nouveau_drm *drm = nouveau_drm(device);
struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL);
int num_formats = ARRAY_SIZE(formats);
int ret;
@@ -264,7 +267,7 @@ nv10_overlay_init(struct drm_device *device)
if (!plane)
return;
- switch (dev->chipset) {
+ switch (drm->device.info.chipset) {
case 0x10:
case 0x11:
case 0x15:
@@ -333,7 +336,7 @@ cleanup:
drm_plane_cleanup(&plane->base);
err:
kfree(plane);
- nv_error(dev, "Failed to create plane\n");
+ NV_ERROR(drm, "Failed to create plane\n");
}
static int
@@ -343,8 +346,9 @@ nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
{
- struct nouveau_device *dev = nouveau_dev(plane->dev);
- struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
+ struct nvif_device *dev = &nouveau_drm(plane->dev)->device;
+ struct nouveau_plane *nv_plane =
+ container_of(plane, struct nouveau_plane, base);
struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb);
struct nouveau_bo *cur = nv_plane->cur;
uint32_t overlay = 1;
@@ -375,43 +379,43 @@ nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
nv_plane->cur = nv_fb->nvbo;
- nv_wr32(dev, NV_PVIDEO_OE_STATE, 0);
- nv_wr32(dev, NV_PVIDEO_SU_STATE, 0);
- nv_wr32(dev, NV_PVIDEO_RM_STATE, 0);
+ nvif_wr32(dev, NV_PVIDEO_OE_STATE, 0);
+ nvif_wr32(dev, NV_PVIDEO_SU_STATE, 0);
+ nvif_wr32(dev, NV_PVIDEO_RM_STATE, 0);
for (i = 0; i < 2; i++) {
- nv_wr32(dev, NV_PVIDEO_BUFF0_START_ADDRESS + 4 * i,
+ nvif_wr32(dev, NV_PVIDEO_BUFF0_START_ADDRESS + 4 * i,
nv_fb->nvbo->bo.offset);
- nv_wr32(dev, NV_PVIDEO_BUFF0_PITCH_LENGTH + 4 * i, pitch);
- nv_wr32(dev, NV_PVIDEO_BUFF0_OFFSET + 4 * i, 0);
+ nvif_wr32(dev, NV_PVIDEO_BUFF0_PITCH_LENGTH + 4 * i, pitch);
+ nvif_wr32(dev, NV_PVIDEO_BUFF0_OFFSET + 4 * i, 0);
}
- nv_wr32(dev, NV_PVIDEO_WINDOW_START, crtc_y << 16 | crtc_x);
- nv_wr32(dev, NV_PVIDEO_WINDOW_SIZE, crtc_h << 16 | crtc_w);
- nv_wr32(dev, NV_PVIDEO_STEP_SIZE,
+ nvif_wr32(dev, NV_PVIDEO_WINDOW_START, crtc_y << 16 | crtc_x);
+ nvif_wr32(dev, NV_PVIDEO_WINDOW_SIZE, crtc_h << 16 | crtc_w);
+ nvif_wr32(dev, NV_PVIDEO_STEP_SIZE,
(uint32_t)(((src_h - 1) << 11) / (crtc_h - 1)) << 16 | (uint32_t)(((src_w - 1) << 11) / (crtc_w - 1)));
/* It should be possible to convert hue/contrast to this */
- nv_wr32(dev, NV_PVIDEO_RED_CSC_OFFSET, 0x69 - brightness);
- nv_wr32(dev, NV_PVIDEO_GREEN_CSC_OFFSET, 0x3e + brightness);
- nv_wr32(dev, NV_PVIDEO_BLUE_CSC_OFFSET, 0x89 - brightness);
- nv_wr32(dev, NV_PVIDEO_CSC_ADJUST, 0);
+ nvif_wr32(dev, NV_PVIDEO_RED_CSC_OFFSET, 0x69 - brightness);
+ nvif_wr32(dev, NV_PVIDEO_GREEN_CSC_OFFSET, 0x3e + brightness);
+ nvif_wr32(dev, NV_PVIDEO_BLUE_CSC_OFFSET, 0x89 - brightness);
+ nvif_wr32(dev, NV_PVIDEO_CSC_ADJUST, 0);
- nv_wr32(dev, NV_PVIDEO_CONTROL_Y, 0x001); /* (BLUR_ON, LINE_HALF) */
- nv_wr32(dev, NV_PVIDEO_CONTROL_X, 0x111); /* (WEIGHT_HEAVY, SHARPENING_ON, SMOOTHING_ON) */
+ nvif_wr32(dev, NV_PVIDEO_CONTROL_Y, 0x001); /* (BLUR_ON, LINE_HALF) */
+ nvif_wr32(dev, NV_PVIDEO_CONTROL_X, 0x111); /* (WEIGHT_HEAVY, SHARPENING_ON, SMOOTHING_ON) */
- nv_wr32(dev, NV_PVIDEO_FIFO_BURST_LENGTH, 0x03);
- nv_wr32(dev, NV_PVIDEO_FIFO_THRES_SIZE, 0x38);
+ nvif_wr32(dev, NV_PVIDEO_FIFO_BURST_LENGTH, 0x03);
+ nvif_wr32(dev, NV_PVIDEO_FIFO_THRES_SIZE, 0x38);
- nv_wr32(dev, NV_PVIDEO_KEY, nv_plane->colorkey);
+ nvif_wr32(dev, NV_PVIDEO_KEY, nv_plane->colorkey);
if (nv_plane->colorkey & (1 << 24))
overlay |= 0x10;
if (fb->pixel_format == DRM_FORMAT_YUYV)
overlay |= 0x100;
- nv_wr32(dev, NV_PVIDEO_OVERLAY, overlay);
+ nvif_wr32(dev, NV_PVIDEO_OVERLAY, overlay);
- nv_wr32(dev, NV_PVIDEO_SU_STATE, nv_rd32(dev, NV_PVIDEO_SU_STATE) ^ (1 << 16));
+ nvif_wr32(dev, NV_PVIDEO_SU_STATE, nvif_rd32(dev, NV_PVIDEO_SU_STATE) ^ (1 << 16));
if (cur)
nouveau_bo_unpin(cur);
@@ -422,13 +426,14 @@ nv04_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
static int
nv04_disable_plane(struct drm_plane *plane)
{
- struct nouveau_device *dev = nouveau_dev(plane->dev);
- struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
-
- nv_mask(dev, NV_PVIDEO_OVERLAY, 1, 0);
- nv_wr32(dev, NV_PVIDEO_OE_STATE, 0);
- nv_wr32(dev, NV_PVIDEO_SU_STATE, 0);
- nv_wr32(dev, NV_PVIDEO_RM_STATE, 0);
+ struct nvif_device *dev = &nouveau_drm(plane->dev)->device;
+ struct nouveau_plane *nv_plane =
+ container_of(plane, struct nouveau_plane, base);
+
+ nvif_mask(dev, NV_PVIDEO_OVERLAY, 1, 0);
+ nvif_wr32(dev, NV_PVIDEO_OE_STATE, 0);
+ nvif_wr32(dev, NV_PVIDEO_SU_STATE, 0);
+ nvif_wr32(dev, NV_PVIDEO_RM_STATE, 0);
if (nv_plane->cur) {
nouveau_bo_unpin(nv_plane->cur);
nv_plane->cur = NULL;
@@ -447,7 +452,7 @@ static const struct drm_plane_funcs nv04_plane_funcs = {
static void
nv04_overlay_init(struct drm_device *device)
{
- struct nouveau_device *dev = nouveau_dev(device);
+ struct nouveau_drm *drm = nouveau_drm(device);
struct nouveau_plane *plane = kzalloc(sizeof(struct nouveau_plane), GFP_KERNEL);
int ret;
@@ -483,15 +488,15 @@ cleanup:
drm_plane_cleanup(&plane->base);
err:
kfree(plane);
- nv_error(dev, "Failed to create plane\n");
+ NV_ERROR(drm, "Failed to create plane\n");
}
void
nouveau_overlay_init(struct drm_device *device)
{
- struct nouveau_device *dev = nouveau_dev(device);
- if (dev->chipset < 0x10)
+ struct nvif_device *dev = &nouveau_drm(device)->device;
+ if (dev->info.chipset < 0x10)
nv04_overlay_init(device);
- else if (dev->chipset <= 0x40)
+ else if (dev->info.chipset <= 0x40)
nv10_overlay_init(device);
}
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
index 8667620b703a..8061d8d0ce79 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
@@ -35,8 +35,6 @@
#include <drm/i2c/ch7006.h>
-#include <subdev/i2c.h>
-
static struct nouveau_i2c_board_info nv04_tv_encoder_info[] = {
{
{
@@ -56,7 +54,7 @@ static struct nouveau_i2c_board_info nv04_tv_encoder_info[] = {
int nv04_tv_identify(struct drm_device *dev, int i2c_index)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
+ struct nouveau_i2c *i2c = nvkm_i2c(&drm->device);
return i2c->identify(i2c, i2c_index, "TV encoder",
nv04_tv_encoder_info, NULL, NULL);
@@ -206,7 +204,7 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry)
struct drm_encoder *encoder;
struct drm_device *dev = connector->dev;
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
+ struct nouveau_i2c *i2c = nvkm_i2c(&drm->device);
struct nouveau_i2c_port *port = i2c->find(i2c, entry->i2c_index);
int type, ret;
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
index 195bd8e86c6a..72d2ab04db47 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
@@ -34,11 +34,6 @@
#include "hw.h"
#include "tvnv17.h"
-#include <core/device.h>
-
-#include <subdev/bios/gpio.h>
-#include <subdev/gpio.h>
-
MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
"\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n"
"\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n"
@@ -51,7 +46,7 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
+ struct nouveau_gpio *gpio = nvkm_gpio(&drm->device);
uint32_t testval, regoffset = nv04_dac_output_offset(encoder);
uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end,
fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c;
@@ -135,17 +130,17 @@ static bool
get_tv_detect_quirks(struct drm_device *dev, uint32_t *pin_mask)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_object *device = drm->device;
+ struct nvif_device *device = &drm->device;
/* Zotac FX5200 */
- if (nv_device_match(device, 0x0322, 0x19da, 0x1035) ||
- nv_device_match(device, 0x0322, 0x19da, 0x2035)) {
+ if (nv_device_match(nvkm_object(device), 0x0322, 0x19da, 0x1035) ||
+ nv_device_match(nvkm_object(device), 0x0322, 0x19da, 0x2035)) {
*pin_mask = 0xc;
return false;
}
/* MSI nForce2 IGP */
- if (nv_device_match(device, 0x01f0, 0x1462, 0x5710)) {
+ if (nv_device_match(nvkm_object(device), 0x01f0, 0x1462, 0x5710)) {
*pin_mask = 0xc;
return false;
}
@@ -167,8 +162,8 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector)
return connector_status_disconnected;
if (reliable) {
- if (nv_device(drm->device)->chipset == 0x42 ||
- nv_device(drm->device)->chipset == 0x43)
+ if (drm->device.info.chipset == 0x42 ||
+ drm->device.info.chipset == 0x43)
tv_enc->pin_mask =
nv42_tv_sample_load(encoder) >> 28 & 0xe;
else
@@ -375,7 +370,7 @@ static void nv17_tv_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
+ struct nouveau_gpio *gpio = nvkm_gpio(&drm->device);
struct nv17_tv_state *regs = &to_tv_enc(encoder)->state;
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
@@ -448,7 +443,7 @@ static void nv17_tv_prepare(struct drm_encoder *encoder)
/* Set the DACCLK register */
dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1;
- if (nv_device(drm->device)->card_type == NV_40)
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CURIE)
dacclk |= 0x1a << 16;
if (tv_norm->kind == CTV_ENC_MODE) {
@@ -505,7 +500,7 @@ static void nv17_tv_mode_set(struct drm_encoder *encoder,
tv_regs->ptv_614 = 0x13;
}
- if (nv_device(drm->device)->card_type >= NV_30) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_RANKINE) {
tv_regs->ptv_500 = 0xe8e0;
tv_regs->ptv_504 = 0x1710;
tv_regs->ptv_604 = 0x0;
@@ -600,7 +595,7 @@ static void nv17_tv_commit(struct drm_encoder *encoder)
nv17_tv_state_load(dev, &to_tv_enc(encoder)->state);
/* This could use refinement for flatpanels, but it should work */
- if (nv_device(drm->device)->chipset < 0x44)
+ if (drm->device.info.chipset < 0x44)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv04_dac_output_offset(encoder),
0xf0000000);
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h
index 7b331543a41b..225894cdcac2 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h
@@ -130,14 +130,14 @@ void nv17_ctv_update_rescaler(struct drm_encoder *encoder);
static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg,
uint32_t val)
{
- struct nouveau_device *device = nouveau_dev(dev);
- nv_wr32(device, reg, val);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
+ nvif_wr32(device, reg, val);
}
static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg)
{
- struct nouveau_device *device = nouveau_dev(dev);
- return nv_rd32(device, reg);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
+ return nvif_rd32(device, reg);
}
static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg,
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c
index b13f441c6431..a24faa5e2a2a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.c
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c
@@ -21,16 +21,10 @@
*
*/
-#include <core/object.h>
-#include <core/client.h>
-#include <core/device.h>
-#include <core/class.h>
-#include <core/mm.h>
-
-#include <subdev/fb.h>
-#include <subdev/timer.h>
-#include <subdev/instmem.h>
-#include <engine/graph.h>
+#include <nvif/client.h>
+#include <nvif/driver.h>
+#include <nvif/ioctl.h>
+#include <nvif/class.h>
#include "nouveau_drm.h"
#include "nouveau_dma.h"
@@ -47,20 +41,20 @@ nouveau_abi16_get(struct drm_file *file_priv, struct drm_device *dev)
struct nouveau_abi16 *abi16;
cli->abi16 = abi16 = kzalloc(sizeof(*abi16), GFP_KERNEL);
if (cli->abi16) {
+ struct nv_device_v0 args = {
+ .device = ~0ULL,
+ };
+
INIT_LIST_HEAD(&abi16->channels);
- abi16->client = nv_object(cli);
/* allocate device object targeting client's default
* device (ie. the one that belongs to the fd it
* opened)
*/
- if (nouveau_object_new(abi16->client, NVDRM_CLIENT,
- NVDRM_DEVICE, 0x0080,
- &(struct nv_device_class) {
- .device = ~0ULL,
- },
- sizeof(struct nv_device_class),
- &abi16->device) == 0)
+ if (nvif_device_init(&cli->base.base, NULL,
+ NOUVEAU_ABI16_DEVICE, NV_DEVICE,
+ &args, sizeof(args),
+ &abi16->device) == 0)
return cli->abi16;
kfree(cli->abi16);
@@ -75,7 +69,7 @@ nouveau_abi16_get(struct drm_file *file_priv, struct drm_device *dev)
int
nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret)
{
- struct nouveau_cli *cli = (void *)abi16->client;
+ struct nouveau_cli *cli = (void *)nvif_client(&abi16->device.base);
mutex_unlock(&cli->mutex);
return ret;
}
@@ -83,21 +77,19 @@ nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret)
u16
nouveau_abi16_swclass(struct nouveau_drm *drm)
{
- switch (nv_device(drm->device)->card_type) {
- case NV_04:
+ switch (drm->device.info.family) {
+ case NV_DEVICE_INFO_V0_TNT:
return 0x006e;
- case NV_10:
- case NV_11:
- case NV_20:
- case NV_30:
- case NV_40:
+ case NV_DEVICE_INFO_V0_CELSIUS:
+ case NV_DEVICE_INFO_V0_KELVIN:
+ case NV_DEVICE_INFO_V0_RANKINE:
+ case NV_DEVICE_INFO_V0_CURIE:
return 0x016e;
- case NV_50:
+ case NV_DEVICE_INFO_V0_TESLA:
return 0x506e;
- case NV_C0:
- case NV_D0:
- case NV_E0:
- case GM100:
+ case NV_DEVICE_INFO_V0_FERMI:
+ case NV_DEVICE_INFO_V0_KEPLER:
+ case NV_DEVICE_INFO_V0_MAXWELL:
return 0x906e;
}
@@ -140,7 +132,7 @@ nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
/* destroy channel object, all children will be killed too */
if (chan->chan) {
- abi16->handles &= ~(1ULL << (chan->chan->handle & 0xffff));
+ abi16->handles &= ~(1ULL << (chan->chan->object->handle & 0xffff));
nouveau_channel_del(&chan->chan);
}
@@ -151,7 +143,7 @@ nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
void
nouveau_abi16_fini(struct nouveau_abi16 *abi16)
{
- struct nouveau_cli *cli = (void *)abi16->client;
+ struct nouveau_cli *cli = (void *)nvif_client(&abi16->device.base);
struct nouveau_abi16_chan *chan, *temp;
/* cleanup channels */
@@ -160,7 +152,7 @@ nouveau_abi16_fini(struct nouveau_abi16 *abi16)
}
/* destroy the device object */
- nouveau_object_del(abi16->client, NVDRM_CLIENT, NVDRM_DEVICE);
+ nvif_device_fini(&abi16->device);
kfree(cli->abi16);
cli->abi16 = NULL;
@@ -169,30 +161,31 @@ nouveau_abi16_fini(struct nouveau_abi16 *abi16)
int
nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
{
+ struct nouveau_cli *cli = nouveau_cli(file_priv);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
- struct nouveau_timer *ptimer = nouveau_timer(device);
- struct nouveau_graph *graph = (void *)nouveau_engine(device, NVDEV_ENGINE_GR);
+ struct nvif_device *device = &drm->device;
+ struct nouveau_timer *ptimer = nvkm_timer(device);
+ struct nouveau_graph *graph = nvkm_gr(device);
struct drm_nouveau_getparam *getparam = data;
switch (getparam->param) {
case NOUVEAU_GETPARAM_CHIPSET_ID:
- getparam->value = device->chipset;
+ getparam->value = device->info.chipset;
break;
case NOUVEAU_GETPARAM_PCI_VENDOR:
- if (nv_device_is_pci(device))
+ if (nv_device_is_pci(nvkm_device(device)))
getparam->value = dev->pdev->vendor;
else
getparam->value = 0;
break;
case NOUVEAU_GETPARAM_PCI_DEVICE:
- if (nv_device_is_pci(device))
+ if (nv_device_is_pci(nvkm_device(device)))
getparam->value = dev->pdev->device;
else
getparam->value = 0;
break;
case NOUVEAU_GETPARAM_BUS_TYPE:
- if (!nv_device_is_pci(device))
+ if (!nv_device_is_pci(nvkm_device(device)))
getparam->value = 3;
else
if (drm_pci_device_is_agp(dev))
@@ -225,7 +218,7 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
getparam->value = graph->units ? graph->units(graph) : 0;
break;
default:
- nv_debug(device, "unknown parameter %lld\n", getparam->param);
+ NV_PRINTK(debug, cli, "unknown parameter %lld\n", getparam->param);
return -EINVAL;
}
@@ -246,10 +239,7 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
struct nouveau_abi16_chan *chan;
- struct nouveau_client *client;
- struct nouveau_device *device;
- struct nouveau_instmem *imem;
- struct nouveau_fb *pfb;
+ struct nvif_device *device;
int ret;
if (unlikely(!abi16))
@@ -258,21 +248,18 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
if (!drm->channel)
return nouveau_abi16_put(abi16, -ENODEV);
- client = nv_client(abi16->client);
- device = nv_device(abi16->device);
- imem = nouveau_instmem(device);
- pfb = nouveau_fb(device);
+ device = &abi16->device;
/* hack to allow channel engine type specification on kepler */
- if (device->card_type >= NV_E0) {
+ if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
if (init->fb_ctxdma_handle != ~0)
- init->fb_ctxdma_handle = NVE0_CHANNEL_IND_ENGINE_GR;
+ init->fb_ctxdma_handle = KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR;
else
init->fb_ctxdma_handle = init->tt_ctxdma_handle;
/* allow flips to be executed if this is a graphics channel */
init->tt_ctxdma_handle = 0;
- if (init->fb_ctxdma_handle == NVE0_CHANNEL_IND_ENGINE_GR)
+ if (init->fb_ctxdma_handle == KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR)
init->tt_ctxdma_handle = 1;
}
@@ -293,13 +280,14 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
abi16->handles |= (1ULL << init->channel);
/* create channel object and initialise dma and fence management */
- ret = nouveau_channel_new(drm, cli, NVDRM_DEVICE, NVDRM_CHAN |
- init->channel, init->fb_ctxdma_handle,
+ ret = nouveau_channel_new(drm, device,
+ NOUVEAU_ABI16_CHAN(init->channel),
+ init->fb_ctxdma_handle,
init->tt_ctxdma_handle, &chan->chan);
if (ret)
goto done;
- if (device->card_type >= NV_50)
+ if (device->info.family >= NV_DEVICE_INFO_V0_TESLA)
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
NOUVEAU_GEM_DOMAIN_GART;
else
@@ -308,10 +296,10 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
else
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
- if (device->card_type < NV_10) {
+ if (device->info.family < NV_DEVICE_INFO_V0_CELSIUS) {
init->subchan[0].handle = 0x00000000;
init->subchan[0].grclass = 0x0000;
- init->subchan[1].handle = NvSw;
+ init->subchan[1].handle = chan->chan->nvsw.handle;
init->subchan[1].grclass = 0x506e;
init->nr_subchan = 2;
}
@@ -324,8 +312,8 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
if (ret)
goto done;
- if (device->card_type >= NV_50) {
- ret = nouveau_bo_vma_add(chan->ntfy, client->vm,
+ if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
+ ret = nouveau_bo_vma_add(chan->ntfy, cli->vm,
&chan->ntfy_vma);
if (ret)
goto done;
@@ -343,6 +331,18 @@ done:
return nouveau_abi16_put(abi16, ret);
}
+static struct nouveau_abi16_chan *
+nouveau_abi16_chan(struct nouveau_abi16 *abi16, int channel)
+{
+ struct nouveau_abi16_chan *chan;
+
+ list_for_each_entry(chan, &abi16->channels, head) {
+ if (chan->chan->object->handle == NOUVEAU_ABI16_CHAN(channel))
+ return chan;
+ }
+
+ return NULL;
+}
int
nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS)
@@ -350,28 +350,38 @@ nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS)
struct drm_nouveau_channel_free *req = data;
struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
struct nouveau_abi16_chan *chan;
- int ret = -ENOENT;
if (unlikely(!abi16))
return -ENOMEM;
- list_for_each_entry(chan, &abi16->channels, head) {
- if (chan->chan->handle == (NVDRM_CHAN | req->channel)) {
- nouveau_abi16_chan_fini(abi16, chan);
- return nouveau_abi16_put(abi16, 0);
- }
- }
-
- return nouveau_abi16_put(abi16, ret);
+ chan = nouveau_abi16_chan(abi16, req->channel);
+ if (!chan)
+ return nouveau_abi16_put(abi16, -ENOENT);
+ nouveau_abi16_chan_fini(abi16, chan);
+ return nouveau_abi16_put(abi16, 0);
}
int
nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
{
struct drm_nouveau_grobj_alloc *init = data;
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_new_v0 new;
+ } args = {
+ .ioctl.owner = NVIF_IOCTL_V0_OWNER_ANY,
+ .ioctl.type = NVIF_IOCTL_V0_NEW,
+ .ioctl.path_nr = 3,
+ .ioctl.path[2] = NOUVEAU_ABI16_CLIENT,
+ .ioctl.path[1] = NOUVEAU_ABI16_DEVICE,
+ .ioctl.path[0] = NOUVEAU_ABI16_CHAN(init->channel),
+ .new.route = NVDRM_OBJECT_ABI16,
+ .new.handle = init->handle,
+ .new.oclass = init->class,
+ };
struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_object *object;
+ struct nvif_client *client;
int ret;
if (unlikely(!abi16))
@@ -379,6 +389,7 @@ nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
if (init->handle == ~0)
return nouveau_abi16_put(abi16, -EINVAL);
+ client = nvif_client(nvif_object(&abi16->device));
/* compatibility with userspace that assumes 506e for all chipsets */
if (init->class == 0x506e) {
@@ -387,8 +398,7 @@ nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
return nouveau_abi16_put(abi16, 0);
}
- ret = nouveau_object_new(abi16->client, NVDRM_CHAN | init->channel,
- init->handle, init->class, NULL, 0, &object);
+ ret = nvif_client_ioctl(client, &args, sizeof(args));
return nouveau_abi16_put(abi16, ret);
}
@@ -396,29 +406,38 @@ int
nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
{
struct drm_nouveau_notifierobj_alloc *info = data;
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_new_v0 new;
+ struct nv_dma_v0 ctxdma;
+ } args = {
+ .ioctl.owner = NVIF_IOCTL_V0_OWNER_ANY,
+ .ioctl.type = NVIF_IOCTL_V0_NEW,
+ .ioctl.path_nr = 3,
+ .ioctl.path[2] = NOUVEAU_ABI16_CLIENT,
+ .ioctl.path[1] = NOUVEAU_ABI16_DEVICE,
+ .ioctl.path[0] = NOUVEAU_ABI16_CHAN(info->channel),
+ .new.route = NVDRM_OBJECT_ABI16,
+ .new.handle = info->handle,
+ .new.oclass = NV_DMA_IN_MEMORY,
+ };
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
- struct nouveau_abi16_chan *chan = NULL, *temp;
+ struct nouveau_abi16_chan *chan;
struct nouveau_abi16_ntfy *ntfy;
- struct nouveau_object *object;
- struct nv_dma_class args = {};
+ struct nvif_device *device = &abi16->device;
+ struct nvif_client *client;
int ret;
if (unlikely(!abi16))
return -ENOMEM;
/* completely unnecessary for these chipsets... */
- if (unlikely(nv_device(abi16->device)->card_type >= NV_C0))
+ if (unlikely(device->info.family >= NV_DEVICE_INFO_V0_FERMI))
return nouveau_abi16_put(abi16, -EINVAL);
+ client = nvif_client(nvif_object(&abi16->device));
- list_for_each_entry(temp, &abi16->channels, head) {
- if (temp->chan->handle == (NVDRM_CHAN | info->channel)) {
- chan = temp;
- break;
- }
- }
-
+ chan = nouveau_abi16_chan(abi16, info->channel);
if (!chan)
return nouveau_abi16_put(abi16, -ENOENT);
@@ -429,31 +448,34 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
list_add(&ntfy->head, &chan->notifiers);
ntfy->handle = info->handle;
- ret = nouveau_mm_head(&chan->heap, 1, info->size, info->size, 1,
+ ret = nouveau_mm_head(&chan->heap, 0, 1, info->size, info->size, 1,
&ntfy->node);
if (ret)
goto done;
- args.start = ntfy->node->offset;
- args.limit = ntfy->node->offset + ntfy->node->length - 1;
- if (device->card_type >= NV_50) {
- args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_VM;
- args.start += chan->ntfy_vma.offset;
- args.limit += chan->ntfy_vma.offset;
+ args.ctxdma.start = ntfy->node->offset;
+ args.ctxdma.limit = ntfy->node->offset + ntfy->node->length - 1;
+ if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
+ args.ctxdma.target = NV_DMA_V0_TARGET_VM;
+ args.ctxdma.access = NV_DMA_V0_ACCESS_VM;
+ args.ctxdma.start += chan->ntfy_vma.offset;
+ args.ctxdma.limit += chan->ntfy_vma.offset;
} else
if (drm->agp.stat == ENABLED) {
- args.flags = NV_DMA_TARGET_AGP | NV_DMA_ACCESS_RDWR;
- args.start += drm->agp.base + chan->ntfy->bo.offset;
- args.limit += drm->agp.base + chan->ntfy->bo.offset;
+ args.ctxdma.target = NV_DMA_V0_TARGET_AGP;
+ args.ctxdma.access = NV_DMA_V0_ACCESS_RDWR;
+ args.ctxdma.start += drm->agp.base + chan->ntfy->bo.offset;
+ args.ctxdma.limit += drm->agp.base + chan->ntfy->bo.offset;
+ client->super = true;
} else {
- args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_RDWR;
- args.start += chan->ntfy->bo.offset;
- args.limit += chan->ntfy->bo.offset;
+ args.ctxdma.target = NV_DMA_V0_TARGET_VM;
+ args.ctxdma.access = NV_DMA_V0_ACCESS_RDWR;
+ args.ctxdma.start += chan->ntfy->bo.offset;
+ args.ctxdma.limit += chan->ntfy->bo.offset;
}
- ret = nouveau_object_new(abi16->client, chan->chan->handle,
- ntfy->handle, 0x003d, &args,
- sizeof(args), &object);
+ ret = nvif_client_ioctl(client, &args, sizeof(args));
+ client->super = false;
if (ret)
goto done;
@@ -469,28 +491,36 @@ int
nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS)
{
struct drm_nouveau_gpuobj_free *fini = data;
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_del del;
+ } args = {
+ .ioctl.owner = NVDRM_OBJECT_ABI16,
+ .ioctl.type = NVIF_IOCTL_V0_DEL,
+ .ioctl.path_nr = 4,
+ .ioctl.path[3] = NOUVEAU_ABI16_CLIENT,
+ .ioctl.path[2] = NOUVEAU_ABI16_DEVICE,
+ .ioctl.path[1] = NOUVEAU_ABI16_CHAN(fini->channel),
+ .ioctl.path[0] = fini->handle,
+ };
struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
- struct nouveau_abi16_chan *chan = NULL, *temp;
+ struct nouveau_abi16_chan *chan;
struct nouveau_abi16_ntfy *ntfy;
+ struct nvif_client *client;
int ret;
if (unlikely(!abi16))
return -ENOMEM;
- list_for_each_entry(temp, &abi16->channels, head) {
- if (temp->chan->handle == (NVDRM_CHAN | fini->channel)) {
- chan = temp;
- break;
- }
- }
-
+ chan = nouveau_abi16_chan(abi16, fini->channel);
if (!chan)
return nouveau_abi16_put(abi16, -ENOENT);
+ client = nvif_client(nvif_object(&abi16->device));
/* synchronize with the user channel and destroy the gpu object */
nouveau_channel_idle(chan->chan);
- ret = nouveau_object_del(abi16->client, chan->chan->handle, fini->handle);
+ ret = nvif_client_ioctl(client, &args, sizeof(args));
if (ret)
return nouveau_abi16_put(abi16, ret);
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.h b/drivers/gpu/drm/nouveau/nouveau_abi16.h
index 90004081a501..39844e6bfbff 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.h
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.h
@@ -28,8 +28,7 @@ struct nouveau_abi16_chan {
};
struct nouveau_abi16 {
- struct nouveau_object *client;
- struct nouveau_object *device;
+ struct nvif_device device;
struct list_head channels;
u64 handles;
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index 279206997e5c..622424692b3b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -46,7 +46,6 @@ static struct nouveau_dsm_priv {
bool dsm_detected;
bool optimus_detected;
acpi_handle dhandle;
- acpi_handle other_handle;
acpi_handle rom_handle;
} nouveau_dsm_priv;
@@ -222,10 +221,9 @@ static int nouveau_dsm_pci_probe(struct pci_dev *pdev)
if (!dhandle)
return false;
- if (!acpi_has_method(dhandle, "_DSM")) {
- nouveau_dsm_priv.other_handle = dhandle;
+ if (!acpi_has_method(dhandle, "_DSM"))
return false;
- }
+
if (acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102,
1 << NOUVEAU_DSM_POWER))
retval |= NOUVEAU_DSM_HAS_MUX;
@@ -301,16 +299,6 @@ static bool nouveau_dsm_detect(void)
printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
acpi_method_name);
nouveau_dsm_priv.dsm_detected = true;
- /*
- * On some systems hotplug events are generated for the device
- * being switched off when _DSM is executed. They cause ACPI
- * hotplug to trigger and attempt to remove the device from
- * the system, which causes it to break down. Prevent that from
- * happening by setting the no_hotplug flag for the involved
- * ACPI device objects.
- */
- acpi_bus_no_hotplug(nouveau_dsm_priv.dhandle);
- acpi_bus_no_hotplug(nouveau_dsm_priv.other_handle);
ret = true;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_agp.c b/drivers/gpu/drm/nouveau/nouveau_agp.c
index 51666daddb94..1f6f6ba6847a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_agp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_agp.c
@@ -1,7 +1,5 @@
#include <linux/module.h>
-#include <core/device.h>
-
#include "nouveau_drm.h"
#include "nouveau_agp.h"
#include "nouveau_reg.h"
@@ -29,7 +27,7 @@ static struct nouveau_agpmode_quirk nouveau_agpmode_quirk_list[] = {
static unsigned long
get_agp_mode(struct nouveau_drm *drm, const struct drm_agp_info *info)
{
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
struct nouveau_agpmode_quirk *quirk = nouveau_agpmode_quirk_list;
int agpmode = nouveau_agpmode;
unsigned long mode = info->mode;
@@ -38,7 +36,7 @@ get_agp_mode(struct nouveau_drm *drm, const struct drm_agp_info *info)
* FW seems to be broken on nv18, it makes the card lock up
* randomly.
*/
- if (device->chipset == 0x18)
+ if (device->info.chipset == 0x18)
mode &= ~PCI_AGP_COMMAND_FW;
/*
@@ -47,10 +45,10 @@ get_agp_mode(struct nouveau_drm *drm, const struct drm_agp_info *info)
while (agpmode == -1 && quirk->hostbridge_vendor) {
if (info->id_vendor == quirk->hostbridge_vendor &&
info->id_device == quirk->hostbridge_device &&
- device->pdev->vendor == quirk->chip_vendor &&
- device->pdev->device == quirk->chip_device) {
+ nvkm_device(device)->pdev->vendor == quirk->chip_vendor &&
+ nvkm_device(device)->pdev->device == quirk->chip_device) {
agpmode = quirk->mode;
- nv_info(device, "Forcing agp mode to %dX. Use agpmode to override.\n",
+ NV_INFO(drm, "Forcing agp mode to %dX. Use agpmode to override.\n",
agpmode);
break;
}
@@ -104,7 +102,7 @@ void
nouveau_agp_reset(struct nouveau_drm *drm)
{
#if __OS_HAS_AGP
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
struct drm_device *dev = drm->dev;
u32 save[2];
int ret;
@@ -115,7 +113,7 @@ nouveau_agp_reset(struct nouveau_drm *drm)
/* First of all, disable fast writes, otherwise if it's
* already enabled in the AGP bridge and we disable the card's
* AGP controller we might be locking ourselves out of it. */
- if ((nv_rd32(device, NV04_PBUS_PCI_NV_19) |
+ if ((nvif_rd32(device, NV04_PBUS_PCI_NV_19) |
dev->agp->mode) & PCI_AGP_COMMAND_FW) {
struct drm_agp_info info;
struct drm_agp_mode mode;
@@ -134,15 +132,15 @@ nouveau_agp_reset(struct nouveau_drm *drm)
/* clear busmaster bit, and disable AGP */
- save[0] = nv_mask(device, NV04_PBUS_PCI_NV_1, 0x00000004, 0x00000000);
- nv_wr32(device, NV04_PBUS_PCI_NV_19, 0);
+ save[0] = nvif_mask(device, NV04_PBUS_PCI_NV_1, 0x00000004, 0x00000000);
+ nvif_wr32(device, NV04_PBUS_PCI_NV_19, 0);
/* reset PGRAPH, PFIFO and PTIMER */
- save[1] = nv_mask(device, 0x000200, 0x00011100, 0x00000000);
- nv_mask(device, 0x000200, 0x00011100, save[1]);
+ save[1] = nvif_mask(device, 0x000200, 0x00011100, 0x00000000);
+ nvif_mask(device, 0x000200, 0x00011100, save[1]);
/* and restore bustmaster bit (gives effect of resetting AGP) */
- nv_wr32(device, NV04_PBUS_PCI_NV_1, save[0]);
+ nvif_wr32(device, NV04_PBUS_PCI_NV_1, save[0]);
#endif
}
@@ -150,7 +148,6 @@ void
nouveau_agp_init(struct nouveau_drm *drm)
{
#if __OS_HAS_AGP
- struct nouveau_device *device = nv_device(drm->device);
struct drm_device *dev = drm->dev;
struct drm_agp_info info;
struct drm_agp_mode mode;
@@ -162,13 +159,13 @@ nouveau_agp_init(struct nouveau_drm *drm)
ret = drm_agp_acquire(dev);
if (ret) {
- nv_error(device, "unable to acquire AGP: %d\n", ret);
+ NV_ERROR(drm, "unable to acquire AGP: %d\n", ret);
return;
}
ret = drm_agp_info(dev, &info);
if (ret) {
- nv_error(device, "unable to get AGP info: %d\n", ret);
+ NV_ERROR(drm, "unable to get AGP info: %d\n", ret);
return;
}
@@ -177,7 +174,7 @@ nouveau_agp_init(struct nouveau_drm *drm)
ret = drm_agp_enable(dev, mode);
if (ret) {
- nv_error(device, "unable to enable AGP: %d\n", ret);
+ NV_ERROR(drm, "unable to enable AGP: %d\n", ret);
return;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c
index 2c1e4aad7da3..e566c5b53651 100644
--- a/drivers/gpu/drm/nouveau/nouveau_backlight.c
+++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c
@@ -40,8 +40,8 @@ static int
nv40_get_intensity(struct backlight_device *bd)
{
struct nouveau_drm *drm = bl_get_data(bd);
- struct nouveau_device *device = nv_device(drm->device);
- int val = (nv_rd32(device, NV40_PMC_BACKLIGHT) &
+ struct nvif_device *device = &drm->device;
+ int val = (nvif_rd32(device, NV40_PMC_BACKLIGHT) &
NV40_PMC_BACKLIGHT_MASK) >> 16;
return val;
@@ -51,11 +51,11 @@ static int
nv40_set_intensity(struct backlight_device *bd)
{
struct nouveau_drm *drm = bl_get_data(bd);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
int val = bd->props.brightness;
- int reg = nv_rd32(device, NV40_PMC_BACKLIGHT);
+ int reg = nvif_rd32(device, NV40_PMC_BACKLIGHT);
- nv_wr32(device, NV40_PMC_BACKLIGHT,
+ nvif_wr32(device, NV40_PMC_BACKLIGHT,
(val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
return 0;
@@ -71,11 +71,11 @@ static int
nv40_backlight_init(struct drm_connector *connector)
{
struct nouveau_drm *drm = nouveau_drm(connector->dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
struct backlight_properties props;
struct backlight_device *bd;
- if (!(nv_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
+ if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
return 0;
memset(&props, 0, sizeof(struct backlight_properties));
@@ -97,12 +97,12 @@ nv50_get_intensity(struct backlight_device *bd)
{
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
int or = nv_encoder->or;
u32 div = 1025;
u32 val;
- val = nv_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
+ val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
val &= NV50_PDISP_SOR_PWM_CTL_VAL;
return ((val * 100) + (div / 2)) / div;
}
@@ -112,12 +112,12 @@ nv50_set_intensity(struct backlight_device *bd)
{
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
int or = nv_encoder->or;
u32 div = 1025;
u32 val = (bd->props.brightness * div) / 100;
- nv_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
+ nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or),
NV50_PDISP_SOR_PWM_CTL_NEW | val);
return 0;
}
@@ -133,12 +133,12 @@ nva3_get_intensity(struct backlight_device *bd)
{
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
int or = nv_encoder->or;
u32 div, val;
- div = nv_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
- val = nv_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
+ div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
+ val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or));
val &= NVA3_PDISP_SOR_PWM_CTL_VAL;
if (div && div >= val)
return ((val * 100) + (div / 2)) / div;
@@ -151,14 +151,14 @@ nva3_set_intensity(struct backlight_device *bd)
{
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
int or = nv_encoder->or;
u32 div, val;
- div = nv_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
+ div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or));
val = (bd->props.brightness * div) / 100;
if (div) {
- nv_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), val |
+ nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), val |
NV50_PDISP_SOR_PWM_CTL_NEW |
NVA3_PDISP_SOR_PWM_CTL_UNK);
return 0;
@@ -177,7 +177,7 @@ static int
nv50_backlight_init(struct drm_connector *connector)
{
struct nouveau_drm *drm = nouveau_drm(connector->dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
struct nouveau_encoder *nv_encoder;
struct backlight_properties props;
struct backlight_device *bd;
@@ -190,12 +190,12 @@ nv50_backlight_init(struct drm_connector *connector)
return -ENODEV;
}
- if (!nv_rd32(device, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or)))
+ if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or)))
return 0;
- if (device->chipset <= 0xa0 ||
- device->chipset == 0xaa ||
- device->chipset == 0xac)
+ if (device->info.chipset <= 0xa0 ||
+ device->info.chipset == 0xaa ||
+ device->info.chipset == 0xac)
ops = &nv50_bl_ops;
else
ops = &nva3_bl_ops;
@@ -218,7 +218,7 @@ int
nouveau_backlight_init(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
struct drm_connector *connector;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
@@ -226,13 +226,12 @@ nouveau_backlight_init(struct drm_device *dev)
connector->connector_type != DRM_MODE_CONNECTOR_eDP)
continue;
- switch (device->card_type) {
- case NV_40:
+ switch (device->info.family) {
+ case NV_DEVICE_INFO_V0_CURIE:
return nv40_backlight_init(connector);
- case NV_50:
- case NV_C0:
- case NV_D0:
- case NV_E0:
+ case NV_DEVICE_INFO_V0_TESLA:
+ case NV_DEVICE_INFO_V0_FERMI:
+ case NV_DEVICE_INFO_V0_KEPLER:
return nv50_backlight_init(connector);
default:
break;
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 8268a4ccac15..dae2c96deef8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -22,8 +22,6 @@
* SOFTWARE.
*/
-#include <subdev/bios.h>
-
#include <drm/drmP.h>
#include "nouveau_drm.h"
@@ -217,7 +215,7 @@ int call_lvds_script(struct drm_device *dev, struct dcb_output *dcbent, int head
*/
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
struct nvbios *bios = &drm->vbios;
uint8_t lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer];
uint32_t sel_clk_binding, sel_clk;
@@ -240,7 +238,7 @@ int call_lvds_script(struct drm_device *dev, struct dcb_output *dcbent, int head
NV_INFO(drm, "Calling LVDS script %d:\n", script);
/* don't let script change pll->head binding */
- sel_clk_binding = nv_rd32(device, NV_PRAMDAC_SEL_CLK) & 0x50000;
+ sel_clk_binding = nvif_rd32(device, NV_PRAMDAC_SEL_CLK) & 0x50000;
if (lvds_ver < 0x30)
ret = call_lvds_manufacturer_script(dev, dcbent, head, script);
@@ -252,7 +250,7 @@ int call_lvds_script(struct drm_device *dev, struct dcb_output *dcbent, int head
sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000;
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding);
/* some scripts set a value in NV_PBUS_POWERCTRL_2 and break video overlay */
- nv_wr32(device, NV_PBUS_POWERCTRL_2, 0);
+ nvif_wr32(device, NV_PBUS_POWERCTRL_2, 0);
return ret;
}
@@ -320,7 +318,7 @@ static int parse_lvds_manufacturer_table_header(struct drm_device *dev, struct n
static int
get_fp_strap(struct drm_device *dev, struct nvbios *bios)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
/*
* The fp strap is normally dictated by the "User Strap" in
@@ -334,10 +332,10 @@ get_fp_strap(struct drm_device *dev, struct nvbios *bios)
if (bios->major_version < 5 && bios->data[0x48] & 0x4)
return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf;
- if (device->card_type >= NV_50)
- return (nv_rd32(device, NV_PEXTDEV_BOOT_0) >> 24) & 0xf;
+ if (device->info.family >= NV_DEVICE_INFO_V0_TESLA)
+ return (nvif_rd32(device, NV_PEXTDEV_BOOT_0) >> 24) & 0xf;
else
- return (nv_rd32(device, NV_PEXTDEV_BOOT_0) >> 16) & 0xf;
+ return (nvif_rd32(device, NV_PEXTDEV_BOOT_0) >> 16) & 0xf;
}
static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
@@ -636,7 +634,7 @@ int run_tmds_table(struct drm_device *dev, struct dcb_output *dcbent, int head,
*/
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
struct nvbios *bios = &drm->vbios;
int cv = bios->chip_version;
uint16_t clktable = 0, scriptptr;
@@ -670,7 +668,7 @@ int run_tmds_table(struct drm_device *dev, struct dcb_output *dcbent, int head,
}
/* don't let script change pll->head binding */
- sel_clk_binding = nv_rd32(device, NV_PRAMDAC_SEL_CLK) & 0x50000;
+ sel_clk_binding = nvif_rd32(device, NV_PRAMDAC_SEL_CLK) & 0x50000;
run_digital_op_script(dev, scriptptr, dcbent, head, pxclk >= 165000);
sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000;
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding);
@@ -1253,7 +1251,7 @@ olddcb_table(struct drm_device *dev)
struct nouveau_drm *drm = nouveau_drm(dev);
u8 *dcb = NULL;
- if (nv_device(drm->device)->card_type > NV_04)
+ if (drm->device.info.family > NV_DEVICE_INFO_V0_TNT)
dcb = ROMPTR(dev, drm->vbios.data[0x36]);
if (!dcb) {
NV_WARN(drm, "No DCB data found in VBIOS\n");
@@ -1399,6 +1397,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
uint32_t conn, uint32_t conf, struct dcb_output *entry)
{
struct nouveau_drm *drm = nouveau_drm(dev);
+ int link = 0;
entry->type = conn & 0xf;
entry->i2c_index = (conn >> 4) & 0xf;
@@ -1444,6 +1443,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
if (conf & 0x4)
entry->lvdsconf.use_power_scripts = true;
entry->lvdsconf.sor.link = (conf & 0x00000030) >> 4;
+ link = entry->lvdsconf.sor.link;
}
if (conf & mask) {
/*
@@ -1492,17 +1492,18 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
entry->dpconf.link_nr = 1;
break;
}
+ link = entry->dpconf.sor.link;
break;
case DCB_OUTPUT_TMDS:
if (dcb->version >= 0x40) {
entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
entry->extdev = (conf & 0x0000ff00) >> 8;
+ link = entry->tmdsconf.sor.link;
}
else if (dcb->version >= 0x30)
entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8;
else if (dcb->version >= 0x22)
entry->tmdsconf.slave_addr = (conf & 0x00000070) >> 4;
-
break;
case DCB_OUTPUT_EOL:
/* weird g80 mobile type that "nv" treats as a terminator */
@@ -1526,6 +1527,8 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
if (conf & 0x100000)
entry->i2c_upper_default = true;
+ entry->hasht = (entry->location << 4) | entry->type;
+ entry->hashm = (entry->heads << 8) | (link << 6) | entry->or;
return true;
}
@@ -1908,7 +1911,7 @@ static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bio
*/
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
uint8_t bytes_to_write;
uint16_t hwsq_entry_offset;
int i;
@@ -1931,15 +1934,15 @@ static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bio
hwsq_entry_offset = hwsq_offset + 2 + entry * bytes_to_write;
/* set sequencer control */
- nv_wr32(device, 0x00001304, ROM32(bios->data[hwsq_entry_offset]));
+ nvif_wr32(device, 0x00001304, ROM32(bios->data[hwsq_entry_offset]));
bytes_to_write -= 4;
/* write ucode */
for (i = 0; i < bytes_to_write; i += 4)
- nv_wr32(device, 0x00001400 + i, ROM32(bios->data[hwsq_entry_offset + i + 4]));
+ nvif_wr32(device, 0x00001400 + i, ROM32(bios->data[hwsq_entry_offset + i + 4]));
/* twiddle NV_PBUS_DEBUG_4 */
- nv_wr32(device, NV_PBUS_DEBUG_4, nv_rd32(device, NV_PBUS_DEBUG_4) | 0x18);
+ nvif_wr32(device, NV_PBUS_DEBUG_4, nvif_rd32(device, NV_PBUS_DEBUG_4) | 0x18);
return 0;
}
@@ -2002,7 +2005,7 @@ uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev)
static bool NVInitVBIOS(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_bios *bios = nouveau_bios(drm->device);
+ struct nouveau_bios *bios = nvkm_bios(&drm->device);
struct nvbios *legacy = &drm->vbios;
memset(legacy, 0, sizeof(struct nvbios));
@@ -2054,7 +2057,7 @@ nouveau_bios_posted(struct drm_device *dev)
struct nouveau_drm *drm = nouveau_drm(dev);
unsigned htotal;
- if (nv_device(drm->device)->card_type >= NV_50)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA)
return true;
htotal = NVReadVgaCrtc(dev, 0, 0x06);
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index b6dc85c614be..3d474ac03f88 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -27,13 +27,9 @@
* Jeremy Kolb <jkolb@brandeis.edu>
*/
-#include <core/engine.h>
+#include <linux/dma-mapping.h>
#include <linux/swiotlb.h>
-#include <subdev/fb.h>
-#include <subdev/vm.h>
-#include <subdev/bar.h>
-
#include "nouveau_drm.h"
#include "nouveau_dma.h"
#include "nouveau_fence.h"
@@ -52,7 +48,7 @@ nv10_bo_update_tile_region(struct drm_device *dev, struct nouveau_drm_tile *reg,
{
struct nouveau_drm *drm = nouveau_drm(dev);
int i = reg - drm->tile.reg;
- struct nouveau_fb *pfb = nouveau_fb(drm->device);
+ struct nouveau_fb *pfb = nvkm_fb(&drm->device);
struct nouveau_fb_tile *tile = &pfb->tile.region[i];
struct nouveau_engine *engine;
@@ -92,13 +88,13 @@ nv10_bo_get_tile_region(struct drm_device *dev, int i)
static void
nv10_bo_put_tile_region(struct drm_device *dev, struct nouveau_drm_tile *tile,
- struct nouveau_fence *fence)
+ struct fence *fence)
{
struct nouveau_drm *drm = nouveau_drm(dev);
if (tile) {
spin_lock(&drm->tile.lock);
- tile->fence = nouveau_fence_ref(fence);
+ tile->fence = (struct nouveau_fence *)fence_get(fence);
tile->used = false;
spin_unlock(&drm->tile.lock);
}
@@ -109,7 +105,7 @@ nv10_bo_set_tiling(struct drm_device *dev, u32 addr,
u32 size, u32 pitch, u32 flags)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_fb *pfb = nouveau_fb(drm->device);
+ struct nouveau_fb *pfb = nvkm_fb(&drm->device);
struct nouveau_drm_tile *tile, *found = NULL;
int i;
@@ -153,23 +149,23 @@ nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags,
int *align, int *size)
{
struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
- if (device->card_type < NV_50) {
+ if (device->info.family < NV_DEVICE_INFO_V0_TESLA) {
if (nvbo->tile_mode) {
- if (device->chipset >= 0x40) {
+ if (device->info.chipset >= 0x40) {
*align = 65536;
*size = roundup(*size, 64 * nvbo->tile_mode);
- } else if (device->chipset >= 0x30) {
+ } else if (device->info.chipset >= 0x30) {
*align = 32768;
*size = roundup(*size, 64 * nvbo->tile_mode);
- } else if (device->chipset >= 0x20) {
+ } else if (device->info.chipset >= 0x20) {
*align = 16384;
*size = roundup(*size, 64 * nvbo->tile_mode);
- } else if (device->chipset >= 0x10) {
+ } else if (device->info.chipset >= 0x10) {
*align = 16384;
*size = roundup(*size, 32 * nvbo->tile_mode);
}
@@ -185,7 +181,7 @@ nouveau_bo_fixup_align(struct nouveau_bo *nvbo, u32 flags,
int
nouveau_bo_new(struct drm_device *dev, int size, int align,
uint32_t flags, uint32_t tile_mode, uint32_t tile_flags,
- struct sg_table *sg,
+ struct sg_table *sg, struct reservation_object *robj,
struct nouveau_bo **pnvbo)
{
struct nouveau_drm *drm = nouveau_drm(dev);
@@ -196,12 +192,12 @@ nouveau_bo_new(struct drm_device *dev, int size, int align,
int lpg_shift = 12;
int max_size;
- if (drm->client.base.vm)
- lpg_shift = drm->client.base.vm->vmm->lpg_shift;
+ if (drm->client.vm)
+ lpg_shift = drm->client.vm->vmm->lpg_shift;
max_size = INT_MAX & ~((1 << lpg_shift) - 1);
if (size <= 0 || size > max_size) {
- nv_warn(drm, "skipped size %x\n", (u32)size);
+ NV_WARN(drm, "skipped size %x\n", (u32)size);
return -EINVAL;
}
@@ -219,9 +215,9 @@ nouveau_bo_new(struct drm_device *dev, int size, int align,
nvbo->bo.bdev = &drm->ttm.bdev;
nvbo->page_shift = 12;
- if (drm->client.base.vm) {
+ if (drm->client.vm) {
if (!(flags & TTM_PL_FLAG_TT) && size > 256 * 1024)
- nvbo->page_shift = drm->client.base.vm->vmm->lpg_shift;
+ nvbo->page_shift = drm->client.vm->vmm->lpg_shift;
}
nouveau_bo_fixup_align(nvbo, flags, &align, &size);
@@ -234,7 +230,7 @@ nouveau_bo_new(struct drm_device *dev, int size, int align,
ret = ttm_bo_init(&drm->ttm.bdev, &nvbo->bo, size,
type, &nvbo->placement,
align >> PAGE_SHIFT, false, NULL, acc_size, sg,
- nouveau_bo_del_ttm);
+ robj, nouveau_bo_del_ttm);
if (ret) {
/* ttm will call nouveau_bo_del_ttm if it fails.. */
return ret;
@@ -245,27 +241,26 @@ nouveau_bo_new(struct drm_device *dev, int size, int align,
}
static void
-set_placement_list(uint32_t *pl, unsigned *n, uint32_t type, uint32_t flags)
+set_placement_list(struct ttm_place *pl, unsigned *n, uint32_t type, uint32_t flags)
{
*n = 0;
if (type & TTM_PL_FLAG_VRAM)
- pl[(*n)++] = TTM_PL_FLAG_VRAM | flags;
+ pl[(*n)++].flags = TTM_PL_FLAG_VRAM | flags;
if (type & TTM_PL_FLAG_TT)
- pl[(*n)++] = TTM_PL_FLAG_TT | flags;
+ pl[(*n)++].flags = TTM_PL_FLAG_TT | flags;
if (type & TTM_PL_FLAG_SYSTEM)
- pl[(*n)++] = TTM_PL_FLAG_SYSTEM | flags;
+ pl[(*n)++].flags = TTM_PL_FLAG_SYSTEM | flags;
}
static void
set_placement_range(struct nouveau_bo *nvbo, uint32_t type)
{
struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev);
- struct nouveau_fb *pfb = nouveau_fb(drm->device);
- u32 vram_pages = pfb->ram->size >> PAGE_SHIFT;
+ u32 vram_pages = drm->device.info.ram_size >> PAGE_SHIFT;
+ unsigned i, fpfn, lpfn;
- if ((nv_device(drm->device)->card_type == NV_10 ||
- nv_device(drm->device)->card_type == NV_11) &&
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_CELSIUS &&
nvbo->tile_mode && (type & TTM_PL_FLAG_VRAM) &&
nvbo->bo.mem.num_pages < vram_pages / 4) {
/*
@@ -275,11 +270,19 @@ set_placement_range(struct nouveau_bo *nvbo, uint32_t type)
* at the same time.
*/
if (nvbo->tile_flags & NOUVEAU_GEM_TILE_ZETA) {
- nvbo->placement.fpfn = vram_pages / 2;
- nvbo->placement.lpfn = ~0;
+ fpfn = vram_pages / 2;
+ lpfn = ~0;
} else {
- nvbo->placement.fpfn = 0;
- nvbo->placement.lpfn = vram_pages / 2;
+ fpfn = 0;
+ lpfn = vram_pages / 2;
+ }
+ for (i = 0; i < nvbo->placement.num_placement; ++i) {
+ nvbo->placements[i].fpfn = fpfn;
+ nvbo->placements[i].lpfn = lpfn;
+ }
+ for (i = 0; i < nvbo->placement.num_busy_placement; ++i) {
+ nvbo->busy_placements[i].fpfn = fpfn;
+ nvbo->busy_placements[i].lpfn = lpfn;
}
}
}
@@ -309,7 +312,7 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)
struct ttm_buffer_object *bo = &nvbo->bo;
int ret;
- ret = ttm_bo_reserve(bo, false, false, false, 0);
+ ret = ttm_bo_reserve(bo, false, false, false, NULL);
if (ret)
goto out;
@@ -350,7 +353,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo)
struct ttm_buffer_object *bo = &nvbo->bo;
int ret, ref;
- ret = ttm_bo_reserve(bo, false, false, false, 0);
+ ret = ttm_bo_reserve(bo, false, false, false, NULL);
if (ret)
return ret;
@@ -385,7 +388,7 @@ nouveau_bo_map(struct nouveau_bo *nvbo)
{
int ret;
- ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
+ ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL);
if (ret)
return ret;
@@ -500,21 +503,28 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_VRAM:
- if (nv_device(drm->device)->card_type >= NV_50) {
+ man->flags = TTM_MEMTYPE_FLAG_FIXED |
+ TTM_MEMTYPE_FLAG_MAPPABLE;
+ man->available_caching = TTM_PL_FLAG_UNCACHED |
+ TTM_PL_FLAG_WC;
+ man->default_caching = TTM_PL_FLAG_WC;
+
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
+ /* Some BARs do not support being ioremapped WC */
+ if (nvkm_bar(&drm->device)->iomap_uncached) {
+ man->available_caching = TTM_PL_FLAG_UNCACHED;
+ man->default_caching = TTM_PL_FLAG_UNCACHED;
+ }
+
man->func = &nouveau_vram_manager;
man->io_reserve_fastpath = false;
man->use_io_reserve_lru = true;
} else {
man->func = &ttm_bo_manager_func;
}
- man->flags = TTM_MEMTYPE_FLAG_FIXED |
- TTM_MEMTYPE_FLAG_MAPPABLE;
- man->available_caching = TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_WC;
- man->default_caching = TTM_PL_FLAG_WC;
break;
case TTM_PL_TT:
- if (nv_device(drm->device)->card_type >= NV_50)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA)
man->func = &nouveau_gart_manager;
else
if (drm->agp.stat != ENABLED)
@@ -763,9 +773,9 @@ nv50_bo_move_init(struct nouveau_channel *chan, u32 handle)
BEGIN_NV04(chan, NvSubCopy, 0x0000, 1);
OUT_RING (chan, handle);
BEGIN_NV04(chan, NvSubCopy, 0x0180, 3);
- OUT_RING (chan, NvNotify0);
- OUT_RING (chan, NvDmaFB);
- OUT_RING (chan, NvDmaFB);
+ OUT_RING (chan, chan->drm->ntfy.handle);
+ OUT_RING (chan, chan->vram.handle);
+ OUT_RING (chan, chan->vram.handle);
}
return ret;
@@ -852,7 +862,7 @@ nv04_bo_move_init(struct nouveau_channel *chan, u32 handle)
BEGIN_NV04(chan, NvSubCopy, 0x0000, 1);
OUT_RING (chan, handle);
BEGIN_NV04(chan, NvSubCopy, 0x0180, 1);
- OUT_RING (chan, NvNotify0);
+ OUT_RING (chan, chan->drm->ntfy.handle);
}
return ret;
@@ -864,7 +874,7 @@ nouveau_bo_mem_ctxdma(struct ttm_buffer_object *bo,
{
if (mem->mem_type == TTM_PL_TT)
return NvDmaTT;
- return NvDmaFB;
+ return chan->vram.handle;
}
static int
@@ -922,12 +932,12 @@ nouveau_bo_move_prep(struct nouveau_drm *drm, struct ttm_buffer_object *bo,
u64 size = (u64)mem->num_pages << PAGE_SHIFT;
int ret;
- ret = nouveau_vm_get(nv_client(drm)->vm, size, old_node->page_shift,
+ ret = nouveau_vm_get(drm->client.vm, size, old_node->page_shift,
NV_MEM_ACCESS_RW, &old_node->vma[0]);
if (ret)
return ret;
- ret = nouveau_vm_get(nv_client(drm)->vm, size, new_node->page_shift,
+ ret = nouveau_vm_get(drm->client.vm, size, new_node->page_shift,
NV_MEM_ACCESS_RW, &old_node->vma[1]);
if (ret) {
nouveau_vm_put(&old_node->vma[0]);
@@ -945,6 +955,7 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
{
struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
struct nouveau_channel *chan = drm->ttm.chan;
+ struct nouveau_cli *cli = (void *)nvif_client(&chan->device->base);
struct nouveau_fence *fence;
int ret;
@@ -952,20 +963,21 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
* old nouveau_mem node, these will get cleaned up after ttm has
* destroyed the ttm_mem_reg
*/
- if (nv_device(drm->device)->card_type >= NV_50) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
ret = nouveau_bo_move_prep(drm, bo, new_mem);
if (ret)
return ret;
}
- mutex_lock_nested(&chan->cli->mutex, SINGLE_DEPTH_NESTING);
- ret = nouveau_fence_sync(bo->sync_obj, chan);
+ mutex_lock_nested(&cli->mutex, SINGLE_DEPTH_NESTING);
+ ret = nouveau_fence_sync(nouveau_bo(bo), chan, true, intr);
if (ret == 0) {
ret = drm->ttm.move(chan, bo, &bo->mem, new_mem);
if (ret == 0) {
ret = nouveau_fence_new(chan, false, &fence);
if (ret == 0) {
- ret = ttm_bo_move_accel_cleanup(bo, fence,
+ ret = ttm_bo_move_accel_cleanup(bo,
+ &fence->base,
evict,
no_wait_gpu,
new_mem);
@@ -973,7 +985,7 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
}
}
}
- mutex_unlock(&chan->cli->mutex);
+ mutex_unlock(&cli->mutex);
return ret;
}
@@ -1005,9 +1017,7 @@ nouveau_bo_move_init(struct nouveau_drm *drm)
int ret;
do {
- struct nouveau_object *object;
struct nouveau_channel *chan;
- u32 handle = (mthd->engine << 16) | mthd->oclass;
if (mthd->engine)
chan = drm->cechan;
@@ -1016,13 +1026,14 @@ nouveau_bo_move_init(struct nouveau_drm *drm)
if (chan == NULL)
continue;
- ret = nouveau_object_new(nv_object(drm), chan->handle, handle,
- mthd->oclass, NULL, 0, &object);
+ ret = nvif_object_init(chan->object, NULL,
+ mthd->oclass | (mthd->engine << 16),
+ mthd->oclass, NULL, 0,
+ &drm->ttm.copy);
if (ret == 0) {
- ret = mthd->init(chan, handle);
+ ret = mthd->init(chan, drm->ttm.copy.handle);
if (ret) {
- nouveau_object_del(nv_object(drm),
- chan->handle, handle);
+ nvif_object_fini(&drm->ttm.copy);
continue;
}
@@ -1040,12 +1051,15 @@ static int
nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
bool no_wait_gpu, struct ttm_mem_reg *new_mem)
{
- u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
+ struct ttm_place placement_memtype = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING
+ };
struct ttm_placement placement;
struct ttm_mem_reg tmp_mem;
int ret;
- placement.fpfn = placement.lpfn = 0;
placement.num_placement = placement.num_busy_placement = 1;
placement.placement = placement.busy_placement = &placement_memtype;
@@ -1073,12 +1087,15 @@ static int
nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,
bool no_wait_gpu, struct ttm_mem_reg *new_mem)
{
- u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
+ struct ttm_place placement_memtype = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING
+ };
struct ttm_placement placement;
struct ttm_mem_reg tmp_mem;
int ret;
- placement.fpfn = placement.lpfn = 0;
placement.num_placement = placement.num_busy_placement = 1;
placement.placement = placement.busy_placement = &placement_memtype;
@@ -1135,7 +1152,7 @@ nouveau_bo_vm_bind(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem,
if (new_mem->mem_type != TTM_PL_VRAM)
return 0;
- if (nv_device(drm->device)->card_type >= NV_10) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_CELSIUS) {
*new_tile = nv10_bo_set_tiling(dev, offset, new_mem->size,
nvbo->tile_mode,
nvbo->tile_flags);
@@ -1151,8 +1168,9 @@ nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo,
{
struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
struct drm_device *dev = drm->dev;
+ struct fence *fence = reservation_object_get_excl(bo->resv);
- nv10_bo_put_tile_region(dev, *old_tile, bo->sync_obj);
+ nv10_bo_put_tile_region(dev, *old_tile, fence);
*old_tile = new_tile;
}
@@ -1166,7 +1184,7 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
struct nouveau_drm_tile *new_tile = NULL;
int ret = 0;
- if (nv_device(drm->device)->card_type < NV_50) {
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) {
ret = nouveau_bo_vm_bind(bo, new_mem, &new_tile);
if (ret)
return ret;
@@ -1196,14 +1214,12 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
}
/* Fallback to software copy. */
- spin_lock(&bo->bdev->fence_lock);
ret = ttm_bo_wait(bo, true, intr, no_wait_gpu);
- spin_unlock(&bo->bdev->fence_lock);
if (ret == 0)
ret = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem);
out:
- if (nv_device(drm->device)->card_type < NV_50) {
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) {
if (ret)
nouveau_bo_vm_cleanup(bo, NULL, &new_tile);
else
@@ -1227,7 +1243,6 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
struct nouveau_drm *drm = nouveau_bdev(bdev);
struct nouveau_mem *node = mem->mm_node;
- struct drm_device *dev = drm->dev;
int ret;
mem->bus.addr = NULL;
@@ -1246,19 +1261,19 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
if (drm->agp.stat == ENABLED) {
mem->bus.offset = mem->start << PAGE_SHIFT;
mem->bus.base = drm->agp.base;
- mem->bus.is_iomem = !dev->agp->cant_use_aperture;
+ mem->bus.is_iomem = !drm->dev->agp->cant_use_aperture;
}
#endif
- if (nv_device(drm->device)->card_type < NV_50 || !node->memtype)
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA || !node->memtype)
/* untiled */
break;
/* fallthrough, tiled memory */
case TTM_PL_VRAM:
mem->bus.offset = mem->start << PAGE_SHIFT;
- mem->bus.base = nv_device_resource_start(nouveau_dev(dev), 1);
+ mem->bus.base = nv_device_resource_start(nvkm_device(&drm->device), 1);
mem->bus.is_iomem = true;
- if (nv_device(drm->device)->card_type >= NV_50) {
- struct nouveau_bar *bar = nouveau_bar(drm->device);
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
+ struct nouveau_bar *bar = nvkm_bar(&drm->device);
ret = bar->umap(bar, node, NV_MEM_ACCESS_RW,
&node->bar_vma);
@@ -1278,7 +1293,7 @@ static void
nouveau_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
{
struct nouveau_drm *drm = nouveau_bdev(bdev);
- struct nouveau_bar *bar = nouveau_bar(drm->device);
+ struct nouveau_bar *bar = nvkm_bar(&drm->device);
struct nouveau_mem *node = mem->mm_node;
if (!node->bar_vma.node)
@@ -1292,15 +1307,15 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo)
{
struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
struct nouveau_bo *nvbo = nouveau_bo(bo);
- struct nouveau_device *device = nv_device(drm->device);
- u32 mappable = nv_device_resource_len(device, 1) >> PAGE_SHIFT;
- int ret;
+ struct nvif_device *device = &drm->device;
+ u32 mappable = nv_device_resource_len(nvkm_device(device), 1) >> PAGE_SHIFT;
+ int i, ret;
/* as long as the bo isn't in vram, and isn't tiled, we've got
* nothing to do here.
*/
if (bo->mem.mem_type != TTM_PL_VRAM) {
- if (nv_device(drm->device)->card_type < NV_50 ||
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA ||
!nouveau_bo_tile_layout(nvbo))
return 0;
@@ -1315,13 +1330,20 @@ nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo)
}
/* make sure bo is in mappable vram */
- if (nv_device(drm->device)->card_type >= NV_50 ||
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA ||
bo->mem.start + bo->mem.num_pages < mappable)
return 0;
+ for (i = 0; i < nvbo->placement.num_placement; ++i) {
+ nvbo->placements[i].fpfn = 0;
+ nvbo->placements[i].lpfn = mappable;
+ }
+
+ for (i = 0; i < nvbo->placement.num_busy_placement; ++i) {
+ nvbo->busy_placements[i].fpfn = 0;
+ nvbo->busy_placements[i].lpfn = mappable;
+ }
- nvbo->placement.fpfn = 0;
- nvbo->placement.lpfn = mappable;
nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_VRAM, 0);
return nouveau_bo_validate(nvbo, false, false);
}
@@ -1333,6 +1355,7 @@ nouveau_ttm_tt_populate(struct ttm_tt *ttm)
struct nouveau_drm *drm;
struct nouveau_device *device;
struct drm_device *dev;
+ struct device *pdev;
unsigned i;
int r;
bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG);
@@ -1349,8 +1372,9 @@ nouveau_ttm_tt_populate(struct ttm_tt *ttm)
}
drm = nouveau_bdev(ttm->bdev);
- device = nv_device(drm->device);
+ device = nvkm_device(&drm->device);
dev = drm->dev;
+ pdev = nv_device_base(device);
#if __OS_HAS_AGP
if (drm->agp.stat == ENABLED) {
@@ -1370,17 +1394,22 @@ nouveau_ttm_tt_populate(struct ttm_tt *ttm)
}
for (i = 0; i < ttm->num_pages; i++) {
- ttm_dma->dma_address[i] = nv_device_map_page(device,
- ttm->pages[i]);
- if (!ttm_dma->dma_address[i]) {
+ dma_addr_t addr;
+
+ addr = dma_map_page(pdev, ttm->pages[i], 0, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(pdev, addr)) {
while (--i) {
- nv_device_unmap_page(device,
- ttm_dma->dma_address[i]);
+ dma_unmap_page(pdev, ttm_dma->dma_address[i],
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
ttm_dma->dma_address[i] = 0;
}
ttm_pool_unpopulate(ttm);
return -EFAULT;
}
+
+ ttm_dma->dma_address[i] = addr;
}
return 0;
}
@@ -1392,6 +1421,7 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
struct nouveau_drm *drm;
struct nouveau_device *device;
struct drm_device *dev;
+ struct device *pdev;
unsigned i;
bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG);
@@ -1399,8 +1429,9 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
return;
drm = nouveau_bdev(ttm->bdev);
- device = nv_device(drm->device);
+ device = nvkm_device(&drm->device);
dev = drm->dev;
+ pdev = nv_device_base(device);
#if __OS_HAS_AGP
if (drm->agp.stat == ENABLED) {
@@ -1418,7 +1449,8 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
for (i = 0; i < ttm->num_pages; i++) {
if (ttm_dma->dma_address[i]) {
- nv_device_unmap_page(device, ttm_dma->dma_address[i]);
+ dma_unmap_page(pdev, ttm_dma->dma_address[i], PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
}
}
@@ -1426,47 +1458,14 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
}
void
-nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence)
-{
- struct nouveau_fence *new_fence = nouveau_fence_ref(fence);
- struct nouveau_fence *old_fence = NULL;
-
- spin_lock(&nvbo->bo.bdev->fence_lock);
- old_fence = nvbo->bo.sync_obj;
- nvbo->bo.sync_obj = new_fence;
- spin_unlock(&nvbo->bo.bdev->fence_lock);
-
- nouveau_fence_unref(&old_fence);
-}
-
-static void
-nouveau_bo_fence_unref(void **sync_obj)
-{
- nouveau_fence_unref((struct nouveau_fence **)sync_obj);
-}
-
-static void *
-nouveau_bo_fence_ref(void *sync_obj)
+nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence, bool exclusive)
{
- return nouveau_fence_ref(sync_obj);
-}
-
-static bool
-nouveau_bo_fence_signalled(void *sync_obj)
-{
- return nouveau_fence_done(sync_obj);
-}
+ struct reservation_object *resv = nvbo->bo.resv;
-static int
-nouveau_bo_fence_wait(void *sync_obj, bool lazy, bool intr)
-{
- return nouveau_fence_wait(sync_obj, lazy, intr);
-}
-
-static int
-nouveau_bo_fence_flush(void *sync_obj)
-{
- return 0;
+ if (exclusive)
+ reservation_object_add_excl_fence(resv, &fence->base);
+ else if (fence)
+ reservation_object_add_shared_fence(resv, &fence->base);
}
struct ttm_bo_driver nouveau_bo_driver = {
@@ -1479,11 +1478,6 @@ struct ttm_bo_driver nouveau_bo_driver = {
.move_notify = nouveau_bo_move_ntfy,
.move = nouveau_bo_move,
.verify_access = nouveau_bo_verify_access,
- .sync_obj_signaled = nouveau_bo_fence_signalled,
- .sync_obj_wait = nouveau_bo_fence_wait,
- .sync_obj_flush = nouveau_bo_fence_flush,
- .sync_obj_unref = nouveau_bo_fence_unref,
- .sync_obj_ref = nouveau_bo_fence_ref,
.fault_reserve_notify = &nouveau_ttm_fault_reserve_notify,
.io_mem_reserve = &nouveau_ttm_io_mem_reserve,
.io_mem_free = &nouveau_ttm_io_mem_free,
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h
index ff17c1f432fc..22d2c764d80b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.h
@@ -1,6 +1,8 @@
#ifndef __NOUVEAU_BO_H__
#define __NOUVEAU_BO_H__
+#include <drm/drm_gem.h>
+
struct nouveau_channel;
struct nouveau_fence;
struct nouveau_vma;
@@ -9,8 +11,8 @@ struct nouveau_bo {
struct ttm_buffer_object bo;
struct ttm_placement placement;
u32 valid_domains;
- u32 placements[3];
- u32 busy_placements[3];
+ struct ttm_place placements[3];
+ struct ttm_place busy_placements[3];
struct ttm_bo_kmap_obj kmap;
struct list_head head;
@@ -68,6 +70,7 @@ extern struct ttm_bo_driver nouveau_bo_driver;
void nouveau_bo_move_init(struct nouveau_drm *);
int nouveau_bo_new(struct drm_device *, int size, int align, u32 flags,
u32 tile_mode, u32 tile_flags, struct sg_table *sg,
+ struct reservation_object *robj,
struct nouveau_bo **);
int nouveau_bo_pin(struct nouveau_bo *, u32 flags);
int nouveau_bo_unpin(struct nouveau_bo *);
@@ -78,7 +81,7 @@ u16 nouveau_bo_rd16(struct nouveau_bo *, unsigned index);
void nouveau_bo_wr16(struct nouveau_bo *, unsigned index, u16 val);
u32 nouveau_bo_rd32(struct nouveau_bo *, unsigned index);
void nouveau_bo_wr32(struct nouveau_bo *, unsigned index, u32 val);
-void nouveau_bo_fence(struct nouveau_bo *, struct nouveau_fence *);
+void nouveau_bo_fence(struct nouveau_bo *, struct nouveau_fence *, bool exclusive);
int nouveau_bo_validate(struct nouveau_bo *, bool interruptible,
bool no_wait_gpu);
diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c
index ccb6b452d6d0..fd3dbd59d73e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_chan.c
+++ b/drivers/gpu/drm/nouveau/nouveau_chan.c
@@ -22,16 +22,11 @@
* Authors: Ben Skeggs
*/
-#include <core/object.h>
-#include <core/client.h>
-#include <core/device.h>
-#include <core/class.h>
-
-#include <subdev/fb.h>
-#include <subdev/vm.h>
-#include <subdev/instmem.h>
+#include <nvif/os.h>
+#include <nvif/class.h>
-#include <engine/software.h>
+/*XXX*/
+#include <core/client.h>
#include "nouveau_drm.h"
#include "nouveau_dma.h"
@@ -41,13 +36,13 @@
#include "nouveau_abi16.h"
MODULE_PARM_DESC(vram_pushbuf, "Create DMA push buffers in VRAM");
-static int nouveau_vram_pushbuf;
+int nouveau_vram_pushbuf;
module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400);
int
nouveau_channel_idle(struct nouveau_channel *chan)
{
- struct nouveau_cli *cli = chan->cli;
+ struct nouveau_cli *cli = (void *)nvif_client(chan->object);
struct nouveau_fence *fence = NULL;
int ret;
@@ -58,8 +53,8 @@ nouveau_channel_idle(struct nouveau_channel *chan)
}
if (ret)
- NV_ERROR(cli, "failed to idle channel 0x%08x [%s]\n",
- chan->handle, cli->base.name);
+ NV_PRINTK(error, cli, "failed to idle channel 0x%08x [%s]\n",
+ chan->object->handle, nvkm_client(&cli->base)->name);
return ret;
}
@@ -68,36 +63,34 @@ nouveau_channel_del(struct nouveau_channel **pchan)
{
struct nouveau_channel *chan = *pchan;
if (chan) {
- struct nouveau_object *client = nv_object(chan->cli);
if (chan->fence) {
nouveau_channel_idle(chan);
nouveau_fence(chan->drm)->context_del(chan);
}
- nouveau_object_del(client, NVDRM_DEVICE, chan->handle);
- nouveau_object_del(client, NVDRM_DEVICE, chan->push.handle);
+ nvif_object_fini(&chan->nvsw);
+ nvif_object_fini(&chan->gart);
+ nvif_object_fini(&chan->vram);
+ nvif_object_ref(NULL, &chan->object);
+ nvif_object_fini(&chan->push.ctxdma);
nouveau_bo_vma_del(chan->push.buffer, &chan->push.vma);
nouveau_bo_unmap(chan->push.buffer);
if (chan->push.buffer && chan->push.buffer->pin_refcnt)
nouveau_bo_unpin(chan->push.buffer);
nouveau_bo_ref(NULL, &chan->push.buffer);
+ nvif_device_ref(NULL, &chan->device);
kfree(chan);
}
*pchan = NULL;
}
static int
-nouveau_channel_prep(struct nouveau_drm *drm, struct nouveau_cli *cli,
- u32 parent, u32 handle, u32 size,
- struct nouveau_channel **pchan)
+nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device,
+ u32 handle, u32 size, struct nouveau_channel **pchan)
{
- struct nouveau_device *device = nv_device(drm->device);
- struct nouveau_instmem *imem = nouveau_instmem(device);
- struct nouveau_vmmgr *vmm = nouveau_vmmgr(device);
- struct nouveau_fb *pfb = nouveau_fb(device);
- struct nouveau_client *client = &cli->base;
- struct nv_dma_class args = {};
+ struct nouveau_cli *cli = (void *)nvif_client(&device->base);
+ struct nouveau_vmmgr *vmm = nvkm_vmmgr(device);
+ struct nv_dma_v0 args = {};
struct nouveau_channel *chan;
- struct nouveau_object *push;
u32 target;
int ret;
@@ -105,16 +98,15 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nouveau_cli *cli,
if (!chan)
return -ENOMEM;
- chan->cli = cli;
+ nvif_device_ref(device, &chan->device);
chan->drm = drm;
- chan->handle = handle;
/* allocate memory for dma push buffer */
target = TTM_PL_FLAG_TT;
if (nouveau_vram_pushbuf)
target = TTM_PL_FLAG_VRAM;
- ret = nouveau_bo_new(drm->dev, size, 0, target, 0, 0, NULL,
+ ret = nouveau_bo_new(drm->dev, size, 0, target, 0, 0, NULL, NULL,
&chan->push.buffer);
if (ret == 0) {
ret = nouveau_bo_pin(chan->push.buffer, target);
@@ -132,51 +124,54 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nouveau_cli *cli,
* we be able to call out to other (indirect) push buffers
*/
chan->push.vma.offset = chan->push.buffer->bo.offset;
- chan->push.handle = NVDRM_PUSH | (handle & 0xffff);
- if (device->card_type >= NV_50) {
- ret = nouveau_bo_vma_add(chan->push.buffer, client->vm,
+ if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
+ ret = nouveau_bo_vma_add(chan->push.buffer, cli->vm,
&chan->push.vma);
if (ret) {
nouveau_channel_del(pchan);
return ret;
}
- args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_VM;
+ args.target = NV_DMA_V0_TARGET_VM;
+ args.access = NV_DMA_V0_ACCESS_VM;
args.start = 0;
- args.limit = client->vm->vmm->limit - 1;
+ args.limit = cli->vm->vmm->limit - 1;
} else
if (chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM) {
- u64 limit = pfb->ram->size - imem->reserved - 1;
- if (device->card_type == NV_04) {
+ if (device->info.family == NV_DEVICE_INFO_V0_TNT) {
/* nv04 vram pushbuf hack, retarget to its location in
* the framebuffer bar rather than direct vram access..
* nfi why this exists, it came from the -nv ddx.
*/
- args.flags = NV_DMA_TARGET_PCI | NV_DMA_ACCESS_RDWR;
- args.start = nv_device_resource_start(device, 1);
- args.limit = args.start + limit;
+ args.target = NV_DMA_V0_TARGET_PCI;
+ args.access = NV_DMA_V0_ACCESS_RDWR;
+ args.start = nv_device_resource_start(nvkm_device(device), 1);
+ args.limit = args.start + device->info.ram_user - 1;
} else {
- args.flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR;
+ args.target = NV_DMA_V0_TARGET_VRAM;
+ args.access = NV_DMA_V0_ACCESS_RDWR;
args.start = 0;
- args.limit = limit;
+ args.limit = device->info.ram_user - 1;
}
} else {
if (chan->drm->agp.stat == ENABLED) {
- args.flags = NV_DMA_TARGET_AGP | NV_DMA_ACCESS_RDWR;
+ args.target = NV_DMA_V0_TARGET_AGP;
+ args.access = NV_DMA_V0_ACCESS_RDWR;
args.start = chan->drm->agp.base;
args.limit = chan->drm->agp.base +
chan->drm->agp.size - 1;
} else {
- args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_RDWR;
+ args.target = NV_DMA_V0_TARGET_VM;
+ args.access = NV_DMA_V0_ACCESS_RDWR;
args.start = 0;
args.limit = vmm->limit - 1;
}
}
- ret = nouveau_object_new(nv_object(chan->cli), parent,
- chan->push.handle, 0x0002,
- &args, sizeof(args), &push);
+ ret = nvif_object_init(nvif_object(device), NULL, NVDRM_PUSH |
+ (handle & 0xffff), NV_DMA_FROM_MEMORY,
+ &args, sizeof(args), &chan->push.ctxdma);
if (ret) {
nouveau_channel_del(pchan);
return ret;
@@ -186,38 +181,56 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nouveau_cli *cli,
}
static int
-nouveau_channel_ind(struct nouveau_drm *drm, struct nouveau_cli *cli,
- u32 parent, u32 handle, u32 engine,
- struct nouveau_channel **pchan)
+nouveau_channel_ind(struct nouveau_drm *drm, struct nvif_device *device,
+ u32 handle, u32 engine, struct nouveau_channel **pchan)
{
- static const u16 oclasses[] = { NVE0_CHANNEL_IND_CLASS,
- NVC0_CHANNEL_IND_CLASS,
- NV84_CHANNEL_IND_CLASS,
- NV50_CHANNEL_IND_CLASS,
+ static const u16 oclasses[] = { KEPLER_CHANNEL_GPFIFO_A,
+ FERMI_CHANNEL_GPFIFO,
+ G82_CHANNEL_GPFIFO,
+ NV50_CHANNEL_GPFIFO,
0 };
const u16 *oclass = oclasses;
- struct nve0_channel_ind_class args;
+ union {
+ struct nv50_channel_gpfifo_v0 nv50;
+ struct kepler_channel_gpfifo_a_v0 kepler;
+ } args, *retn;
struct nouveau_channel *chan;
+ u32 size;
int ret;
/* allocate dma push buffer */
- ret = nouveau_channel_prep(drm, cli, parent, handle, 0x12000, &chan);
+ ret = nouveau_channel_prep(drm, device, handle, 0x12000, &chan);
*pchan = chan;
if (ret)
return ret;
/* create channel object */
- args.pushbuf = chan->push.handle;
- args.ioffset = 0x10000 + chan->push.vma.offset;
- args.ilength = 0x02000;
- args.engine = engine;
-
do {
- ret = nouveau_object_new(nv_object(cli), parent, handle,
- *oclass++, &args, sizeof(args),
- &chan->object);
- if (ret == 0)
+ if (oclass[0] >= KEPLER_CHANNEL_GPFIFO_A) {
+ args.kepler.version = 0;
+ args.kepler.engine = engine;
+ args.kepler.pushbuf = chan->push.ctxdma.handle;
+ args.kepler.ilength = 0x02000;
+ args.kepler.ioffset = 0x10000 + chan->push.vma.offset;
+ size = sizeof(args.kepler);
+ } else {
+ args.nv50.version = 0;
+ args.nv50.pushbuf = chan->push.ctxdma.handle;
+ args.nv50.ilength = 0x02000;
+ args.nv50.ioffset = 0x10000 + chan->push.vma.offset;
+ size = sizeof(args.nv50);
+ }
+
+ ret = nvif_object_new(nvif_object(device), handle, *oclass++,
+ &args, size, &chan->object);
+ if (ret == 0) {
+ retn = chan->object->data;
+ if (chan->object->oclass >= KEPLER_CHANNEL_GPFIFO_A)
+ chan->chid = retn->kepler.chid;
+ else
+ chan->chid = retn->nv50.chid;
return ret;
+ }
} while (*oclass);
nouveau_channel_del(pchan);
@@ -225,35 +238,38 @@ nouveau_channel_ind(struct nouveau_drm *drm, struct nouveau_cli *cli,
}
static int
-nouveau_channel_dma(struct nouveau_drm *drm, struct nouveau_cli *cli,
- u32 parent, u32 handle, struct nouveau_channel **pchan)
+nouveau_channel_dma(struct nouveau_drm *drm, struct nvif_device *device,
+ u32 handle, struct nouveau_channel **pchan)
{
- static const u16 oclasses[] = { NV40_CHANNEL_DMA_CLASS,
- NV17_CHANNEL_DMA_CLASS,
- NV10_CHANNEL_DMA_CLASS,
- NV03_CHANNEL_DMA_CLASS,
+ static const u16 oclasses[] = { NV40_CHANNEL_DMA,
+ NV17_CHANNEL_DMA,
+ NV10_CHANNEL_DMA,
+ NV03_CHANNEL_DMA,
0 };
const u16 *oclass = oclasses;
- struct nv03_channel_dma_class args;
+ struct nv03_channel_dma_v0 args, *retn;
struct nouveau_channel *chan;
int ret;
/* allocate dma push buffer */
- ret = nouveau_channel_prep(drm, cli, parent, handle, 0x10000, &chan);
+ ret = nouveau_channel_prep(drm, device, handle, 0x10000, &chan);
*pchan = chan;
if (ret)
return ret;
/* create channel object */
- args.pushbuf = chan->push.handle;
+ args.version = 0;
+ args.pushbuf = chan->push.ctxdma.handle;
args.offset = chan->push.vma.offset;
do {
- ret = nouveau_object_new(nv_object(cli), parent, handle,
- *oclass++, &args, sizeof(args),
- &chan->object);
- if (ret == 0)
+ ret = nvif_object_new(nvif_object(device), handle, *oclass++,
+ &args, sizeof(args), &chan->object);
+ if (ret == 0) {
+ retn = chan->object->data;
+ chan->chid = retn->chid;
return ret;
+ }
} while (ret && *oclass);
nouveau_channel_del(pchan);
@@ -263,60 +279,64 @@ nouveau_channel_dma(struct nouveau_drm *drm, struct nouveau_cli *cli,
static int
nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart)
{
- struct nouveau_client *client = nv_client(chan->cli);
- struct nouveau_device *device = nv_device(chan->drm->device);
- struct nouveau_instmem *imem = nouveau_instmem(device);
- struct nouveau_vmmgr *vmm = nouveau_vmmgr(device);
- struct nouveau_fb *pfb = nouveau_fb(device);
+ struct nvif_device *device = chan->device;
+ struct nouveau_cli *cli = (void *)nvif_client(&device->base);
+ struct nouveau_vmmgr *vmm = nvkm_vmmgr(device);
struct nouveau_software_chan *swch;
- struct nouveau_object *object;
- struct nv_dma_class args = {};
+ struct nv_dma_v0 args = {};
int ret, i;
+ bool save;
+
+ nvif_object_map(chan->object);
/* allocate dma objects to cover all allowed vram, and gart */
- if (device->card_type < NV_C0) {
- if (device->card_type >= NV_50) {
- args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_VM;
+ if (device->info.family < NV_DEVICE_INFO_V0_FERMI) {
+ if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
+ args.target = NV_DMA_V0_TARGET_VM;
+ args.access = NV_DMA_V0_ACCESS_VM;
args.start = 0;
- args.limit = client->vm->vmm->limit - 1;
+ args.limit = cli->vm->vmm->limit - 1;
} else {
- args.flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR;
+ args.target = NV_DMA_V0_TARGET_VRAM;
+ args.access = NV_DMA_V0_ACCESS_RDWR;
args.start = 0;
- args.limit = pfb->ram->size - imem->reserved - 1;
+ args.limit = device->info.ram_user - 1;
}
- ret = nouveau_object_new(nv_object(client), chan->handle, vram,
- 0x003d, &args, sizeof(args), &object);
+ ret = nvif_object_init(chan->object, NULL, vram,
+ NV_DMA_IN_MEMORY, &args,
+ sizeof(args), &chan->vram);
if (ret)
return ret;
- if (device->card_type >= NV_50) {
- args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_VM;
+ if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
+ args.target = NV_DMA_V0_TARGET_VM;
+ args.access = NV_DMA_V0_ACCESS_VM;
args.start = 0;
- args.limit = client->vm->vmm->limit - 1;
+ args.limit = cli->vm->vmm->limit - 1;
} else
if (chan->drm->agp.stat == ENABLED) {
- args.flags = NV_DMA_TARGET_AGP | NV_DMA_ACCESS_RDWR;
+ args.target = NV_DMA_V0_TARGET_AGP;
+ args.access = NV_DMA_V0_ACCESS_RDWR;
args.start = chan->drm->agp.base;
args.limit = chan->drm->agp.base +
chan->drm->agp.size - 1;
} else {
- args.flags = NV_DMA_TARGET_VM | NV_DMA_ACCESS_RDWR;
+ args.target = NV_DMA_V0_TARGET_VM;
+ args.access = NV_DMA_V0_ACCESS_RDWR;
args.start = 0;
args.limit = vmm->limit - 1;
}
- ret = nouveau_object_new(nv_object(client), chan->handle, gart,
- 0x003d, &args, sizeof(args), &object);
+ ret = nvif_object_init(chan->object, NULL, gart,
+ NV_DMA_IN_MEMORY, &args,
+ sizeof(args), &chan->gart);
if (ret)
return ret;
-
- chan->vram = vram;
- chan->gart = gart;
}
/* initialise dma tracking parameters */
- switch (nv_hclass(chan->object) & 0x00ff) {
+ switch (chan->object->oclass & 0x00ff) {
case 0x006b:
case 0x006e:
chan->user_put = 0x40;
@@ -347,13 +367,13 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart)
OUT_RING(chan, 0x00000000);
/* allocate software object class (used for fences on <= nv05) */
- if (device->card_type < NV_10) {
- ret = nouveau_object_new(nv_object(client), chan->handle,
- NvSw, 0x006e, NULL, 0, &object);
+ if (device->info.family < NV_DEVICE_INFO_V0_CELSIUS) {
+ ret = nvif_object_init(chan->object, NULL, 0x006e, 0x006e,
+ NULL, 0, &chan->nvsw);
if (ret)
return ret;
- swch = (void *)object->parent;
+ swch = (void *)nvkm_object(&chan->nvsw)->parent;
swch->flip = nouveau_flip_complete;
swch->flip_data = chan;
@@ -362,37 +382,48 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart)
return ret;
BEGIN_NV04(chan, NvSubSw, 0x0000, 1);
- OUT_RING (chan, NvSw);
+ OUT_RING (chan, chan->nvsw.handle);
FIRE_RING (chan);
}
/* initialise synchronisation */
- return nouveau_fence(chan->drm)->context_new(chan);
+ save = cli->base.super;
+ cli->base.super = true; /* hack until fencenv50 fixed */
+ ret = nouveau_fence(chan->drm)->context_new(chan);
+ cli->base.super = save;
+ return ret;
}
int
-nouveau_channel_new(struct nouveau_drm *drm, struct nouveau_cli *cli,
- u32 parent, u32 handle, u32 arg0, u32 arg1,
+nouveau_channel_new(struct nouveau_drm *drm, struct nvif_device *device,
+ u32 handle, u32 arg0, u32 arg1,
struct nouveau_channel **pchan)
{
+ struct nouveau_cli *cli = (void *)nvif_client(&device->base);
+ bool super;
int ret;
- ret = nouveau_channel_ind(drm, cli, parent, handle, arg0, pchan);
+ /* hack until fencenv50 is fixed, and agp access relaxed */
+ super = cli->base.super;
+ cli->base.super = true;
+
+ ret = nouveau_channel_ind(drm, device, handle, arg0, pchan);
if (ret) {
- NV_DEBUG(cli, "ib channel create, %d\n", ret);
- ret = nouveau_channel_dma(drm, cli, parent, handle, pchan);
+ NV_PRINTK(debug, cli, "ib channel create, %d\n", ret);
+ ret = nouveau_channel_dma(drm, device, handle, pchan);
if (ret) {
- NV_DEBUG(cli, "dma channel create, %d\n", ret);
- return ret;
+ NV_PRINTK(debug, cli, "dma channel create, %d\n", ret);
+ goto done;
}
}
ret = nouveau_channel_init(*pchan, arg0, arg1);
if (ret) {
- NV_ERROR(cli, "channel failed to initialise, %d\n", ret);
+ NV_PRINTK(error, cli, "channel failed to initialise, %d\n", ret);
nouveau_channel_del(pchan);
- return ret;
}
- return 0;
+done:
+ cli->base.super = super;
+ return ret;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.h b/drivers/gpu/drm/nouveau/nouveau_chan.h
index 40f97e2c47b6..8309c24ee698 100644
--- a/drivers/gpu/drm/nouveau/nouveau_chan.h
+++ b/drivers/gpu/drm/nouveau/nouveau_chan.h
@@ -1,20 +1,23 @@
#ifndef __NOUVEAU_CHAN_H__
#define __NOUVEAU_CHAN_H__
-struct nouveau_cli;
+#include <nvif/object.h>
+struct nvif_device;
struct nouveau_channel {
- struct nouveau_cli *cli;
+ struct nvif_device *device;
struct nouveau_drm *drm;
- u32 handle;
- u32 vram;
- u32 gart;
+ int chid;
+
+ struct nvif_object vram;
+ struct nvif_object gart;
+ struct nvif_object nvsw;
struct {
struct nouveau_bo *buffer;
struct nouveau_vma vma;
- u32 handle;
+ struct nvif_object ctxdma;
} push;
/* TODO: this will be reworked in the near future */
@@ -34,14 +37,16 @@ struct nouveau_channel {
u32 user_get;
u32 user_put;
- struct nouveau_object *object;
+ struct nvif_object *object;
};
-int nouveau_channel_new(struct nouveau_drm *, struct nouveau_cli *,
- u32 parent, u32 handle, u32 arg0, u32 arg1,
+int nouveau_channel_new(struct nouveau_drm *, struct nvif_device *,
+ u32 handle, u32 arg0, u32 arg1,
struct nouveau_channel **);
void nouveau_channel_del(struct nouveau_channel **);
int nouveau_channel_idle(struct nouveau_channel *);
+extern int nouveau_vram_pushbuf;
+
#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 1fa222e8f007..c8ac9482cf2e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -42,20 +42,18 @@
#include "nouveau_encoder.h"
#include "nouveau_crtc.h"
-#include <subdev/i2c.h>
-#include <subdev/gpio.h>
-#include <engine/disp.h>
+#include <nvif/event.h>
MODULE_PARM_DESC(tv_disable, "Disable TV-out detection");
-static int nouveau_tv_disable = 0;
+int nouveau_tv_disable = 0;
module_param_named(tv_disable, nouveau_tv_disable, int, 0400);
MODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status");
-static int nouveau_ignorelid = 0;
+int nouveau_ignorelid = 0;
module_param_named(ignorelid, nouveau_ignorelid, int, 0400);
MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (default: enabled)");
-static int nouveau_duallink = 1;
+int nouveau_duallink = 1;
module_param_named(duallink, nouveau_duallink, int, 0400);
struct nouveau_encoder *
@@ -63,7 +61,7 @@ find_encoder(struct drm_connector *connector, int type)
{
struct drm_device *dev = connector->dev;
struct nouveau_encoder *nv_encoder;
- struct drm_mode_object *obj;
+ struct drm_encoder *enc;
int i, id;
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
@@ -71,10 +69,10 @@ find_encoder(struct drm_connector *connector, int type)
if (!id)
break;
- obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
- if (!obj)
+ enc = drm_encoder_find(dev, id);
+ if (!enc)
continue;
- nv_encoder = nouveau_encoder(obj_to_encoder(obj));
+ nv_encoder = nouveau_encoder(enc);
if (type == DCB_OUTPUT_ANY ||
(nv_encoder->dcb && nv_encoder->dcb->type == type))
@@ -102,9 +100,9 @@ static void
nouveau_connector_destroy(struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
- nouveau_event_ref(NULL, &nv_connector->hpd);
+ nvif_notify_fini(&nv_connector->hpd);
kfree(nv_connector->edid);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
if (nv_connector->aux.transfer)
drm_dp_aux_unregister(&nv_connector->aux);
@@ -117,9 +115,9 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
struct drm_device *dev = connector->dev;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
+ struct nouveau_gpio *gpio = nvkm_gpio(&drm->device);
struct nouveau_encoder *nv_encoder;
- struct drm_mode_object *obj;
+ struct drm_encoder *encoder;
int i, panel = -ENODEV;
/* eDP panels need powering on by us (if the VBIOS doesn't default it
@@ -139,10 +137,10 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
if (id == 0)
break;
- obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
- if (!obj)
+ encoder = drm_encoder_find(dev, id);
+ if (!encoder)
continue;
- nv_encoder = nouveau_encoder(obj_to_encoder(obj));
+ nv_encoder = nouveau_encoder(encoder);
if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
int ret = nouveau_dp_detect(nv_encoder);
@@ -206,7 +204,7 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
return;
nv_connector->detected_encoder = nv_encoder;
- if (nv_device(drm->device)->card_type >= NV_50) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
connector->interlace_allowed = true;
connector->doublescan_allowed = true;
} else
@@ -216,9 +214,8 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
connector->interlace_allowed = false;
} else {
connector->doublescan_allowed = true;
- if (nv_device(drm->device)->card_type == NV_20 ||
- ((nv_device(drm->device)->card_type == NV_10 ||
- nv_device(drm->device)->card_type == NV_11) &&
+ if (drm->device.info.family == NV_DEVICE_INFO_V0_KELVIN ||
+ (drm->device.info.family == NV_DEVICE_INFO_V0_CELSIUS &&
(dev->pdev->device & 0x0ff0) != 0x0100 &&
(dev->pdev->device & 0x0ff0) != 0x0150))
/* HW is broken */
@@ -802,11 +799,11 @@ get_tmds_link_bandwidth(struct drm_connector *connector)
struct dcb_output *dcb = nv_connector->detected_encoder->dcb;
if (dcb->location != DCB_LOC_ON_CHIP ||
- nv_device(drm->device)->chipset >= 0x46)
+ drm->device.info.chipset >= 0x46)
return 165000;
- else if (nv_device(drm->device)->chipset >= 0x40)
+ else if (drm->device.info.chipset >= 0x40)
return 155000;
- else if (nv_device(drm->device)->chipset >= 0x18)
+ else if (drm->device.info.chipset >= 0x18)
return 135000;
else
return 112000;
@@ -939,18 +936,19 @@ nouveau_connector_funcs_dp = {
.force = nouveau_connector_force
};
-static void
-nouveau_connector_hotplug_work(struct work_struct *work)
+static int
+nouveau_connector_hotplug(struct nvif_notify *notify)
{
struct nouveau_connector *nv_connector =
- container_of(work, typeof(*nv_connector), work);
+ container_of(notify, typeof(*nv_connector), hpd);
struct drm_connector *connector = &nv_connector->base;
struct nouveau_drm *drm = nouveau_drm(connector->dev);
+ const struct nvif_notify_conn_rep_v0 *rep = notify->data;
const char *name = connector->name;
- if (nv_connector->status & NVKM_HPD_IRQ) {
+ if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
} else {
- bool plugged = (nv_connector->status != NVKM_HPD_UNPLUG);
+ bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
@@ -961,16 +959,7 @@ nouveau_connector_hotplug_work(struct work_struct *work)
drm_helper_hpd_irq_event(connector->dev);
}
- nouveau_event_get(nv_connector->hpd);
-}
-
-static int
-nouveau_connector_hotplug(void *data, u32 type, int index)
-{
- struct nouveau_connector *nv_connector = data;
- nv_connector->status = type;
- schedule_work(&nv_connector->work);
- return NVKM_EVENT_DROP;
+ return NVIF_NOTIFY_KEEP;
}
static ssize_t
@@ -1040,7 +1029,6 @@ nouveau_connector_create(struct drm_device *dev, int index)
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_display *disp = nouveau_display(dev);
struct nouveau_connector *nv_connector = NULL;
- struct nouveau_disp *pdisp = nouveau_disp(drm->device);
struct drm_connector *connector;
int type, ret = 0;
bool dummy;
@@ -1194,7 +1182,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
switch (nv_connector->type) {
case DCB_CONNECTOR_VGA:
- if (nv_device(drm->device)->card_type >= NV_50) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
drm_object_attach_property(&connector->base,
dev->mode_config.scaling_mode_property,
nv_connector->scaling_mode);
@@ -1226,16 +1214,20 @@ nouveau_connector_create(struct drm_device *dev, int index)
break;
}
- ret = nouveau_event_new(pdisp->hpd, NVKM_HPD, index,
- nouveau_connector_hotplug,
- nv_connector, &nv_connector->hpd);
+ ret = nvif_notify_init(&disp->disp, NULL, nouveau_connector_hotplug,
+ true, NV04_DISP_NTFY_CONN,
+ &(struct nvif_notify_conn_req_v0) {
+ .mask = NVIF_NOTIFY_CONN_V0_ANY,
+ .conn = index,
+ },
+ sizeof(struct nvif_notify_conn_req_v0),
+ sizeof(struct nvif_notify_conn_rep_v0),
+ &nv_connector->hpd);
if (ret)
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
else
connector->polled = DRM_CONNECTOR_POLL_HPD;
- INIT_WORK(&nv_connector->work, nouveau_connector_hotplug_work);
-
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return connector;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
index 8861b6c579ad..629a380c7085 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -27,14 +27,12 @@
#ifndef __NOUVEAU_CONNECTOR_H__
#define __NOUVEAU_CONNECTOR_H__
+#include <nvif/notify.h>
+
#include <drm/drm_edid.h>
#include <drm/drm_dp_helper.h>
#include "nouveau_crtc.h"
-#include <core/event.h>
-
-#include <subdev/bios.h>
-
struct nouveau_i2c_port;
enum nouveau_underscan_type {
@@ -67,9 +65,7 @@ struct nouveau_connector {
u8 index;
u8 *dcb;
- struct nouveau_eventh *hpd;
- u32 status;
- struct work_struct work;
+ struct nvif_notify hpd;
struct drm_dp_aux aux;
@@ -109,4 +105,8 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
struct drm_connector *
nouveau_connector_create(struct drm_device *, int index);
+extern int nouveau_tv_disable;
+extern int nouveau_ignorelid;
+extern int nouveau_duallink;
+
#endif /* __NOUVEAU_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h
index a0534489d23f..f19cb1c5fc5a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_crtc.h
+++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h
@@ -27,10 +27,13 @@
#ifndef __NOUVEAU_CRTC_H__
#define __NOUVEAU_CRTC_H__
+#include <nvif/notify.h>
+
struct nouveau_crtc {
struct drm_crtc base;
int index;
+ struct nvif_notify vblank;
uint32_t dpms_saved_fp_control;
uint32_t fp_users;
@@ -46,7 +49,7 @@ struct nouveau_crtc {
int cpp;
bool blanked;
uint32_t offset;
- uint32_t tile_flags;
+ uint32_t handle;
} fb;
struct {
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 47ad74255bf1..a88e6927f571 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -27,6 +27,8 @@
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
+#include <nvif/class.h>
+
#include "nouveau_fbcon.h"
#include "dispnv04/hw.h"
#include "nouveau_crtc.h"
@@ -37,35 +39,42 @@
#include "nouveau_fence.h"
-#include <engine/disp.h>
-
-#include <core/class.h>
+#include <nvif/event.h>
static int
-nouveau_display_vblank_handler(void *data, u32 type, int head)
+nouveau_display_vblank_handler(struct nvif_notify *notify)
{
- struct nouveau_drm *drm = data;
- drm_handle_vblank(drm->dev, head);
- return NVKM_EVENT_KEEP;
+ struct nouveau_crtc *nv_crtc =
+ container_of(notify, typeof(*nv_crtc), vblank);
+ drm_handle_vblank(nv_crtc->base.dev, nv_crtc->index);
+ return NVIF_NOTIFY_KEEP;
}
int
nouveau_display_vblank_enable(struct drm_device *dev, int head)
{
- struct nouveau_display *disp = nouveau_display(dev);
- if (disp) {
- nouveau_event_get(disp->vblank[head]);
- return 0;
+ struct drm_crtc *crtc;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ if (nv_crtc->index == head) {
+ nvif_notify_get(&nv_crtc->vblank);
+ return 0;
+ }
}
- return -EIO;
+ return -EINVAL;
}
void
nouveau_display_vblank_disable(struct drm_device *dev, int head)
{
- struct nouveau_display *disp = nouveau_display(dev);
- if (disp)
- nouveau_event_put(disp->vblank[head]);
+ struct drm_crtc *crtc;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ if (nv_crtc->index == head) {
+ nvif_notify_put(&nv_crtc->vblank);
+ return;
+ }
+ }
}
static inline int
@@ -86,17 +95,22 @@ int
nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime)
{
- const u32 mthd = NV04_DISP_SCANOUTPOS + nouveau_crtc(crtc)->index;
+ struct {
+ struct nv04_disp_mthd_v0 base;
+ struct nv04_disp_scanoutpos_v0 scan;
+ } args = {
+ .base.method = NV04_DISP_SCANOUTPOS,
+ .base.head = nouveau_crtc(crtc)->index,
+ };
struct nouveau_display *disp = nouveau_display(crtc->dev);
- struct nv04_display_scanoutpos args;
int ret, retry = 1;
do {
- ret = nv_exec(disp->core, mthd, &args, sizeof(args));
+ ret = nvif_mthd(&disp->disp, 0, &args, sizeof(args));
if (ret != 0)
return 0;
- if (args.vline) {
+ if (args.scan.vline) {
ret |= DRM_SCANOUTPOS_ACCURATE;
ret |= DRM_SCANOUTPOS_VALID;
break;
@@ -105,13 +119,14 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
if (retry) ndelay(crtc->linedur_ns);
} while (retry--);
- *hpos = args.hline;
- *vpos = calc(args.vblanks, args.vblanke, args.vtotal, args.vline);
- if (stime) *stime = ns_to_ktime(args.time[0]);
- if (etime) *etime = ns_to_ktime(args.time[1]);
+ *hpos = args.scan.hline;
+ *vpos = calc(args.scan.vblanks, args.scan.vblanke,
+ args.scan.vtotal, args.scan.vline);
+ if (stime) *stime = ns_to_ktime(args.scan.time[0]);
+ if (etime) *etime = ns_to_ktime(args.scan.time[1]);
if (*vpos < 0)
- ret |= DRM_SCANOUTPOS_INVBL;
+ ret |= DRM_SCANOUTPOS_IN_VBLANK;
return ret;
}
@@ -151,16 +166,13 @@ nouveau_display_vblstamp(struct drm_device *dev, int head, int *max_error,
static void
nouveau_display_vblank_fini(struct drm_device *dev)
{
- struct nouveau_display *disp = nouveau_display(dev);
- int i;
+ struct drm_crtc *crtc;
drm_vblank_cleanup(dev);
- if (disp->vblank) {
- for (i = 0; i < dev->mode_config.num_crtc; i++)
- nouveau_event_ref(NULL, &disp->vblank[i]);
- kfree(disp->vblank);
- disp->vblank = NULL;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ nvif_notify_fini(&nv_crtc->vblank);
}
}
@@ -168,19 +180,20 @@ static int
nouveau_display_vblank_init(struct drm_device *dev)
{
struct nouveau_display *disp = nouveau_display(dev);
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_disp *pdisp = nouveau_disp(drm->device);
- int ret, i;
-
- disp->vblank = kzalloc(dev->mode_config.num_crtc *
- sizeof(*disp->vblank), GFP_KERNEL);
- if (!disp->vblank)
- return -ENOMEM;
+ struct drm_crtc *crtc;
+ int ret;
- for (i = 0; i < dev->mode_config.num_crtc; i++) {
- ret = nouveau_event_new(pdisp->vblank, 1, i,
- nouveau_display_vblank_handler,
- drm, &disp->vblank[i]);
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ ret = nvif_notify_init(&disp->disp, NULL,
+ nouveau_display_vblank_handler, false,
+ NV04_DISP_NTFY_VBLANK,
+ &(struct nvif_notify_head_req_v0) {
+ .head = nv_crtc->index,
+ },
+ sizeof(struct nvif_notify_head_req_v0),
+ sizeof(struct nvif_notify_head_rep_v0),
+ &nv_crtc->vblank);
if (ret) {
nouveau_display_vblank_fini(dev);
return ret;
@@ -200,6 +213,10 @@ static void
nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
{
struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
+ struct nouveau_display *disp = nouveau_display(drm_fb->dev);
+
+ if (disp->fb_dtor)
+ disp->fb_dtor(drm_fb);
if (fb->nvbo)
drm_gem_object_unreference_unlocked(&fb->nvbo->gem);
@@ -229,63 +246,24 @@ nouveau_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd,
struct nouveau_bo *nvbo)
{
- struct nouveau_drm *drm = nouveau_drm(dev);
+ struct nouveau_display *disp = nouveau_display(dev);
struct drm_framebuffer *fb = &nv_fb->base;
int ret;
drm_helper_mode_fill_fb_struct(fb, mode_cmd);
nv_fb->nvbo = nvbo;
- if (nv_device(drm->device)->card_type >= NV_50) {
- u32 tile_flags = nouveau_bo_tile_layout(nvbo);
- if (tile_flags == 0x7a00 ||
- tile_flags == 0xfe00)
- nv_fb->r_dma = NvEvoFB32;
- else
- if (tile_flags == 0x7000)
- nv_fb->r_dma = NvEvoFB16;
- else
- nv_fb->r_dma = NvEvoVRAM_LP;
-
- switch (fb->depth) {
- case 8: nv_fb->r_format = 0x1e00; break;
- case 15: nv_fb->r_format = 0xe900; break;
- case 16: nv_fb->r_format = 0xe800; break;
- case 24:
- case 32: nv_fb->r_format = 0xcf00; break;
- case 30: nv_fb->r_format = 0xd100; break;
- default:
- NV_ERROR(drm, "unknown depth %d\n", fb->depth);
- return -EINVAL;
- }
-
- if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG) {
- NV_ERROR(drm, "framebuffer requires contiguous bo\n");
- return -EINVAL;
- }
-
- if (nv_device(drm->device)->chipset == 0x50)
- nv_fb->r_format |= (tile_flags << 8);
-
- if (!tile_flags) {
- if (nv_device(drm->device)->card_type < NV_D0)
- nv_fb->r_pitch = 0x00100000 | fb->pitches[0];
- else
- nv_fb->r_pitch = 0x01000000 | fb->pitches[0];
- } else {
- u32 mode = nvbo->tile_mode;
- if (nv_device(drm->device)->card_type >= NV_C0)
- mode >>= 4;
- nv_fb->r_pitch = ((fb->pitches[0] / 4) << 4) | mode;
- }
- }
-
ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs);
- if (ret) {
+ if (ret)
return ret;
+
+ if (disp->fb_ctor) {
+ ret = disp->fb_ctor(fb);
+ if (ret)
+ disp->fb_dtor(fb);
}
- return 0;
+ return ret;
}
static struct drm_framebuffer *
@@ -393,7 +371,7 @@ nouveau_display_init(struct drm_device *dev)
/* enable hotplug interrupts */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct nouveau_connector *conn = nouveau_connector(connector);
- if (conn->hpd) nouveau_event_get(conn->hpd);
+ nvif_notify_get(&conn->hpd);
}
return ret;
@@ -404,37 +382,32 @@ nouveau_display_fini(struct drm_device *dev)
{
struct nouveau_display *disp = nouveau_display(dev);
struct drm_connector *connector;
+ int head;
+
+ /* Make sure that drm and hw vblank irqs get properly disabled. */
+ for (head = 0; head < dev->mode_config.num_crtc; head++)
+ drm_vblank_off(dev, head);
/* disable hotplug interrupts */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct nouveau_connector *conn = nouveau_connector(connector);
- if (conn->hpd) nouveau_event_put(conn->hpd);
+ nvif_notify_put(&conn->hpd);
}
drm_kms_helper_poll_disable(dev);
disp->fini(dev);
}
-int
-nouveau_display_create(struct drm_device *dev)
+static void
+nouveau_display_create_properties(struct drm_device *dev)
{
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nouveau_dev(dev);
- struct nouveau_display *disp;
- int ret, gen;
-
- disp = drm->display = kzalloc(sizeof(*disp), GFP_KERNEL);
- if (!disp)
- return -ENOMEM;
-
- drm_mode_config_init(dev);
- drm_mode_create_scaling_mode_property(dev);
- drm_mode_create_dvi_i_properties(dev);
+ struct nouveau_display *disp = nouveau_display(dev);
+ int gen;
- if (nv_device(drm->device)->card_type < NV_50)
+ if (disp->disp.oclass < NV50_DISP)
gen = 0;
else
- if (nv_device(drm->device)->card_type < NV_D0)
+ if (disp->disp.oclass < GF110_DISP)
gen = 1;
else
gen = 2;
@@ -449,26 +422,43 @@ nouveau_display_create(struct drm_device *dev)
disp->underscan_vborder_property =
drm_property_create_range(dev, 0, "underscan vborder", 0, 128);
- if (gen >= 1) {
- /* -90..+90 */
- disp->vibrant_hue_property =
- drm_property_create_range(dev, 0, "vibrant hue", 0, 180);
+ if (gen < 1)
+ return;
- /* -100..+100 */
- disp->color_vibrance_property =
- drm_property_create_range(dev, 0, "color vibrance", 0, 200);
- }
+ /* -90..+90 */
+ disp->vibrant_hue_property =
+ drm_property_create_range(dev, 0, "vibrant hue", 0, 180);
+
+ /* -100..+100 */
+ disp->color_vibrance_property =
+ drm_property_create_range(dev, 0, "color vibrance", 0, 200);
+}
+
+int
+nouveau_display_create(struct drm_device *dev)
+{
+ struct nouveau_drm *drm = nouveau_drm(dev);
+ struct nouveau_display *disp;
+ int ret;
+
+ disp = drm->display = kzalloc(sizeof(*disp), GFP_KERNEL);
+ if (!disp)
+ return -ENOMEM;
+
+ drm_mode_config_init(dev);
+ drm_mode_create_scaling_mode_property(dev);
+ drm_mode_create_dvi_i_properties(dev);
dev->mode_config.funcs = &nouveau_mode_config_funcs;
- dev->mode_config.fb_base = nv_device_resource_start(device, 1);
+ dev->mode_config.fb_base = nv_device_resource_start(nvkm_device(&drm->device), 1);
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
- if (nv_device(drm->device)->card_type < NV_10) {
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_CELSIUS) {
dev->mode_config.max_width = 2048;
dev->mode_config.max_height = 2048;
} else
- if (nv_device(drm->device)->card_type < NV_50) {
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) {
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
} else {
@@ -479,7 +469,7 @@ nouveau_display_create(struct drm_device *dev)
dev->mode_config.preferred_depth = 24;
dev->mode_config.prefer_shadow = 1;
- if (nv_device(drm->device)->chipset < 0x11)
+ if (drm->device.info.chipset < 0x11)
dev->mode_config.async_page_flip = false;
else
dev->mode_config.async_page_flip = true;
@@ -487,29 +477,30 @@ nouveau_display_create(struct drm_device *dev)
drm_kms_helper_poll_init(dev);
drm_kms_helper_poll_disable(dev);
- if (drm->vbios.dcb.entries) {
+ if (nouveau_modeset != 2 && drm->vbios.dcb.entries) {
static const u16 oclass[] = {
- GM107_DISP_CLASS,
- NVF0_DISP_CLASS,
- NVE0_DISP_CLASS,
- NVD0_DISP_CLASS,
- NVA3_DISP_CLASS,
- NV94_DISP_CLASS,
- NVA0_DISP_CLASS,
- NV84_DISP_CLASS,
- NV50_DISP_CLASS,
- NV04_DISP_CLASS,
+ GM107_DISP,
+ GK110_DISP,
+ GK104_DISP,
+ GF110_DISP,
+ GT214_DISP,
+ GT206_DISP,
+ GT200_DISP,
+ G82_DISP,
+ NV50_DISP,
+ NV04_DISP,
};
int i;
for (i = 0, ret = -ENODEV; ret && i < ARRAY_SIZE(oclass); i++) {
- ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE,
- NVDRM_DISPLAY, oclass[i],
- NULL, 0, &disp->core);
+ ret = nvif_object_init(nvif_object(&drm->device), NULL,
+ NVDRM_DISPLAY, oclass[i],
+ NULL, 0, &disp->disp);
}
if (ret == 0) {
- if (nv_mclass(disp->core) < NV50_DISP_CLASS)
+ nouveau_display_create_properties(dev);
+ if (disp->disp.oclass < NV50_DISP)
ret = nv04_display_create(dev);
else
ret = nv50_display_create(dev);
@@ -542,7 +533,6 @@ void
nouveau_display_destroy(struct drm_device *dev)
{
struct nouveau_display *disp = nouveau_display(dev);
- struct nouveau_drm *drm = nouveau_drm(dev);
nouveau_backlight_exit(dev);
nouveau_display_vblank_fini(dev);
@@ -553,21 +543,19 @@ nouveau_display_destroy(struct drm_device *dev)
if (disp->dtor)
disp->dtor(dev);
- nouveau_object_del(nv_object(drm), NVDRM_DEVICE, NVDRM_DISPLAY);
+ nvif_object_fini(&disp->disp);
nouveau_drm(dev)->display = NULL;
kfree(disp);
}
int
-nouveau_display_suspend(struct drm_device *dev)
+nouveau_display_suspend(struct drm_device *dev, bool runtime)
{
- struct nouveau_drm *drm = nouveau_drm(dev);
struct drm_crtc *crtc;
nouveau_display_fini(dev);
- NV_INFO(drm, "unpinning framebuffer(s)...\n");
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb;
@@ -589,12 +577,13 @@ nouveau_display_suspend(struct drm_device *dev)
}
void
-nouveau_display_repin(struct drm_device *dev)
+nouveau_display_resume(struct drm_device *dev, bool runtime)
{
struct nouveau_drm *drm = nouveau_drm(dev);
struct drm_crtc *crtc;
- int ret;
+ int ret, head;
+ /* re-pin fb/cursors */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb;
@@ -602,7 +591,9 @@ nouveau_display_repin(struct drm_device *dev)
if (!nouveau_fb || !nouveau_fb->nvbo)
continue;
- nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM);
+ ret = nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM);
+ if (ret)
+ NV_ERROR(drm, "Could not pin framebuffer\n");
}
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@@ -614,12 +605,7 @@ nouveau_display_repin(struct drm_device *dev)
if (ret)
NV_ERROR(drm, "Could not pin/map cursor.\n");
}
-}
-void
-nouveau_display_resume(struct drm_device *dev)
-{
- struct drm_crtc *crtc;
nouveau_display_init(dev);
/* Force CLUT to get re-loaded during modeset */
@@ -629,6 +615,17 @@ nouveau_display_resume(struct drm_device *dev)
nv_crtc->lut.depth = 0;
}
+ /* Make sure that drm and hw vblank irqs get resumed if needed. */
+ for (head = 0; head < dev->mode_config.num_crtc; head++)
+ drm_vblank_on(dev, head);
+
+ /* This should ensure we don't hit a locking problem when someone
+ * wakes us up via a connector. We should never go into suspend
+ * while the display is on anyways.
+ */
+ if (runtime)
+ return;
+
drm_helper_resume_force_mode(dev);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@@ -660,7 +657,7 @@ nouveau_page_flip_emit(struct nouveau_channel *chan,
spin_unlock_irqrestore(&dev->event_lock, flags);
/* Synchronize with the old framebuffer */
- ret = nouveau_fence_sync(old_bo->bo.sync_obj, chan);
+ ret = nouveau_fence_sync(old_bo, chan, false, false);
if (ret)
goto fail;
@@ -669,7 +666,7 @@ nouveau_page_flip_emit(struct nouveau_channel *chan,
if (ret)
goto fail;
- if (nv_device(drm->device)->card_type < NV_C0)
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_FERMI)
BEGIN_NV04(chan, NvSubSw, NV_SW_PAGE_FLIP, 1);
else
BEGIN_NVC0(chan, FermiSw, NV_SW_PAGE_FLIP, 1);
@@ -698,12 +695,15 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->primary->fb)->nvbo;
struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo;
struct nouveau_page_flip_state *s;
- struct nouveau_channel *chan = drm->channel;
+ struct nouveau_channel *chan;
+ struct nouveau_cli *cli;
struct nouveau_fence *fence;
int ret;
- if (!drm->channel)
+ chan = drm->channel;
+ if (!chan)
return -ENODEV;
+ cli = (void *)nvif_client(&chan->device->base);
s = kzalloc(sizeof(*s), GFP_KERNEL);
if (!s)
@@ -715,20 +715,25 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
goto fail_free;
}
- mutex_lock(&chan->cli->mutex);
-
- /* synchronise rendering channel with the kernel's channel */
- spin_lock(&new_bo->bo.bdev->fence_lock);
- fence = nouveau_fence_ref(new_bo->bo.sync_obj);
- spin_unlock(&new_bo->bo.bdev->fence_lock);
- ret = nouveau_fence_sync(fence, chan);
- nouveau_fence_unref(&fence);
+ mutex_lock(&cli->mutex);
+ ret = ttm_bo_reserve(&new_bo->bo, true, false, false, NULL);
if (ret)
goto fail_unpin;
- ret = ttm_bo_reserve(&old_bo->bo, true, false, false, NULL);
- if (ret)
+ /* synchronise rendering channel with the kernel's channel */
+ ret = nouveau_fence_sync(new_bo, chan, false, true);
+ if (ret) {
+ ttm_bo_unreserve(&new_bo->bo);
goto fail_unpin;
+ }
+
+ if (new_bo != old_bo) {
+ ttm_bo_unreserve(&new_bo->bo);
+
+ ret = ttm_bo_reserve(&old_bo->bo, true, false, false, NULL);
+ if (ret)
+ goto fail_unpin;
+ }
/* Initialize a page flip struct */
*s = (struct nouveau_page_flip_state)
@@ -740,7 +745,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
drm_vblank_get(dev, nouveau_crtc(crtc)->index);
/* Emit a page flip */
- if (nv_device(drm->device)->card_type >= NV_50) {
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
ret = nv50_display_flip_next(crtc, fb, chan, swap_interval);
if (ret)
goto fail_unreserve;
@@ -769,12 +774,12 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence);
if (ret)
goto fail_unreserve;
- mutex_unlock(&chan->cli->mutex);
+ mutex_unlock(&cli->mutex);
/* Update the crtc struct and cleanup */
crtc->primary->fb = fb;
- nouveau_bo_fence(old_bo, fence);
+ nouveau_bo_fence(old_bo, fence, false);
ttm_bo_unreserve(&old_bo->bo);
if (old_bo != new_bo)
nouveau_bo_unpin(old_bo);
@@ -785,7 +790,7 @@ fail_unreserve:
drm_vblank_put(dev, nouveau_crtc(crtc)->index);
ttm_bo_unreserve(&old_bo->bo);
fail_unpin:
- mutex_unlock(&chan->cli->mutex);
+ mutex_unlock(&cli->mutex);
if (old_bo != new_bo)
nouveau_bo_unpin(new_bo);
fail_free:
@@ -815,7 +820,7 @@ nouveau_finish_page_flip(struct nouveau_channel *chan,
s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head);
if (s->event) {
/* Vblank timestamps/counts are only correct on >= NV-50 */
- if (nv_device(drm->device)->card_type >= NV_50)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA)
crtcid = s->crtc;
drm_send_vblank_event(dev, crtcid, s->event);
@@ -841,7 +846,7 @@ nouveau_flip_complete(void *data)
struct nouveau_page_flip_state state;
if (!nouveau_finish_page_flip(chan, &state)) {
- if (nv_device(drm->device)->card_type < NV_50) {
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) {
nv_set_crtc_base(drm->dev, state.crtc, state.offset +
state.y * state.pitch +
state.x * state.bpp / 8);
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h
index a71cf77e55b2..be3d5947c6be 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.h
+++ b/drivers/gpu/drm/nouveau/nouveau_display.h
@@ -9,9 +9,11 @@ struct nouveau_framebuffer {
struct drm_framebuffer base;
struct nouveau_bo *nvbo;
struct nouveau_vma vma;
- u32 r_dma;
+ u32 r_handle;
u32 r_format;
u32 r_pitch;
+ struct nvif_object h_base[4];
+ struct nvif_object h_core;
};
static inline struct nouveau_framebuffer *
@@ -36,8 +38,10 @@ struct nouveau_display {
int (*init)(struct drm_device *);
void (*fini)(struct drm_device *);
- struct nouveau_object *core;
- struct nouveau_eventh **vblank;
+ int (*fb_ctor)(struct drm_framebuffer *);
+ void (*fb_dtor)(struct drm_framebuffer *);
+
+ struct nvif_object disp;
struct drm_property *dithering_mode;
struct drm_property *dithering_depth;
@@ -59,9 +63,8 @@ int nouveau_display_create(struct drm_device *dev);
void nouveau_display_destroy(struct drm_device *dev);
int nouveau_display_init(struct drm_device *dev);
void nouveau_display_fini(struct drm_device *dev);
-int nouveau_display_suspend(struct drm_device *dev);
-void nouveau_display_repin(struct drm_device *dev);
-void nouveau_display_resume(struct drm_device *dev);
+int nouveau_display_suspend(struct drm_device *dev, bool runtime);
+void nouveau_display_resume(struct drm_device *dev, bool runtime);
int nouveau_display_vblank_enable(struct drm_device *, int);
void nouveau_display_vblank_disable(struct drm_device *, int);
int nouveau_display_scanoutpos(struct drm_device *, int, unsigned int,
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
index c177272152e2..8508603cc8c3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
@@ -24,8 +24,6 @@
*
*/
-#include <core/client.h>
-
#include "nouveau_drm.h"
#include "nouveau_dma.h"
@@ -54,9 +52,9 @@ READ_GET(struct nouveau_channel *chan, uint64_t *prev_get, int *timeout)
{
uint64_t val;
- val = nv_ro32(chan->object, chan->user_get);
+ val = nvif_rd32(chan, chan->user_get);
if (chan->user_get_hi)
- val |= (uint64_t)nv_ro32(chan->object, chan->user_get_hi) << 32;
+ val |= (uint64_t)nvif_rd32(chan, chan->user_get_hi) << 32;
/* reset counter as long as GET is still advancing, this is
* to avoid misdetecting a GPU lockup if the GPU happens to
@@ -84,12 +82,13 @@ void
nv50_dma_push(struct nouveau_channel *chan, struct nouveau_bo *bo,
int delta, int length)
{
+ struct nouveau_cli *cli = (void *)nvif_client(&chan->device->base);
struct nouveau_bo *pb = chan->push.buffer;
struct nouveau_vma *vma;
int ip = (chan->dma.ib_put * 2) + chan->dma.ib_base;
u64 offset;
- vma = nouveau_bo_vma_find(bo, nv_client(chan->cli)->vm);
+ vma = nouveau_bo_vma_find(bo, cli->vm);
BUG_ON(!vma);
offset = vma->offset + delta;
@@ -104,7 +103,7 @@ nv50_dma_push(struct nouveau_channel *chan, struct nouveau_bo *bo,
/* Flush writes. */
nouveau_bo_rd32(pb, 0);
- nv_wo32(chan->object, 0x8c, chan->dma.ib_put);
+ nvif_wr32(chan, 0x8c, chan->dma.ib_put);
chan->dma.ib_free--;
}
@@ -114,7 +113,7 @@ nv50_dma_push_wait(struct nouveau_channel *chan, int count)
uint32_t cnt = 0, prev_get = 0;
while (chan->dma.ib_free < count) {
- uint32_t get = nv_ro32(chan->object, 0x88);
+ uint32_t get = nvif_rd32(chan, 0x88);
if (get != prev_get) {
prev_get = get;
cnt = 0;
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h
index dc0e0c5cadb4..8da0a272c45a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.h
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.h
@@ -58,31 +58,14 @@ enum {
FermiSw = 5, /* DO NOT CHANGE (well.. 6/7 will work...) */
};
-/* Object handles. */
+/* Object handles - for stuff that's doesn't use handle == oclass. */
enum {
- NvM2MF = 0x80000001,
NvDmaFB = 0x80000002,
NvDmaTT = 0x80000003,
NvNotify0 = 0x80000006,
- Nv2D = 0x80000007,
- NvCtxSurf2D = 0x80000008,
- NvRop = 0x80000009,
- NvImagePatt = 0x8000000a,
- NvClipRect = 0x8000000b,
- NvGdiRect = 0x8000000c,
- NvImageBlit = 0x8000000d,
- NvSw = 0x8000000e,
NvSema = 0x8000000f,
NvEvoSema0 = 0x80000010,
NvEvoSema1 = 0x80000011,
- NvNotify1 = 0x80000012,
-
- /* G80+ display objects */
- NvEvoVRAM = 0x01000000,
- NvEvoFB16 = 0x01000001,
- NvEvoFB32 = 0x01000002,
- NvEvoVRAM_LP = 0x01000003,
- NvEvoSync = 0xcafe0000
};
#define NV_MEMORY_TO_MEMORY_FORMAT 0x00000039
@@ -157,7 +140,7 @@ BEGIN_IMC0(struct nouveau_channel *chan, int subc, int mthd, u16 data)
#define WRITE_PUT(val) do { \
mb(); \
nouveau_bo_rd32(chan->push.buffer, 0); \
- nv_wo32(chan->object, chan->user_put, ((val) << 2) + chan->push.vma.offset); \
+ nvif_wr32(chan, chan->user_put, ((val) << 2) + chan->push.vma.offset); \
} while (0)
static inline void
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 5675ffc175ae..c5137cccce7d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -30,11 +30,6 @@
#include "nouveau_encoder.h"
#include "nouveau_crtc.h"
-#include <core/class.h>
-
-#include <subdev/gpio.h>
-#include <subdev/i2c.h>
-
static void
nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_port *auxch,
u8 *dpcd)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index c9428c943afb..57238076049f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -27,21 +27,14 @@
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/vga_switcheroo.h>
+
#include "drmP.h"
#include "drm_crtc_helper.h"
+
#include <core/device.h>
-#include <core/client.h>
#include <core/gpuobj.h>
-#include <core/class.h>
#include <core/option.h>
-#include <engine/device.h>
-#include <engine/disp.h>
-#include <engine/fifo.h>
-#include <engine/software.h>
-
-#include <subdev/vm.h>
-
#include "nouveau_drm.h"
#include "nouveau_dma.h"
#include "nouveau_ttm.h"
@@ -57,6 +50,8 @@
#include "nouveau_fbcon.h"
#include "nouveau_fence.h"
#include "nouveau_debugfs.h"
+#include "nouveau_usif.h"
+#include "nouveau_connector.h"
MODULE_PARM_DESC(config, "option string to pass to driver core");
static char *nouveau_config;
@@ -79,7 +74,9 @@ MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1
int nouveau_runtime_pm = -1;
module_param_named(runpm, nouveau_runtime_pm, int, 0400);
-static struct drm_driver driver;
+static struct drm_driver driver_stub;
+static struct drm_driver driver_pci;
+static struct drm_driver driver_platform;
static u64
nouveau_pci_name(struct pci_dev *pdev)
@@ -109,40 +106,37 @@ static int
nouveau_cli_create(u64 name, const char *sname,
int size, void **pcli)
{
- struct nouveau_cli *cli;
- int ret;
-
- *pcli = NULL;
- ret = nouveau_client_create_(sname, name, nouveau_config,
- nouveau_debug, size, pcli);
- cli = *pcli;
- if (ret) {
- if (cli)
- nouveau_client_destroy(&cli->base);
- *pcli = NULL;
+ struct nouveau_cli *cli = *pcli = kzalloc(size, GFP_KERNEL);
+ if (cli) {
+ int ret = nvif_client_init(NULL, NULL, sname, name,
+ nouveau_config, nouveau_debug,
+ &cli->base);
+ if (ret == 0) {
+ mutex_init(&cli->mutex);
+ usif_client_init(cli);
+ }
return ret;
}
-
- mutex_init(&cli->mutex);
- return 0;
+ return -ENOMEM;
}
static void
nouveau_cli_destroy(struct nouveau_cli *cli)
{
- struct nouveau_object *client = nv_object(cli);
- nouveau_vm_ref(NULL, &cli->base.vm, NULL);
- nouveau_client_fini(&cli->base, false);
- atomic_set(&client->refcount, 1);
- nouveau_object_ref(NULL, &client);
+ nouveau_vm_ref(NULL, &nvkm_client(&cli->base)->vm, NULL);
+ nvif_client_fini(&cli->base);
+ usif_client_fini(cli);
}
static void
nouveau_accel_fini(struct nouveau_drm *drm)
{
- nouveau_gpuobj_ref(NULL, &drm->notify);
nouveau_channel_del(&drm->channel);
+ nvif_object_fini(&drm->ntfy);
+ nouveau_gpuobj_ref(NULL, &drm->notify);
+ nvif_object_fini(&drm->nvsw);
nouveau_channel_del(&drm->cechan);
+ nvif_object_fini(&drm->ttm.copy);
if (drm->fence)
nouveau_fence(drm)->dtor(drm);
}
@@ -150,46 +144,71 @@ nouveau_accel_fini(struct nouveau_drm *drm)
static void
nouveau_accel_init(struct nouveau_drm *drm)
{
- struct nouveau_device *device = nv_device(drm->device);
- struct nouveau_object *object;
+ struct nvif_device *device = &drm->device;
u32 arg0, arg1;
- int ret;
+ u32 sclass[16];
+ int ret, i;
- if (nouveau_noaccel || !nouveau_fifo(device) /*XXX*/)
+ if (nouveau_noaccel)
return;
/* initialise synchronisation routines */
- if (device->card_type < NV_10) ret = nv04_fence_create(drm);
- else if (device->card_type < NV_11 ||
- device->chipset < 0x17) ret = nv10_fence_create(drm);
- else if (device->card_type < NV_50) ret = nv17_fence_create(drm);
- else if (device->chipset < 0x84) ret = nv50_fence_create(drm);
- else if (device->card_type < NV_C0) ret = nv84_fence_create(drm);
- else ret = nvc0_fence_create(drm);
+ /*XXX: this is crap, but the fence/channel stuff is a little
+ * backwards in some places. this will be fixed.
+ */
+ ret = nvif_object_sclass(&device->base, sclass, ARRAY_SIZE(sclass));
+ if (ret < 0)
+ return;
+
+ for (ret = -ENOSYS, i = 0; ret && i < ARRAY_SIZE(sclass); i++) {
+ switch (sclass[i]) {
+ case NV03_CHANNEL_DMA:
+ ret = nv04_fence_create(drm);
+ break;
+ case NV10_CHANNEL_DMA:
+ ret = nv10_fence_create(drm);
+ break;
+ case NV17_CHANNEL_DMA:
+ case NV40_CHANNEL_DMA:
+ ret = nv17_fence_create(drm);
+ break;
+ case NV50_CHANNEL_GPFIFO:
+ ret = nv50_fence_create(drm);
+ break;
+ case G82_CHANNEL_GPFIFO:
+ ret = nv84_fence_create(drm);
+ break;
+ case FERMI_CHANNEL_GPFIFO:
+ case KEPLER_CHANNEL_GPFIFO_A:
+ ret = nvc0_fence_create(drm);
+ break;
+ default:
+ break;
+ }
+ }
+
if (ret) {
NV_ERROR(drm, "failed to initialise sync subsystem, %d\n", ret);
nouveau_accel_fini(drm);
return;
}
- if (device->card_type >= NV_E0) {
- ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE,
- NVDRM_CHAN + 1,
- NVE0_CHANNEL_IND_ENGINE_CE0 |
- NVE0_CHANNEL_IND_ENGINE_CE1, 0,
- &drm->cechan);
+ if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
+ ret = nouveau_channel_new(drm, &drm->device, NVDRM_CHAN + 1,
+ KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_CE0|
+ KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_CE1,
+ 0, &drm->cechan);
if (ret)
NV_ERROR(drm, "failed to create ce channel, %d\n", ret);
- arg0 = NVE0_CHANNEL_IND_ENGINE_GR;
+ arg0 = KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR;
arg1 = 1;
} else
- if (device->chipset >= 0xa3 &&
- device->chipset != 0xaa &&
- device->chipset != 0xac) {
- ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE,
- NVDRM_CHAN + 1, NvDmaFB, NvDmaTT,
- &drm->cechan);
+ if (device->info.chipset >= 0xa3 &&
+ device->info.chipset != 0xaa &&
+ device->info.chipset != 0xac) {
+ ret = nouveau_channel_new(drm, &drm->device, NVDRM_CHAN + 1,
+ NvDmaFB, NvDmaTT, &drm->cechan);
if (ret)
NV_ERROR(drm, "failed to create ce channel, %d\n", ret);
@@ -200,30 +219,30 @@ nouveau_accel_init(struct nouveau_drm *drm)
arg1 = NvDmaTT;
}
- ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE, NVDRM_CHAN,
- arg0, arg1, &drm->channel);
+ ret = nouveau_channel_new(drm, &drm->device, NVDRM_CHAN, arg0, arg1,
+ &drm->channel);
if (ret) {
NV_ERROR(drm, "failed to create kernel channel, %d\n", ret);
nouveau_accel_fini(drm);
return;
}
- ret = nouveau_object_new(nv_object(drm), NVDRM_CHAN, NVDRM_NVSW,
- nouveau_abi16_swclass(drm), NULL, 0, &object);
+ ret = nvif_object_init(drm->channel->object, NULL, NVDRM_NVSW,
+ nouveau_abi16_swclass(drm), NULL, 0, &drm->nvsw);
if (ret == 0) {
- struct nouveau_software_chan *swch = (void *)object->parent;
+ struct nouveau_software_chan *swch;
ret = RING_SPACE(drm->channel, 2);
if (ret == 0) {
- if (device->card_type < NV_C0) {
+ if (device->info.family < NV_DEVICE_INFO_V0_FERMI) {
BEGIN_NV04(drm->channel, NvSubSw, 0, 1);
OUT_RING (drm->channel, NVDRM_NVSW);
} else
- if (device->card_type < NV_E0) {
+ if (device->info.family < NV_DEVICE_INFO_V0_KEPLER) {
BEGIN_NVC0(drm->channel, FermiSw, 0, 1);
OUT_RING (drm->channel, 0x001f0000);
}
}
- swch = (void *)object->parent;
+ swch = (void *)nvkm_object(&drm->nvsw)->parent;
swch->flip = nouveau_flip_complete;
swch->flip_data = drm->channel;
}
@@ -234,24 +253,24 @@ nouveau_accel_init(struct nouveau_drm *drm)
return;
}
- if (device->card_type < NV_C0) {
- ret = nouveau_gpuobj_new(drm->device, NULL, 32, 0, 0,
- &drm->notify);
+ if (device->info.family < NV_DEVICE_INFO_V0_FERMI) {
+ ret = nouveau_gpuobj_new(nvkm_object(&drm->device), NULL, 32,
+ 0, 0, &drm->notify);
if (ret) {
NV_ERROR(drm, "failed to allocate notifier, %d\n", ret);
nouveau_accel_fini(drm);
return;
}
- ret = nouveau_object_new(nv_object(drm),
- drm->channel->handle, NvNotify0,
- 0x003d, &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
+ ret = nvif_object_init(drm->channel->object, NULL, NvNotify0,
+ NV_DMA_IN_MEMORY,
+ &(struct nv_dma_v0) {
+ .target = NV_DMA_V0_TARGET_VRAM,
+ .access = NV_DMA_V0_ACCESS_RDWR,
.start = drm->notify->addr,
.limit = drm->notify->addr + 31
- }, sizeof(struct nv_dma_class),
- &object);
+ }, sizeof(struct nv_dma_v0),
+ &drm->ntfy);
if (ret) {
nouveau_accel_fini(drm);
return;
@@ -294,7 +313,8 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
#ifdef CONFIG_X86
boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
#endif
- remove_conflicting_framebuffers(aper, "nouveaufb", boot);
+ if (nouveau_modeset != 2)
+ remove_conflicting_framebuffers(aper, "nouveaufb", boot);
kfree(aper);
ret = nouveau_device_create(pdev, NOUVEAU_BUS_PCI,
@@ -305,7 +325,7 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
pci_set_master(pdev);
- ret = drm_get_pci_dev(pdev, pent, &driver);
+ ret = drm_get_pci_dev(pdev, pent, &driver_pci);
if (ret) {
nouveau_object_ref(NULL, (struct nouveau_object **)&device);
return ret;
@@ -348,7 +368,6 @@ static int
nouveau_drm_load(struct drm_device *dev, unsigned long flags)
{
struct pci_dev *pdev = dev->pdev;
- struct nouveau_device *device;
struct nouveau_drm *drm;
int ret;
@@ -359,7 +378,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
dev->dev_private = drm;
drm->dev = dev;
- nouveau_client(drm)->debug = nouveau_dbgopt(nouveau_debug, "DRM");
+ nvkm_client(&drm->client.base)->debug =
+ nouveau_dbgopt(nouveau_debug, "DRM");
INIT_LIST_HEAD(&drm->clients);
spin_lock_init(&drm->tile.lock);
@@ -370,33 +390,34 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
* (possibly) execute vbios init tables (see nouveau_agp.h)
*/
if (pdev && drm_pci_device_is_agp(dev) && dev->agp) {
+ const u64 enables = NV_DEVICE_V0_DISABLE_IDENTIFY |
+ NV_DEVICE_V0_DISABLE_MMIO;
/* dummy device object, doesn't init anything, but allows
* agp code access to registers
*/
- ret = nouveau_object_new(nv_object(drm), NVDRM_CLIENT,
- NVDRM_DEVICE, 0x0080,
- &(struct nv_device_class) {
+ ret = nvif_device_init(&drm->client.base.base, NULL,
+ NVDRM_DEVICE, NV_DEVICE,
+ &(struct nv_device_v0) {
.device = ~0,
- .disable =
- ~(NV_DEVICE_DISABLE_MMIO |
- NV_DEVICE_DISABLE_IDENTIFY),
+ .disable = ~enables,
.debug0 = ~0,
- }, sizeof(struct nv_device_class),
- &drm->device);
+ }, sizeof(struct nv_device_v0),
+ &drm->device);
if (ret)
goto fail_device;
nouveau_agp_reset(drm);
- nouveau_object_del(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE);
+ nvif_device_fini(&drm->device);
}
- ret = nouveau_object_new(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE,
- 0x0080, &(struct nv_device_class) {
+ ret = nvif_device_init(&drm->client.base.base, NULL, NVDRM_DEVICE,
+ NV_DEVICE,
+ &(struct nv_device_v0) {
.device = ~0,
.disable = 0,
.debug0 = 0,
- }, sizeof(struct nv_device_class),
- &drm->device);
+ }, sizeof(struct nv_device_v0),
+ &drm->device);
if (ret)
goto fail_device;
@@ -406,18 +427,19 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
* nosnoop capability. hopefully won't cause issues until a
* better fix is found - assuming there is one...
*/
- device = nv_device(drm->device);
- if (nv_device(drm->device)->chipset == 0xc1)
- nv_mask(device, 0x00088080, 0x00000800, 0x00000000);
+ if (drm->device.info.chipset == 0xc1)
+ nvif_mask(&drm->device, 0x00088080, 0x00000800, 0x00000000);
nouveau_vga_init(drm);
nouveau_agp_init(drm);
- if (device->card_type >= NV_50) {
- ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40),
- 0x1000, &drm->client.base.vm);
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
+ ret = nouveau_vm_new(nvkm_device(&drm->device), 0, (1ULL << 40),
+ 0x1000, &drm->client.vm);
if (ret)
goto fail_device;
+
+ nvkm_client(&drm->client.base)->vm = drm->client.vm;
}
ret = nouveau_ttm_init(drm);
@@ -463,6 +485,7 @@ fail_ttm:
nouveau_agp_fini(drm);
nouveau_vga_fini(drm);
fail_device:
+ nvif_device_fini(&drm->device);
nouveau_cli_destroy(&drm->client);
return ret;
}
@@ -488,26 +511,37 @@ nouveau_drm_unload(struct drm_device *dev)
nouveau_agp_fini(drm);
nouveau_vga_fini(drm);
+ nvif_device_fini(&drm->device);
if (drm->hdmi_device)
pci_dev_put(drm->hdmi_device);
nouveau_cli_destroy(&drm->client);
return 0;
}
-static void
-nouveau_drm_remove(struct pci_dev *pdev)
+void
+nouveau_drm_device_remove(struct drm_device *dev)
{
- struct drm_device *dev = pci_get_drvdata(pdev);
struct nouveau_drm *drm = nouveau_drm(dev);
+ struct nouveau_client *client;
struct nouveau_object *device;
dev->irq_enabled = false;
- device = drm->client.base.device;
+ client = nvkm_client(&drm->client.base);
+ device = client->device;
drm_put_dev(dev);
nouveau_object_ref(NULL, &device);
nouveau_object_debug();
}
+EXPORT_SYMBOL(nouveau_drm_device_remove);
+
+static void
+nouveau_drm_remove(struct pci_dev *pdev)
+{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+
+ nouveau_drm_device_remove(dev);
+}
static int
nouveau_do_suspend(struct drm_device *dev, bool runtime)
@@ -516,9 +550,11 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime)
struct nouveau_cli *cli;
int ret;
- if (dev->mode_config.num_crtc && !runtime) {
+ if (dev->mode_config.num_crtc) {
+ NV_INFO(drm, "suspending console...\n");
+ nouveau_fbcon_set_suspend(dev, 1);
NV_INFO(drm, "suspending display...\n");
- ret = nouveau_display_suspend(dev);
+ ret = nouveau_display_suspend(dev, runtime);
if (ret)
return ret;
}
@@ -548,13 +584,13 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime)
}
list_for_each_entry(cli, &drm->clients, head) {
- ret = nouveau_client_fini(&cli->base, true);
+ ret = nvif_client_suspend(&cli->base);
if (ret)
goto fail_client;
}
NV_INFO(drm, "suspending kernel object tree...\n");
- ret = nouveau_client_fini(&drm->client.base, true);
+ ret = nvif_client_suspend(&drm->client.base);
if (ret)
goto fail_client;
@@ -563,7 +599,7 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime)
fail_client:
list_for_each_entry_continue_reverse(cli, &drm->clients, head) {
- nouveau_client_init(&cli->base);
+ nvif_client_resume(&cli->base);
}
if (drm->fence && nouveau_fence(drm)->resume)
@@ -572,7 +608,7 @@ fail_client:
fail_display:
if (dev->mode_config.num_crtc) {
NV_INFO(drm, "resuming display...\n");
- nouveau_display_resume(dev);
+ nouveau_display_resume(dev, runtime);
}
return ret;
}
@@ -587,21 +623,19 @@ int nouveau_pmops_suspend(struct device *dev)
drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF)
return 0;
- if (drm_dev->mode_config.num_crtc)
- nouveau_fbcon_set_suspend(drm_dev, 1);
-
ret = nouveau_do_suspend(drm_dev, false);
if (ret)
return ret;
pci_save_state(pdev);
pci_disable_device(pdev);
+ pci_ignore_hotplug(pdev);
pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
static int
-nouveau_do_resume(struct drm_device *dev)
+nouveau_do_resume(struct drm_device *dev, bool runtime)
{
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_cli *cli;
@@ -611,7 +645,7 @@ nouveau_do_resume(struct drm_device *dev)
nouveau_agp_reset(drm);
NV_INFO(drm, "resuming kernel object tree...\n");
- nouveau_client_init(&drm->client.base);
+ nvif_client_resume(&drm->client.base);
nouveau_agp_init(drm);
NV_INFO(drm, "resuming client object trees...\n");
@@ -619,14 +653,16 @@ nouveau_do_resume(struct drm_device *dev)
nouveau_fence(drm)->resume(drm);
list_for_each_entry(cli, &drm->clients, head) {
- nouveau_client_init(&cli->base);
+ nvif_client_resume(&cli->base);
}
nouveau_run_vbios_init(dev);
if (dev->mode_config.num_crtc) {
NV_INFO(drm, "resuming display...\n");
- nouveau_display_repin(dev);
+ nouveau_display_resume(dev, runtime);
+ NV_INFO(drm, "resuming console...\n");
+ nouveau_fbcon_set_suspend(dev, 0);
}
return 0;
@@ -649,47 +685,21 @@ int nouveau_pmops_resume(struct device *dev)
return ret;
pci_set_master(pdev);
- ret = nouveau_do_resume(drm_dev);
- if (ret)
- return ret;
-
- if (drm_dev->mode_config.num_crtc) {
- nouveau_display_resume(drm_dev);
- nouveau_fbcon_set_suspend(drm_dev, 0);
- }
-
- return 0;
+ return nouveau_do_resume(drm_dev, false);
}
static int nouveau_pmops_freeze(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
- int ret;
-
- if (drm_dev->mode_config.num_crtc)
- nouveau_fbcon_set_suspend(drm_dev, 1);
-
- ret = nouveau_do_suspend(drm_dev, false);
- return ret;
+ return nouveau_do_suspend(drm_dev, false);
}
static int nouveau_pmops_thaw(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
- int ret;
-
- ret = nouveau_do_resume(drm_dev);
- if (ret)
- return ret;
-
- if (drm_dev->mode_config.num_crtc) {
- nouveau_display_resume(drm_dev);
- nouveau_fbcon_set_suspend(drm_dev, 0);
- }
-
- return 0;
+ return nouveau_do_resume(drm_dev, false);
}
@@ -715,13 +725,17 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
if (ret)
goto out_suspend;
- if (nv_device(drm->device)->card_type >= NV_50) {
- ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40),
- 0x1000, &cli->base.vm);
+ cli->base.super = false;
+
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
+ ret = nouveau_vm_new(nvkm_device(&drm->device), 0, (1ULL << 40),
+ 0x1000, &cli->vm);
if (ret) {
nouveau_cli_destroy(cli);
goto out_suspend;
}
+
+ nvkm_client(&cli->base)->vm = cli->vm;
}
fpriv->driver_priv = cli;
@@ -779,24 +793,31 @@ nouveau_ioctls[] = {
DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
};
-long nouveau_drm_ioctl(struct file *filp,
- unsigned int cmd, unsigned long arg)
+long
+nouveau_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct drm_file *file_priv = filp->private_data;
- struct drm_device *dev;
+ struct drm_file *filp = file->private_data;
+ struct drm_device *dev = filp->minor->dev;
long ret;
- dev = file_priv->minor->dev;
ret = pm_runtime_get_sync(dev->dev);
if (ret < 0 && ret != -EACCES)
return ret;
- ret = drm_ioctl(filp, cmd, arg);
+ switch (_IOC_NR(cmd) - DRM_COMMAND_BASE) {
+ case DRM_NOUVEAU_NVIF:
+ ret = usif_ioctl(filp, (void __user *)arg, _IOC_SIZE(cmd));
+ break;
+ default:
+ ret = drm_ioctl(file, cmd, arg);
+ break;
+ }
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return ret;
}
+
static const struct file_operations
nouveau_driver_fops = {
.owner = THIS_MODULE,
@@ -813,7 +834,7 @@ nouveau_driver_fops = {
};
static struct drm_driver
-driver = {
+driver_stub = {
.driver_features =
DRIVER_USE_AGP |
DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER,
@@ -921,7 +942,7 @@ static int nouveau_pmops_runtime_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
- struct nouveau_device *device = nouveau_dev(drm_dev);
+ struct nvif_device *device = &nouveau_drm(drm_dev)->device;
int ret;
if (nouveau_runtime_pm == 0)
@@ -934,10 +955,10 @@ static int nouveau_pmops_runtime_resume(struct device *dev)
return ret;
pci_set_master(pdev);
- ret = nouveau_do_resume(drm_dev);
+ ret = nouveau_do_resume(drm_dev, true);
drm_kms_helper_poll_enable(drm_dev);
/* do magic */
- nv_mask(device, 0x88488, (1 << 25), (1 << 25));
+ nvif_mask(device, 0x88488, (1 << 25), (1 << 25));
vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
nv_debug_level(NORMAL);
@@ -984,6 +1005,23 @@ static int nouveau_pmops_runtime_idle(struct device *dev)
return 1;
}
+static void nouveau_display_options(void)
+{
+ DRM_DEBUG_DRIVER("Loading Nouveau with parameters:\n");
+
+ DRM_DEBUG_DRIVER("... tv_disable : %d\n", nouveau_tv_disable);
+ DRM_DEBUG_DRIVER("... ignorelid : %d\n", nouveau_ignorelid);
+ DRM_DEBUG_DRIVER("... duallink : %d\n", nouveau_duallink);
+ DRM_DEBUG_DRIVER("... nofbaccel : %d\n", nouveau_nofbaccel);
+ DRM_DEBUG_DRIVER("... config : %s\n", nouveau_config);
+ DRM_DEBUG_DRIVER("... debug : %s\n", nouveau_debug);
+ DRM_DEBUG_DRIVER("... noaccel : %d\n", nouveau_noaccel);
+ DRM_DEBUG_DRIVER("... modeset : %d\n", nouveau_modeset);
+ DRM_DEBUG_DRIVER("... runpm : %d\n", nouveau_runtime_pm);
+ DRM_DEBUG_DRIVER("... vram_pushbuf : %d\n", nouveau_vram_pushbuf);
+ DRM_DEBUG_DRIVER("... pstate : %d\n", nouveau_pstate);
+}
+
static const struct dev_pm_ops nouveau_pm_ops = {
.suspend = nouveau_pmops_suspend,
.resume = nouveau_pmops_resume,
@@ -1005,28 +1043,52 @@ nouveau_drm_pci_driver = {
.driver.pm = &nouveau_pm_ops,
};
-int nouveau_drm_platform_probe(struct platform_device *pdev)
+struct drm_device *
+nouveau_platform_device_create_(struct platform_device *pdev, int size,
+ void **pobject)
{
- struct nouveau_device *device;
- int ret;
+ struct drm_device *drm;
+ int err;
- ret = nouveau_device_create(pdev, NOUVEAU_BUS_PLATFORM,
+ err = nouveau_device_create_(pdev, NOUVEAU_BUS_PLATFORM,
nouveau_platform_name(pdev),
dev_name(&pdev->dev), nouveau_config,
- nouveau_debug, &device);
-
- ret = drm_platform_init(&driver, pdev);
- if (ret) {
- nouveau_object_ref(NULL, (struct nouveau_object **)&device);
- return ret;
+ nouveau_debug, size, pobject);
+ if (err)
+ return ERR_PTR(err);
+
+ drm = drm_dev_alloc(&driver_platform, &pdev->dev);
+ if (!drm) {
+ err = -ENOMEM;
+ goto err_free;
}
- return ret;
+ err = drm_dev_set_unique(drm, "%s", dev_name(&pdev->dev));
+ if (err < 0)
+ goto err_free;
+
+ drm->platformdev = pdev;
+ platform_set_drvdata(pdev, drm);
+
+ return drm;
+
+err_free:
+ nouveau_object_ref(NULL, (struct nouveau_object **)pobject);
+
+ return ERR_PTR(err);
}
+EXPORT_SYMBOL(nouveau_platform_device_create_);
static int __init
nouveau_drm_init(void)
{
+ driver_pci = driver_stub;
+ driver_pci.set_busid = drm_pci_set_busid;
+ driver_platform = driver_stub;
+ driver_platform.set_busid = drm_platform_set_busid;
+
+ nouveau_display_options();
+
if (nouveau_modeset == -1) {
#ifdef CONFIG_VGA_CONSOLE
if (vgacon_text_force())
@@ -1038,7 +1100,7 @@ nouveau_drm_init(void)
return 0;
nouveau_register_dsm_handler();
- return drm_pci_init(&driver, &nouveau_drm_pci_driver);
+ return drm_pci_init(&driver_pci, &nouveau_drm_pci_driver);
}
static void __exit
@@ -1047,7 +1109,7 @@ nouveau_drm_exit(void)
if (!nouveau_modeset)
return;
- drm_pci_exit(&driver, &nouveau_drm_pci_driver);
+ drm_pci_exit(&driver_pci, &nouveau_drm_pci_driver);
nouveau_unregister_dsm_handler();
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h
index 7efbafaf7c1d..8ae36f265fb8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.h
@@ -9,7 +9,7 @@
#define DRIVER_DATE "20120801"
#define DRIVER_MAJOR 1
-#define DRIVER_MINOR 1
+#define DRIVER_MINOR 2
#define DRIVER_PATCHLEVEL 1
/*
@@ -21,15 +21,19 @@
* to control registers on the MPs to enable performance counters,
* and to control the warp error enable mask (OpenGL requires out of
* bounds access to local memory to be silently ignored / return 0).
+ * 1.1.2:
+ * - fixes multiple bugs in flip completion events and timestamping
+ * 1.2.0:
+ * - object api exposed to userspace
+ * - fermi,kepler,maxwell zbc
+ * 1.2.1:
+ * - allow concurrent access to bo's mapped read/write.
*/
-#include <core/client.h>
-#include <core/event.h>
-
-#include <subdev/vm.h>
+#include <nvif/client.h>
+#include <nvif/device.h>
#include <drmP.h>
-#include <drm/nouveau_drm.h>
#include <drm/ttm/ttm_bo_api.h>
#include <drm/ttm/ttm_bo_driver.h>
@@ -38,7 +42,10 @@
#include <drm/ttm/ttm_module.h>
#include <drm/ttm/ttm_page_alloc.h>
+#include "uapi/drm/nouveau_drm.h"
+
struct nouveau_channel;
+struct platform_device;
#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
@@ -50,6 +57,17 @@ struct nouveau_drm_tile {
bool used;
};
+enum nouveau_drm_object_route {
+ NVDRM_OBJECT_NVIF = 0,
+ NVDRM_OBJECT_USIF,
+ NVDRM_OBJECT_ABI16,
+};
+
+enum nouveau_drm_notify_route {
+ NVDRM_NOTIFY_NVIF = 0,
+ NVDRM_NOTIFY_USIF
+};
+
enum nouveau_drm_handle {
NVDRM_CLIENT = 0xffffffff,
NVDRM_DEVICE = 0xdddddddd,
@@ -61,10 +79,13 @@ enum nouveau_drm_handle {
};
struct nouveau_cli {
- struct nouveau_client base;
+ struct nvif_client base;
+ struct nouveau_vm *vm; /*XXX*/
struct list_head head;
struct mutex mutex;
void *abi16;
+ struct list_head objects;
+ struct list_head notifys;
};
static inline struct nouveau_cli *
@@ -73,13 +94,16 @@ nouveau_cli(struct drm_file *fpriv)
return fpriv ? fpriv->driver_priv : NULL;
}
+#include <nvif/object.h>
+#include <nvif/device.h>
+
extern int nouveau_runtime_pm;
struct nouveau_drm {
struct nouveau_cli client;
struct drm_device *dev;
- struct nouveau_object *device;
+ struct nvif_device device;
struct list_head clients;
struct {
@@ -102,6 +126,7 @@ struct nouveau_drm {
struct ttm_buffer_object *,
struct ttm_mem_reg *, struct ttm_mem_reg *);
struct nouveau_channel *chan;
+ struct nvif_object copy;
int mtrr;
} ttm;
@@ -119,6 +144,8 @@ struct nouveau_drm {
struct nouveau_channel *channel;
struct nouveau_gpuobj *notify;
struct nouveau_fbdev *fbcon;
+ struct nvif_object nvsw;
+ struct nvif_object ntfy;
/* nv10-nv40 tiling regions */
struct {
@@ -148,20 +175,25 @@ nouveau_drm(struct drm_device *dev)
return dev->dev_private;
}
-static inline struct nouveau_device *
-nouveau_dev(struct drm_device *dev)
-{
- return nv_device(nouveau_drm(dev)->device);
-}
-
int nouveau_pmops_suspend(struct device *);
int nouveau_pmops_resume(struct device *);
-#define NV_FATAL(cli, fmt, args...) nv_fatal((cli), fmt, ##args)
-#define NV_ERROR(cli, fmt, args...) nv_error((cli), fmt, ##args)
-#define NV_WARN(cli, fmt, args...) nv_warn((cli), fmt, ##args)
-#define NV_INFO(cli, fmt, args...) nv_info((cli), fmt, ##args)
-#define NV_DEBUG(cli, fmt, args...) nv_debug((cli), fmt, ##args)
+#define nouveau_platform_device_create(p, u) \
+ nouveau_platform_device_create_(p, sizeof(**u), (void **)u)
+struct drm_device *
+nouveau_platform_device_create_(struct platform_device *pdev,
+ int size, void **pobject);
+void nouveau_drm_device_remove(struct drm_device *dev);
+
+#define NV_PRINTK(l,c,f,a...) do { \
+ struct nouveau_cli *_cli = (c); \
+ nv_##l(_cli->base.base.priv, f, ##a); \
+} while(0)
+#define NV_FATAL(drm,f,a...) NV_PRINTK(fatal, &(drm)->client, f, ##a)
+#define NV_ERROR(drm,f,a...) NV_PRINTK(error, &(drm)->client, f, ##a)
+#define NV_WARN(drm,f,a...) NV_PRINTK(warn, &(drm)->client, f, ##a)
+#define NV_INFO(drm,f,a...) NV_PRINTK(info, &(drm)->client, f, ##a)
+#define NV_DEBUG(drm,f,a...) NV_PRINTK(debug, &(drm)->client, f, ##a)
extern int nouveau_modeset;
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 191665ee7f52..593ef8a2a069 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -51,13 +51,8 @@
#include "nouveau_crtc.h"
-#include <core/client.h>
-#include <core/device.h>
-
-#include <subdev/fb.h>
-
MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration");
-static int nouveau_nofbaccel = 0;
+int nouveau_nofbaccel = 0;
module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400);
static void
@@ -65,7 +60,7 @@ nouveau_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
struct nouveau_fbdev *fbcon = info->par;
struct nouveau_drm *drm = nouveau_drm(fbcon->dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
int ret;
if (info->state != FBINFO_STATE_RUNNING)
@@ -74,10 +69,10 @@ nouveau_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
ret = -ENODEV;
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
mutex_trylock(&drm->client.mutex)) {
- if (device->card_type < NV_50)
+ if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
ret = nv04_fbcon_fillrect(info, rect);
else
- if (device->card_type < NV_C0)
+ if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
ret = nv50_fbcon_fillrect(info, rect);
else
ret = nvc0_fbcon_fillrect(info, rect);
@@ -97,7 +92,7 @@ nouveau_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *image)
{
struct nouveau_fbdev *fbcon = info->par;
struct nouveau_drm *drm = nouveau_drm(fbcon->dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
int ret;
if (info->state != FBINFO_STATE_RUNNING)
@@ -106,10 +101,10 @@ nouveau_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *image)
ret = -ENODEV;
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
mutex_trylock(&drm->client.mutex)) {
- if (device->card_type < NV_50)
+ if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
ret = nv04_fbcon_copyarea(info, image);
else
- if (device->card_type < NV_C0)
+ if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
ret = nv50_fbcon_copyarea(info, image);
else
ret = nvc0_fbcon_copyarea(info, image);
@@ -129,7 +124,7 @@ nouveau_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
{
struct nouveau_fbdev *fbcon = info->par;
struct nouveau_drm *drm = nouveau_drm(fbcon->dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
int ret;
if (info->state != FBINFO_STATE_RUNNING)
@@ -138,10 +133,10 @@ nouveau_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
ret = -ENODEV;
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
mutex_trylock(&drm->client.mutex)) {
- if (device->card_type < NV_50)
+ if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
ret = nv04_fbcon_imageblit(info, image);
else
- if (device->card_type < NV_C0)
+ if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
ret = nv50_fbcon_imageblit(info, image);
else
ret = nvc0_fbcon_imageblit(info, image);
@@ -212,6 +207,65 @@ static struct fb_ops nouveau_fbcon_sw_ops = {
.fb_debug_leave = drm_fb_helper_debug_leave,
};
+void
+nouveau_fbcon_accel_save_disable(struct drm_device *dev)
+{
+ struct nouveau_drm *drm = nouveau_drm(dev);
+ if (drm->fbcon) {
+ drm->fbcon->saved_flags = drm->fbcon->helper.fbdev->flags;
+ drm->fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED;
+ }
+}
+
+void
+nouveau_fbcon_accel_restore(struct drm_device *dev)
+{
+ struct nouveau_drm *drm = nouveau_drm(dev);
+ if (drm->fbcon) {
+ drm->fbcon->helper.fbdev->flags = drm->fbcon->saved_flags;
+ }
+}
+
+static void
+nouveau_fbcon_accel_fini(struct drm_device *dev)
+{
+ struct nouveau_drm *drm = nouveau_drm(dev);
+ struct nouveau_fbdev *fbcon = drm->fbcon;
+ if (fbcon && drm->channel) {
+ console_lock();
+ fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED;
+ console_unlock();
+ nouveau_channel_idle(drm->channel);
+ nvif_object_fini(&fbcon->twod);
+ nvif_object_fini(&fbcon->blit);
+ nvif_object_fini(&fbcon->gdi);
+ nvif_object_fini(&fbcon->patt);
+ nvif_object_fini(&fbcon->rop);
+ nvif_object_fini(&fbcon->clip);
+ nvif_object_fini(&fbcon->surf2d);
+ }
+}
+
+static void
+nouveau_fbcon_accel_init(struct drm_device *dev)
+{
+ struct nouveau_drm *drm = nouveau_drm(dev);
+ struct nouveau_fbdev *fbcon = drm->fbcon;
+ struct fb_info *info = fbcon->helper.fbdev;
+ int ret;
+
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA)
+ ret = nv04_fbcon_accel_init(info);
+ else
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_FERMI)
+ ret = nv50_fbcon_accel_init(info);
+ else
+ ret = nvc0_fbcon_accel_init(info);
+
+ if (ret == 0)
+ info->fbops = &nouveau_fbcon_ops;
+}
+
static void nouveau_fbcon_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
u16 blue, int regno)
{
@@ -254,10 +308,11 @@ static int
nouveau_fbcon_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
- struct nouveau_fbdev *fbcon = (struct nouveau_fbdev *)helper;
+ struct nouveau_fbdev *fbcon =
+ container_of(helper, struct nouveau_fbdev, helper);
struct drm_device *dev = fbcon->dev;
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
struct fb_info *info;
struct drm_framebuffer *fb;
struct nouveau_framebuffer *nouveau_fb;
@@ -299,8 +354,8 @@ nouveau_fbcon_create(struct drm_fb_helper *helper,
}
chan = nouveau_nofbaccel ? NULL : drm->channel;
- if (chan && device->card_type >= NV_50) {
- ret = nouveau_bo_vma_add(nvbo, nv_client(chan->cli)->vm,
+ if (chan && device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
+ ret = nouveau_bo_vma_add(nvbo, drm->client.vm,
&fbcon->nouveau_fb.vma);
if (ret) {
NV_ERROR(drm, "failed to map fb into chan: %d\n", ret);
@@ -357,20 +412,8 @@ nouveau_fbcon_create(struct drm_fb_helper *helper,
mutex_unlock(&dev->struct_mutex);
- if (chan) {
- ret = -ENODEV;
- if (device->card_type < NV_50)
- ret = nv04_fbcon_accel_init(info);
- else
- if (device->card_type < NV_C0)
- ret = nv50_fbcon_accel_init(info);
- else
- ret = nvc0_fbcon_accel_init(info);
-
- if (ret == 0)
- info->fbops = &nouveau_fbcon_ops;
- }
-
+ if (chan)
+ nouveau_fbcon_accel_init(dev);
nouveau_fbcon_zfill(dev, fbcon);
/* To allow resizeing without swapping buffers */
@@ -438,18 +481,27 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info)
info->flags |= FBINFO_HWACCEL_DISABLED;
}
-static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
+static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
.gamma_set = nouveau_fbcon_gamma_set,
.gamma_get = nouveau_fbcon_gamma_get,
.fb_probe = nouveau_fbcon_create,
};
+static void
+nouveau_fbcon_set_suspend_work(struct work_struct *work)
+{
+ struct nouveau_fbdev *fbcon = container_of(work, typeof(*fbcon), work);
+ console_lock();
+ nouveau_fbcon_accel_restore(fbcon->dev);
+ nouveau_fbcon_zfill(fbcon->dev, fbcon);
+ fb_set_suspend(fbcon->helper.fbdev, FBINFO_STATE_RUNNING);
+ console_unlock();
+}
int
nouveau_fbcon_init(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_fb *pfb = nouveau_fb(drm->device);
struct nouveau_fbdev *fbcon;
int preferred_bpp;
int ret;
@@ -462,9 +514,11 @@ nouveau_fbcon_init(struct drm_device *dev)
if (!fbcon)
return -ENOMEM;
+ INIT_WORK(&fbcon->work, nouveau_fbcon_set_suspend_work);
fbcon->dev = dev;
drm->fbcon = fbcon;
- fbcon->helper.funcs = &nouveau_fbcon_helper_funcs;
+
+ drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
ret = drm_fb_helper_init(dev, &fbcon->helper,
dev->mode_config.num_crtc, 4);
@@ -475,10 +529,10 @@ nouveau_fbcon_init(struct drm_device *dev)
drm_fb_helper_single_add_all_connectors(&fbcon->helper);
- if (pfb->ram->size <= 32 * 1024 * 1024)
+ if (drm->device.info.ram_size <= 32 * 1024 * 1024)
preferred_bpp = 8;
else
- if (pfb->ram->size <= 64 * 1024 * 1024)
+ if (drm->device.info.ram_size <= 64 * 1024 * 1024)
preferred_bpp = 16;
else
preferred_bpp = 32;
@@ -498,43 +552,25 @@ nouveau_fbcon_fini(struct drm_device *dev)
if (!drm->fbcon)
return;
+ nouveau_fbcon_accel_fini(dev);
nouveau_fbcon_destroy(dev, drm->fbcon);
kfree(drm->fbcon);
drm->fbcon = NULL;
}
void
-nouveau_fbcon_save_disable_accel(struct drm_device *dev)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- if (drm->fbcon) {
- drm->fbcon->saved_flags = drm->fbcon->helper.fbdev->flags;
- drm->fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED;
- }
-}
-
-void
-nouveau_fbcon_restore_accel(struct drm_device *dev)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- if (drm->fbcon) {
- drm->fbcon->helper.fbdev->flags = drm->fbcon->saved_flags;
- }
-}
-
-void
nouveau_fbcon_set_suspend(struct drm_device *dev, int state)
{
struct nouveau_drm *drm = nouveau_drm(dev);
if (drm->fbcon) {
+ if (state == FBINFO_STATE_RUNNING) {
+ schedule_work(&drm->fbcon->work);
+ return;
+ }
+ flush_work(&drm->fbcon->work);
console_lock();
- if (state == 1)
- nouveau_fbcon_save_disable_accel(dev);
fb_set_suspend(drm->fbcon->helper.fbdev, state);
- if (state == 0) {
- nouveau_fbcon_restore_accel(dev);
- nouveau_fbcon_zfill(dev, drm->fbcon);
- }
+ nouveau_fbcon_accel_save_disable(dev);
console_unlock();
}
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
index fcff797d2084..6208e70e4a1c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
@@ -36,7 +36,15 @@ struct nouveau_fbdev {
struct nouveau_framebuffer nouveau_fb;
struct list_head fbdev_list;
struct drm_device *dev;
+ struct work_struct work;
unsigned int saved_flags;
+ struct nvif_object surf2d;
+ struct nvif_object clip;
+ struct nvif_object rop;
+ struct nvif_object patt;
+ struct nvif_object gdi;
+ struct nvif_object blit;
+ struct nvif_object twod;
};
void nouveau_fbcon_restore(void);
@@ -61,9 +69,12 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info);
int nouveau_fbcon_init(struct drm_device *dev);
void nouveau_fbcon_fini(struct drm_device *dev);
void nouveau_fbcon_set_suspend(struct drm_device *dev, int state);
-void nouveau_fbcon_save_disable_accel(struct drm_device *dev);
-void nouveau_fbcon_restore_accel(struct drm_device *dev);
+void nouveau_fbcon_accel_save_disable(struct drm_device *dev);
+void nouveau_fbcon_accel_restore(struct drm_device *dev);
void nouveau_fbcon_output_poll_changed(struct drm_device *dev);
+
+extern int nouveau_nofbaccel;
+
#endif /* __NV50_FBCON_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index ab5ea3b0d666..515cd9aebb99 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -28,130 +28,243 @@
#include <linux/ktime.h>
#include <linux/hrtimer.h>
+#include <trace/events/fence.h>
+
+#include <nvif/notify.h>
+#include <nvif/event.h>
#include "nouveau_drm.h"
#include "nouveau_dma.h"
#include "nouveau_fence.h"
-#include <engine/fifo.h>
+static const struct fence_ops nouveau_fence_ops_uevent;
+static const struct fence_ops nouveau_fence_ops_legacy;
-struct fence_work {
- struct work_struct base;
- struct list_head head;
- void (*func)(void *);
- void *data;
-};
+static inline struct nouveau_fence *
+from_fence(struct fence *fence)
+{
+ return container_of(fence, struct nouveau_fence, base);
+}
+
+static inline struct nouveau_fence_chan *
+nouveau_fctx(struct nouveau_fence *fence)
+{
+ return container_of(fence->base.lock, struct nouveau_fence_chan, lock);
+}
static void
nouveau_fence_signal(struct nouveau_fence *fence)
{
- struct fence_work *work, *temp;
+ fence_signal_locked(&fence->base);
+ list_del(&fence->head);
+
+ if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) {
+ struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
- list_for_each_entry_safe(work, temp, &fence->work, head) {
- schedule_work(&work->base);
- list_del(&work->head);
+ if (!--fctx->notify_ref)
+ nvif_notify_put(&fctx->notify);
}
- fence->channel = NULL;
- list_del(&fence->head);
+ fence_put(&fence->base);
+}
+
+static struct nouveau_fence *
+nouveau_local_fence(struct fence *fence, struct nouveau_drm *drm) {
+ struct nouveau_fence_priv *priv = (void*)drm->fence;
+
+ if (fence->ops != &nouveau_fence_ops_legacy &&
+ fence->ops != &nouveau_fence_ops_uevent)
+ return NULL;
+
+ if (fence->context < priv->context_base ||
+ fence->context >= priv->context_base + priv->contexts)
+ return NULL;
+
+ return from_fence(fence);
}
void
nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
{
- struct nouveau_fence *fence, *fnext;
- spin_lock(&fctx->lock);
- list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
+ struct nouveau_fence *fence;
+
+ nvif_notify_fini(&fctx->notify);
+
+ spin_lock_irq(&fctx->lock);
+ while (!list_empty(&fctx->pending)) {
+ fence = list_entry(fctx->pending.next, typeof(*fence), head);
+
+ nouveau_fence_signal(fence);
+ fence->channel = NULL;
+ }
+ spin_unlock_irq(&fctx->lock);
+}
+
+static void
+nouveau_fence_context_put(struct kref *fence_ref)
+{
+ kfree(container_of(fence_ref, struct nouveau_fence_chan, fence_ref));
+}
+
+void
+nouveau_fence_context_free(struct nouveau_fence_chan *fctx)
+{
+ kref_put(&fctx->fence_ref, nouveau_fence_context_put);
+}
+
+static void
+nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx)
+{
+ struct nouveau_fence *fence;
+
+ u32 seq = fctx->read(chan);
+
+ while (!list_empty(&fctx->pending)) {
+ fence = list_entry(fctx->pending.next, typeof(*fence), head);
+
+ if ((int)(seq - fence->base.seqno) < 0)
+ return;
+
nouveau_fence_signal(fence);
}
- spin_unlock(&fctx->lock);
+}
+
+static int
+nouveau_fence_wait_uevent_handler(struct nvif_notify *notify)
+{
+ struct nouveau_fence_chan *fctx =
+ container_of(notify, typeof(*fctx), notify);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fctx->lock, flags);
+ if (!list_empty(&fctx->pending)) {
+ struct nouveau_fence *fence;
+
+ fence = list_entry(fctx->pending.next, typeof(*fence), head);
+ nouveau_fence_update(fence->channel, fctx);
+ }
+ spin_unlock_irqrestore(&fctx->lock, flags);
+
+ /* Always return keep here. NVIF refcount is handled with nouveau_fence_update */
+ return NVIF_NOTIFY_KEEP;
}
void
-nouveau_fence_context_new(struct nouveau_fence_chan *fctx)
+nouveau_fence_context_new(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx)
{
+ struct nouveau_fence_priv *priv = (void*)chan->drm->fence;
+ struct nouveau_cli *cli = (void *)nvif_client(chan->object);
+ int ret;
+
INIT_LIST_HEAD(&fctx->flip);
INIT_LIST_HEAD(&fctx->pending);
spin_lock_init(&fctx->lock);
+ fctx->context = priv->context_base + chan->chid;
+
+ if (chan == chan->drm->cechan)
+ strcpy(fctx->name, "copy engine channel");
+ else if (chan == chan->drm->channel)
+ strcpy(fctx->name, "generic kernel channel");
+ else
+ strcpy(fctx->name, nvkm_client(&cli->base)->name);
+
+ kref_init(&fctx->fence_ref);
+ if (!priv->uevent)
+ return;
+
+ ret = nvif_notify_init(chan->object, NULL,
+ nouveau_fence_wait_uevent_handler, false,
+ G82_CHANNEL_DMA_V0_NTFY_UEVENT,
+ &(struct nvif_notify_uevent_req) { },
+ sizeof(struct nvif_notify_uevent_req),
+ sizeof(struct nvif_notify_uevent_rep),
+ &fctx->notify);
+
+ WARN_ON(ret);
}
+struct nouveau_fence_work {
+ struct work_struct work;
+ struct fence_cb cb;
+ void (*func)(void *);
+ void *data;
+};
+
static void
nouveau_fence_work_handler(struct work_struct *kwork)
{
- struct fence_work *work = container_of(kwork, typeof(*work), base);
+ struct nouveau_fence_work *work = container_of(kwork, typeof(*work), work);
work->func(work->data);
kfree(work);
}
+static void nouveau_fence_work_cb(struct fence *fence, struct fence_cb *cb)
+{
+ struct nouveau_fence_work *work = container_of(cb, typeof(*work), cb);
+
+ schedule_work(&work->work);
+}
+
void
-nouveau_fence_work(struct nouveau_fence *fence,
+nouveau_fence_work(struct fence *fence,
void (*func)(void *), void *data)
{
- struct nouveau_channel *chan = fence->channel;
- struct nouveau_fence_chan *fctx;
- struct fence_work *work = NULL;
+ struct nouveau_fence_work *work;
- if (nouveau_fence_done(fence)) {
- func(data);
- return;
- }
+ if (fence_is_signaled(fence))
+ goto err;
- fctx = chan->fence;
work = kmalloc(sizeof(*work), GFP_KERNEL);
if (!work) {
- WARN_ON(nouveau_fence_wait(fence, false, false));
- func(data);
- return;
- }
-
- spin_lock(&fctx->lock);
- if (!fence->channel) {
- spin_unlock(&fctx->lock);
- kfree(work);
- func(data);
- return;
+ /*
+ * this might not be a nouveau fence any more,
+ * so force a lazy wait here
+ */
+ WARN_ON(nouveau_fence_wait((struct nouveau_fence *)fence,
+ true, false));
+ goto err;
}
- INIT_WORK(&work->base, nouveau_fence_work_handler);
+ INIT_WORK(&work->work, nouveau_fence_work_handler);
work->func = func;
work->data = data;
- list_add(&work->head, &fence->work);
- spin_unlock(&fctx->lock);
-}
-
-static void
-nouveau_fence_update(struct nouveau_channel *chan)
-{
- struct nouveau_fence_chan *fctx = chan->fence;
- struct nouveau_fence *fence, *fnext;
- spin_lock(&fctx->lock);
- list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
- if (fctx->read(chan) < fence->sequence)
- break;
+ if (fence_add_callback(fence, &work->cb, nouveau_fence_work_cb) < 0)
+ goto err_free;
+ return;
- nouveau_fence_signal(fence);
- nouveau_fence_unref(&fence);
- }
- spin_unlock(&fctx->lock);
+err_free:
+ kfree(work);
+err:
+ func(data);
}
int
nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
{
struct nouveau_fence_chan *fctx = chan->fence;
+ struct nouveau_fence_priv *priv = (void*)chan->drm->fence;
int ret;
fence->channel = chan;
fence->timeout = jiffies + (15 * HZ);
- fence->sequence = ++fctx->sequence;
+ if (priv->uevent)
+ fence_init(&fence->base, &nouveau_fence_ops_uevent,
+ &fctx->lock, fctx->context, ++fctx->sequence);
+ else
+ fence_init(&fence->base, &nouveau_fence_ops_legacy,
+ &fctx->lock, fctx->context, ++fctx->sequence);
+ kref_get(&fctx->fence_ref);
+
+ trace_fence_emit(&fence->base);
ret = fctx->emit(fence);
if (!ret) {
- kref_get(&fence->kref);
- spin_lock(&fctx->lock);
+ fence_get(&fence->base);
+ spin_lock_irq(&fctx->lock);
+ nouveau_fence_update(chan, fctx);
list_add_tail(&fence->head, &fctx->pending);
- spin_unlock(&fctx->lock);
+ spin_unlock_irq(&fctx->lock);
}
return ret;
@@ -160,104 +273,70 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
bool
nouveau_fence_done(struct nouveau_fence *fence)
{
- if (fence->channel)
- nouveau_fence_update(fence->channel);
- return !fence->channel;
-}
+ if (fence->base.ops == &nouveau_fence_ops_legacy ||
+ fence->base.ops == &nouveau_fence_ops_uevent) {
+ struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
+ unsigned long flags;
-static int
-nouveau_fence_wait_uevent_handler(void *data, u32 type, int index)
-{
- struct nouveau_fence_priv *priv = data;
- wake_up_all(&priv->waiting);
- return NVKM_EVENT_KEEP;
-}
+ if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags))
+ return true;
-static int
-nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
+ spin_lock_irqsave(&fctx->lock, flags);
+ nouveau_fence_update(fence->channel, fctx);
+ spin_unlock_irqrestore(&fctx->lock, flags);
+ }
+ return fence_is_signaled(&fence->base);
+}
+static long
+nouveau_fence_wait_legacy(struct fence *f, bool intr, long wait)
{
- struct nouveau_channel *chan = fence->channel;
- struct nouveau_fifo *pfifo = nouveau_fifo(chan->drm->device);
- struct nouveau_fence_priv *priv = chan->drm->fence;
- struct nouveau_eventh *handler;
- int ret = 0;
+ struct nouveau_fence *fence = from_fence(f);
+ unsigned long sleep_time = NSEC_PER_MSEC / 1000;
+ unsigned long t = jiffies, timeout = t + wait;
- ret = nouveau_event_new(pfifo->uevent, 1, 0,
- nouveau_fence_wait_uevent_handler,
- priv, &handler);
- if (ret)
- return ret;
+ while (!nouveau_fence_done(fence)) {
+ ktime_t kt;
- nouveau_event_get(handler);
-
- if (fence->timeout) {
- unsigned long timeout = fence->timeout - jiffies;
-
- if (time_before(jiffies, fence->timeout)) {
- if (intr) {
- ret = wait_event_interruptible_timeout(
- priv->waiting,
- nouveau_fence_done(fence),
- timeout);
- } else {
- ret = wait_event_timeout(priv->waiting,
- nouveau_fence_done(fence),
- timeout);
- }
- }
+ t = jiffies;
- if (ret >= 0) {
- fence->timeout = jiffies + ret;
- if (time_after_eq(jiffies, fence->timeout))
- ret = -EBUSY;
- }
- } else {
- if (intr) {
- ret = wait_event_interruptible(priv->waiting,
- nouveau_fence_done(fence));
- } else {
- wait_event(priv->waiting, nouveau_fence_done(fence));
+ if (wait != MAX_SCHEDULE_TIMEOUT && time_after_eq(t, timeout)) {
+ __set_current_state(TASK_RUNNING);
+ return 0;
}
+
+ __set_current_state(intr ? TASK_INTERRUPTIBLE :
+ TASK_UNINTERRUPTIBLE);
+
+ kt = ktime_set(0, sleep_time);
+ schedule_hrtimeout(&kt, HRTIMER_MODE_REL);
+ sleep_time *= 2;
+ if (sleep_time > NSEC_PER_MSEC)
+ sleep_time = NSEC_PER_MSEC;
+
+ if (intr && signal_pending(current))
+ return -ERESTARTSYS;
}
- nouveau_event_ref(NULL, &handler);
- if (unlikely(ret < 0))
- return ret;
+ __set_current_state(TASK_RUNNING);
- return 0;
+ return timeout - t;
}
-int
-nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
+static int
+nouveau_fence_wait_busy(struct nouveau_fence *fence, bool intr)
{
- struct nouveau_channel *chan = fence->channel;
- struct nouveau_fence_priv *priv = chan ? chan->drm->fence : NULL;
- unsigned long sleep_time = NSEC_PER_MSEC / 1000;
- ktime_t t;
int ret = 0;
- while (priv && priv->uevent && lazy && !nouveau_fence_done(fence)) {
- ret = nouveau_fence_wait_uevent(fence, intr);
- if (ret < 0)
- return ret;
- }
-
while (!nouveau_fence_done(fence)) {
- if (fence->timeout && time_after_eq(jiffies, fence->timeout)) {
+ if (time_after_eq(jiffies, fence->timeout)) {
ret = -EBUSY;
break;
}
- __set_current_state(intr ? TASK_INTERRUPTIBLE :
- TASK_UNINTERRUPTIBLE);
- if (lazy) {
- t = ktime_set(0, sleep_time);
- schedule_hrtimeout(&t, HRTIMER_MODE_REL);
- sleep_time *= 2;
- if (sleep_time > NSEC_PER_MSEC)
- sleep_time = NSEC_PER_MSEC;
- }
+ __set_current_state(intr ?
+ TASK_INTERRUPTIBLE :
+ TASK_UNINTERRUPTIBLE);
if (intr && signal_pending(current)) {
ret = -ERESTARTSYS;
@@ -270,47 +349,86 @@ nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
}
int
-nouveau_fence_sync(struct nouveau_fence *fence, struct nouveau_channel *chan)
+nouveau_fence_wait(struct nouveau_fence *fence, bool lazy, bool intr)
+{
+ long ret;
+
+ if (!lazy)
+ return nouveau_fence_wait_busy(fence, intr);
+
+ ret = fence_wait_timeout(&fence->base, intr, 15 * HZ);
+ if (ret < 0)
+ return ret;
+ else if (!ret)
+ return -EBUSY;
+ else
+ return 0;
+}
+
+int
+nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool exclusive, bool intr)
{
struct nouveau_fence_chan *fctx = chan->fence;
- struct nouveau_channel *prev;
- int ret = 0;
+ struct fence *fence;
+ struct reservation_object *resv = nvbo->bo.resv;
+ struct reservation_object_list *fobj;
+ struct nouveau_fence *f;
+ int ret = 0, i;
- prev = fence ? fence->channel : NULL;
- if (prev) {
- if (unlikely(prev != chan && !nouveau_fence_done(fence))) {
- ret = fctx->sync(fence, prev, chan);
- if (unlikely(ret))
- ret = nouveau_fence_wait(fence, true, false);
- }
+ if (!exclusive) {
+ ret = reservation_object_reserve_shared(resv);
+
+ if (ret)
+ return ret;
}
- return ret;
-}
+ fobj = reservation_object_get_list(resv);
+ fence = reservation_object_get_excl(resv);
-static void
-nouveau_fence_del(struct kref *kref)
-{
- struct nouveau_fence *fence = container_of(kref, typeof(*fence), kref);
- kfree(fence);
+ if (fence && (!exclusive || !fobj || !fobj->shared_count)) {
+ struct nouveau_channel *prev = NULL;
+
+ f = nouveau_local_fence(fence, chan->drm);
+ if (f)
+ prev = f->channel;
+
+ if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan))))
+ ret = fence_wait(fence, intr);
+
+ return ret;
+ }
+
+ if (!exclusive || !fobj)
+ return ret;
+
+ for (i = 0; i < fobj->shared_count && !ret; ++i) {
+ struct nouveau_channel *prev = NULL;
+
+ fence = rcu_dereference_protected(fobj->shared[i],
+ reservation_object_held(resv));
+
+ f = nouveau_local_fence(fence, chan->drm);
+ if (f)
+ prev = f->channel;
+
+ if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan))))
+ ret = fence_wait(fence, intr);
+
+ if (ret)
+ break;
+ }
+
+ return ret;
}
void
nouveau_fence_unref(struct nouveau_fence **pfence)
{
if (*pfence)
- kref_put(&(*pfence)->kref, nouveau_fence_del);
+ fence_put(&(*pfence)->base);
*pfence = NULL;
}
-struct nouveau_fence *
-nouveau_fence_ref(struct nouveau_fence *fence)
-{
- if (fence)
- kref_get(&fence->kref);
- return fence;
-}
-
int
nouveau_fence_new(struct nouveau_channel *chan, bool sysmem,
struct nouveau_fence **pfence)
@@ -325,9 +443,7 @@ nouveau_fence_new(struct nouveau_channel *chan, bool sysmem,
if (!fence)
return -ENOMEM;
- INIT_LIST_HEAD(&fence->work);
fence->sysmem = sysmem;
- kref_init(&fence->kref);
ret = nouveau_fence_emit(fence, chan);
if (ret)
@@ -336,3 +452,101 @@ nouveau_fence_new(struct nouveau_channel *chan, bool sysmem,
*pfence = fence;
return ret;
}
+
+static const char *nouveau_fence_get_get_driver_name(struct fence *fence)
+{
+ return "nouveau";
+}
+
+static const char *nouveau_fence_get_timeline_name(struct fence *f)
+{
+ struct nouveau_fence *fence = from_fence(f);
+ struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
+
+ return fence->channel ? fctx->name : "dead channel";
+}
+
+/*
+ * In an ideal world, read would not assume the channel context is still alive.
+ * This function may be called from another device, running into free memory as a
+ * result. The drm node should still be there, so we can derive the index from
+ * the fence context.
+ */
+static bool nouveau_fence_is_signaled(struct fence *f)
+{
+ struct nouveau_fence *fence = from_fence(f);
+ struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
+ struct nouveau_channel *chan = fence->channel;
+
+ return (int)(fctx->read(chan) - fence->base.seqno) >= 0;
+}
+
+static bool nouveau_fence_no_signaling(struct fence *f)
+{
+ struct nouveau_fence *fence = from_fence(f);
+
+ /*
+ * caller should have a reference on the fence,
+ * else fence could get freed here
+ */
+ WARN_ON(atomic_read(&fence->base.refcount.refcount) <= 1);
+
+ /*
+ * This needs uevents to work correctly, but fence_add_callback relies on
+ * being able to enable signaling. It will still get signaled eventually,
+ * just not right away.
+ */
+ if (nouveau_fence_is_signaled(f)) {
+ list_del(&fence->head);
+
+ fence_put(&fence->base);
+ return false;
+ }
+
+ return true;
+}
+
+static void nouveau_fence_release(struct fence *f)
+{
+ struct nouveau_fence *fence = from_fence(f);
+ struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
+
+ kref_put(&fctx->fence_ref, nouveau_fence_context_put);
+ fence_free(&fence->base);
+}
+
+static const struct fence_ops nouveau_fence_ops_legacy = {
+ .get_driver_name = nouveau_fence_get_get_driver_name,
+ .get_timeline_name = nouveau_fence_get_timeline_name,
+ .enable_signaling = nouveau_fence_no_signaling,
+ .signaled = nouveau_fence_is_signaled,
+ .wait = nouveau_fence_wait_legacy,
+ .release = nouveau_fence_release
+};
+
+static bool nouveau_fence_enable_signaling(struct fence *f)
+{
+ struct nouveau_fence *fence = from_fence(f);
+ struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
+ bool ret;
+
+ if (!fctx->notify_ref++)
+ nvif_notify_get(&fctx->notify);
+
+ ret = nouveau_fence_no_signaling(f);
+ if (ret)
+ set_bit(FENCE_FLAG_USER_BITS, &fence->base.flags);
+ else if (!--fctx->notify_ref)
+ nvif_notify_put(&fctx->notify);
+
+ return ret;
+}
+
+static const struct fence_ops nouveau_fence_ops_uevent = {
+ .get_driver_name = nouveau_fence_get_get_driver_name,
+ .get_timeline_name = nouveau_fence_get_timeline_name,
+ .enable_signaling = nouveau_fence_enable_signaling,
+ .signaled = nouveau_fence_is_signaled,
+ .wait = fence_default_wait,
+ .release = NULL
+};
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h
index c57bb61da58c..943b0b17b1fc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.h
@@ -1,33 +1,37 @@
#ifndef __NOUVEAU_FENCE_H__
#define __NOUVEAU_FENCE_H__
+#include <linux/fence.h>
+#include <nvif/notify.h>
+
struct nouveau_drm;
+struct nouveau_bo;
struct nouveau_fence {
+ struct fence base;
+
struct list_head head;
- struct list_head work;
- struct kref kref;
bool sysmem;
struct nouveau_channel *channel;
unsigned long timeout;
- u32 sequence;
};
int nouveau_fence_new(struct nouveau_channel *, bool sysmem,
struct nouveau_fence **);
-struct nouveau_fence *
-nouveau_fence_ref(struct nouveau_fence *);
void nouveau_fence_unref(struct nouveau_fence **);
int nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *);
bool nouveau_fence_done(struct nouveau_fence *);
-void nouveau_fence_work(struct nouveau_fence *, void (*)(void *), void *);
+void nouveau_fence_work(struct fence *, void (*)(void *), void *);
int nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr);
-int nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *);
+int nouveau_fence_sync(struct nouveau_bo *, struct nouveau_channel *, bool exclusive, bool intr);
struct nouveau_fence_chan {
+ spinlock_t lock;
+ struct kref fence_ref;
+
struct list_head pending;
struct list_head flip;
@@ -38,8 +42,12 @@ struct nouveau_fence_chan {
int (*emit32)(struct nouveau_channel *, u64, u32);
int (*sync32)(struct nouveau_channel *, u64, u32);
- spinlock_t lock;
u32 sequence;
+ u32 context;
+ char name[32];
+
+ struct nvif_notify notify;
+ int notify_ref;
};
struct nouveau_fence_priv {
@@ -49,14 +57,15 @@ struct nouveau_fence_priv {
int (*context_new)(struct nouveau_channel *);
void (*context_del)(struct nouveau_channel *);
- wait_queue_head_t waiting;
+ u32 contexts, context_base;
bool uevent;
};
#define nouveau_fence(drm) ((struct nouveau_fence_priv *)(drm)->fence)
-void nouveau_fence_context_new(struct nouveau_fence_chan *);
+void nouveau_fence_context_new(struct nouveau_channel *, struct nouveau_fence_chan *);
void nouveau_fence_context_del(struct nouveau_fence_chan *);
+void nouveau_fence_context_free(struct nouveau_fence_chan *);
int nv04_fence_create(struct nouveau_drm *);
int nv04_fence_mthd(struct nouveau_channel *, u32, u32, u32);
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index c90c0dc0afe8..36951ee4b157 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -24,8 +24,6 @@
*
*/
-#include <subdev/fb.h>
-
#include "nouveau_drm.h"
#include "nouveau_dma.h"
#include "nouveau_fence.h"
@@ -58,14 +56,14 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
struct nouveau_vma *vma;
int ret;
- if (!cli->base.vm)
+ if (!cli->vm)
return 0;
- ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
+ ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL);
if (ret)
return ret;
- vma = nouveau_bo_vma_find(nvbo, cli->base.vm);
+ vma = nouveau_bo_vma_find(nvbo, cli->vm);
if (!vma) {
vma = kzalloc(sizeof(*vma), GFP_KERNEL);
if (!vma) {
@@ -73,7 +71,7 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
goto out;
}
- ret = nouveau_bo_vma_add(nvbo, cli->base.vm, vma);
+ ret = nouveau_bo_vma_add(nvbo, cli->vm, vma);
if (ret) {
kfree(vma);
goto out;
@@ -100,17 +98,23 @@ static void
nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nouveau_vma *vma)
{
const bool mapped = nvbo->bo.mem.mem_type != TTM_PL_SYSTEM;
- struct nouveau_fence *fence = NULL;
+ struct reservation_object *resv = nvbo->bo.resv;
+ struct reservation_object_list *fobj;
+ struct fence *fence = NULL;
+
+ fobj = reservation_object_get_list(resv);
list_del(&vma->head);
- if (mapped) {
- spin_lock(&nvbo->bo.bdev->fence_lock);
- fence = nouveau_fence_ref(nvbo->bo.sync_obj);
- spin_unlock(&nvbo->bo.bdev->fence_lock);
- }
+ if (fobj && fobj->shared_count > 1)
+ ttm_bo_wait(&nvbo->bo, true, false, false);
+ else if (fobj && fobj->shared_count == 1)
+ fence = rcu_dereference_protected(fobj->shared[0],
+ reservation_object_held(resv));
+ else
+ fence = reservation_object_get_excl(nvbo->bo.resv);
- if (fence) {
+ if (fence && mapped) {
nouveau_fence_work(fence, nouveau_gem_object_delete, vma);
} else {
if (mapped)
@@ -118,7 +122,6 @@ nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nouveau_vma *vma)
nouveau_vm_put(vma);
kfree(vma);
}
- nouveau_fence_unref(&fence);
}
void
@@ -129,14 +132,14 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
struct nouveau_vma *vma;
int ret;
- if (!cli->base.vm)
+ if (!cli->vm)
return;
- ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
+ ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL);
if (ret)
return;
- vma = nouveau_bo_vma_find(nvbo, cli->base.vm);
+ vma = nouveau_bo_vma_find(nvbo, cli->vm);
if (vma) {
if (--vma->refcount == 0)
nouveau_gem_object_unmap(nvbo, vma);
@@ -162,7 +165,7 @@ nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain,
flags |= TTM_PL_FLAG_SYSTEM;
ret = nouveau_bo_new(dev, size, align, flags, tile_mode,
- tile_flags, NULL, pnvbo);
+ tile_flags, NULL, NULL, pnvbo);
if (ret)
return ret;
nvbo = *pnvbo;
@@ -173,7 +176,7 @@ nouveau_gem_new(struct drm_device *dev, int size, int align, uint32_t domain,
*/
nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_VRAM |
NOUVEAU_GEM_DOMAIN_GART;
- if (nv_device(drm->device)->card_type >= NV_50)
+ if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA)
nvbo->valid_domains &= domain;
/* Initialize the embedded gem-object. We return a single gem-reference
@@ -202,8 +205,8 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem,
rep->domain = NOUVEAU_GEM_DOMAIN_VRAM;
rep->offset = nvbo->bo.offset;
- if (cli->base.vm) {
- vma = nouveau_bo_vma_find(nvbo, cli->base.vm);
+ if (cli->vm) {
+ vma = nouveau_bo_vma_find(nvbo, cli->vm);
if (!vma)
return -EINVAL;
@@ -223,13 +226,13 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
{
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_cli *cli = nouveau_cli(file_priv);
- struct nouveau_fb *pfb = nouveau_fb(drm->device);
+ struct nouveau_fb *pfb = nvkm_fb(&drm->device);
struct drm_nouveau_gem_new *req = data;
struct nouveau_bo *nvbo = NULL;
int ret = 0;
if (!pfb->memtype_valid(pfb, req->info.tile_flags)) {
- NV_ERROR(cli, "bad page flags: 0x%08x\n", req->info.tile_flags);
+ NV_PRINTK(error, cli, "bad page flags: 0x%08x\n", req->info.tile_flags);
return -EINVAL;
}
@@ -290,24 +293,23 @@ nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains,
}
struct validate_op {
- struct list_head vram_list;
- struct list_head gart_list;
- struct list_head both_list;
+ struct list_head list;
struct ww_acquire_ctx ticket;
};
static void
-validate_fini_list(struct list_head *list, struct nouveau_fence *fence,
- struct ww_acquire_ctx *ticket)
+validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence,
+ struct drm_nouveau_gem_pushbuf_bo *pbbo)
{
- struct list_head *entry, *tmp;
struct nouveau_bo *nvbo;
+ struct drm_nouveau_gem_pushbuf_bo *b;
- list_for_each_safe(entry, tmp, list) {
- nvbo = list_entry(entry, struct nouveau_bo, entry);
+ while (!list_empty(&op->list)) {
+ nvbo = list_entry(op->list.next, struct nouveau_bo, entry);
+ b = &pbbo[nvbo->pbbo_index];
if (likely(fence))
- nouveau_bo_fence(nvbo, fence);
+ nouveau_bo_fence(nvbo, fence, !!b->write_domains);
if (unlikely(nvbo->validate_mapped)) {
ttm_bo_kunmap(&nvbo->kmap);
@@ -316,23 +318,16 @@ validate_fini_list(struct list_head *list, struct nouveau_fence *fence,
list_del(&nvbo->entry);
nvbo->reserved_by = NULL;
- ttm_bo_unreserve_ticket(&nvbo->bo, ticket);
+ ttm_bo_unreserve_ticket(&nvbo->bo, &op->ticket);
drm_gem_object_unreference_unlocked(&nvbo->gem);
}
}
static void
-validate_fini_no_ticket(struct validate_op *op, struct nouveau_fence *fence)
+validate_fini(struct validate_op *op, struct nouveau_fence *fence,
+ struct drm_nouveau_gem_pushbuf_bo *pbbo)
{
- validate_fini_list(&op->vram_list, fence, &op->ticket);
- validate_fini_list(&op->gart_list, fence, &op->ticket);
- validate_fini_list(&op->both_list, fence, &op->ticket);
-}
-
-static void
-validate_fini(struct validate_op *op, struct nouveau_fence *fence)
-{
- validate_fini_no_ticket(op, fence);
+ validate_fini_no_ticket(op, fence, pbbo);
ww_acquire_fini(&op->ticket);
}
@@ -346,11 +341,14 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
int trycnt = 0;
int ret, i;
struct nouveau_bo *res_bo = NULL;
+ LIST_HEAD(gart_list);
+ LIST_HEAD(vram_list);
+ LIST_HEAD(both_list);
ww_acquire_init(&op->ticket, &reservation_ww_class);
retry:
if (++trycnt > 100000) {
- NV_ERROR(cli, "%s failed and gave up.\n", __func__);
+ NV_PRINTK(error, cli, "%s failed and gave up.\n", __func__);
return -EINVAL;
}
@@ -361,10 +359,9 @@ retry:
gem = drm_gem_object_lookup(dev, file_priv, b->handle);
if (!gem) {
- NV_ERROR(cli, "Unknown handle 0x%08x\n", b->handle);
- ww_acquire_done(&op->ticket);
- validate_fini(op, NULL);
- return -ENOENT;
+ NV_PRINTK(error, cli, "Unknown handle 0x%08x\n", b->handle);
+ ret = -ENOENT;
+ break;
}
nvbo = nouveau_gem_object(gem);
if (nvbo == res_bo) {
@@ -374,17 +371,19 @@ retry:
}
if (nvbo->reserved_by && nvbo->reserved_by == file_priv) {
- NV_ERROR(cli, "multiple instances of buffer %d on "
+ NV_PRINTK(error, cli, "multiple instances of buffer %d on "
"validation list\n", b->handle);
drm_gem_object_unreference_unlocked(gem);
- ww_acquire_done(&op->ticket);
- validate_fini(op, NULL);
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
ret = ttm_bo_reserve(&nvbo->bo, true, false, true, &op->ticket);
if (ret) {
- validate_fini_no_ticket(op, NULL);
+ list_splice_tail_init(&vram_list, &op->list);
+ list_splice_tail_init(&gart_list, &op->list);
+ list_splice_tail_init(&both_list, &op->list);
+ validate_fini_no_ticket(op, NULL, NULL);
if (unlikely(ret == -EDEADLK)) {
ret = ttm_bo_reserve_slowpath(&nvbo->bo, true,
&op->ticket);
@@ -392,12 +391,9 @@ retry:
res_bo = nvbo;
}
if (unlikely(ret)) {
- ww_acquire_done(&op->ticket);
- ww_acquire_fini(&op->ticket);
- drm_gem_object_unreference_unlocked(gem);
if (ret != -ERESTARTSYS)
- NV_ERROR(cli, "fail reserve\n");
- return ret;
+ NV_PRINTK(error, cli, "fail reserve\n");
+ break;
}
}
@@ -406,45 +402,32 @@ retry:
nvbo->pbbo_index = i;
if ((b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
(b->valid_domains & NOUVEAU_GEM_DOMAIN_GART))
- list_add_tail(&nvbo->entry, &op->both_list);
+ list_add_tail(&nvbo->entry, &both_list);
else
if (b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM)
- list_add_tail(&nvbo->entry, &op->vram_list);
+ list_add_tail(&nvbo->entry, &vram_list);
else
if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART)
- list_add_tail(&nvbo->entry, &op->gart_list);
+ list_add_tail(&nvbo->entry, &gart_list);
else {
- NV_ERROR(cli, "invalid valid domains: 0x%08x\n",
+ NV_PRINTK(error, cli, "invalid valid domains: 0x%08x\n",
b->valid_domains);
- list_add_tail(&nvbo->entry, &op->both_list);
- ww_acquire_done(&op->ticket);
- validate_fini(op, NULL);
- return -EINVAL;
+ list_add_tail(&nvbo->entry, &both_list);
+ ret = -EINVAL;
+ break;
}
if (nvbo == res_bo)
goto retry;
}
ww_acquire_done(&op->ticket);
- return 0;
-}
-
-static int
-validate_sync(struct nouveau_channel *chan, struct nouveau_bo *nvbo)
-{
- struct nouveau_fence *fence = NULL;
- int ret = 0;
-
- spin_lock(&nvbo->bo.bdev->fence_lock);
- fence = nouveau_fence_ref(nvbo->bo.sync_obj);
- spin_unlock(&nvbo->bo.bdev->fence_lock);
-
- if (fence) {
- ret = nouveau_fence_sync(fence, chan);
- nouveau_fence_unref(&fence);
- }
-
+ list_splice_tail(&vram_list, &op->list);
+ list_splice_tail(&gart_list, &op->list);
+ list_splice_tail(&both_list, &op->list);
+ if (ret)
+ validate_fini(op, NULL, NULL);
return ret;
+
}
static int
@@ -465,24 +448,25 @@ validate_list(struct nouveau_channel *chan, struct nouveau_cli *cli,
b->write_domains,
b->valid_domains);
if (unlikely(ret)) {
- NV_ERROR(cli, "fail set_domain\n");
+ NV_PRINTK(error, cli, "fail set_domain\n");
return ret;
}
ret = nouveau_bo_validate(nvbo, true, false);
if (unlikely(ret)) {
if (ret != -ERESTARTSYS)
- NV_ERROR(cli, "fail ttm_validate\n");
+ NV_PRINTK(error, cli, "fail ttm_validate\n");
return ret;
}
- ret = validate_sync(chan, nvbo);
+ ret = nouveau_fence_sync(nvbo, chan, !!b->write_domains, true);
if (unlikely(ret)) {
- NV_ERROR(cli, "fail post-validate sync\n");
+ if (ret != -ERESTARTSYS)
+ NV_PRINTK(error, cli, "fail post-validate sync\n");
return ret;
}
- if (nv_device(drm->device)->card_type < NV_50) {
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) {
if (nvbo->bo.offset == b->presumed.offset &&
((nvbo->bo.mem.mem_type == TTM_PL_VRAM &&
b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
@@ -515,11 +499,9 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
struct validate_op *op, int *apply_relocs)
{
struct nouveau_cli *cli = nouveau_cli(file_priv);
- int ret, relocs = 0;
+ int ret;
- INIT_LIST_HEAD(&op->vram_list);
- INIT_LIST_HEAD(&op->gart_list);
- INIT_LIST_HEAD(&op->both_list);
+ INIT_LIST_HEAD(&op->list);
if (nr_buffers == 0)
return 0;
@@ -527,38 +509,18 @@ nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
ret = validate_init(chan, file_priv, pbbo, nr_buffers, op);
if (unlikely(ret)) {
if (ret != -ERESTARTSYS)
- NV_ERROR(cli, "validate_init\n");
+ NV_PRINTK(error, cli, "validate_init\n");
return ret;
}
- ret = validate_list(chan, cli, &op->vram_list, pbbo, user_buffers);
+ ret = validate_list(chan, cli, &op->list, pbbo, user_buffers);
if (unlikely(ret < 0)) {
if (ret != -ERESTARTSYS)
- NV_ERROR(cli, "validate vram_list\n");
- validate_fini(op, NULL);
+ NV_PRINTK(error, cli, "validating bo list\n");
+ validate_fini(op, NULL, NULL);
return ret;
}
- relocs += ret;
-
- ret = validate_list(chan, cli, &op->gart_list, pbbo, user_buffers);
- if (unlikely(ret < 0)) {
- if (ret != -ERESTARTSYS)
- NV_ERROR(cli, "validate gart_list\n");
- validate_fini(op, NULL);
- return ret;
- }
- relocs += ret;
-
- ret = validate_list(chan, cli, &op->both_list, pbbo, user_buffers);
- if (unlikely(ret < 0)) {
- if (ret != -ERESTARTSYS)
- NV_ERROR(cli, "validate both_list\n");
- validate_fini(op, NULL);
- return ret;
- }
- relocs += ret;
-
- *apply_relocs = relocs;
+ *apply_relocs = ret;
return 0;
}
@@ -613,7 +575,7 @@ nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli,
uint32_t data;
if (unlikely(r->bo_index > req->nr_buffers)) {
- NV_ERROR(cli, "reloc bo index invalid\n");
+ NV_PRINTK(error, cli, "reloc bo index invalid\n");
ret = -EINVAL;
break;
}
@@ -623,7 +585,7 @@ nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli,
continue;
if (unlikely(r->reloc_bo_index > req->nr_buffers)) {
- NV_ERROR(cli, "reloc container bo index invalid\n");
+ NV_PRINTK(error, cli, "reloc container bo index invalid\n");
ret = -EINVAL;
break;
}
@@ -631,7 +593,7 @@ nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli,
if (unlikely(r->reloc_bo_offset + 4 >
nvbo->bo.mem.num_pages << PAGE_SHIFT)) {
- NV_ERROR(cli, "reloc outside of bo\n");
+ NV_PRINTK(error, cli, "reloc outside of bo\n");
ret = -EINVAL;
break;
}
@@ -640,7 +602,7 @@ nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli,
ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages,
&nvbo->kmap);
if (ret) {
- NV_ERROR(cli, "failed kmap for reloc\n");
+ NV_PRINTK(error, cli, "failed kmap for reloc\n");
break;
}
nvbo->validate_mapped = true;
@@ -661,11 +623,9 @@ nouveau_gem_pushbuf_reloc_apply(struct nouveau_cli *cli,
data |= r->vor;
}
- spin_lock(&nvbo->bo.bdev->fence_lock);
- ret = ttm_bo_wait(&nvbo->bo, false, false, false);
- spin_unlock(&nvbo->bo.bdev->fence_lock);
+ ret = ttm_bo_wait(&nvbo->bo, true, false, false);
if (ret) {
- NV_ERROR(cli, "reloc wait_idle failed: %d\n", ret);
+ NV_PRINTK(error, cli, "reloc wait_idle failed: %d\n", ret);
break;
}
@@ -696,7 +656,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
return -ENOMEM;
list_for_each_entry(temp, &abi16->channels, head) {
- if (temp->chan->handle == (NVDRM_CHAN | req->channel)) {
+ if (temp->chan->object->handle == (NVDRM_CHAN | req->channel)) {
chan = temp->chan;
break;
}
@@ -711,19 +671,19 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
goto out_next;
if (unlikely(req->nr_push > NOUVEAU_GEM_MAX_PUSH)) {
- NV_ERROR(cli, "pushbuf push count exceeds limit: %d max %d\n",
+ NV_PRINTK(error, cli, "pushbuf push count exceeds limit: %d max %d\n",
req->nr_push, NOUVEAU_GEM_MAX_PUSH);
return nouveau_abi16_put(abi16, -EINVAL);
}
if (unlikely(req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS)) {
- NV_ERROR(cli, "pushbuf bo count exceeds limit: %d max %d\n",
+ NV_PRINTK(error, cli, "pushbuf bo count exceeds limit: %d max %d\n",
req->nr_buffers, NOUVEAU_GEM_MAX_BUFFERS);
return nouveau_abi16_put(abi16, -EINVAL);
}
if (unlikely(req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS)) {
- NV_ERROR(cli, "pushbuf reloc count exceeds limit: %d max %d\n",
+ NV_PRINTK(error, cli, "pushbuf reloc count exceeds limit: %d max %d\n",
req->nr_relocs, NOUVEAU_GEM_MAX_RELOCS);
return nouveau_abi16_put(abi16, -EINVAL);
}
@@ -741,7 +701,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
/* Ensure all push buffers are on validate list */
for (i = 0; i < req->nr_push; i++) {
if (push[i].bo_index >= req->nr_buffers) {
- NV_ERROR(cli, "push %d buffer not in list\n", i);
+ NV_PRINTK(error, cli, "push %d buffer not in list\n", i);
ret = -EINVAL;
goto out_prevalid;
}
@@ -752,7 +712,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
req->nr_buffers, &op, &do_reloc);
if (ret) {
if (ret != -ERESTARTSYS)
- NV_ERROR(cli, "validate: %d\n", ret);
+ NV_PRINTK(error, cli, "validate: %d\n", ret);
goto out_prevalid;
}
@@ -760,7 +720,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
if (do_reloc) {
ret = nouveau_gem_pushbuf_reloc_apply(cli, req, bo);
if (ret) {
- NV_ERROR(cli, "reloc apply: %d\n", ret);
+ NV_PRINTK(error, cli, "reloc apply: %d\n", ret);
goto out;
}
}
@@ -768,7 +728,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
if (chan->dma.ib_max) {
ret = nouveau_dma_wait(chan, req->nr_push + 1, 16);
if (ret) {
- NV_ERROR(cli, "nv50cal_space: %d\n", ret);
+ NV_PRINTK(error, cli, "nv50cal_space: %d\n", ret);
goto out;
}
@@ -780,10 +740,10 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
push[i].length);
}
} else
- if (nv_device(drm->device)->chipset >= 0x25) {
+ if (drm->device.info.chipset >= 0x25) {
ret = RING_SPACE(chan, req->nr_push * 2);
if (ret) {
- NV_ERROR(cli, "cal_space: %d\n", ret);
+ NV_PRINTK(error, cli, "cal_space: %d\n", ret);
goto out;
}
@@ -797,7 +757,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
} else {
ret = RING_SPACE(chan, req->nr_push * (2 + NOUVEAU_DMA_SKIPS));
if (ret) {
- NV_ERROR(cli, "jmp_space: %d\n", ret);
+ NV_PRINTK(error, cli, "jmp_space: %d\n", ret);
goto out;
}
@@ -835,13 +795,13 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
ret = nouveau_fence_new(chan, false, &fence);
if (ret) {
- NV_ERROR(cli, "error fencing pushbuf: %d\n", ret);
+ NV_PRINTK(error, cli, "error fencing pushbuf: %d\n", ret);
WIND_RING(chan);
goto out;
}
out:
- validate_fini(&op, fence);
+ validate_fini(&op, fence, bo);
nouveau_fence_unref(&fence);
out_prevalid:
@@ -853,7 +813,7 @@ out_next:
req->suffix0 = 0x00000000;
req->suffix1 = 0x00000000;
} else
- if (nv_device(drm->device)->chipset >= 0x25) {
+ if (drm->device.info.chipset >= 0x25) {
req->suffix0 = 0x00020000;
req->suffix1 = 0x00000000;
} else {
@@ -886,17 +846,29 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
struct drm_gem_object *gem;
struct nouveau_bo *nvbo;
bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT);
- int ret = -EINVAL;
+ bool write = !!(req->flags & NOUVEAU_GEM_CPU_PREP_WRITE);
+ int ret;
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
return -ENOENT;
nvbo = nouveau_gem_object(gem);
- spin_lock(&nvbo->bo.bdev->fence_lock);
- ret = ttm_bo_wait(&nvbo->bo, true, true, no_wait);
- spin_unlock(&nvbo->bo.bdev->fence_lock);
+ if (no_wait)
+ ret = reservation_object_test_signaled_rcu(nvbo->bo.resv, write) ? 0 : -EBUSY;
+ else {
+ long lret;
+
+ lret = reservation_object_wait_timeout_rcu(nvbo->bo.resv, write, true, 30 * HZ);
+ if (!lret)
+ ret = -EBUSY;
+ else if (lret > 0)
+ ret = 0;
+ else
+ ret = lret;
+ }
drm_gem_object_unreference_unlocked(gem);
+
return ret;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.h b/drivers/gpu/drm/nouveau/nouveau_gem.h
index ddab762d81fe..e4049faca780 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.h
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.h
@@ -39,7 +39,7 @@ struct reservation_object *nouveau_gem_prime_res_obj(struct drm_gem_object *);
extern void nouveau_gem_prime_unpin(struct drm_gem_object *);
extern struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *);
extern struct drm_gem_object *nouveau_gem_prime_import_sg_table(
- struct drm_device *, size_t size, struct sg_table *);
+ struct drm_device *, struct dma_buf_attachment *, struct sg_table *);
extern void *nouveau_gem_prime_vmap(struct drm_gem_object *);
extern void nouveau_gem_prime_vunmap(struct drm_gem_object *, void *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_hwmon.c b/drivers/gpu/drm/nouveau/nouveau_hwmon.c
index 19fd767bab10..afb36d66e78d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hwmon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hwmon.c
@@ -34,17 +34,13 @@
#include "nouveau_drm.h"
#include "nouveau_hwmon.h"
-#include <subdev/gpio.h>
-#include <subdev/timer.h>
-#include <subdev/therm.h>
-
#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
static ssize_t
nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
int temp = therm->temp_get(therm);
if (temp < 0)
@@ -70,7 +66,7 @@ nouveau_hwmon_temp1_auto_point1_temp(struct device *d,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
return snprintf(buf, PAGE_SIZE, "%d\n",
therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST) * 1000);
@@ -82,7 +78,7 @@ nouveau_hwmon_set_temp1_auto_point1_temp(struct device *d,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
long value;
if (kstrtol(buf, 10, &value) == -EINVAL)
@@ -103,7 +99,7 @@ nouveau_hwmon_temp1_auto_point1_temp_hyst(struct device *d,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
return snprintf(buf, PAGE_SIZE, "%d\n",
therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST) * 1000);
@@ -115,7 +111,7 @@ nouveau_hwmon_set_temp1_auto_point1_temp_hyst(struct device *d,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
long value;
if (kstrtol(buf, 10, &value) == -EINVAL)
@@ -135,7 +131,7 @@ nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
return snprintf(buf, PAGE_SIZE, "%d\n",
therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK) * 1000);
@@ -146,7 +142,7 @@ nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
long value;
if (kstrtol(buf, 10, &value) == -EINVAL)
@@ -166,7 +162,7 @@ nouveau_hwmon_max_temp_hyst(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
return snprintf(buf, PAGE_SIZE, "%d\n",
therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST) * 1000);
@@ -177,7 +173,7 @@ nouveau_hwmon_set_max_temp_hyst(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
long value;
if (kstrtol(buf, 10, &value) == -EINVAL)
@@ -198,7 +194,7 @@ nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
return snprintf(buf, PAGE_SIZE, "%d\n",
therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL) * 1000);
@@ -210,7 +206,7 @@ nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
long value;
if (kstrtol(buf, 10, &value) == -EINVAL)
@@ -231,7 +227,7 @@ nouveau_hwmon_critical_temp_hyst(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
return snprintf(buf, PAGE_SIZE, "%d\n",
therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST) * 1000);
@@ -244,7 +240,7 @@ nouveau_hwmon_set_critical_temp_hyst(struct device *d,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
long value;
if (kstrtol(buf, 10, &value) == -EINVAL)
@@ -264,7 +260,7 @@ nouveau_hwmon_emergency_temp(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
return snprintf(buf, PAGE_SIZE, "%d\n",
therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN) * 1000);
@@ -276,7 +272,7 @@ nouveau_hwmon_set_emergency_temp(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
long value;
if (kstrtol(buf, 10, &value) == -EINVAL)
@@ -297,7 +293,7 @@ nouveau_hwmon_emergency_temp_hyst(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
return snprintf(buf, PAGE_SIZE, "%d\n",
therm->attr_get(therm, NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST) * 1000);
@@ -310,7 +306,7 @@ nouveau_hwmon_set_emergency_temp_hyst(struct device *d,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
long value;
if (kstrtol(buf, 10, &value) == -EINVAL)
@@ -350,7 +346,7 @@ nouveau_hwmon_show_fan1_input(struct device *d, struct device_attribute *attr,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
return snprintf(buf, PAGE_SIZE, "%d\n", therm->fan_sense(therm));
}
@@ -363,7 +359,7 @@ nouveau_hwmon_get_pwm1_enable(struct device *d,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
int ret;
ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MODE);
@@ -379,7 +375,7 @@ nouveau_hwmon_set_pwm1_enable(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
long value;
int ret;
@@ -402,7 +398,7 @@ nouveau_hwmon_get_pwm1(struct device *d, struct device_attribute *a, char *buf)
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
int ret;
ret = therm->fan_get(therm);
@@ -418,7 +414,7 @@ nouveau_hwmon_set_pwm1(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
int ret = -ENODEV;
long value;
@@ -442,7 +438,7 @@ nouveau_hwmon_get_pwm1_min(struct device *d,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
int ret;
ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MIN_DUTY);
@@ -458,7 +454,7 @@ nouveau_hwmon_set_pwm1_min(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
long value;
int ret;
@@ -482,7 +478,7 @@ nouveau_hwmon_get_pwm1_max(struct device *d,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
int ret;
ret = therm->attr_get(therm, NOUVEAU_THERM_ATTR_FAN_MAX_DUTY);
@@ -498,7 +494,7 @@ nouveau_hwmon_set_pwm1_max(struct device *d, struct device_attribute *a,
{
struct drm_device *dev = dev_get_drvdata(d);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
long value;
int ret;
@@ -565,7 +561,7 @@ nouveau_hwmon_init(struct drm_device *dev)
{
#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_therm *therm = nouveau_therm(drm->device);
+ struct nouveau_therm *therm = nvkm_therm(&drm->device);
struct nouveau_hwmon *hwmon;
struct device *hwmon_dev;
int ret = 0;
diff --git a/drivers/gpu/drm/nouveau/nouveau_nvif.c b/drivers/gpu/drm/nouveau/nouveau_nvif.c
new file mode 100644
index 000000000000..6544b84f0303
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_nvif.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+/*******************************************************************************
+ * NVIF client driver - NVKM directly linked
+ ******************************************************************************/
+
+#include <core/client.h>
+#include <core/notify.h>
+#include <core/ioctl.h>
+
+#include <nvif/client.h>
+#include <nvif/driver.h>
+#include <nvif/notify.h>
+#include <nvif/event.h>
+#include <nvif/ioctl.h>
+
+#include "nouveau_drm.h"
+#include "nouveau_usif.h"
+
+static void
+nvkm_client_unmap(void *priv, void __iomem *ptr, u32 size)
+{
+ iounmap(ptr);
+}
+
+static void __iomem *
+nvkm_client_map(void *priv, u64 handle, u32 size)
+{
+ return ioremap(handle, size);
+}
+
+static int
+nvkm_client_ioctl(void *priv, bool super, void *data, u32 size, void **hack)
+{
+ return nvkm_ioctl(priv, super, data, size, hack);
+}
+
+static int
+nvkm_client_resume(void *priv)
+{
+ return nouveau_client_init(priv);
+}
+
+static int
+nvkm_client_suspend(void *priv)
+{
+ return nouveau_client_fini(priv, true);
+}
+
+static void
+nvkm_client_fini(void *priv)
+{
+ struct nouveau_object *client = priv;
+ nouveau_client_fini(nv_client(client), false);
+ atomic_set(&client->refcount, 1);
+ nouveau_object_ref(NULL, &client);
+}
+
+static int
+nvkm_client_ntfy(const void *header, u32 length, const void *data, u32 size)
+{
+ const union {
+ struct nvif_notify_req_v0 v0;
+ } *args = header;
+ u8 route;
+
+ if (length == sizeof(args->v0) && args->v0.version == 0) {
+ route = args->v0.route;
+ } else {
+ WARN_ON(1);
+ return NVKM_NOTIFY_DROP;
+ }
+
+ switch (route) {
+ case NVDRM_NOTIFY_NVIF:
+ return nvif_notify(header, length, data, size);
+ case NVDRM_NOTIFY_USIF:
+ return usif_notify(header, length, data, size);
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ return NVKM_NOTIFY_DROP;
+}
+
+static int
+nvkm_client_init(const char *name, u64 device, const char *cfg,
+ const char *dbg, void **ppriv)
+{
+ struct nouveau_client *client;
+ int ret;
+
+ ret = nouveau_client_create(name, device, cfg, dbg, &client);
+ *ppriv = client;
+ if (ret)
+ return ret;
+
+ client->ntfy = nvkm_client_ntfy;
+ return 0;
+}
+
+const struct nvif_driver
+nvif_driver_nvkm = {
+ .name = "nvkm",
+ .init = nvkm_client_init,
+ .fini = nvkm_client_fini,
+ .suspend = nvkm_client_suspend,
+ .resume = nvkm_client_resume,
+ .ioctl = nvkm_client_ioctl,
+ .map = nvkm_client_map,
+ .unmap = nvkm_client_unmap,
+ .keep = false,
+};
diff --git a/drivers/gpu/drm/nouveau/nouveau_platform.c b/drivers/gpu/drm/nouveau/nouveau_platform.c
new file mode 100644
index 000000000000..246a824c16ca
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_platform.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+#include <soc/tegra/pmc.h>
+
+#include "nouveau_drm.h"
+#include "nouveau_platform.h"
+
+static int nouveau_platform_power_up(struct nouveau_platform_gpu *gpu)
+{
+ int err;
+
+ err = regulator_enable(gpu->vdd);
+ if (err)
+ goto err_power;
+
+ err = clk_prepare_enable(gpu->clk);
+ if (err)
+ goto err_clk;
+ err = clk_prepare_enable(gpu->clk_pwr);
+ if (err)
+ goto err_clk_pwr;
+ clk_set_rate(gpu->clk_pwr, 204000000);
+ udelay(10);
+
+ reset_control_assert(gpu->rst);
+ udelay(10);
+
+ err = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D);
+ if (err)
+ goto err_clamp;
+ udelay(10);
+
+ reset_control_deassert(gpu->rst);
+ udelay(10);
+
+ return 0;
+
+err_clamp:
+ clk_disable_unprepare(gpu->clk_pwr);
+err_clk_pwr:
+ clk_disable_unprepare(gpu->clk);
+err_clk:
+ regulator_disable(gpu->vdd);
+err_power:
+ return err;
+}
+
+static int nouveau_platform_power_down(struct nouveau_platform_gpu *gpu)
+{
+ int err;
+
+ reset_control_assert(gpu->rst);
+ udelay(10);
+
+ clk_disable_unprepare(gpu->clk_pwr);
+ clk_disable_unprepare(gpu->clk);
+ udelay(10);
+
+ err = regulator_disable(gpu->vdd);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int nouveau_platform_probe(struct platform_device *pdev)
+{
+ struct nouveau_platform_gpu *gpu;
+ struct nouveau_platform_device *device;
+ struct drm_device *drm;
+ int err;
+
+ gpu = devm_kzalloc(&pdev->dev, sizeof(*gpu), GFP_KERNEL);
+ if (!gpu)
+ return -ENOMEM;
+
+ gpu->vdd = devm_regulator_get(&pdev->dev, "vdd");
+ if (IS_ERR(gpu->vdd))
+ return PTR_ERR(gpu->vdd);
+
+ gpu->rst = devm_reset_control_get(&pdev->dev, "gpu");
+ if (IS_ERR(gpu->rst))
+ return PTR_ERR(gpu->rst);
+
+ gpu->clk = devm_clk_get(&pdev->dev, "gpu");
+ if (IS_ERR(gpu->clk))
+ return PTR_ERR(gpu->clk);
+
+ gpu->clk_pwr = devm_clk_get(&pdev->dev, "pwr");
+ if (IS_ERR(gpu->clk_pwr))
+ return PTR_ERR(gpu->clk_pwr);
+
+ err = nouveau_platform_power_up(gpu);
+ if (err)
+ return err;
+
+ drm = nouveau_platform_device_create(pdev, &device);
+ if (IS_ERR(drm)) {
+ err = PTR_ERR(drm);
+ goto power_down;
+ }
+
+ device->gpu = gpu;
+
+ err = drm_dev_register(drm, 0);
+ if (err < 0)
+ goto err_unref;
+
+ return 0;
+
+err_unref:
+ drm_dev_unref(drm);
+
+ return 0;
+
+power_down:
+ nouveau_platform_power_down(gpu);
+
+ return err;
+}
+
+static int nouveau_platform_remove(struct platform_device *pdev)
+{
+ struct drm_device *drm_dev = platform_get_drvdata(pdev);
+ struct nouveau_drm *drm = nouveau_drm(drm_dev);
+ struct nouveau_device *device = nvkm_device(&drm->device);
+ struct nouveau_platform_gpu *gpu = nv_device_to_platform(device)->gpu;
+
+ nouveau_drm_device_remove(drm_dev);
+
+ return nouveau_platform_power_down(gpu);
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id nouveau_platform_match[] = {
+ { .compatible = "nvidia,gk20a" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, nouveau_platform_match);
+#endif
+
+struct platform_driver nouveau_platform_driver = {
+ .driver = {
+ .name = "nouveau",
+ .of_match_table = of_match_ptr(nouveau_platform_match),
+ },
+ .probe = nouveau_platform_probe,
+ .remove = nouveau_platform_remove,
+};
+
+module_platform_driver(nouveau_platform_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/nouveau/nouveau_platform.h b/drivers/gpu/drm/nouveau/nouveau_platform.h
new file mode 100644
index 000000000000..91f66504900e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_platform.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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.
+ */
+
+#ifndef __NOUVEAU_PLATFORM_H__
+#define __NOUVEAU_PLATFORM_H__
+
+#include "core/device.h"
+
+struct reset_control;
+struct clk;
+struct regulator;
+
+struct nouveau_platform_gpu {
+ struct reset_control *rst;
+ struct clk *clk;
+ struct clk *clk_pwr;
+
+ struct regulator *vdd;
+};
+
+struct nouveau_platform_device {
+ struct nouveau_device device;
+
+ struct nouveau_platform_gpu *gpu;
+};
+
+#define nv_device_to_platform(d) \
+ container_of(d, struct nouveau_platform_device, device)
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c
index 1f51008e4d26..228226ab27fc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_prime.c
+++ b/drivers/gpu/drm/nouveau/nouveau_prime.c
@@ -23,6 +23,7 @@
*/
#include <drm/drmP.h>
+#include <linux/dma-buf.h>
#include "nouveau_drm.h"
#include "nouveau_gem.h"
@@ -56,17 +57,20 @@ void nouveau_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
}
struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev,
- size_t size,
+ struct dma_buf_attachment *attach,
struct sg_table *sg)
{
struct nouveau_bo *nvbo;
+ struct reservation_object *robj = attach->dmabuf->resv;
u32 flags = 0;
int ret;
flags = TTM_PL_FLAG_TT;
- ret = nouveau_bo_new(dev, size, 0, flags, 0, 0,
- sg, &nvbo);
+ ww_mutex_lock(&robj->lock, NULL);
+ ret = nouveau_bo_new(dev, attach->dmabuf->size, 0, flags, 0, 0,
+ sg, robj, &nvbo);
+ ww_mutex_unlock(&robj->lock);
if (ret)
return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
index a4d22e5eb176..01707e7deaf5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
@@ -1,8 +1,6 @@
#include <linux/pagemap.h>
#include <linux/slab.h>
-#include <subdev/fb.h>
-
#include "nouveau_drm.h"
#include "nouveau_ttm.h"
@@ -104,7 +102,7 @@ nouveau_sgdma_create_ttm(struct ttm_bo_device *bdev,
return NULL;
nvbe->dev = drm->dev;
- if (nv_device(drm->device)->card_type < NV_50)
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA)
nvbe->ttm.ttm.func = &nv04_sgdma_backend;
else
nvbe->ttm.ttm.func = &nv50_sgdma_backend;
diff --git a/drivers/gpu/drm/nouveau/nouveau_sysfs.c b/drivers/gpu/drm/nouveau/nouveau_sysfs.c
index 75dda2b07176..8fbbf3093d86 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sysfs.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sysfs.c
@@ -22,10 +22,15 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
+#include <nvif/os.h>
+#include <nvif/class.h>
+#include <nvif/ioctl.h>
+
#include "nouveau_sysfs.h"
-#include <core/object.h>
-#include <core/class.h>
+MODULE_PARM_DESC(pstate, "enable sysfs pstate file, which will be moved in the future");
+int nouveau_pstate;
+module_param_named(pstate, nouveau_pstate, int, 0400);
static inline struct drm_device *
drm_device(struct device *d)
@@ -43,38 +48,42 @@ static ssize_t
nouveau_sysfs_pstate_get(struct device *d, struct device_attribute *a, char *b)
{
struct nouveau_sysfs *sysfs = nouveau_sysfs(drm_device(d));
- struct nv_control_pstate_info info;
+ struct nvif_control_pstate_info_v0 info = {};
size_t cnt = PAGE_SIZE;
char *buf = b;
int ret, i;
- ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_INFO, &info, sizeof(info));
+ ret = nvif_mthd(&sysfs->ctrl, NVIF_CONTROL_PSTATE_INFO,
+ &info, sizeof(info));
if (ret)
return ret;
for (i = 0; i < info.count + 1; i++) {
const s32 state = i < info.count ? i :
- NV_CONTROL_PSTATE_ATTR_STATE_CURRENT;
- struct nv_control_pstate_attr attr = {
+ NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT;
+ struct nvif_control_pstate_attr_v0 attr = {
.state = state,
.index = 0,
};
- ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_ATTR,
- &attr, sizeof(attr));
+ ret = nvif_mthd(&sysfs->ctrl, NVIF_CONTROL_PSTATE_ATTR,
+ &attr, sizeof(attr));
if (ret)
return ret;
if (i < info.count)
snappendf(buf, cnt, "%02x:", attr.state);
else
- snappendf(buf, cnt, "--:");
+ snappendf(buf, cnt, "%s:", info.pwrsrc == 0 ? "DC" :
+ info.pwrsrc == 1 ? "AC" :
+ "--");
attr.index = 0;
do {
attr.state = state;
- ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_ATTR,
- &attr, sizeof(attr));
+ ret = nvif_mthd(&sysfs->ctrl,
+ NVIF_CONTROL_PSTATE_ATTR,
+ &attr, sizeof(attr));
if (ret)
return ret;
@@ -84,9 +93,20 @@ nouveau_sysfs_pstate_get(struct device *d, struct device_attribute *a, char *b)
snappendf(buf, cnt, " %s", attr.unit);
} while (attr.index);
- if ((state >= 0 && info.pstate == state) ||
- (state < 0 && info.ustate < 0))
- snappendf(buf, cnt, " *");
+ if (state >= 0) {
+ if (info.ustate_ac == state)
+ snappendf(buf, cnt, " AC");
+ if (info.ustate_dc == state)
+ snappendf(buf, cnt, " DC");
+ if (info.pstate == state)
+ snappendf(buf, cnt, " *");
+ } else {
+ if (info.ustate_ac < -1)
+ snappendf(buf, cnt, " AC");
+ if (info.ustate_dc < -1)
+ snappendf(buf, cnt, " DC");
+ }
+
snappendf(buf, cnt, "\n");
}
@@ -98,26 +118,36 @@ nouveau_sysfs_pstate_set(struct device *d, struct device_attribute *a,
const char *buf, size_t count)
{
struct nouveau_sysfs *sysfs = nouveau_sysfs(drm_device(d));
- struct nv_control_pstate_user args;
+ struct nvif_control_pstate_user_v0 args = { .pwrsrc = -EINVAL };
long value, ret;
char *tmp;
if ((tmp = strchr(buf, '\n')))
*tmp = '\0';
+ if (!strncasecmp(buf, "dc:", 3)) {
+ args.pwrsrc = 0;
+ buf += 3;
+ } else
+ if (!strncasecmp(buf, "ac:", 3)) {
+ args.pwrsrc = 1;
+ buf += 3;
+ }
+
if (!strcasecmp(buf, "none"))
- args.state = NV_CONTROL_PSTATE_USER_STATE_UNKNOWN;
+ args.ustate = NVIF_CONTROL_PSTATE_USER_V0_STATE_UNKNOWN;
else
if (!strcasecmp(buf, "auto"))
- args.state = NV_CONTROL_PSTATE_USER_STATE_PERFMON;
+ args.ustate = NVIF_CONTROL_PSTATE_USER_V0_STATE_PERFMON;
else {
ret = kstrtol(buf, 16, &value);
if (ret)
return ret;
- args.state = value;
+ args.ustate = value;
}
- ret = nv_exec(sysfs->ctrl, NV_CONTROL_PSTATE_USER, &args, sizeof(args));
+ ret = nvif_mthd(&sysfs->ctrl, NVIF_CONTROL_PSTATE_USER,
+ &args, sizeof(args));
if (ret < 0)
return ret;
@@ -132,11 +162,11 @@ nouveau_sysfs_fini(struct drm_device *dev)
{
struct nouveau_sysfs *sysfs = nouveau_sysfs(dev);
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
- if (sysfs->ctrl) {
- device_remove_file(nv_device_base(device), &dev_attr_pstate);
- nouveau_object_del(nv_object(drm), NVDRM_DEVICE, NVDRM_CONTROL);
+ if (sysfs && sysfs->ctrl.priv) {
+ device_remove_file(nv_device_base(nvkm_device(device)), &dev_attr_pstate);
+ nvif_object_fini(&sysfs->ctrl);
}
drm->sysfs = NULL;
@@ -147,18 +177,22 @@ int
nouveau_sysfs_init(struct drm_device *dev)
{
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_device *device = nv_device(drm->device);
+ struct nvif_device *device = &drm->device;
struct nouveau_sysfs *sysfs;
int ret;
+ if (!nouveau_pstate)
+ return 0;
+
sysfs = drm->sysfs = kzalloc(sizeof(*sysfs), GFP_KERNEL);
if (!sysfs)
return -ENOMEM;
- ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE, NVDRM_CONTROL,
- NV_CONTROL_CLASS, NULL, 0, &sysfs->ctrl);
+ ret = nvif_object_init(nvif_object(device), NULL, NVDRM_CONTROL,
+ NVIF_IOCTL_NEW_V0_CONTROL, NULL, 0,
+ &sysfs->ctrl);
if (ret == 0)
- device_create_file(nv_device_base(device), &dev_attr_pstate);
+ device_create_file(nv_device_base(nvkm_device(device)), &dev_attr_pstate);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_sysfs.h b/drivers/gpu/drm/nouveau/nouveau_sysfs.h
index 74b47f1e01ed..4e5ea9241b28 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sysfs.h
+++ b/drivers/gpu/drm/nouveau/nouveau_sysfs.h
@@ -4,7 +4,7 @@
#include "nouveau_drm.h"
struct nouveau_sysfs {
- struct nouveau_object *ctrl;
+ struct nvif_object ctrl;
};
static inline struct nouveau_sysfs *
@@ -16,4 +16,6 @@ nouveau_sysfs(struct drm_device *dev)
int nouveau_sysfs_init(struct drm_device *);
void nouveau_sysfs_fini(struct drm_device *);
+extern int nouveau_pstate;
+
#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c
index ab0228f640a5..753a6def61e7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_ttm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c
@@ -24,10 +24,6 @@
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-#include <subdev/fb.h>
-#include <subdev/vm.h>
-#include <subdev/instmem.h>
-
#include "nouveau_drm.h"
#include "nouveau_ttm.h"
#include "nouveau_gem.h"
@@ -36,7 +32,7 @@ static int
nouveau_vram_manager_init(struct ttm_mem_type_manager *man, unsigned long psize)
{
struct nouveau_drm *drm = nouveau_bdev(man->bdev);
- struct nouveau_fb *pfb = nouveau_fb(drm->device);
+ struct nouveau_fb *pfb = nvkm_fb(&drm->device);
man->priv = pfb;
return 0;
}
@@ -67,7 +63,7 @@ nouveau_vram_manager_del(struct ttm_mem_type_manager *man,
struct ttm_mem_reg *mem)
{
struct nouveau_drm *drm = nouveau_bdev(man->bdev);
- struct nouveau_fb *pfb = nouveau_fb(drm->device);
+ struct nouveau_fb *pfb = nvkm_fb(&drm->device);
nouveau_mem_node_cleanup(mem->mm_node);
pfb->ram->put(pfb, (struct nouveau_mem **)&mem->mm_node);
}
@@ -75,11 +71,11 @@ nouveau_vram_manager_del(struct ttm_mem_type_manager *man,
static int
nouveau_vram_manager_new(struct ttm_mem_type_manager *man,
struct ttm_buffer_object *bo,
- struct ttm_placement *placement,
+ const struct ttm_place *place,
struct ttm_mem_reg *mem)
{
struct nouveau_drm *drm = nouveau_bdev(man->bdev);
- struct nouveau_fb *pfb = nouveau_fb(drm->device);
+ struct nouveau_fb *pfb = nvkm_fb(&drm->device);
struct nouveau_bo *nvbo = nouveau_bo(bo);
struct nouveau_mem *node;
u32 size_nc = 0;
@@ -161,7 +157,7 @@ nouveau_gart_manager_del(struct ttm_mem_type_manager *man,
static int
nouveau_gart_manager_new(struct ttm_mem_type_manager *man,
struct ttm_buffer_object *bo,
- struct ttm_placement *placement,
+ const struct ttm_place *place,
struct ttm_mem_reg *mem)
{
struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
@@ -174,14 +170,13 @@ nouveau_gart_manager_new(struct ttm_mem_type_manager *man,
node->page_shift = 12;
- switch (nv_device(drm->device)->card_type) {
- case NV_50:
- if (nv_device(drm->device)->chipset != 0x50)
+ switch (drm->device.info.family) {
+ case NV_DEVICE_INFO_V0_TESLA:
+ if (drm->device.info.chipset != 0x50)
node->memtype = (nvbo->tile_flags & 0x7f00) >> 8;
break;
- case NV_C0:
- case NV_D0:
- case NV_E0:
+ case NV_DEVICE_INFO_V0_FERMI:
+ case NV_DEVICE_INFO_V0_KEPLER:
node->memtype = (nvbo->tile_flags & 0xff00) >> 8;
break;
default:
@@ -206,12 +201,13 @@ const struct ttm_mem_type_manager_func nouveau_gart_manager = {
nouveau_gart_manager_debug
};
+/*XXX*/
#include <core/subdev/vm/nv04.h>
static int
nv04_gart_manager_init(struct ttm_mem_type_manager *man, unsigned long psize)
{
struct nouveau_drm *drm = nouveau_bdev(man->bdev);
- struct nouveau_vmmgr *vmm = nouveau_vmmgr(drm->device);
+ struct nouveau_vmmgr *vmm = nvkm_vmmgr(&drm->device);
struct nv04_vmmgr_priv *priv = (void *)vmm;
struct nouveau_vm *vm = NULL;
nouveau_vm_ref(priv->vm, &vm, NULL);
@@ -241,7 +237,7 @@ nv04_gart_manager_del(struct ttm_mem_type_manager *man, struct ttm_mem_reg *mem)
static int
nv04_gart_manager_new(struct ttm_mem_type_manager *man,
struct ttm_buffer_object *bo,
- struct ttm_placement *placement,
+ const struct ttm_place *place,
struct ttm_mem_reg *mem)
{
struct nouveau_mem *node;
@@ -285,7 +281,7 @@ nouveau_ttm_mmap(struct file *filp, struct vm_area_struct *vma)
struct nouveau_drm *drm = nouveau_drm(file_priv->minor->dev);
if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
- return drm_mmap(filp, vma);
+ return -EINVAL;
return ttm_bo_mmap(filp, vma, &drm->ttm.bdev);
}
@@ -354,12 +350,11 @@ int
nouveau_ttm_init(struct nouveau_drm *drm)
{
struct drm_device *dev = drm->dev;
- struct nouveau_device *device = nv_device(drm->device);
u32 bits;
int ret;
- bits = nouveau_vmmgr(drm->device)->dma_bits;
- if (nv_device_is_pci(device)) {
+ bits = nvkm_vmmgr(&drm->device)->dma_bits;
+ if (nv_device_is_pci(nvkm_device(&drm->device))) {
if (drm->agp.stat == ENABLED ||
!pci_dma_supported(dev->pdev, DMA_BIT_MASK(bits)))
bits = 32;
@@ -391,8 +386,7 @@ nouveau_ttm_init(struct nouveau_drm *drm)
}
/* VRAM init */
- drm->gem.vram_available = nouveau_fb(drm->device)->ram->size;
- drm->gem.vram_available -= nouveau_instmem(drm->device)->reserved;
+ drm->gem.vram_available = drm->device.info.ram_user;
ret = ttm_bo_init_mm(&drm->ttm.bdev, TTM_PL_VRAM,
drm->gem.vram_available >> PAGE_SHIFT);
@@ -401,12 +395,12 @@ nouveau_ttm_init(struct nouveau_drm *drm)
return ret;
}
- drm->ttm.mtrr = arch_phys_wc_add(nv_device_resource_start(device, 1),
- nv_device_resource_len(device, 1));
+ drm->ttm.mtrr = arch_phys_wc_add(nv_device_resource_start(nvkm_device(&drm->device), 1),
+ nv_device_resource_len(nvkm_device(&drm->device), 1));
/* GART init */
if (drm->agp.stat != ENABLED) {
- drm->gem.gart_available = nouveau_vmmgr(drm->device)->limit;
+ drm->gem.gart_available = nvkm_vmmgr(&drm->device)->limit;
} else {
drm->gem.gart_available = drm->agp.size;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_usif.c b/drivers/gpu/drm/nouveau/nouveau_usif.c
new file mode 100644
index 000000000000..cb1182d7e80e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_usif.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "nouveau_drm.h"
+#include "nouveau_usif.h"
+
+#include <nvif/notify.h>
+#include <nvif/unpack.h>
+#include <nvif/client.h>
+#include <nvif/event.h>
+#include <nvif/ioctl.h>
+
+struct usif_notify_p {
+ struct drm_pending_event base;
+ struct {
+ struct drm_event base;
+ u8 data[];
+ } e;
+};
+
+struct usif_notify {
+ struct list_head head;
+ atomic_t enabled;
+ u32 handle;
+ u16 reply;
+ u8 route;
+ u64 token;
+ struct usif_notify_p *p;
+};
+
+static inline struct usif_notify *
+usif_notify_find(struct drm_file *filp, u32 handle)
+{
+ struct nouveau_cli *cli = nouveau_cli(filp);
+ struct usif_notify *ntfy;
+ list_for_each_entry(ntfy, &cli->notifys, head) {
+ if (ntfy->handle == handle)
+ return ntfy;
+ }
+ return NULL;
+}
+
+static inline void
+usif_notify_dtor(struct usif_notify *ntfy)
+{
+ list_del(&ntfy->head);
+ kfree(ntfy);
+}
+
+int
+usif_notify(const void *header, u32 length, const void *data, u32 size)
+{
+ struct usif_notify *ntfy = NULL;
+ const union {
+ struct nvif_notify_rep_v0 v0;
+ } *rep = header;
+ struct drm_device *dev;
+ struct drm_file *filp;
+ unsigned long flags;
+
+ if (length == sizeof(rep->v0) && rep->v0.version == 0) {
+ if (WARN_ON(!(ntfy = (void *)(unsigned long)rep->v0.token)))
+ return NVIF_NOTIFY_DROP;
+ BUG_ON(rep->v0.route != NVDRM_NOTIFY_USIF);
+ } else
+ if (WARN_ON(1))
+ return NVIF_NOTIFY_DROP;
+
+ if (WARN_ON(!ntfy->p || ntfy->reply != (length + size)))
+ return NVIF_NOTIFY_DROP;
+ filp = ntfy->p->base.file_priv;
+ dev = filp->minor->dev;
+
+ memcpy(&ntfy->p->e.data[0], header, length);
+ memcpy(&ntfy->p->e.data[length], data, size);
+ switch (rep->v0.version) {
+ case 0: {
+ struct nvif_notify_rep_v0 *rep = (void *)ntfy->p->e.data;
+ rep->route = ntfy->route;
+ rep->token = ntfy->token;
+ }
+ break;
+ default:
+ BUG_ON(1);
+ break;
+ }
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ if (!WARN_ON(filp->event_space < ntfy->p->e.base.length)) {
+ list_add_tail(&ntfy->p->base.link, &filp->event_list);
+ filp->event_space -= ntfy->p->e.base.length;
+ }
+ wake_up_interruptible(&filp->event_wait);
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ atomic_set(&ntfy->enabled, 0);
+ return NVIF_NOTIFY_DROP;
+}
+
+static int
+usif_notify_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
+{
+ struct nouveau_cli *cli = nouveau_cli(f);
+ struct nvif_client *client = &cli->base;
+ union {
+ struct nvif_ioctl_ntfy_new_v0 v0;
+ } *args = data;
+ union {
+ struct nvif_notify_req_v0 v0;
+ } *req;
+ struct usif_notify *ntfy;
+ int ret;
+
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ if (usif_notify_find(f, args->v0.index))
+ return -EEXIST;
+ } else
+ return ret;
+ req = data;
+
+ if (!(ntfy = kmalloc(sizeof(*ntfy), GFP_KERNEL)))
+ return -ENOMEM;
+ atomic_set(&ntfy->enabled, 0);
+
+ if (nvif_unpack(req->v0, 0, 0, true)) {
+ ntfy->reply = sizeof(struct nvif_notify_rep_v0) + req->v0.reply;
+ ntfy->route = req->v0.route;
+ ntfy->token = req->v0.token;
+ req->v0.route = NVDRM_NOTIFY_USIF;
+ req->v0.token = (unsigned long)(void *)ntfy;
+ ret = nvif_client_ioctl(client, argv, argc);
+ req->v0.token = ntfy->token;
+ req->v0.route = ntfy->route;
+ ntfy->handle = args->v0.index;
+ }
+
+ if (ret == 0)
+ list_add(&ntfy->head, &cli->notifys);
+ if (ret)
+ kfree(ntfy);
+ return ret;
+}
+
+static int
+usif_notify_del(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
+{
+ struct nouveau_cli *cli = nouveau_cli(f);
+ struct nvif_client *client = &cli->base;
+ union {
+ struct nvif_ioctl_ntfy_del_v0 v0;
+ } *args = data;
+ struct usif_notify *ntfy;
+ int ret;
+
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ if (!(ntfy = usif_notify_find(f, args->v0.index)))
+ return -ENOENT;
+ } else
+ return ret;
+
+ ret = nvif_client_ioctl(client, argv, argc);
+ if (ret == 0)
+ usif_notify_dtor(ntfy);
+ return ret;
+}
+
+static int
+usif_notify_get(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
+{
+ struct nouveau_cli *cli = nouveau_cli(f);
+ struct nvif_client *client = &cli->base;
+ union {
+ struct nvif_ioctl_ntfy_del_v0 v0;
+ } *args = data;
+ struct usif_notify *ntfy;
+ int ret;
+
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ if (!(ntfy = usif_notify_find(f, args->v0.index)))
+ return -ENOENT;
+ } else
+ return ret;
+
+ if (atomic_xchg(&ntfy->enabled, 1))
+ return 0;
+
+ ntfy->p = kmalloc(sizeof(*ntfy->p) + ntfy->reply, GFP_KERNEL);
+ if (ret = -ENOMEM, !ntfy->p)
+ goto done;
+ ntfy->p->base.event = &ntfy->p->e.base;
+ ntfy->p->base.file_priv = f;
+ ntfy->p->base.pid = current->pid;
+ ntfy->p->base.destroy =(void(*)(struct drm_pending_event *))kfree;
+ ntfy->p->e.base.type = DRM_NOUVEAU_EVENT_NVIF;
+ ntfy->p->e.base.length = sizeof(ntfy->p->e.base) + ntfy->reply;
+
+ ret = nvif_client_ioctl(client, argv, argc);
+done:
+ if (ret) {
+ atomic_set(&ntfy->enabled, 0);
+ kfree(ntfy->p);
+ }
+ return ret;
+}
+
+static int
+usif_notify_put(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
+{
+ struct nouveau_cli *cli = nouveau_cli(f);
+ struct nvif_client *client = &cli->base;
+ union {
+ struct nvif_ioctl_ntfy_put_v0 v0;
+ } *args = data;
+ struct usif_notify *ntfy;
+ int ret;
+
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ if (!(ntfy = usif_notify_find(f, args->v0.index)))
+ return -ENOENT;
+ } else
+ return ret;
+
+ ret = nvif_client_ioctl(client, argv, argc);
+ if (ret == 0 && atomic_xchg(&ntfy->enabled, 0))
+ kfree(ntfy->p);
+ return ret;
+}
+
+struct usif_object {
+ struct list_head head;
+ struct list_head ntfy;
+ u8 route;
+ u64 token;
+};
+
+static void
+usif_object_dtor(struct usif_object *object)
+{
+ list_del(&object->head);
+ kfree(object);
+}
+
+static int
+usif_object_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
+{
+ struct nouveau_cli *cli = nouveau_cli(f);
+ struct nvif_client *client = &cli->base;
+ union {
+ struct nvif_ioctl_new_v0 v0;
+ } *args = data;
+ struct usif_object *object;
+ int ret;
+
+ if (!(object = kmalloc(sizeof(*object), GFP_KERNEL)))
+ return -ENOMEM;
+ list_add(&object->head, &cli->objects);
+
+ if (nvif_unpack(args->v0, 0, 0, true)) {
+ object->route = args->v0.route;
+ object->token = args->v0.token;
+ args->v0.route = NVDRM_OBJECT_USIF;
+ args->v0.token = (unsigned long)(void *)object;
+ ret = nvif_client_ioctl(client, argv, argc);
+ args->v0.token = object->token;
+ args->v0.route = object->route;
+ }
+
+ if (ret)
+ usif_object_dtor(object);
+ return ret;
+}
+
+int
+usif_ioctl(struct drm_file *filp, void __user *user, u32 argc)
+{
+ struct nouveau_cli *cli = nouveau_cli(filp);
+ struct nvif_client *client = &cli->base;
+ void *data = kmalloc(argc, GFP_KERNEL);
+ u32 size = argc;
+ union {
+ struct nvif_ioctl_v0 v0;
+ } *argv = data;
+ struct usif_object *object;
+ u8 owner;
+ int ret;
+
+ if (ret = -ENOMEM, !argv)
+ goto done;
+ if (ret = -EFAULT, copy_from_user(argv, user, size))
+ goto done;
+
+ if (nvif_unpack(argv->v0, 0, 0, true)) {
+ /* block access to objects not created via this interface */
+ owner = argv->v0.owner;
+ argv->v0.owner = NVDRM_OBJECT_USIF;
+ } else
+ goto done;
+
+ mutex_lock(&cli->mutex);
+ switch (argv->v0.type) {
+ case NVIF_IOCTL_V0_NEW:
+ /* ... except if we're creating children */
+ argv->v0.owner = NVIF_IOCTL_V0_OWNER_ANY;
+ ret = usif_object_new(filp, data, size, argv, argc);
+ break;
+ case NVIF_IOCTL_V0_NTFY_NEW:
+ ret = usif_notify_new(filp, data, size, argv, argc);
+ break;
+ case NVIF_IOCTL_V0_NTFY_DEL:
+ ret = usif_notify_del(filp, data, size, argv, argc);
+ break;
+ case NVIF_IOCTL_V0_NTFY_GET:
+ ret = usif_notify_get(filp, data, size, argv, argc);
+ break;
+ case NVIF_IOCTL_V0_NTFY_PUT:
+ ret = usif_notify_put(filp, data, size, argv, argc);
+ break;
+ default:
+ ret = nvif_client_ioctl(client, argv, argc);
+ break;
+ }
+ if (argv->v0.route == NVDRM_OBJECT_USIF) {
+ object = (void *)(unsigned long)argv->v0.token;
+ argv->v0.route = object->route;
+ argv->v0.token = object->token;
+ if (ret == 0 && argv->v0.type == NVIF_IOCTL_V0_DEL) {
+ list_del(&object->head);
+ kfree(object);
+ }
+ } else {
+ argv->v0.route = NVIF_IOCTL_V0_ROUTE_HIDDEN;
+ argv->v0.token = 0;
+ }
+ argv->v0.owner = owner;
+ mutex_unlock(&cli->mutex);
+
+ if (copy_to_user(user, argv, argc))
+ ret = -EFAULT;
+done:
+ kfree(argv);
+ return ret;
+}
+
+void
+usif_client_fini(struct nouveau_cli *cli)
+{
+ struct usif_object *object, *otemp;
+ struct usif_notify *notify, *ntemp;
+
+ list_for_each_entry_safe(notify, ntemp, &cli->notifys, head) {
+ usif_notify_dtor(notify);
+ }
+
+ list_for_each_entry_safe(object, otemp, &cli->objects, head) {
+ usif_object_dtor(object);
+ }
+}
+
+void
+usif_client_init(struct nouveau_cli *cli)
+{
+ INIT_LIST_HEAD(&cli->objects);
+ INIT_LIST_HEAD(&cli->notifys);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_usif.h b/drivers/gpu/drm/nouveau/nouveau_usif.h
new file mode 100644
index 000000000000..c037e3ae8c70
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_usif.h
@@ -0,0 +1,9 @@
+#ifndef __NOUVEAU_USIF_H__
+#define __NOUVEAU_USIF_H__
+
+void usif_client_init(struct nouveau_cli *);
+void usif_client_fini(struct nouveau_cli *);
+int usif_ioctl(struct drm_file *, void __user *, u32);
+int usif_notify(const void *, u32, const void *, u32);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c
index 4f4c3fec6916..c7592ec8ecb8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -12,14 +12,16 @@
static unsigned int
nouveau_vga_set_decode(void *priv, bool state)
{
- struct nouveau_device *device = nouveau_dev(priv);
+ struct nvif_device *device = &nouveau_drm(priv)->device;
- if (device->card_type == NV_40 && device->chipset >= 0x4c)
- nv_wr32(device, 0x088060, state);
- else if (device->chipset >= 0x40)
- nv_wr32(device, 0x088054, state);
+ if (device->info.family == NV_DEVICE_INFO_V0_CURIE &&
+ device->info.chipset >= 0x4c)
+ nvif_wr32(device, 0x088060, state);
else
- nv_wr32(device, 0x001854, state);
+ if (device->info.chipset >= 0x40)
+ nvif_wr32(device, 0x088054, state);
+ else
+ nvif_wr32(device, 0x001854, state);
if (state)
return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM |
@@ -106,7 +108,16 @@ void
nouveau_vga_fini(struct nouveau_drm *drm)
{
struct drm_device *dev = drm->dev;
+ bool runtime = false;
+
+ if (nouveau_runtime_pm == 1)
+ runtime = true;
+ if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm()))
+ runtime = true;
+
vga_switcheroo_unregister_client(dev->pdev);
+ if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus())
+ vga_switcheroo_fini_domain_pm_ops(drm->dev->dev);
vga_client_register(dev->pdev, NULL, NULL, NULL);
}
diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c
index 8fe32bbed99a..4ef602c5469d 100644
--- a/drivers/gpu/drm/nouveau/nv04_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c
@@ -22,8 +22,6 @@
* DEALINGS IN THE SOFTWARE.
*/
-#include <core/object.h>
-
#include "nouveau_drm.h"
#include "nouveau_dma.h"
#include "nouveau_fbcon.h"
@@ -141,8 +139,7 @@ nv04_fbcon_accel_init(struct fb_info *info)
struct drm_device *dev = nfbdev->dev;
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_channel *chan = drm->channel;
- struct nouveau_device *device = nv_device(drm->device);
- struct nouveau_object *object;
+ struct nvif_device *device = &drm->device;
int surface_fmt, pattern_fmt, rect_fmt;
int ret;
@@ -174,35 +171,35 @@ nv04_fbcon_accel_init(struct fb_info *info)
return -EINVAL;
}
- ret = nouveau_object_new(nv_object(chan->cli), NVDRM_CHAN, NvCtxSurf2D,
- device->card_type >= NV_10 ? 0x0062 : 0x0042,
- NULL, 0, &object);
+ ret = nvif_object_init(chan->object, NULL, 0x0062,
+ device->info.family >= NV_DEVICE_INFO_V0_CELSIUS ?
+ 0x0062 : 0x0042, NULL, 0, &nfbdev->surf2d);
if (ret)
return ret;
- ret = nouveau_object_new(nv_object(chan->cli), NVDRM_CHAN, NvClipRect,
- 0x0019, NULL, 0, &object);
+ ret = nvif_object_init(chan->object, NULL, 0x0019, 0x0019, NULL, 0,
+ &nfbdev->clip);
if (ret)
return ret;
- ret = nouveau_object_new(nv_object(chan->cli), NVDRM_CHAN, NvRop,
- 0x0043, NULL, 0, &object);
+ ret = nvif_object_init(chan->object, NULL, 0x0043, 0x0043, NULL, 0,
+ &nfbdev->rop);
if (ret)
return ret;
- ret = nouveau_object_new(nv_object(chan->cli), NVDRM_CHAN, NvImagePatt,
- 0x0044, NULL, 0, &object);
+ ret = nvif_object_init(chan->object, NULL, 0x0044, 0x0044, NULL, 0,
+ &nfbdev->patt);
if (ret)
return ret;
- ret = nouveau_object_new(nv_object(chan->cli), NVDRM_CHAN, NvGdiRect,
- 0x004a, NULL, 0, &object);
+ ret = nvif_object_init(chan->object, NULL, 0x004a, 0x004a, NULL, 0,
+ &nfbdev->gdi);
if (ret)
return ret;
- ret = nouveau_object_new(nv_object(chan->cli), NVDRM_CHAN, NvImageBlit,
- device->chipset >= 0x11 ? 0x009f : 0x005f,
- NULL, 0, &object);
+ ret = nvif_object_init(chan->object, NULL, 0x005f,
+ device->info.chipset >= 0x11 ? 0x009f : 0x005f,
+ NULL, 0, &nfbdev->blit);
if (ret)
return ret;
@@ -212,10 +209,10 @@ nv04_fbcon_accel_init(struct fb_info *info)
}
BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0000, 1);
- OUT_RING(chan, NvCtxSurf2D);
+ OUT_RING(chan, nfbdev->surf2d.handle);
BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0184, 2);
- OUT_RING(chan, NvDmaFB);
- OUT_RING(chan, NvDmaFB);
+ OUT_RING(chan, chan->vram.handle);
+ OUT_RING(chan, chan->vram.handle);
BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0300, 4);
OUT_RING(chan, surface_fmt);
OUT_RING(chan, info->fix.line_length | (info->fix.line_length << 16));
@@ -223,12 +220,12 @@ nv04_fbcon_accel_init(struct fb_info *info)
OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base);
BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0000, 1);
- OUT_RING(chan, NvRop);
+ OUT_RING(chan, nfbdev->rop.handle);
BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0300, 1);
OUT_RING(chan, 0x55);
BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0000, 1);
- OUT_RING(chan, NvImagePatt);
+ OUT_RING(chan, nfbdev->patt.handle);
BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0300, 8);
OUT_RING(chan, pattern_fmt);
#ifdef __BIG_ENDIAN
@@ -244,18 +241,18 @@ nv04_fbcon_accel_init(struct fb_info *info)
OUT_RING(chan, ~0);
BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0000, 1);
- OUT_RING(chan, NvClipRect);
+ OUT_RING(chan, nfbdev->clip.handle);
BEGIN_NV04(chan, NvSubCtxSurf2D, 0x0300, 2);
OUT_RING(chan, 0);
OUT_RING(chan, (info->var.yres_virtual << 16) | info->var.xres_virtual);
BEGIN_NV04(chan, NvSubImageBlit, 0x0000, 1);
- OUT_RING(chan, NvImageBlit);
+ OUT_RING(chan, nfbdev->blit.handle);
BEGIN_NV04(chan, NvSubImageBlit, 0x019c, 1);
- OUT_RING(chan, NvCtxSurf2D);
+ OUT_RING(chan, nfbdev->surf2d.handle);
BEGIN_NV04(chan, NvSubImageBlit, 0x02fc, 1);
OUT_RING(chan, 3);
- if (device->chipset >= 0x11 /*XXX: oclass == 0x009f*/) {
+ if (device->info.chipset >= 0x11 /*XXX: oclass == 0x009f*/) {
BEGIN_NV04(chan, NvSubImageBlit, 0x0120, 3);
OUT_RING(chan, 0);
OUT_RING(chan, 1);
@@ -263,12 +260,12 @@ nv04_fbcon_accel_init(struct fb_info *info)
}
BEGIN_NV04(chan, NvSubGdiRect, 0x0000, 1);
- OUT_RING(chan, NvGdiRect);
+ OUT_RING(chan, nfbdev->gdi.handle);
BEGIN_NV04(chan, NvSubGdiRect, 0x0198, 1);
- OUT_RING(chan, NvCtxSurf2D);
+ OUT_RING(chan, nfbdev->surf2d.handle);
BEGIN_NV04(chan, NvSubGdiRect, 0x0188, 2);
- OUT_RING(chan, NvImagePatt);
- OUT_RING(chan, NvRop);
+ OUT_RING(chan, nfbdev->patt.handle);
+ OUT_RING(chan, nfbdev->rop.handle);
BEGIN_NV04(chan, NvSubGdiRect, 0x0304, 1);
OUT_RING(chan, 1);
BEGIN_NV04(chan, NvSubGdiRect, 0x0300, 1);
diff --git a/drivers/gpu/drm/nouveau/nv04_fence.c b/drivers/gpu/drm/nouveau/nv04_fence.c
index 94eadd1dd10a..f9859deb108a 100644
--- a/drivers/gpu/drm/nouveau/nv04_fence.c
+++ b/drivers/gpu/drm/nouveau/nv04_fence.c
@@ -22,8 +22,6 @@
* Authors: Ben Skeggs
*/
-#include <engine/fifo.h>
-
#include "nouveau_drm.h"
#include "nouveau_dma.h"
#include "nouveau_fence.h"
@@ -43,7 +41,7 @@ nv04_fence_emit(struct nouveau_fence *fence)
int ret = RING_SPACE(chan, 2);
if (ret == 0) {
BEGIN_NV04(chan, NvSubSw, 0x0150, 1);
- OUT_RING (chan, fence->sequence);
+ OUT_RING (chan, fence->base.seqno);
FIRE_RING (chan);
}
return ret;
@@ -59,7 +57,7 @@ nv04_fence_sync(struct nouveau_fence *fence,
static u32
nv04_fence_read(struct nouveau_channel *chan)
{
- struct nouveau_fifo_chan *fifo = (void *)chan->object;
+ struct nouveau_fifo_chan *fifo = nvkm_fifo_chan(chan);;
return atomic_read(&fifo->refcnt);
}
@@ -69,7 +67,7 @@ nv04_fence_context_del(struct nouveau_channel *chan)
struct nv04_fence_chan *fctx = chan->fence;
nouveau_fence_context_del(&fctx->base);
chan->fence = NULL;
- kfree(fctx);
+ nouveau_fence_context_free(&fctx->base);
}
static int
@@ -77,7 +75,7 @@ nv04_fence_context_new(struct nouveau_channel *chan)
{
struct nv04_fence_chan *fctx = kzalloc(sizeof(*fctx), GFP_KERNEL);
if (fctx) {
- nouveau_fence_context_new(&fctx->base);
+ nouveau_fence_context_new(chan, &fctx->base);
fctx->base.emit = nv04_fence_emit;
fctx->base.sync = nv04_fence_sync;
fctx->base.read = nv04_fence_read;
@@ -107,5 +105,7 @@ nv04_fence_create(struct nouveau_drm *drm)
priv->base.dtor = nv04_fence_destroy;
priv->base.context_new = nv04_fence_context_new;
priv->base.context_del = nv04_fence_context_del;
+ priv->base.contexts = 15;
+ priv->base.context_base = fence_context_alloc(priv->base.contexts);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv10_fence.c b/drivers/gpu/drm/nouveau/nv10_fence.c
index 06f434f03fba..5e1ea1cdce75 100644
--- a/drivers/gpu/drm/nouveau/nv10_fence.c
+++ b/drivers/gpu/drm/nouveau/nv10_fence.c
@@ -22,9 +22,6 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include <core/object.h>
-#include <core/class.h>
-
#include "nouveau_drm.h"
#include "nouveau_dma.h"
#include "nv10_fence.h"
@@ -36,7 +33,7 @@ nv10_fence_emit(struct nouveau_fence *fence)
int ret = RING_SPACE(chan, 2);
if (ret == 0) {
BEGIN_NV04(chan, 0, NV10_SUBCHAN_REF_CNT, 1);
- OUT_RING (chan, fence->sequence);
+ OUT_RING (chan, fence->base.seqno);
FIRE_RING (chan);
}
return ret;
@@ -53,16 +50,20 @@ nv10_fence_sync(struct nouveau_fence *fence,
u32
nv10_fence_read(struct nouveau_channel *chan)
{
- return nv_ro32(chan->object, 0x0048);
+ return nvif_rd32(chan, 0x0048);
}
void
nv10_fence_context_del(struct nouveau_channel *chan)
{
struct nv10_fence_chan *fctx = chan->fence;
+ int i;
nouveau_fence_context_del(&fctx->base);
+ for (i = 0; i < ARRAY_SIZE(fctx->head); i++)
+ nvif_object_fini(&fctx->head[i]);
+ nvif_object_fini(&fctx->sema);
chan->fence = NULL;
- kfree(fctx);
+ nouveau_fence_context_free(&fctx->base);
}
int
@@ -74,7 +75,7 @@ nv10_fence_context_new(struct nouveau_channel *chan)
if (!fctx)
return -ENOMEM;
- nouveau_fence_context_new(&fctx->base);
+ nouveau_fence_context_new(chan, &fctx->base);
fctx->base.emit = nv10_fence_emit;
fctx->base.read = nv10_fence_read;
fctx->base.sync = nv10_fence_sync;
@@ -105,6 +106,8 @@ nv10_fence_create(struct nouveau_drm *drm)
priv->base.dtor = nv10_fence_destroy;
priv->base.context_new = nv10_fence_context_new;
priv->base.context_del = nv10_fence_context_del;
+ priv->base.contexts = 31;
+ priv->base.context_base = fence_context_alloc(priv->base.contexts);
spin_lock_init(&priv->lock);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv10_fence.h b/drivers/gpu/drm/nouveau/nv10_fence.h
index e5d9204826c2..a87259f3983a 100644
--- a/drivers/gpu/drm/nouveau/nv10_fence.h
+++ b/drivers/gpu/drm/nouveau/nv10_fence.h
@@ -1,12 +1,13 @@
#ifndef __NV10_FENCE_H_
#define __NV10_FENCE_H_
-#include <core/os.h>
#include "nouveau_fence.h"
#include "nouveau_bo.h"
struct nv10_fence_chan {
struct nouveau_fence_chan base;
+ struct nvif_object sema;
+ struct nvif_object head[4];
};
struct nv10_fence_priv {
diff --git a/drivers/gpu/drm/nouveau/nv17_fence.c b/drivers/gpu/drm/nouveau/nv17_fence.c
index 22aa9963ea6f..40b461c7d5c5 100644
--- a/drivers/gpu/drm/nouveau/nv17_fence.c
+++ b/drivers/gpu/drm/nouveau/nv17_fence.c
@@ -22,8 +22,8 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include <core/object.h>
-#include <core/class.h>
+#include <nvif/os.h>
+#include <nvif/class.h>
#include "nouveau_drm.h"
#include "nouveau_dma.h"
@@ -33,11 +33,13 @@ int
nv17_fence_sync(struct nouveau_fence *fence,
struct nouveau_channel *prev, struct nouveau_channel *chan)
{
+ struct nouveau_cli *cli = (void *)nvif_client(&prev->device->base);
struct nv10_fence_priv *priv = chan->drm->fence;
+ struct nv10_fence_chan *fctx = chan->fence;
u32 value;
int ret;
- if (!mutex_trylock(&prev->cli->mutex))
+ if (!mutex_trylock(&cli->mutex))
return -EBUSY;
spin_lock(&priv->lock);
@@ -48,7 +50,7 @@ nv17_fence_sync(struct nouveau_fence *fence,
ret = RING_SPACE(prev, 5);
if (!ret) {
BEGIN_NV04(prev, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4);
- OUT_RING (prev, NvSema);
+ OUT_RING (prev, fctx->sema.handle);
OUT_RING (prev, 0);
OUT_RING (prev, value + 0);
OUT_RING (prev, value + 1);
@@ -57,14 +59,14 @@ nv17_fence_sync(struct nouveau_fence *fence,
if (!ret && !(ret = RING_SPACE(chan, 5))) {
BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 4);
- OUT_RING (chan, NvSema);
+ OUT_RING (chan, fctx->sema.handle);
OUT_RING (chan, 0);
OUT_RING (chan, value + 1);
OUT_RING (chan, value + 2);
FIRE_RING (chan);
}
- mutex_unlock(&prev->cli->mutex);
+ mutex_unlock(&cli->mutex);
return 0;
}
@@ -74,7 +76,6 @@ nv17_fence_context_new(struct nouveau_channel *chan)
struct nv10_fence_priv *priv = chan->drm->fence;
struct nv10_fence_chan *fctx;
struct ttm_mem_reg *mem = &priv->bo->bo.mem;
- struct nouveau_object *object;
u32 start = mem->start * PAGE_SIZE;
u32 limit = start + mem->size - 1;
int ret = 0;
@@ -83,20 +84,19 @@ nv17_fence_context_new(struct nouveau_channel *chan)
if (!fctx)
return -ENOMEM;
- nouveau_fence_context_new(&fctx->base);
+ nouveau_fence_context_new(chan, &fctx->base);
fctx->base.emit = nv10_fence_emit;
fctx->base.read = nv10_fence_read;
fctx->base.sync = nv17_fence_sync;
- ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
- NvSema, 0x0002,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
+ ret = nvif_object_init(chan->object, NULL, NvSema, NV_DMA_FROM_MEMORY,
+ &(struct nv_dma_v0) {
+ .target = NV_DMA_V0_TARGET_VRAM,
+ .access = NV_DMA_V0_ACCESS_RDWR,
.start = start,
.limit = limit,
- }, sizeof(struct nv_dma_class),
- &object);
+ }, sizeof(struct nv_dma_v0),
+ &fctx->sema);
if (ret)
nv10_fence_context_del(chan);
return ret;
@@ -124,10 +124,12 @@ nv17_fence_create(struct nouveau_drm *drm)
priv->base.resume = nv17_fence_resume;
priv->base.context_new = nv17_fence_context_new;
priv->base.context_del = nv10_fence_context_del;
+ priv->base.contexts = 31;
+ priv->base.context_base = fence_context_alloc(priv->base.contexts);
spin_lock_init(&priv->lock);
ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
- 0, 0x0000, NULL, &priv->bo);
+ 0, 0x0000, NULL, NULL, &priv->bo);
if (!ret) {
ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
if (!ret) {
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 4c534b7b04da..eb8b36714fa1 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -28,6 +28,8 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_dp_helper.h>
+#include <nvif/class.h>
+
#include "nouveau_drm.h"
#include "nouveau_dma.h"
#include "nouveau_gem.h"
@@ -37,15 +39,6 @@
#include "nouveau_fence.h"
#include "nv50_display.h"
-#include <core/client.h>
-#include <core/gpuobj.h>
-#include <core/class.h>
-
-#include <subdev/timer.h>
-#include <subdev/bar.h>
-#include <subdev/fb.h>
-#include <subdev/i2c.h>
-
#define EVO_DMA_NR 9
#define EVO_MASTER (0x00)
@@ -60,45 +53,34 @@
#define EVO_FLIP_SEM0(c) EVO_SYNC((c) + 1, 0x00)
#define EVO_FLIP_SEM1(c) EVO_SYNC((c) + 1, 0x10)
-#define EVO_CORE_HANDLE (0xd1500000)
-#define EVO_CHAN_HANDLE(t,i) (0xd15c0000 | (((t) & 0x00ff) << 8) | (i))
-#define EVO_CHAN_OCLASS(t,c) ((nv_hclass(c) & 0xff00) | ((t) & 0x00ff))
-#define EVO_PUSH_HANDLE(t,i) (0xd15b0000 | (i) | \
- (((NV50_DISP_##t##_CLASS) & 0x00ff) << 8))
-
/******************************************************************************
* EVO channel
*****************************************************************************/
struct nv50_chan {
- struct nouveau_object *user;
- u32 handle;
+ struct nvif_object user;
};
static int
-nv50_chan_create(struct nouveau_object *core, u32 bclass, u8 head,
+nv50_chan_create(struct nvif_object *disp, const u32 *oclass, u8 head,
void *data, u32 size, struct nv50_chan *chan)
{
- struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS);
- const u32 oclass = EVO_CHAN_OCLASS(bclass, core);
- const u32 handle = EVO_CHAN_HANDLE(bclass, head);
- int ret;
-
- ret = nouveau_object_new(client, EVO_CORE_HANDLE, handle,
- oclass, data, size, &chan->user);
- if (ret)
- return ret;
-
- chan->handle = handle;
- return 0;
+ while (oclass[0]) {
+ int ret = nvif_object_init(disp, NULL, (oclass[0] << 16) | head,
+ oclass[0], data, size,
+ &chan->user);
+ if (oclass++, ret == 0) {
+ nvif_object_map(&chan->user);
+ return ret;
+ }
+ }
+ return -ENOSYS;
}
static void
-nv50_chan_destroy(struct nouveau_object *core, struct nv50_chan *chan)
+nv50_chan_destroy(struct nv50_chan *chan)
{
- struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS);
- if (chan->handle)
- nouveau_object_del(client, EVO_CORE_HANDLE, chan->handle);
+ nvif_object_fini(&chan->user);
}
/******************************************************************************
@@ -110,16 +92,70 @@ struct nv50_pioc {
};
static void
-nv50_pioc_destroy(struct nouveau_object *core, struct nv50_pioc *pioc)
+nv50_pioc_destroy(struct nv50_pioc *pioc)
{
- nv50_chan_destroy(core, &pioc->base);
+ nv50_chan_destroy(&pioc->base);
}
static int
-nv50_pioc_create(struct nouveau_object *core, u32 bclass, u8 head,
+nv50_pioc_create(struct nvif_object *disp, const u32 *oclass, u8 head,
void *data, u32 size, struct nv50_pioc *pioc)
{
- return nv50_chan_create(core, bclass, head, data, size, &pioc->base);
+ return nv50_chan_create(disp, oclass, head, data, size, &pioc->base);
+}
+
+/******************************************************************************
+ * Cursor Immediate
+ *****************************************************************************/
+
+struct nv50_curs {
+ struct nv50_pioc base;
+};
+
+static int
+nv50_curs_create(struct nvif_object *disp, int head, struct nv50_curs *curs)
+{
+ struct nv50_disp_cursor_v0 args = {
+ .head = head,
+ };
+ static const u32 oclass[] = {
+ GK104_DISP_CURSOR,
+ GF110_DISP_CURSOR,
+ GT214_DISP_CURSOR,
+ G82_DISP_CURSOR,
+ NV50_DISP_CURSOR,
+ 0
+ };
+
+ return nv50_pioc_create(disp, oclass, head, &args, sizeof(args),
+ &curs->base);
+}
+
+/******************************************************************************
+ * Overlay Immediate
+ *****************************************************************************/
+
+struct nv50_oimm {
+ struct nv50_pioc base;
+};
+
+static int
+nv50_oimm_create(struct nvif_object *disp, int head, struct nv50_oimm *oimm)
+{
+ struct nv50_disp_cursor_v0 args = {
+ .head = head,
+ };
+ static const u32 oclass[] = {
+ GK104_DISP_OVERLAY,
+ GF110_DISP_OVERLAY,
+ GT214_DISP_OVERLAY,
+ G82_DISP_OVERLAY,
+ NV50_DISP_OVERLAY,
+ 0
+ };
+
+ return nv50_pioc_create(disp, oclass, head, &args, sizeof(args),
+ &oimm->base);
}
/******************************************************************************
@@ -131,6 +167,9 @@ struct nv50_dmac {
dma_addr_t handle;
u32 *ptr;
+ struct nvif_object sync;
+ struct nvif_object vram;
+
/* Protects against concurrent pushbuf access to this channel, lock is
* grabbed by evo_wait (if the pushbuf reservation is successful) and
* dropped again by evo_kick. */
@@ -138,207 +177,113 @@ struct nv50_dmac {
};
static void
-nv50_dmac_destroy(struct nouveau_object *core, struct nv50_dmac *dmac)
+nv50_dmac_destroy(struct nv50_dmac *dmac, struct nvif_object *disp)
{
+ nvif_object_fini(&dmac->vram);
+ nvif_object_fini(&dmac->sync);
+
+ nv50_chan_destroy(&dmac->base);
+
if (dmac->ptr) {
- struct pci_dev *pdev = nv_device(core)->pdev;
+ struct pci_dev *pdev = nvkm_device(nvif_device(disp))->pdev;
pci_free_consistent(pdev, PAGE_SIZE, dmac->ptr, dmac->handle);
}
-
- nv50_chan_destroy(core, &dmac->base);
-}
-
-static int
-nv50_dmac_create_fbdma(struct nouveau_object *core, u32 parent)
-{
- struct nouveau_fb *pfb = nouveau_fb(core);
- struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS);
- struct nouveau_object *object;
- int ret = nouveau_object_new(client, parent, NvEvoVRAM_LP,
- NV_DMA_IN_MEMORY_CLASS,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
- .start = 0,
- .limit = pfb->ram->size - 1,
- .conf0 = NV50_DMA_CONF0_ENABLE |
- NV50_DMA_CONF0_PART_256,
- }, sizeof(struct nv_dma_class), &object);
- if (ret)
- return ret;
-
- ret = nouveau_object_new(client, parent, NvEvoFB16,
- NV_DMA_IN_MEMORY_CLASS,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
- .start = 0,
- .limit = pfb->ram->size - 1,
- .conf0 = NV50_DMA_CONF0_ENABLE | 0x70 |
- NV50_DMA_CONF0_PART_256,
- }, sizeof(struct nv_dma_class), &object);
- if (ret)
- return ret;
-
- ret = nouveau_object_new(client, parent, NvEvoFB32,
- NV_DMA_IN_MEMORY_CLASS,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
- .start = 0,
- .limit = pfb->ram->size - 1,
- .conf0 = NV50_DMA_CONF0_ENABLE | 0x7a |
- NV50_DMA_CONF0_PART_256,
- }, sizeof(struct nv_dma_class), &object);
- return ret;
-}
-
-static int
-nvc0_dmac_create_fbdma(struct nouveau_object *core, u32 parent)
-{
- struct nouveau_fb *pfb = nouveau_fb(core);
- struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS);
- struct nouveau_object *object;
- int ret = nouveau_object_new(client, parent, NvEvoVRAM_LP,
- NV_DMA_IN_MEMORY_CLASS,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
- .start = 0,
- .limit = pfb->ram->size - 1,
- .conf0 = NVC0_DMA_CONF0_ENABLE,
- }, sizeof(struct nv_dma_class), &object);
- if (ret)
- return ret;
-
- ret = nouveau_object_new(client, parent, NvEvoFB16,
- NV_DMA_IN_MEMORY_CLASS,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
- .start = 0,
- .limit = pfb->ram->size - 1,
- .conf0 = NVC0_DMA_CONF0_ENABLE | 0xfe,
- }, sizeof(struct nv_dma_class), &object);
- if (ret)
- return ret;
-
- ret = nouveau_object_new(client, parent, NvEvoFB32,
- NV_DMA_IN_MEMORY_CLASS,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
- .start = 0,
- .limit = pfb->ram->size - 1,
- .conf0 = NVC0_DMA_CONF0_ENABLE | 0xfe,
- }, sizeof(struct nv_dma_class), &object);
- return ret;
-}
-
-static int
-nvd0_dmac_create_fbdma(struct nouveau_object *core, u32 parent)
-{
- struct nouveau_fb *pfb = nouveau_fb(core);
- struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS);
- struct nouveau_object *object;
- int ret = nouveau_object_new(client, parent, NvEvoVRAM_LP,
- NV_DMA_IN_MEMORY_CLASS,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
- .start = 0,
- .limit = pfb->ram->size - 1,
- .conf0 = NVD0_DMA_CONF0_ENABLE |
- NVD0_DMA_CONF0_PAGE_LP,
- }, sizeof(struct nv_dma_class), &object);
- if (ret)
- return ret;
-
- ret = nouveau_object_new(client, parent, NvEvoFB32,
- NV_DMA_IN_MEMORY_CLASS,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
- .start = 0,
- .limit = pfb->ram->size - 1,
- .conf0 = NVD0_DMA_CONF0_ENABLE | 0xfe |
- NVD0_DMA_CONF0_PAGE_LP,
- }, sizeof(struct nv_dma_class), &object);
- return ret;
}
static int
-nv50_dmac_create(struct nouveau_object *core, u32 bclass, u8 head,
+nv50_dmac_create(struct nvif_object *disp, const u32 *oclass, u8 head,
void *data, u32 size, u64 syncbuf,
struct nv50_dmac *dmac)
{
- struct nouveau_fb *pfb = nouveau_fb(core);
- struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS);
- struct nouveau_object *object;
- u32 pushbuf = *(u32 *)data;
+ struct nvif_device *device = nvif_device(disp);
+ struct nv50_disp_core_channel_dma_v0 *args = data;
+ struct nvif_object pushbuf;
int ret;
mutex_init(&dmac->lock);
- dmac->ptr = pci_alloc_consistent(nv_device(core)->pdev, PAGE_SIZE,
- &dmac->handle);
+ dmac->ptr = pci_alloc_consistent(nvkm_device(device)->pdev,
+ PAGE_SIZE, &dmac->handle);
if (!dmac->ptr)
return -ENOMEM;
- ret = nouveau_object_new(client, NVDRM_DEVICE, pushbuf,
- NV_DMA_FROM_MEMORY_CLASS,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_PCI_US |
- NV_DMA_ACCESS_RD,
+ ret = nvif_object_init(nvif_object(device), NULL,
+ args->pushbuf, NV_DMA_FROM_MEMORY,
+ &(struct nv_dma_v0) {
+ .target = NV_DMA_V0_TARGET_PCI_US,
+ .access = NV_DMA_V0_ACCESS_RD,
.start = dmac->handle + 0x0000,
.limit = dmac->handle + 0x0fff,
- }, sizeof(struct nv_dma_class), &object);
+ }, sizeof(struct nv_dma_v0), &pushbuf);
if (ret)
return ret;
- ret = nv50_chan_create(core, bclass, head, data, size, &dmac->base);
+ ret = nv50_chan_create(disp, oclass, head, data, size, &dmac->base);
+ nvif_object_fini(&pushbuf);
if (ret)
return ret;
- ret = nouveau_object_new(client, dmac->base.handle, NvEvoSync,
- NV_DMA_IN_MEMORY_CLASS,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
+ ret = nvif_object_init(&dmac->base.user, NULL, 0xf0000000,
+ NV_DMA_IN_MEMORY,
+ &(struct nv_dma_v0) {
+ .target = NV_DMA_V0_TARGET_VRAM,
+ .access = NV_DMA_V0_ACCESS_RDWR,
.start = syncbuf + 0x0000,
.limit = syncbuf + 0x0fff,
- }, sizeof(struct nv_dma_class), &object);
+ }, sizeof(struct nv_dma_v0),
+ &dmac->sync);
if (ret)
return ret;
- ret = nouveau_object_new(client, dmac->base.handle, NvEvoVRAM,
- NV_DMA_IN_MEMORY_CLASS,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
+ ret = nvif_object_init(&dmac->base.user, NULL, 0xf0000001,
+ NV_DMA_IN_MEMORY,
+ &(struct nv_dma_v0) {
+ .target = NV_DMA_V0_TARGET_VRAM,
+ .access = NV_DMA_V0_ACCESS_RDWR,
.start = 0,
- .limit = pfb->ram->size - 1,
- }, sizeof(struct nv_dma_class), &object);
+ .limit = device->info.ram_user - 1,
+ }, sizeof(struct nv_dma_v0),
+ &dmac->vram);
if (ret)
return ret;
- if (nv_device(core)->card_type < NV_C0)
- ret = nv50_dmac_create_fbdma(core, dmac->base.handle);
- else
- if (nv_device(core)->card_type < NV_D0)
- ret = nvc0_dmac_create_fbdma(core, dmac->base.handle);
- else
- ret = nvd0_dmac_create_fbdma(core, dmac->base.handle);
return ret;
}
+/******************************************************************************
+ * Core
+ *****************************************************************************/
+
struct nv50_mast {
struct nv50_dmac base;
};
-struct nv50_curs {
- struct nv50_pioc base;
-};
+static int
+nv50_core_create(struct nvif_object *disp, u64 syncbuf, struct nv50_mast *core)
+{
+ struct nv50_disp_core_channel_dma_v0 args = {
+ .pushbuf = 0xb0007d00,
+ };
+ static const u32 oclass[] = {
+ GM107_DISP_CORE_CHANNEL_DMA,
+ GK110_DISP_CORE_CHANNEL_DMA,
+ GK104_DISP_CORE_CHANNEL_DMA,
+ GF110_DISP_CORE_CHANNEL_DMA,
+ GT214_DISP_CORE_CHANNEL_DMA,
+ GT206_DISP_CORE_CHANNEL_DMA,
+ GT200_DISP_CORE_CHANNEL_DMA,
+ G82_DISP_CORE_CHANNEL_DMA,
+ NV50_DISP_CORE_CHANNEL_DMA,
+ 0
+ };
+
+ return nv50_dmac_create(disp, oclass, 0, &args, sizeof(args), syncbuf,
+ &core->base);
+}
+
+/******************************************************************************
+ * Base
+ *****************************************************************************/
struct nv50_sync {
struct nv50_dmac base;
@@ -346,13 +291,58 @@ struct nv50_sync {
u32 data;
};
+static int
+nv50_base_create(struct nvif_object *disp, int head, u64 syncbuf,
+ struct nv50_sync *base)
+{
+ struct nv50_disp_base_channel_dma_v0 args = {
+ .pushbuf = 0xb0007c00 | head,
+ .head = head,
+ };
+ static const u32 oclass[] = {
+ GK110_DISP_BASE_CHANNEL_DMA,
+ GK104_DISP_BASE_CHANNEL_DMA,
+ GF110_DISP_BASE_CHANNEL_DMA,
+ GT214_DISP_BASE_CHANNEL_DMA,
+ GT200_DISP_BASE_CHANNEL_DMA,
+ G82_DISP_BASE_CHANNEL_DMA,
+ NV50_DISP_BASE_CHANNEL_DMA,
+ 0
+ };
+
+ return nv50_dmac_create(disp, oclass, head, &args, sizeof(args),
+ syncbuf, &base->base);
+}
+
+/******************************************************************************
+ * Overlay
+ *****************************************************************************/
+
struct nv50_ovly {
struct nv50_dmac base;
};
-struct nv50_oimm {
- struct nv50_pioc base;
-};
+static int
+nv50_ovly_create(struct nvif_object *disp, int head, u64 syncbuf,
+ struct nv50_ovly *ovly)
+{
+ struct nv50_disp_overlay_channel_dma_v0 args = {
+ .pushbuf = 0xb0007e00 | head,
+ .head = head,
+ };
+ static const u32 oclass[] = {
+ GK104_DISP_OVERLAY_CONTROL_DMA,
+ GF110_DISP_OVERLAY_CONTROL_DMA,
+ GT214_DISP_OVERLAY_CHANNEL_DMA,
+ GT200_DISP_OVERLAY_CHANNEL_DMA,
+ G82_DISP_OVERLAY_CHANNEL_DMA,
+ NV50_DISP_OVERLAY_CHANNEL_DMA,
+ 0
+ };
+
+ return nv50_dmac_create(disp, oclass, head, &args, sizeof(args),
+ syncbuf, &ovly->base);
+}
struct nv50_head {
struct nouveau_crtc base;
@@ -369,13 +359,19 @@ struct nv50_head {
#define nv50_ovly(c) (&nv50_head(c)->ovly)
#define nv50_oimm(c) (&nv50_head(c)->oimm)
#define nv50_chan(c) (&(c)->base.base)
-#define nv50_vers(c) nv_mclass(nv50_chan(c)->user)
+#define nv50_vers(c) nv50_chan(c)->user.oclass
+
+struct nv50_fbdma {
+ struct list_head head;
+ struct nvif_object core;
+ struct nvif_object base[4];
+};
struct nv50_disp {
- struct nouveau_object *core;
+ struct nvif_object *disp;
struct nv50_mast mast;
- u32 modeset;
+ struct list_head fbdma;
struct nouveau_bo *sync;
};
@@ -401,16 +397,16 @@ static u32 *
evo_wait(void *evoc, int nr)
{
struct nv50_dmac *dmac = evoc;
- u32 put = nv_ro32(dmac->base.user, 0x0000) / 4;
+ u32 put = nvif_rd32(&dmac->base.user, 0x0000) / 4;
mutex_lock(&dmac->lock);
if (put + nr >= (PAGE_SIZE / 4) - 8) {
dmac->ptr[put] = 0x20000000;
- nv_wo32(dmac->base.user, 0x0000, 0x00000000);
- if (!nv_wait(dmac->base.user, 0x0004, ~0, 0x00000000)) {
+ nvif_wr32(&dmac->base.user, 0x0000, 0x00000000);
+ if (!nvkm_wait(&dmac->base.user, 0x0004, ~0, 0x00000000)) {
mutex_unlock(&dmac->lock);
- NV_ERROR(dmac->base.user, "channel stalled\n");
+ nv_error(nvkm_object(&dmac->base.user), "channel stalled\n");
return NULL;
}
@@ -424,7 +420,7 @@ static void
evo_kick(u32 *push, void *evoc)
{
struct nv50_dmac *dmac = evoc;
- nv_wo32(dmac->base.user, 0x0000, (push - dmac->ptr) << 2);
+ nvif_wr32(&dmac->base.user, 0x0000, (push - dmac->ptr) << 2);
mutex_unlock(&dmac->lock);
}
@@ -443,7 +439,7 @@ evo_sync_wait(void *data)
static int
evo_sync(struct drm_device *dev)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
struct nv50_disp *disp = nv50_disp(dev);
struct nv50_mast *mast = nv50_mast(dev);
u32 *push = evo_wait(mast, 8);
@@ -455,7 +451,7 @@ evo_sync(struct drm_device *dev)
evo_data(push, 0x00000000);
evo_data(push, 0x00000000);
evo_kick(push, mast);
- if (nv_wait_cb(device, evo_sync_wait, disp->sync))
+ if (nv_wait_cb(nvkm_device(device), evo_sync_wait, disp->sync))
return 0;
}
@@ -490,7 +486,7 @@ nv50_display_flip_wait(void *data)
void
nv50_display_flip_stop(struct drm_crtc *crtc)
{
- struct nouveau_device *device = nouveau_dev(crtc->dev);
+ struct nvif_device *device = &nouveau_drm(crtc->dev)->device;
struct nv50_display_flip flip = {
.disp = nv50_disp(crtc->dev),
.chan = nv50_sync(crtc),
@@ -510,7 +506,7 @@ nv50_display_flip_stop(struct drm_crtc *crtc)
evo_kick(push, flip.chan);
}
- nv_wait_cb(device, nv50_display_flip_wait, &flip);
+ nv_wait_cb(nvkm_device(device), nv50_display_flip_wait, &flip);
}
int
@@ -534,7 +530,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
if (unlikely(push == NULL))
return -EBUSY;
- if (chan && nv_mclass(chan->object) < NV84_CHANNEL_IND_CLASS) {
+ if (chan && chan->object->oclass < G82_CHANNEL_GPFIFO) {
ret = RING_SPACE(chan, 8);
if (ret)
return ret;
@@ -548,14 +544,14 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
OUT_RING (chan, sync->addr);
OUT_RING (chan, sync->data);
} else
- if (chan && nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) {
+ if (chan && chan->object->oclass < FERMI_CHANNEL_GPFIFO) {
u64 addr = nv84_fence_crtc(chan, nv_crtc->index) + sync->addr;
ret = RING_SPACE(chan, 12);
if (ret)
return ret;
BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
- OUT_RING (chan, chan->vram);
+ OUT_RING (chan, chan->vram.handle);
BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(addr ^ 0x10));
OUT_RING (chan, lower_32_bits(addr ^ 0x10));
@@ -606,16 +602,16 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
evo_data(push, sync->addr);
evo_data(push, sync->data++);
evo_data(push, sync->data);
- evo_data(push, NvEvoSync);
+ evo_data(push, sync->base.sync.handle);
evo_mthd(push, 0x00a0, 2);
evo_data(push, 0x00000000);
evo_data(push, 0x00000000);
evo_mthd(push, 0x00c0, 1);
- evo_data(push, nv_fb->r_dma);
+ evo_data(push, nv_fb->r_handle);
evo_mthd(push, 0x0110, 2);
evo_data(push, 0x00000000);
evo_data(push, 0x00000000);
- if (nv50_vers(sync) < NVD0_DISP_SYNC_CLASS) {
+ if (nv50_vers(sync) < GF110_DISP_BASE_CHANNEL_DMA) {
evo_mthd(push, 0x0800, 5);
evo_data(push, nv_fb->nvbo->bo.offset >> 8);
evo_data(push, 0);
@@ -667,11 +663,11 @@ nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update)
push = evo_wait(mast, 4);
if (push) {
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x08a0 + (nv_crtc->index * 0x0400), 1);
evo_data(push, mode);
} else
- if (nv50_vers(mast) < NVE0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GK104_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0490 + (nv_crtc->index * 0x0300), 1);
evo_data(push, mode);
} else {
@@ -762,7 +758,7 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
push = evo_wait(mast, 8);
if (push) {
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
/*XXX: SCALE_CTRL_ACTIVE??? */
evo_mthd(push, 0x08d8 + (nv_crtc->index * 0x400), 2);
evo_data(push, (oY << 16) | oX);
@@ -795,6 +791,22 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
}
static int
+nv50_crtc_set_raster_vblank_dmi(struct nouveau_crtc *nv_crtc, u32 usec)
+{
+ struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev);
+ u32 *push;
+
+ push = evo_wait(mast, 8);
+ if (!push)
+ return -ENOMEM;
+
+ evo_mthd(push, 0x0828 + (nv_crtc->index * 0x400), 1);
+ evo_data(push, usec);
+ evo_kick(push, mast);
+ return 0;
+}
+
+static int
nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update)
{
struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev);
@@ -807,7 +819,7 @@ nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update)
push = evo_wait(mast, 16);
if (push) {
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x08a8 + (nv_crtc->index * 0x400), 1);
evo_data(push, (hue << 20) | (vib << 8));
} else {
@@ -835,7 +847,7 @@ nv50_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb,
push = evo_wait(mast, 16);
if (push) {
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0860 + (nv_crtc->index * 0x400), 1);
evo_data(push, nvfb->nvbo->bo.offset >> 8);
evo_mthd(push, 0x0868 + (nv_crtc->index * 0x400), 3);
@@ -844,9 +856,9 @@ nv50_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb,
evo_data(push, nvfb->r_format);
evo_mthd(push, 0x08c0 + (nv_crtc->index * 0x400), 1);
evo_data(push, (y << 16) | x);
- if (nv50_vers(mast) > NV50_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) > NV50_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
- evo_data(push, nvfb->r_dma);
+ evo_data(push, nvfb->r_handle);
}
} else {
evo_mthd(push, 0x0460 + (nv_crtc->index * 0x300), 1);
@@ -855,7 +867,7 @@ nv50_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb,
evo_data(push, (fb->height << 16) | fb->width);
evo_data(push, nvfb->r_pitch);
evo_data(push, nvfb->r_format);
- evo_data(push, nvfb->r_dma);
+ evo_data(push, nvfb->r_handle);
evo_mthd(push, 0x04b0 + (nv_crtc->index * 0x300), 1);
evo_data(push, (y << 16) | x);
}
@@ -867,7 +879,7 @@ nv50_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb,
evo_kick(push, mast);
}
- nv_crtc->fb.tile_flags = nvfb->r_dma;
+ nv_crtc->fb.handle = nvfb->r_handle;
return 0;
}
@@ -877,23 +889,23 @@ nv50_crtc_cursor_show(struct nouveau_crtc *nv_crtc)
struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev);
u32 *push = evo_wait(mast, 16);
if (push) {
- if (nv50_vers(mast) < NV84_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < G82_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 2);
evo_data(push, 0x85000000);
evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8);
} else
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 2);
evo_data(push, 0x85000000);
evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8);
evo_mthd(push, 0x089c + (nv_crtc->index * 0x400), 1);
- evo_data(push, NvEvoVRAM);
+ evo_data(push, mast->base.vram.handle);
} else {
evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 2);
evo_data(push, 0x85000000);
evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8);
evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1);
- evo_data(push, NvEvoVRAM);
+ evo_data(push, mast->base.vram.handle);
}
evo_kick(push, mast);
}
@@ -905,11 +917,11 @@ nv50_crtc_cursor_hide(struct nouveau_crtc *nv_crtc)
struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev);
u32 *push = evo_wait(mast, 16);
if (push) {
- if (nv50_vers(mast) < NV84_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < G82_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 1);
evo_data(push, 0x05000000);
} else
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 1);
evo_data(push, 0x05000000);
evo_mthd(push, 0x089c + (nv_crtc->index * 0x400), 1);
@@ -960,13 +972,13 @@ nv50_crtc_prepare(struct drm_crtc *crtc)
push = evo_wait(mast, 6);
if (push) {
- if (nv50_vers(mast) < NV84_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < G82_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
evo_data(push, 0x00000000);
evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 1);
evo_data(push, 0x40000000);
} else
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
evo_data(push, 0x00000000);
evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 1);
@@ -997,31 +1009,31 @@ nv50_crtc_commit(struct drm_crtc *crtc)
push = evo_wait(mast, 32);
if (push) {
- if (nv50_vers(mast) < NV84_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < G82_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
- evo_data(push, NvEvoVRAM_LP);
+ evo_data(push, nv_crtc->fb.handle);
evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 2);
evo_data(push, 0xc0000000);
evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8);
} else
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
- evo_data(push, nv_crtc->fb.tile_flags);
+ evo_data(push, nv_crtc->fb.handle);
evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 2);
evo_data(push, 0xc0000000);
evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8);
evo_mthd(push, 0x085c + (nv_crtc->index * 0x400), 1);
- evo_data(push, NvEvoVRAM);
+ evo_data(push, mast->base.vram.handle);
} else {
evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1);
- evo_data(push, nv_crtc->fb.tile_flags);
+ evo_data(push, nv_crtc->fb.handle);
evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 4);
evo_data(push, 0x83000000);
evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8);
evo_data(push, 0x00000000);
evo_data(push, 0x00000000);
evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1);
- evo_data(push, NvEvoVRAM);
+ evo_data(push, mast->base.vram.handle);
evo_mthd(push, 0x0430 + (nv_crtc->index * 0x300), 1);
evo_data(push, 0xffffff00);
}
@@ -1070,7 +1082,7 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1;
u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks;
u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks;
- u32 vblan2e = 0, vblan2s = 1;
+ u32 vblan2e = 0, vblan2s = 1, vblankus = 0;
u32 *push;
int ret;
@@ -1087,6 +1099,11 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
vblanke = vsynce + vbackp;
vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace;
vblanks = vactive - vfrontp - 1;
+ /* XXX: Safe underestimate, even "0" works */
+ vblankus = (vactive - mode->vdisplay - 2) * hactive;
+ vblankus *= 1000;
+ vblankus /= mode->clock;
+
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
vblan2e = vactive + vsynce + vbackp;
vblan2s = vblan2e + (mode->vdisplay * vscan / ilace);
@@ -1099,7 +1116,7 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
push = evo_wait(mast, 64);
if (push) {
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0804 + (nv_crtc->index * 0x400), 2);
evo_data(push, 0x00800000 | mode->clock);
evo_data(push, (ilace == 2) ? 2 : 0);
@@ -1140,6 +1157,11 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
nv_connector = nouveau_crtc_connector_get(nv_crtc);
nv50_crtc_set_dither(nv_crtc, false);
nv50_crtc_set_scale(nv_crtc, false);
+
+ /* G94 only accepts this after setting scale */
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA)
+ nv50_crtc_set_raster_vblank_dmi(nv_crtc, vblankus);
+
nv50_crtc_set_color_vibrance(nv_crtc, false);
nv50_crtc_set_image(nv_crtc, crtc->primary->fb, x, y, false);
return 0;
@@ -1192,7 +1214,7 @@ nv50_crtc_lut_load(struct drm_crtc *crtc)
u16 g = nv_crtc->lut.g[i] >> 2;
u16 b = nv_crtc->lut.b[i] >> 2;
- if (nv_mclass(disp->core) < NVD0_DISP_CLASS) {
+ if (disp->disp->oclass < GF110_DISP) {
writew(r + 0x0000, lut + (i * 0x08) + 0);
writew(g + 0x0000, lut + (i * 0x08) + 2);
writew(b + 0x0000, lut + (i * 0x08) + 4);
@@ -1259,8 +1281,8 @@ nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
{
struct nv50_curs *curs = nv50_curs(crtc);
struct nv50_chan *chan = nv50_chan(curs);
- nv_wo32(chan->user, 0x0084, (y << 16) | (x & 0xffff));
- nv_wo32(chan->user, 0x0080, 0x00000000);
+ nvif_wr32(&chan->user, 0x0084, (y << 16) | (x & 0xffff));
+ nvif_wr32(&chan->user, 0x0080, 0x00000000);
return 0;
}
@@ -1287,11 +1309,16 @@ nv50_crtc_destroy(struct drm_crtc *crtc)
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct nv50_disp *disp = nv50_disp(crtc->dev);
struct nv50_head *head = nv50_head(crtc);
+ struct nv50_fbdma *fbdma;
- nv50_dmac_destroy(disp->core, &head->ovly.base);
- nv50_pioc_destroy(disp->core, &head->oimm.base);
- nv50_dmac_destroy(disp->core, &head->sync.base);
- nv50_pioc_destroy(disp->core, &head->curs.base);
+ list_for_each_entry(fbdma, &disp->fbdma, head) {
+ nvif_object_fini(&fbdma->base[nv_crtc->index]);
+ }
+
+ nv50_dmac_destroy(&head->ovly.base, disp->disp);
+ nv50_pioc_destroy(&head->oimm.base);
+ nv50_dmac_destroy(&head->sync.base, disp->disp);
+ nv50_pioc_destroy(&head->curs.base);
/*XXX: this shouldn't be necessary, but the core doesn't call
* disconnect() during the cleanup paths
@@ -1346,7 +1373,7 @@ nv50_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
}
static int
-nv50_crtc_create(struct drm_device *dev, struct nouveau_object *core, int index)
+nv50_crtc_create(struct drm_device *dev, int index)
{
struct nv50_disp *disp = nv50_disp(dev);
struct nv50_head *head;
@@ -1377,7 +1404,7 @@ nv50_crtc_create(struct drm_device *dev, struct nouveau_object *core, int index)
drm_mode_crtc_set_gamma_size(crtc, 256);
ret = nouveau_bo_new(dev, 8192, 0x100, TTM_PL_FLAG_VRAM,
- 0, 0x0000, NULL, &head->base.lut.nvbo);
+ 0, 0x0000, NULL, NULL, &head->base.lut.nvbo);
if (!ret) {
ret = nouveau_bo_pin(head->base.lut.nvbo, TTM_PL_FLAG_VRAM);
if (!ret) {
@@ -1395,16 +1422,12 @@ nv50_crtc_create(struct drm_device *dev, struct nouveau_object *core, int index)
nv50_crtc_lut_load(crtc);
/* allocate cursor resources */
- ret = nv50_pioc_create(disp->core, NV50_DISP_CURS_CLASS, index,
- &(struct nv50_display_curs_class) {
- .head = index,
- }, sizeof(struct nv50_display_curs_class),
- &head->curs.base);
+ ret = nv50_curs_create(disp->disp, index, &head->curs);
if (ret)
goto out;
ret = nouveau_bo_new(dev, 64 * 64 * 4, 0x100, TTM_PL_FLAG_VRAM,
- 0, 0x0000, NULL, &head->base.cursor.nvbo);
+ 0, 0x0000, NULL, NULL, &head->base.cursor.nvbo);
if (!ret) {
ret = nouveau_bo_pin(head->base.cursor.nvbo, TTM_PL_FLAG_VRAM);
if (!ret) {
@@ -1420,12 +1443,8 @@ nv50_crtc_create(struct drm_device *dev, struct nouveau_object *core, int index)
goto out;
/* allocate page flip / sync resources */
- ret = nv50_dmac_create(disp->core, NV50_DISP_SYNC_CLASS, index,
- &(struct nv50_display_sync_class) {
- .pushbuf = EVO_PUSH_HANDLE(SYNC, index),
- .head = index,
- }, sizeof(struct nv50_display_sync_class),
- disp->sync->bo.offset, &head->sync.base);
+ ret = nv50_base_create(disp->disp, index, disp->sync->bo.offset,
+ &head->sync);
if (ret)
goto out;
@@ -1433,20 +1452,12 @@ nv50_crtc_create(struct drm_device *dev, struct nouveau_object *core, int index)
head->sync.data = 0x00000000;
/* allocate overlay resources */
- ret = nv50_pioc_create(disp->core, NV50_DISP_OIMM_CLASS, index,
- &(struct nv50_display_oimm_class) {
- .head = index,
- }, sizeof(struct nv50_display_oimm_class),
- &head->oimm.base);
+ ret = nv50_oimm_create(disp->disp, index, &head->oimm);
if (ret)
goto out;
- ret = nv50_dmac_create(disp->core, NV50_DISP_OVLY_CLASS, index,
- &(struct nv50_display_ovly_class) {
- .pushbuf = EVO_PUSH_HANDLE(OVLY, index),
- .head = index,
- }, sizeof(struct nv50_display_ovly_class),
- disp->sync->bo.offset, &head->ovly.base);
+ ret = nv50_ovly_create(disp->disp, index, disp->sync->bo.offset,
+ &head->ovly);
if (ret)
goto out;
@@ -1464,16 +1475,23 @@ nv50_dac_dpms(struct drm_encoder *encoder, int mode)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nv50_disp *disp = nv50_disp(encoder->dev);
- int or = nv_encoder->or;
- u32 dpms_ctrl;
-
- dpms_ctrl = 0x00000000;
- if (mode == DRM_MODE_DPMS_STANDBY || mode == DRM_MODE_DPMS_OFF)
- dpms_ctrl |= 0x00000001;
- if (mode == DRM_MODE_DPMS_SUSPEND || mode == DRM_MODE_DPMS_OFF)
- dpms_ctrl |= 0x00000004;
+ struct {
+ struct nv50_disp_mthd_v1 base;
+ struct nv50_disp_dac_pwr_v0 pwr;
+ } args = {
+ .base.version = 1,
+ .base.method = NV50_DISP_MTHD_V1_DAC_PWR,
+ .base.hasht = nv_encoder->dcb->hasht,
+ .base.hashm = nv_encoder->dcb->hashm,
+ .pwr.state = 1,
+ .pwr.data = 1,
+ .pwr.vsync = (mode != DRM_MODE_DPMS_SUSPEND &&
+ mode != DRM_MODE_DPMS_OFF),
+ .pwr.hsync = (mode != DRM_MODE_DPMS_STANDBY &&
+ mode != DRM_MODE_DPMS_OFF),
+ };
- nv_call(disp->core, NV50_DISP_DAC_PWR + or, dpms_ctrl);
+ nvif_mthd(disp->disp, 0, &args, sizeof(args));
}
static bool
@@ -1514,7 +1532,7 @@ nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
push = evo_wait(mast, 8);
if (push) {
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
u32 syncs = 0x00000000;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
@@ -1563,7 +1581,7 @@ nv50_dac_disconnect(struct drm_encoder *encoder)
push = evo_wait(mast, 4);
if (push) {
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0400 + (or * 0x080), 1);
evo_data(push, 0x00000000);
} else {
@@ -1580,14 +1598,25 @@ nv50_dac_disconnect(struct drm_encoder *encoder)
static enum drm_connector_status
nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nv50_disp *disp = nv50_disp(encoder->dev);
- int ret, or = nouveau_encoder(encoder)->or;
- u32 load = nouveau_drm(encoder->dev)->vbios.dactestval;
- if (load == 0)
- load = 340;
+ struct {
+ struct nv50_disp_mthd_v1 base;
+ struct nv50_disp_dac_load_v0 load;
+ } args = {
+ .base.version = 1,
+ .base.method = NV50_DISP_MTHD_V1_DAC_LOAD,
+ .base.hasht = nv_encoder->dcb->hasht,
+ .base.hashm = nv_encoder->dcb->hashm,
+ };
+ int ret;
- ret = nv_exec(disp->core, NV50_DISP_DAC_LOAD + or, &load, sizeof(load));
- if (ret || !load)
+ args.load.data = nouveau_drm(encoder->dev)->vbios.dactestval;
+ if (args.load.data == 0)
+ args.load.data = 340;
+
+ ret = nvif_mthd(disp->disp, 0, &args, sizeof(args));
+ if (ret || !args.load.load)
return connector_status_disconnected;
return connector_status_connected;
@@ -1619,7 +1648,7 @@ static int
nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe)
{
struct nouveau_drm *drm = nouveau_drm(connector->dev);
- struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
+ struct nouveau_i2c *i2c = nvkm_i2c(&drm->device);
struct nouveau_encoder *nv_encoder;
struct drm_encoder *encoder;
int type = DRM_MODE_ENCODER_DAC;
@@ -1648,27 +1677,50 @@ static void
nv50_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct nouveau_connector *nv_connector;
struct nv50_disp *disp = nv50_disp(encoder->dev);
+ struct __packed {
+ struct {
+ struct nv50_disp_mthd_v1 mthd;
+ struct nv50_disp_sor_hda_eld_v0 eld;
+ } base;
+ u8 data[sizeof(nv_connector->base.eld)];
+ } args = {
+ .base.mthd.version = 1,
+ .base.mthd.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD,
+ .base.mthd.hasht = nv_encoder->dcb->hasht,
+ .base.mthd.hashm = (0xf0ff & nv_encoder->dcb->hashm) |
+ (0x0100 << nv_crtc->index),
+ };
nv_connector = nouveau_encoder_connector_get(nv_encoder);
if (!drm_detect_monitor_audio(nv_connector->edid))
return;
drm_edid_to_eld(&nv_connector->base, nv_connector->edid);
+ memcpy(args.data, nv_connector->base.eld, sizeof(args.data));
- nv_exec(disp->core, NVA3_DISP_SOR_HDA_ELD + nv_encoder->or,
- nv_connector->base.eld,
- nv_connector->base.eld[2] * 4);
+ nvif_mthd(disp->disp, 0, &args, sizeof(args.base) + args.data[2] * 4);
}
static void
-nv50_audio_disconnect(struct drm_encoder *encoder)
+nv50_audio_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nv50_disp *disp = nv50_disp(encoder->dev);
+ struct {
+ struct nv50_disp_mthd_v1 base;
+ struct nv50_disp_sor_hda_eld_v0 eld;
+ } args = {
+ .base.version = 1,
+ .base.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD,
+ .base.hasht = nv_encoder->dcb->hasht,
+ .base.hashm = (0xf0ff & nv_encoder->dcb->hashm) |
+ (0x0100 << nv_crtc->index),
+ };
- nv_exec(disp->core, NVA3_DISP_SOR_HDA_ELD + nv_encoder->or, NULL, 0);
+ nvif_mthd(disp->disp, 0, &args, sizeof(args));
}
/******************************************************************************
@@ -1679,10 +1731,20 @@ nv50_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
- struct nouveau_connector *nv_connector;
struct nv50_disp *disp = nv50_disp(encoder->dev);
- const u32 moff = (nv_crtc->index << 3) | nv_encoder->or;
- u32 rekey = 56; /* binary driver, and tegra constant */
+ struct {
+ struct nv50_disp_mthd_v1 base;
+ struct nv50_disp_sor_hdmi_pwr_v0 pwr;
+ } args = {
+ .base.version = 1,
+ .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR,
+ .base.hasht = nv_encoder->dcb->hasht,
+ .base.hashm = (0xf0ff & nv_encoder->dcb->hashm) |
+ (0x0100 << nv_crtc->index),
+ .pwr.state = 1,
+ .pwr.rekey = 56, /* binary driver, and tegra, constant */
+ };
+ struct nouveau_connector *nv_connector;
u32 max_ac_packet;
nv_connector = nouveau_encoder_connector_get(nv_encoder);
@@ -1690,14 +1752,11 @@ nv50_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode)
return;
max_ac_packet = mode->htotal - mode->hdisplay;
- max_ac_packet -= rekey;
+ max_ac_packet -= args.pwr.rekey;
max_ac_packet -= 18; /* constant from tegra */
- max_ac_packet /= 32;
-
- nv_call(disp->core, NV84_DISP_SOR_HDMI_PWR + moff,
- NV84_DISP_SOR_HDMI_PWR_STATE_ON |
- (max_ac_packet << 16) | rekey);
+ args.pwr.max_ac_packet = max_ac_packet / 32;
+ nvif_mthd(disp->disp, 0, &args, sizeof(args));
nv50_audio_mode_set(encoder, mode);
}
@@ -1706,11 +1765,18 @@ nv50_hdmi_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nv50_disp *disp = nv50_disp(encoder->dev);
- const u32 moff = (nv_crtc->index << 3) | nv_encoder->or;
-
- nv50_audio_disconnect(encoder);
+ struct {
+ struct nv50_disp_mthd_v1 base;
+ struct nv50_disp_sor_hdmi_pwr_v0 pwr;
+ } args = {
+ .base.version = 1,
+ .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR,
+ .base.hasht = nv_encoder->dcb->hasht,
+ .base.hashm = (0xf0ff & nv_encoder->dcb->hashm) |
+ (0x0100 << nv_crtc->index),
+ };
- nv_call(disp->core, NV84_DISP_SOR_HDMI_PWR + moff, 0x00000000);
+ nvif_mthd(disp->disp, 0, &args, sizeof(args));
}
/******************************************************************************
@@ -1720,10 +1786,29 @@ static void
nv50_sor_dpms(struct drm_encoder *encoder, int mode)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct nv50_disp *disp = nv50_disp(encoder->dev);
+ struct {
+ struct nv50_disp_mthd_v1 base;
+ struct nv50_disp_sor_pwr_v0 pwr;
+ } args = {
+ .base.version = 1,
+ .base.method = NV50_DISP_MTHD_V1_SOR_PWR,
+ .base.hasht = nv_encoder->dcb->hasht,
+ .base.hashm = nv_encoder->dcb->hashm,
+ .pwr.state = mode == DRM_MODE_DPMS_ON,
+ };
+ struct {
+ struct nv50_disp_mthd_v1 base;
+ struct nv50_disp_sor_dp_pwr_v0 pwr;
+ } link = {
+ .base.version = 1,
+ .base.method = NV50_DISP_MTHD_V1_SOR_DP_PWR,
+ .base.hasht = nv_encoder->dcb->hasht,
+ .base.hashm = nv_encoder->dcb->hashm,
+ .pwr.state = mode == DRM_MODE_DPMS_ON,
+ };
struct drm_device *dev = encoder->dev;
- struct nv50_disp *disp = nv50_disp(dev);
struct drm_encoder *partner;
- u32 mthd;
nv_encoder->last_dpms = mode;
@@ -1741,18 +1826,13 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
}
}
- mthd = (ffs(nv_encoder->dcb->heads) - 1) << 3;
- mthd |= (ffs(nv_encoder->dcb->sorconf.link) - 1) << 2;
- mthd |= nv_encoder->or;
-
if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
- nv_call(disp->core, NV50_DISP_SOR_PWR | mthd, 1);
- mthd |= NV94_DISP_SOR_DP_PWR;
+ args.pwr.state = 1;
+ nvif_mthd(disp->disp, 0, &args, sizeof(args));
+ nvif_mthd(disp->disp, 0, &link, sizeof(link));
} else {
- mthd |= NV50_DISP_SOR_PWR;
+ nvif_mthd(disp->disp, 0, &args, sizeof(args));
}
-
- nv_call(disp->core, mthd, (mode == DRM_MODE_DPMS_ON));
}
static bool
@@ -1781,7 +1861,7 @@ nv50_sor_ctrl(struct nouveau_encoder *nv_encoder, u32 mask, u32 data)
struct nv50_mast *mast = nv50_mast(nv_encoder->base.base.dev);
u32 temp = (nv_encoder->ctrl & ~mask) | (data & mask), *push;
if (temp != nv_encoder->ctrl && (push = evo_wait(mast, 2))) {
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0600 + (nv_encoder->or * 0x40), 1);
evo_data(push, (nv_encoder->ctrl = temp));
} else {
@@ -1804,6 +1884,7 @@ nv50_sor_disconnect(struct drm_encoder *encoder)
if (nv_crtc) {
nv50_crtc_prepare(&nv_crtc->base);
nv50_sor_ctrl(nv_encoder, 1 << nv_crtc->index, 0);
+ nv50_audio_disconnect(encoder, nv_crtc);
nv50_hdmi_disconnect(&nv_encoder->base.base, nv_crtc);
}
}
@@ -1817,15 +1898,24 @@ static void
nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
struct drm_display_mode *mode)
{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+ struct {
+ struct nv50_disp_mthd_v1 base;
+ struct nv50_disp_sor_lvds_script_v0 lvds;
+ } lvds = {
+ .base.version = 1,
+ .base.method = NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT,
+ .base.hasht = nv_encoder->dcb->hasht,
+ .base.hashm = nv_encoder->dcb->hashm,
+ };
struct nv50_disp *disp = nv50_disp(encoder->dev);
struct nv50_mast *mast = nv50_mast(encoder->dev);
struct drm_device *dev = encoder->dev;
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct nouveau_connector *nv_connector;
struct nvbios *bios = &drm->vbios;
- u32 lvds = 0, mask, ctrl;
+ u32 mask, ctrl;
u8 owner = 1 << nv_crtc->index;
u8 proto = 0xf;
u8 depth = 0x0;
@@ -1851,31 +1941,31 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
if (bios->fp_no_ddc) {
if (bios->fp.dual_link)
- lvds |= 0x0100;
+ lvds.lvds.script |= 0x0100;
if (bios->fp.if_is_24bit)
- lvds |= 0x0200;
+ lvds.lvds.script |= 0x0200;
} else {
if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
if (((u8 *)nv_connector->edid)[121] == 2)
- lvds |= 0x0100;
+ lvds.lvds.script |= 0x0100;
} else
if (mode->clock >= bios->fp.duallink_transition_clk) {
- lvds |= 0x0100;
+ lvds.lvds.script |= 0x0100;
}
- if (lvds & 0x0100) {
+ if (lvds.lvds.script & 0x0100) {
if (bios->fp.strapless_is_24bit & 2)
- lvds |= 0x0200;
+ lvds.lvds.script |= 0x0200;
} else {
if (bios->fp.strapless_is_24bit & 1)
- lvds |= 0x0200;
+ lvds.lvds.script |= 0x0200;
}
if (nv_connector->base.display_info.bpc == 8)
- lvds |= 0x0200;
+ lvds.lvds.script |= 0x0200;
}
- nv_call(disp->core, NV50_DISP_SOR_LVDS_SCRIPT + nv_encoder->or, lvds);
+ nvif_mthd(disp->disp, 0, &lvds, sizeof(lvds));
break;
case DCB_OUTPUT_DP:
if (nv_connector->base.display_info.bpc == 6) {
@@ -1894,6 +1984,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
proto = 0x8;
else
proto = 0x9;
+ nv50_audio_mode_set(encoder, mode);
break;
default:
BUG_ON(1);
@@ -1902,7 +1993,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
nv50_sor_dpms(&nv_encoder->base.base, DRM_MODE_DPMS_ON);
- if (nv50_vers(mast) >= NVD0_DISP_CLASS) {
+ if (nv50_vers(mast) >= GF110_DISP) {
u32 *push = evo_wait(mast, 3);
if (push) {
u32 magic = 0x31ec6000 | (nv_crtc->index << 25);
@@ -1961,7 +2052,7 @@ static int
nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
{
struct nouveau_drm *drm = nouveau_drm(connector->dev);
- struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
+ struct nouveau_i2c *i2c = nvkm_i2c(&drm->device);
struct nouveau_encoder *nv_encoder;
struct drm_encoder *encoder;
int type;
@@ -2002,9 +2093,19 @@ nv50_pior_dpms(struct drm_encoder *encoder, int mode)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nv50_disp *disp = nv50_disp(encoder->dev);
- u32 mthd = (nv_encoder->dcb->type << 12) | nv_encoder->or;
- u32 ctrl = (mode == DRM_MODE_DPMS_ON);
- nv_call(disp->core, NV50_DISP_PIOR_PWR + mthd, ctrl);
+ struct {
+ struct nv50_disp_mthd_v1 base;
+ struct nv50_disp_pior_pwr_v0 pwr;
+ } args = {
+ .base.version = 1,
+ .base.method = NV50_DISP_MTHD_V1_PIOR_PWR,
+ .base.hasht = nv_encoder->dcb->hasht,
+ .base.hashm = nv_encoder->dcb->hashm,
+ .pwr.state = mode == DRM_MODE_DPMS_ON,
+ .pwr.type = nv_encoder->dcb->type,
+ };
+
+ nvif_mthd(disp->disp, 0, &args, sizeof(args));
}
static bool
@@ -2067,7 +2168,7 @@ nv50_pior_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
push = evo_wait(mast, 8);
if (push) {
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
u32 ctrl = (depth << 16) | (proto << 8) | owner;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
ctrl |= 0x00001000;
@@ -2096,7 +2197,7 @@ nv50_pior_disconnect(struct drm_encoder *encoder)
push = evo_wait(mast, 4);
if (push) {
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
evo_mthd(push, 0x0700 + (or * 0x040), 1);
evo_data(push, 0x00000000);
}
@@ -2132,7 +2233,7 @@ static int
nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe)
{
struct nouveau_drm *drm = nouveau_drm(connector->dev);
- struct nouveau_i2c *i2c = nouveau_i2c(drm->device);
+ struct nouveau_i2c *i2c = nvkm_i2c(&drm->device);
struct nouveau_i2c_port *ddc = NULL;
struct nouveau_encoder *nv_encoder;
struct drm_encoder *encoder;
@@ -2169,8 +2270,151 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe)
}
/******************************************************************************
+ * Framebuffer
+ *****************************************************************************/
+
+static void
+nv50_fbdma_fini(struct nv50_fbdma *fbdma)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(fbdma->base); i++)
+ nvif_object_fini(&fbdma->base[i]);
+ nvif_object_fini(&fbdma->core);
+ list_del(&fbdma->head);
+ kfree(fbdma);
+}
+
+static int
+nv50_fbdma_init(struct drm_device *dev, u32 name, u64 offset, u64 length, u8 kind)
+{
+ struct nouveau_drm *drm = nouveau_drm(dev);
+ struct nv50_disp *disp = nv50_disp(dev);
+ struct nv50_mast *mast = nv50_mast(dev);
+ struct __attribute__ ((packed)) {
+ struct nv_dma_v0 base;
+ union {
+ struct nv50_dma_v0 nv50;
+ struct gf100_dma_v0 gf100;
+ struct gf110_dma_v0 gf110;
+ };
+ } args = {};
+ struct nv50_fbdma *fbdma;
+ struct drm_crtc *crtc;
+ u32 size = sizeof(args.base);
+ int ret;
+
+ list_for_each_entry(fbdma, &disp->fbdma, head) {
+ if (fbdma->core.handle == name)
+ return 0;
+ }
+
+ fbdma = kzalloc(sizeof(*fbdma), GFP_KERNEL);
+ if (!fbdma)
+ return -ENOMEM;
+ list_add(&fbdma->head, &disp->fbdma);
+
+ args.base.target = NV_DMA_V0_TARGET_VRAM;
+ args.base.access = NV_DMA_V0_ACCESS_RDWR;
+ args.base.start = offset;
+ args.base.limit = offset + length - 1;
+
+ if (drm->device.info.chipset < 0x80) {
+ args.nv50.part = NV50_DMA_V0_PART_256;
+ size += sizeof(args.nv50);
+ } else
+ if (drm->device.info.chipset < 0xc0) {
+ args.nv50.part = NV50_DMA_V0_PART_256;
+ args.nv50.kind = kind;
+ size += sizeof(args.nv50);
+ } else
+ if (drm->device.info.chipset < 0xd0) {
+ args.gf100.kind = kind;
+ size += sizeof(args.gf100);
+ } else {
+ args.gf110.page = GF110_DMA_V0_PAGE_LP;
+ args.gf110.kind = kind;
+ size += sizeof(args.gf110);
+ }
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct nv50_head *head = nv50_head(crtc);
+ int ret = nvif_object_init(&head->sync.base.base.user, NULL,
+ name, NV_DMA_IN_MEMORY, &args, size,
+ &fbdma->base[head->base.index]);
+ if (ret) {
+ nv50_fbdma_fini(fbdma);
+ return ret;
+ }
+ }
+
+ ret = nvif_object_init(&mast->base.base.user, NULL, name,
+ NV_DMA_IN_MEMORY, &args, size,
+ &fbdma->core);
+ if (ret) {
+ nv50_fbdma_fini(fbdma);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void
+nv50_fb_dtor(struct drm_framebuffer *fb)
+{
+}
+
+static int
+nv50_fb_ctor(struct drm_framebuffer *fb)
+{
+ struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb);
+ struct nouveau_drm *drm = nouveau_drm(fb->dev);
+ struct nouveau_bo *nvbo = nv_fb->nvbo;
+ struct nv50_disp *disp = nv50_disp(fb->dev);
+ u8 kind = nouveau_bo_tile_layout(nvbo) >> 8;
+ u8 tile = nvbo->tile_mode;
+
+ if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG) {
+ NV_ERROR(drm, "framebuffer requires contiguous bo\n");
+ return -EINVAL;
+ }
+
+ if (drm->device.info.chipset >= 0xc0)
+ tile >>= 4; /* yep.. */
+
+ switch (fb->depth) {
+ case 8: nv_fb->r_format = 0x1e00; break;
+ case 15: nv_fb->r_format = 0xe900; break;
+ case 16: nv_fb->r_format = 0xe800; break;
+ case 24:
+ case 32: nv_fb->r_format = 0xcf00; break;
+ case 30: nv_fb->r_format = 0xd100; break;
+ default:
+ NV_ERROR(drm, "unknown depth %d\n", fb->depth);
+ return -EINVAL;
+ }
+
+ if (disp->disp->oclass < G82_DISP) {
+ nv_fb->r_pitch = kind ? (((fb->pitches[0] / 4) << 4) | tile) :
+ (fb->pitches[0] | 0x00100000);
+ nv_fb->r_format |= kind << 16;
+ } else
+ if (disp->disp->oclass < GF110_DISP) {
+ nv_fb->r_pitch = kind ? (((fb->pitches[0] / 4) << 4) | tile) :
+ (fb->pitches[0] | 0x00100000);
+ } else {
+ nv_fb->r_pitch = kind ? (((fb->pitches[0] / 4) << 4) | tile) :
+ (fb->pitches[0] | 0x01000000);
+ }
+ nv_fb->r_handle = 0xffff0000 | kind;
+
+ return nv50_fbdma_init(fb->dev, nv_fb->r_handle, 0,
+ drm->device.info.ram_user, kind);
+}
+
+/******************************************************************************
* Init
*****************************************************************************/
+
void
nv50_display_fini(struct drm_device *dev)
{
@@ -2193,7 +2437,7 @@ nv50_display_init(struct drm_device *dev)
}
evo_mthd(push, 0x0088, 1);
- evo_data(push, NvEvoSync);
+ evo_data(push, nv50_mast(dev)->base.sync.handle);
evo_kick(push, nv50_mast(dev));
return 0;
}
@@ -2202,8 +2446,13 @@ void
nv50_display_destroy(struct drm_device *dev)
{
struct nv50_disp *disp = nv50_disp(dev);
+ struct nv50_fbdma *fbdma, *fbtmp;
+
+ list_for_each_entry_safe(fbdma, fbtmp, &disp->fbdma, head) {
+ nv50_fbdma_fini(fbdma);
+ }
- nv50_dmac_destroy(disp->core, &disp->mast.base);
+ nv50_dmac_destroy(&disp->mast.base, disp->disp);
nouveau_bo_unmap(disp->sync);
if (disp->sync)
@@ -2217,7 +2466,7 @@ nv50_display_destroy(struct drm_device *dev)
int
nv50_display_create(struct drm_device *dev)
{
- struct nouveau_device *device = nouveau_dev(dev);
+ struct nvif_device *device = &nouveau_drm(dev)->device;
struct nouveau_drm *drm = nouveau_drm(dev);
struct dcb_table *dcb = &drm->vbios.dcb;
struct drm_connector *connector, *tmp;
@@ -2228,16 +2477,19 @@ nv50_display_create(struct drm_device *dev)
disp = kzalloc(sizeof(*disp), GFP_KERNEL);
if (!disp)
return -ENOMEM;
+ INIT_LIST_HEAD(&disp->fbdma);
nouveau_display(dev)->priv = disp;
nouveau_display(dev)->dtor = nv50_display_destroy;
nouveau_display(dev)->init = nv50_display_init;
nouveau_display(dev)->fini = nv50_display_fini;
- disp->core = nouveau_display(dev)->core;
+ nouveau_display(dev)->fb_ctor = nv50_fb_ctor;
+ nouveau_display(dev)->fb_dtor = nv50_fb_dtor;
+ disp->disp = &nouveau_display(dev)->disp;
/* small shared memory area we use for notifiers and semaphores */
ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
- 0, 0x0000, NULL, &disp->sync);
+ 0, 0x0000, NULL, NULL, &disp->sync);
if (!ret) {
ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM);
if (!ret) {
@@ -2253,22 +2505,19 @@ nv50_display_create(struct drm_device *dev)
goto out;
/* allocate master evo channel */
- ret = nv50_dmac_create(disp->core, NV50_DISP_MAST_CLASS, 0,
- &(struct nv50_display_mast_class) {
- .pushbuf = EVO_PUSH_HANDLE(MAST, 0),
- }, sizeof(struct nv50_display_mast_class),
- disp->sync->bo.offset, &disp->mast.base);
+ ret = nv50_core_create(disp->disp, disp->sync->bo.offset,
+ &disp->mast);
if (ret)
goto out;
/* create crtc objects to represent the hw heads */
- if (nv_mclass(disp->core) >= NVD0_DISP_CLASS)
- crtcs = nv_rd32(device, 0x022448);
+ if (disp->disp->oclass >= GF110_DISP)
+ crtcs = nvif_rd32(device, 0x022448);
else
crtcs = 2;
for (i = 0; i < crtcs; i++) {
- ret = nv50_crtc_create(dev, disp->core, i);
+ ret = nv50_crtc_create(dev, i);
if (ret)
goto out;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c
index 52068a0910dc..394c89abcc97 100644
--- a/drivers/gpu/drm/nouveau/nv50_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c
@@ -154,7 +154,6 @@ nv50_fbcon_accel_init(struct fb_info *info)
struct drm_device *dev = nfbdev->dev;
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_channel *chan = drm->channel;
- struct nouveau_object *object;
int ret, format;
switch (info->var.bits_per_pixel) {
@@ -184,8 +183,8 @@ nv50_fbcon_accel_init(struct fb_info *info)
return -EINVAL;
}
- ret = nouveau_object_new(nv_object(chan->cli), NVDRM_CHAN, Nv2D,
- 0x502d, NULL, 0, &object);
+ ret = nvif_object_init(chan->object, NULL, 0x502d, 0x502d, NULL, 0,
+ &nfbdev->twod);
if (ret)
return ret;
@@ -196,11 +195,11 @@ nv50_fbcon_accel_init(struct fb_info *info)
}
BEGIN_NV04(chan, NvSub2D, 0x0000, 1);
- OUT_RING(chan, Nv2D);
+ OUT_RING(chan, nfbdev->twod.handle);
BEGIN_NV04(chan, NvSub2D, 0x0184, 3);
- OUT_RING(chan, NvDmaFB);
- OUT_RING(chan, NvDmaFB);
- OUT_RING(chan, NvDmaFB);
+ OUT_RING(chan, chan->vram.handle);
+ OUT_RING(chan, chan->vram.handle);
+ OUT_RING(chan, chan->vram.handle);
BEGIN_NV04(chan, NvSub2D, 0x0290, 1);
OUT_RING(chan, 0);
BEGIN_NV04(chan, NvSub2D, 0x0888, 1);
diff --git a/drivers/gpu/drm/nouveau/nv50_fence.c b/drivers/gpu/drm/nouveau/nv50_fence.c
index 0ee363840035..22d242b37962 100644
--- a/drivers/gpu/drm/nouveau/nv50_fence.c
+++ b/drivers/gpu/drm/nouveau/nv50_fence.c
@@ -22,8 +22,8 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include <core/object.h>
-#include <core/class.h>
+#include <nvif/os.h>
+#include <nvif/class.h>
#include "nouveau_drm.h"
#include "nouveau_dma.h"
@@ -38,7 +38,6 @@ nv50_fence_context_new(struct nouveau_channel *chan)
struct nv10_fence_priv *priv = chan->drm->fence;
struct nv10_fence_chan *fctx;
struct ttm_mem_reg *mem = &priv->bo->bo.mem;
- struct nouveau_object *object;
u32 start = mem->start * PAGE_SIZE;
u32 limit = start + mem->size - 1;
int ret, i;
@@ -47,20 +46,19 @@ nv50_fence_context_new(struct nouveau_channel *chan)
if (!fctx)
return -ENOMEM;
- nouveau_fence_context_new(&fctx->base);
+ nouveau_fence_context_new(chan, &fctx->base);
fctx->base.emit = nv10_fence_emit;
fctx->base.read = nv10_fence_read;
fctx->base.sync = nv17_fence_sync;
- ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
- NvSema, 0x003d,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
+ ret = nvif_object_init(chan->object, NULL, NvSema, NV_DMA_IN_MEMORY,
+ &(struct nv_dma_v0) {
+ .target = NV_DMA_V0_TARGET_VRAM,
+ .access = NV_DMA_V0_ACCESS_RDWR,
.start = start,
.limit = limit,
- }, sizeof(struct nv_dma_class),
- &object);
+ }, sizeof(struct nv_dma_v0),
+ &fctx->sema);
/* dma objects for display sync channel semaphore blocks */
for (i = 0; !ret && i < dev->mode_config.num_crtc; i++) {
@@ -68,15 +66,14 @@ nv50_fence_context_new(struct nouveau_channel *chan)
u32 start = bo->bo.mem.start * PAGE_SIZE;
u32 limit = start + bo->bo.mem.size - 1;
- ret = nouveau_object_new(nv_object(chan->cli), chan->handle,
- NvEvoSema0 + i, 0x003d,
- &(struct nv_dma_class) {
- .flags = NV_DMA_TARGET_VRAM |
- NV_DMA_ACCESS_RDWR,
+ ret = nvif_object_init(chan->object, NULL, NvEvoSema0 + i,
+ NV_DMA_IN_MEMORY, &(struct nv_dma_v0) {
+ .target = NV_DMA_V0_TARGET_VRAM,
+ .access = NV_DMA_V0_ACCESS_RDWR,
.start = start,
.limit = limit,
- }, sizeof(struct nv_dma_class),
- &object);
+ }, sizeof(struct nv_dma_v0),
+ &fctx->head[i]);
}
if (ret)
@@ -98,10 +95,12 @@ nv50_fence_create(struct nouveau_drm *drm)
priv->base.resume = nv17_fence_resume;
priv->base.context_new = nv50_fence_context_new;
priv->base.context_del = nv10_fence_context_del;
+ priv->base.contexts = 127;
+ priv->base.context_base = fence_context_alloc(priv->base.contexts);
spin_lock_init(&priv->lock);
ret = nouveau_bo_new(drm->dev, 4096, 0x1000, TTM_PL_FLAG_VRAM,
- 0, 0x0000, NULL, &priv->bo);
+ 0, 0x0000, NULL, NULL, &priv->bo);
if (!ret) {
ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
if (!ret) {
diff --git a/drivers/gpu/drm/nouveau/nv84_fence.c b/drivers/gpu/drm/nouveau/nv84_fence.c
index 9fd475c89820..d6c6c87c3f07 100644
--- a/drivers/gpu/drm/nouveau/nv84_fence.c
+++ b/drivers/gpu/drm/nouveau/nv84_fence.c
@@ -22,12 +22,6 @@
* Authors: Ben Skeggs
*/
-#include <core/object.h>
-#include <core/client.h>
-#include <core/class.h>
-
-#include <engine/fifo.h>
-
#include "nouveau_drm.h"
#include "nouveau_dma.h"
#include "nouveau_fence.h"
@@ -47,7 +41,7 @@ nv84_fence_emit32(struct nouveau_channel *chan, u64 virtual, u32 sequence)
int ret = RING_SPACE(chan, 8);
if (ret == 0) {
BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
- OUT_RING (chan, chan->vram);
+ OUT_RING (chan, chan->vram.handle);
BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 5);
OUT_RING (chan, upper_32_bits(virtual));
OUT_RING (chan, lower_32_bits(virtual));
@@ -65,7 +59,7 @@ nv84_fence_sync32(struct nouveau_channel *chan, u64 virtual, u32 sequence)
int ret = RING_SPACE(chan, 7);
if (ret == 0) {
BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
- OUT_RING (chan, chan->vram);
+ OUT_RING (chan, chan->vram.handle);
BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(virtual));
OUT_RING (chan, lower_32_bits(virtual));
@@ -81,15 +75,14 @@ nv84_fence_emit(struct nouveau_fence *fence)
{
struct nouveau_channel *chan = fence->channel;
struct nv84_fence_chan *fctx = chan->fence;
- struct nouveau_fifo_chan *fifo = (void *)chan->object;
- u64 addr = fifo->chid * 16;
+ u64 addr = chan->chid * 16;
if (fence->sysmem)
addr += fctx->vma_gart.offset;
else
addr += fctx->vma.offset;
- return fctx->base.emit32(chan, addr, fence->sequence);
+ return fctx->base.emit32(chan, addr, fence->base.seqno);
}
static int
@@ -97,23 +90,21 @@ nv84_fence_sync(struct nouveau_fence *fence,
struct nouveau_channel *prev, struct nouveau_channel *chan)
{
struct nv84_fence_chan *fctx = chan->fence;
- struct nouveau_fifo_chan *fifo = (void *)prev->object;
- u64 addr = fifo->chid * 16;
+ u64 addr = prev->chid * 16;
if (fence->sysmem)
addr += fctx->vma_gart.offset;
else
addr += fctx->vma.offset;
- return fctx->base.sync32(chan, addr, fence->sequence);
+ return fctx->base.sync32(chan, addr, fence->base.seqno);
}
static u32
nv84_fence_read(struct nouveau_channel *chan)
{
- struct nouveau_fifo_chan *fifo = (void *)chan->object;
struct nv84_fence_priv *priv = chan->drm->fence;
- return nouveau_bo_rd32(priv->bo, fifo->chid * 16/4);
+ return nouveau_bo_rd32(priv->bo, chan->chid * 16/4);
}
static void
@@ -129,18 +120,18 @@ nv84_fence_context_del(struct nouveau_channel *chan)
nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]);
}
+ nouveau_bo_wr32(priv->bo, chan->chid * 16 / 4, fctx->base.sequence);
nouveau_bo_vma_del(priv->bo, &fctx->vma_gart);
nouveau_bo_vma_del(priv->bo, &fctx->vma);
nouveau_fence_context_del(&fctx->base);
chan->fence = NULL;
- kfree(fctx);
+ nouveau_fence_context_free(&fctx->base);
}
int
nv84_fence_context_new(struct nouveau_channel *chan)
{
- struct nouveau_fifo_chan *fifo = (void *)chan->object;
- struct nouveau_client *client = nouveau_client(fifo);
+ struct nouveau_cli *cli = (void *)nvif_client(&chan->device->base);
struct nv84_fence_priv *priv = chan->drm->fence;
struct nv84_fence_chan *fctx;
int ret, i;
@@ -149,27 +140,26 @@ nv84_fence_context_new(struct nouveau_channel *chan)
if (!fctx)
return -ENOMEM;
- nouveau_fence_context_new(&fctx->base);
+ nouveau_fence_context_new(chan, &fctx->base);
fctx->base.emit = nv84_fence_emit;
fctx->base.sync = nv84_fence_sync;
fctx->base.read = nv84_fence_read;
fctx->base.emit32 = nv84_fence_emit32;
fctx->base.sync32 = nv84_fence_sync32;
+ fctx->base.sequence = nv84_fence_read(chan);
- ret = nouveau_bo_vma_add(priv->bo, client->vm, &fctx->vma);
+ ret = nouveau_bo_vma_add(priv->bo, cli->vm, &fctx->vma);
if (ret == 0) {
- ret = nouveau_bo_vma_add(priv->bo_gart, client->vm,
+ ret = nouveau_bo_vma_add(priv->bo_gart, cli->vm,
&fctx->vma_gart);
}
/* map display semaphore buffers into channel's vm */
for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) {
struct nouveau_bo *bo = nv50_display_crtc_sema(chan->drm->dev, i);
- ret = nouveau_bo_vma_add(bo, client->vm, &fctx->dispc_vma[i]);
+ ret = nouveau_bo_vma_add(bo, cli->vm, &fctx->dispc_vma[i]);
}
- nouveau_bo_wr32(priv->bo, fifo->chid * 16/4, 0x00000000);
-
if (ret)
nv84_fence_context_del(chan);
return ret;
@@ -178,13 +168,12 @@ nv84_fence_context_new(struct nouveau_channel *chan)
static bool
nv84_fence_suspend(struct nouveau_drm *drm)
{
- struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
struct nv84_fence_priv *priv = drm->fence;
int i;
- priv->suspend = vmalloc((pfifo->max + 1) * sizeof(u32));
+ priv->suspend = vmalloc(priv->base.contexts * sizeof(u32));
if (priv->suspend) {
- for (i = 0; i <= pfifo->max; i++)
+ for (i = 0; i < priv->base.contexts; i++)
priv->suspend[i] = nouveau_bo_rd32(priv->bo, i*4);
}
@@ -194,12 +183,11 @@ nv84_fence_suspend(struct nouveau_drm *drm)
static void
nv84_fence_resume(struct nouveau_drm *drm)
{
- struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
struct nv84_fence_priv *priv = drm->fence;
int i;
if (priv->suspend) {
- for (i = 0; i <= pfifo->max; i++)
+ for (i = 0; i < priv->base.contexts; i++)
nouveau_bo_wr32(priv->bo, i*4, priv->suspend[i]);
vfree(priv->suspend);
priv->suspend = NULL;
@@ -225,7 +213,7 @@ nv84_fence_destroy(struct nouveau_drm *drm)
int
nv84_fence_create(struct nouveau_drm *drm)
{
- struct nouveau_fifo *pfifo = nouveau_fifo(drm->device);
+ struct nouveau_fifo *pfifo = nvkm_fifo(&drm->device);
struct nv84_fence_priv *priv;
int ret;
@@ -239,11 +227,12 @@ nv84_fence_create(struct nouveau_drm *drm)
priv->base.context_new = nv84_fence_context_new;
priv->base.context_del = nv84_fence_context_del;
- init_waitqueue_head(&priv->base.waiting);
+ priv->base.contexts = pfifo->max + 1;
+ priv->base.context_base = fence_context_alloc(priv->base.contexts);
priv->base.uevent = true;
- ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
- TTM_PL_FLAG_VRAM, 0, 0, NULL, &priv->bo);
+ ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0,
+ TTM_PL_FLAG_VRAM, 0, 0, NULL, NULL, &priv->bo);
if (ret == 0) {
ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM);
if (ret == 0) {
@@ -256,8 +245,8 @@ nv84_fence_create(struct nouveau_drm *drm)
}
if (ret == 0)
- ret = nouveau_bo_new(drm->dev, 16 * (pfifo->max + 1), 0,
- TTM_PL_FLAG_TT, 0, 0, NULL,
+ ret = nouveau_bo_new(drm->dev, 16 * priv->base.contexts, 0,
+ TTM_PL_FLAG_TT, 0, 0, NULL, NULL,
&priv->bo_gart);
if (ret == 0) {
ret = nouveau_bo_pin(priv->bo_gart, TTM_PL_FLAG_TT);
diff --git a/drivers/gpu/drm/nouveau/nvc0_fbcon.c b/drivers/gpu/drm/nouveau/nvc0_fbcon.c
index 9dcd30f3e1e0..61246677e8dc 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fbcon.c
@@ -154,11 +154,10 @@ nvc0_fbcon_accel_init(struct fb_info *info)
struct nouveau_framebuffer *fb = &nfbdev->nouveau_fb;
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_channel *chan = drm->channel;
- struct nouveau_object *object;
int ret, format;
- ret = nouveau_object_new(nv_object(chan->cli), NVDRM_CHAN, Nv2D,
- 0x902d, NULL, 0, &object);
+ ret = nvif_object_init(chan->object, NULL, 0x902d, 0x902d, NULL, 0,
+ &nfbdev->twod);
if (ret)
return ret;
@@ -197,7 +196,7 @@ nvc0_fbcon_accel_init(struct fb_info *info)
}
BEGIN_NVC0(chan, NvSub2D, 0x0000, 1);
- OUT_RING (chan, 0x0000902d);
+ OUT_RING (chan, nfbdev->twod.handle);
BEGIN_NVC0(chan, NvSub2D, 0x0290, 1);
OUT_RING (chan, 0);
BEGIN_NVC0(chan, NvSub2D, 0x0888, 1);
diff --git a/drivers/gpu/drm/nouveau/nvc0_fence.c b/drivers/gpu/drm/nouveau/nvc0_fence.c
index 9566267fbc42..becf19abda2d 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fence.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fence.c
@@ -22,12 +22,6 @@
* Authors: Ben Skeggs
*/
-#include <core/object.h>
-#include <core/client.h>
-#include <core/class.h>
-
-#include <engine/fifo.h>
-
#include "nouveau_drm.h"
#include "nouveau_dma.h"
#include "nouveau_fence.h"
diff --git a/drivers/gpu/drm/nouveau/nvif/class.h b/drivers/gpu/drm/nouveau/nvif/class.h
new file mode 100644
index 000000000000..e5a27df0672b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/class.h
@@ -0,0 +1,567 @@
+#ifndef __NVIF_CLASS_H__
+#define __NVIF_CLASS_H__
+
+/*******************************************************************************
+ * class identifiers
+ ******************************************************************************/
+
+/* the below match nvidia-assigned (either in hw, or sw) class numbers */
+#define NV_DEVICE 0x00000080
+
+#define NV_DMA_FROM_MEMORY 0x00000002
+#define NV_DMA_TO_MEMORY 0x00000003
+#define NV_DMA_IN_MEMORY 0x0000003d
+
+#define NV04_DISP 0x00000046
+
+#define NV03_CHANNEL_DMA 0x0000006b
+#define NV10_CHANNEL_DMA 0x0000006e
+#define NV17_CHANNEL_DMA 0x0000176e
+#define NV40_CHANNEL_DMA 0x0000406e
+#define NV50_CHANNEL_DMA 0x0000506e
+#define G82_CHANNEL_DMA 0x0000826e
+
+#define NV50_CHANNEL_GPFIFO 0x0000506f
+#define G82_CHANNEL_GPFIFO 0x0000826f
+#define FERMI_CHANNEL_GPFIFO 0x0000906f
+#define KEPLER_CHANNEL_GPFIFO_A 0x0000a06f
+
+#define NV50_DISP 0x00005070
+#define G82_DISP 0x00008270
+#define GT200_DISP 0x00008370
+#define GT214_DISP 0x00008570
+#define GT206_DISP 0x00008870
+#define GF110_DISP 0x00009070
+#define GK104_DISP 0x00009170
+#define GK110_DISP 0x00009270
+#define GM107_DISP 0x00009470
+
+#define NV50_DISP_CURSOR 0x0000507a
+#define G82_DISP_CURSOR 0x0000827a
+#define GT214_DISP_CURSOR 0x0000857a
+#define GF110_DISP_CURSOR 0x0000907a
+#define GK104_DISP_CURSOR 0x0000917a
+
+#define NV50_DISP_OVERLAY 0x0000507b
+#define G82_DISP_OVERLAY 0x0000827b
+#define GT214_DISP_OVERLAY 0x0000857b
+#define GF110_DISP_OVERLAY 0x0000907b
+#define GK104_DISP_OVERLAY 0x0000917b
+
+#define NV50_DISP_BASE_CHANNEL_DMA 0x0000507c
+#define G82_DISP_BASE_CHANNEL_DMA 0x0000827c
+#define GT200_DISP_BASE_CHANNEL_DMA 0x0000837c
+#define GT214_DISP_BASE_CHANNEL_DMA 0x0000857c
+#define GF110_DISP_BASE_CHANNEL_DMA 0x0000907c
+#define GK104_DISP_BASE_CHANNEL_DMA 0x0000917c
+#define GK110_DISP_BASE_CHANNEL_DMA 0x0000927c
+
+#define NV50_DISP_CORE_CHANNEL_DMA 0x0000507d
+#define G82_DISP_CORE_CHANNEL_DMA 0x0000827d
+#define GT200_DISP_CORE_CHANNEL_DMA 0x0000837d
+#define GT214_DISP_CORE_CHANNEL_DMA 0x0000857d
+#define GT206_DISP_CORE_CHANNEL_DMA 0x0000887d
+#define GF110_DISP_CORE_CHANNEL_DMA 0x0000907d
+#define GK104_DISP_CORE_CHANNEL_DMA 0x0000917d
+#define GK110_DISP_CORE_CHANNEL_DMA 0x0000927d
+#define GM107_DISP_CORE_CHANNEL_DMA 0x0000947d
+
+#define NV50_DISP_OVERLAY_CHANNEL_DMA 0x0000507e
+#define G82_DISP_OVERLAY_CHANNEL_DMA 0x0000827e
+#define GT200_DISP_OVERLAY_CHANNEL_DMA 0x0000837e
+#define GT214_DISP_OVERLAY_CHANNEL_DMA 0x0000857e
+#define GF110_DISP_OVERLAY_CONTROL_DMA 0x0000907e
+#define GK104_DISP_OVERLAY_CONTROL_DMA 0x0000917e
+
+#define FERMI_A 0x00009097
+#define FERMI_B 0x00009197
+#define FERMI_C 0x00009297
+
+#define KEPLER_A 0x0000a097
+#define KEPLER_B 0x0000a197
+#define KEPLER_C 0x0000a297
+
+#define MAXWELL_A 0x0000b097
+
+#define FERMI_COMPUTE_A 0x000090c0
+#define FERMI_COMPUTE_B 0x000091c0
+
+#define KEPLER_COMPUTE_A 0x0000a0c0
+#define KEPLER_COMPUTE_B 0x0000a1c0
+
+#define MAXWELL_COMPUTE_A 0x0000b0c0
+
+
+/*******************************************************************************
+ * client
+ ******************************************************************************/
+
+#define NV_CLIENT_DEVLIST 0x00
+
+struct nv_client_devlist_v0 {
+ __u8 version;
+ __u8 count;
+ __u8 pad02[6];
+ __u64 device[];
+};
+
+
+/*******************************************************************************
+ * device
+ ******************************************************************************/
+
+struct nv_device_v0 {
+ __u8 version;
+ __u8 pad01[7];
+ __u64 device; /* device identifier, ~0 for client default */
+#define NV_DEVICE_V0_DISABLE_IDENTIFY 0x0000000000000001ULL
+#define NV_DEVICE_V0_DISABLE_MMIO 0x0000000000000002ULL
+#define NV_DEVICE_V0_DISABLE_VBIOS 0x0000000000000004ULL
+#define NV_DEVICE_V0_DISABLE_CORE 0x0000000000000008ULL
+#define NV_DEVICE_V0_DISABLE_DISP 0x0000000000010000ULL
+#define NV_DEVICE_V0_DISABLE_FIFO 0x0000000000020000ULL
+#define NV_DEVICE_V0_DISABLE_GRAPH 0x0000000100000000ULL
+#define NV_DEVICE_V0_DISABLE_MPEG 0x0000000200000000ULL
+#define NV_DEVICE_V0_DISABLE_ME 0x0000000400000000ULL
+#define NV_DEVICE_V0_DISABLE_VP 0x0000000800000000ULL
+#define NV_DEVICE_V0_DISABLE_CRYPT 0x0000001000000000ULL
+#define NV_DEVICE_V0_DISABLE_BSP 0x0000002000000000ULL
+#define NV_DEVICE_V0_DISABLE_PPP 0x0000004000000000ULL
+#define NV_DEVICE_V0_DISABLE_COPY0 0x0000008000000000ULL
+#define NV_DEVICE_V0_DISABLE_COPY1 0x0000010000000000ULL
+#define NV_DEVICE_V0_DISABLE_VIC 0x0000020000000000ULL
+#define NV_DEVICE_V0_DISABLE_VENC 0x0000040000000000ULL
+ __u64 disable; /* disable particular subsystems */
+ __u64 debug0; /* as above, but *internal* ids, and *NOT* ABI */
+};
+
+#define NV_DEVICE_V0_INFO 0x00
+
+struct nv_device_info_v0 {
+ __u8 version;
+#define NV_DEVICE_INFO_V0_IGP 0x00
+#define NV_DEVICE_INFO_V0_PCI 0x01
+#define NV_DEVICE_INFO_V0_AGP 0x02
+#define NV_DEVICE_INFO_V0_PCIE 0x03
+#define NV_DEVICE_INFO_V0_SOC 0x04
+ __u8 platform;
+ __u16 chipset; /* from NV_PMC_BOOT_0 */
+ __u8 revision; /* from NV_PMC_BOOT_0 */
+#define NV_DEVICE_INFO_V0_TNT 0x01
+#define NV_DEVICE_INFO_V0_CELSIUS 0x02
+#define NV_DEVICE_INFO_V0_KELVIN 0x03
+#define NV_DEVICE_INFO_V0_RANKINE 0x04
+#define NV_DEVICE_INFO_V0_CURIE 0x05
+#define NV_DEVICE_INFO_V0_TESLA 0x06
+#define NV_DEVICE_INFO_V0_FERMI 0x07
+#define NV_DEVICE_INFO_V0_KEPLER 0x08
+#define NV_DEVICE_INFO_V0_MAXWELL 0x09
+ __u8 family;
+ __u8 pad06[2];
+ __u64 ram_size;
+ __u64 ram_user;
+};
+
+
+/*******************************************************************************
+ * context dma
+ ******************************************************************************/
+
+struct nv_dma_v0 {
+ __u8 version;
+#define NV_DMA_V0_TARGET_VM 0x00
+#define NV_DMA_V0_TARGET_VRAM 0x01
+#define NV_DMA_V0_TARGET_PCI 0x02
+#define NV_DMA_V0_TARGET_PCI_US 0x03
+#define NV_DMA_V0_TARGET_AGP 0x04
+ __u8 target;
+#define NV_DMA_V0_ACCESS_VM 0x00
+#define NV_DMA_V0_ACCESS_RD 0x01
+#define NV_DMA_V0_ACCESS_WR 0x02
+#define NV_DMA_V0_ACCESS_RDWR (NV_DMA_V0_ACCESS_RD | NV_DMA_V0_ACCESS_WR)
+ __u8 access;
+ __u8 pad03[5];
+ __u64 start;
+ __u64 limit;
+ /* ... chipset-specific class data */
+};
+
+struct nv50_dma_v0 {
+ __u8 version;
+#define NV50_DMA_V0_PRIV_VM 0x00
+#define NV50_DMA_V0_PRIV_US 0x01
+#define NV50_DMA_V0_PRIV__S 0x02
+ __u8 priv;
+#define NV50_DMA_V0_PART_VM 0x00
+#define NV50_DMA_V0_PART_256 0x01
+#define NV50_DMA_V0_PART_1KB 0x02
+ __u8 part;
+#define NV50_DMA_V0_COMP_NONE 0x00
+#define NV50_DMA_V0_COMP_1 0x01
+#define NV50_DMA_V0_COMP_2 0x02
+#define NV50_DMA_V0_COMP_VM 0x03
+ __u8 comp;
+#define NV50_DMA_V0_KIND_PITCH 0x00
+#define NV50_DMA_V0_KIND_VM 0x7f
+ __u8 kind;
+ __u8 pad05[3];
+};
+
+struct gf100_dma_v0 {
+ __u8 version;
+#define GF100_DMA_V0_PRIV_VM 0x00
+#define GF100_DMA_V0_PRIV_US 0x01
+#define GF100_DMA_V0_PRIV__S 0x02
+ __u8 priv;
+#define GF100_DMA_V0_KIND_PITCH 0x00
+#define GF100_DMA_V0_KIND_VM 0xff
+ __u8 kind;
+ __u8 pad03[5];
+};
+
+struct gf110_dma_v0 {
+ __u8 version;
+#define GF110_DMA_V0_PAGE_LP 0x00
+#define GF110_DMA_V0_PAGE_SP 0x01
+ __u8 page;
+#define GF110_DMA_V0_KIND_PITCH 0x00
+#define GF110_DMA_V0_KIND_VM 0xff
+ __u8 kind;
+ __u8 pad03[5];
+};
+
+
+/*******************************************************************************
+ * perfmon
+ ******************************************************************************/
+
+struct nvif_perfctr_v0 {
+ __u8 version;
+ __u8 pad01[1];
+ __u16 logic_op;
+ __u8 pad04[4];
+ char name[4][64];
+};
+
+#define NVIF_PERFCTR_V0_QUERY 0x00
+#define NVIF_PERFCTR_V0_SAMPLE 0x01
+#define NVIF_PERFCTR_V0_READ 0x02
+
+struct nvif_perfctr_query_v0 {
+ __u8 version;
+ __u8 pad01[3];
+ __u32 iter;
+ char name[64];
+};
+
+struct nvif_perfctr_sample {
+};
+
+struct nvif_perfctr_read_v0 {
+ __u8 version;
+ __u8 pad01[7];
+ __u32 ctr;
+ __u32 clk;
+};
+
+
+/*******************************************************************************
+ * device control
+ ******************************************************************************/
+
+#define NVIF_CONTROL_PSTATE_INFO 0x00
+#define NVIF_CONTROL_PSTATE_ATTR 0x01
+#define NVIF_CONTROL_PSTATE_USER 0x02
+
+struct nvif_control_pstate_info_v0 {
+ __u8 version;
+ __u8 count; /* out: number of power states */
+#define NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE (-1)
+#define NVIF_CONTROL_PSTATE_INFO_V0_USTATE_PERFMON (-2)
+ __s8 ustate_ac; /* out: target pstate index */
+ __s8 ustate_dc; /* out: target pstate index */
+ __s8 pwrsrc; /* out: current power source */
+#define NVIF_CONTROL_PSTATE_INFO_V0_PSTATE_UNKNOWN (-1)
+#define NVIF_CONTROL_PSTATE_INFO_V0_PSTATE_PERFMON (-2)
+ __s8 pstate; /* out: current pstate index */
+ __u8 pad06[2];
+};
+
+struct nvif_control_pstate_attr_v0 {
+ __u8 version;
+#define NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT (-1)
+ __s8 state; /* in: index of pstate to query
+ * out: pstate identifier
+ */
+ __u8 index; /* in: index of attribute to query
+ * out: index of next attribute, or 0 if no more
+ */
+ __u8 pad03[5];
+ __u32 min;
+ __u32 max;
+ char name[32];
+ char unit[16];
+};
+
+struct nvif_control_pstate_user_v0 {
+ __u8 version;
+#define NVIF_CONTROL_PSTATE_USER_V0_STATE_UNKNOWN (-1)
+#define NVIF_CONTROL_PSTATE_USER_V0_STATE_PERFMON (-2)
+ __s8 ustate; /* in: pstate identifier */
+ __s8 pwrsrc; /* in: target power source */
+ __u8 pad03[5];
+};
+
+
+/*******************************************************************************
+ * DMA FIFO channels
+ ******************************************************************************/
+
+struct nv03_channel_dma_v0 {
+ __u8 version;
+ __u8 chid;
+ __u8 pad02[2];
+ __u32 pushbuf;
+ __u64 offset;
+};
+
+#define G82_CHANNEL_DMA_V0_NTFY_UEVENT 0x00
+
+/*******************************************************************************
+ * GPFIFO channels
+ ******************************************************************************/
+
+struct nv50_channel_gpfifo_v0 {
+ __u8 version;
+ __u8 chid;
+ __u8 pad01[6];
+ __u32 pushbuf;
+ __u32 ilength;
+ __u64 ioffset;
+};
+
+struct kepler_channel_gpfifo_a_v0 {
+ __u8 version;
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR 0x01
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_VP 0x02
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_PPP 0x04
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_BSP 0x08
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_CE0 0x10
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_CE1 0x20
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_ENC 0x40
+ __u8 engine;
+ __u16 chid;
+ __u8 pad04[4];
+ __u32 pushbuf;
+ __u32 ilength;
+ __u64 ioffset;
+};
+
+/*******************************************************************************
+ * legacy display
+ ******************************************************************************/
+
+#define NV04_DISP_NTFY_VBLANK 0x00
+#define NV04_DISP_NTFY_CONN 0x01
+
+struct nv04_disp_mthd_v0 {
+ __u8 version;
+#define NV04_DISP_SCANOUTPOS 0x00
+ __u8 method;
+ __u8 head;
+ __u8 pad03[5];
+};
+
+struct nv04_disp_scanoutpos_v0 {
+ __u8 version;
+ __u8 pad01[7];
+ __s64 time[2];
+ __u16 vblanks;
+ __u16 vblanke;
+ __u16 vtotal;
+ __u16 vline;
+ __u16 hblanks;
+ __u16 hblanke;
+ __u16 htotal;
+ __u16 hline;
+};
+
+/*******************************************************************************
+ * display
+ ******************************************************************************/
+
+#define NV50_DISP_MTHD 0x00
+
+struct nv50_disp_mthd_v0 {
+ __u8 version;
+#define NV50_DISP_SCANOUTPOS 0x00
+ __u8 method;
+ __u8 head;
+ __u8 pad03[5];
+};
+
+struct nv50_disp_mthd_v1 {
+ __u8 version;
+#define NV50_DISP_MTHD_V1_DAC_PWR 0x10
+#define NV50_DISP_MTHD_V1_DAC_LOAD 0x11
+#define NV50_DISP_MTHD_V1_SOR_PWR 0x20
+#define NV50_DISP_MTHD_V1_SOR_HDA_ELD 0x21
+#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR 0x22
+#define NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT 0x23
+#define NV50_DISP_MTHD_V1_SOR_DP_PWR 0x24
+#define NV50_DISP_MTHD_V1_PIOR_PWR 0x30
+ __u8 method;
+ __u16 hasht;
+ __u16 hashm;
+ __u8 pad06[2];
+};
+
+struct nv50_disp_dac_pwr_v0 {
+ __u8 version;
+ __u8 state;
+ __u8 data;
+ __u8 vsync;
+ __u8 hsync;
+ __u8 pad05[3];
+};
+
+struct nv50_disp_dac_load_v0 {
+ __u8 version;
+ __u8 load;
+ __u8 pad02[2];
+ __u32 data;
+};
+
+struct nv50_disp_sor_pwr_v0 {
+ __u8 version;
+ __u8 state;
+ __u8 pad02[6];
+};
+
+struct nv50_disp_sor_hda_eld_v0 {
+ __u8 version;
+ __u8 pad01[7];
+ __u8 data[];
+};
+
+struct nv50_disp_sor_hdmi_pwr_v0 {
+ __u8 version;
+ __u8 state;
+ __u8 max_ac_packet;
+ __u8 rekey;
+ __u8 pad04[4];
+};
+
+struct nv50_disp_sor_lvds_script_v0 {
+ __u8 version;
+ __u8 pad01[1];
+ __u16 script;
+ __u8 pad04[4];
+};
+
+struct nv50_disp_sor_dp_pwr_v0 {
+ __u8 version;
+ __u8 state;
+ __u8 pad02[6];
+};
+
+struct nv50_disp_pior_pwr_v0 {
+ __u8 version;
+ __u8 state;
+ __u8 type;
+ __u8 pad03[5];
+};
+
+/* core */
+struct nv50_disp_core_channel_dma_v0 {
+ __u8 version;
+ __u8 pad01[3];
+ __u32 pushbuf;
+};
+
+#define NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT 0x00
+
+/* cursor immediate */
+struct nv50_disp_cursor_v0 {
+ __u8 version;
+ __u8 head;
+ __u8 pad02[6];
+};
+
+#define NV50_DISP_CURSOR_V0_NTFY_UEVENT 0x00
+
+/* base */
+struct nv50_disp_base_channel_dma_v0 {
+ __u8 version;
+ __u8 pad01[2];
+ __u8 head;
+ __u32 pushbuf;
+};
+
+#define NV50_DISP_BASE_CHANNEL_DMA_V0_NTFY_UEVENT 0x00
+
+/* overlay */
+struct nv50_disp_overlay_channel_dma_v0 {
+ __u8 version;
+ __u8 pad01[2];
+ __u8 head;
+ __u32 pushbuf;
+};
+
+#define NV50_DISP_OVERLAY_CHANNEL_DMA_V0_NTFY_UEVENT 0x00
+
+/* overlay immediate */
+struct nv50_disp_overlay_v0 {
+ __u8 version;
+ __u8 head;
+ __u8 pad02[6];
+};
+
+#define NV50_DISP_OVERLAY_V0_NTFY_UEVENT 0x00
+
+/*******************************************************************************
+ * fermi
+ ******************************************************************************/
+
+#define FERMI_A_ZBC_COLOR 0x00
+#define FERMI_A_ZBC_DEPTH 0x01
+
+struct fermi_a_zbc_color_v0 {
+ __u8 version;
+#define FERMI_A_ZBC_COLOR_V0_FMT_ZERO 0x01
+#define FERMI_A_ZBC_COLOR_V0_FMT_UNORM_ONE 0x02
+#define FERMI_A_ZBC_COLOR_V0_FMT_RF32_GF32_BF32_AF32 0x04
+#define FERMI_A_ZBC_COLOR_V0_FMT_R16_G16_B16_A16 0x08
+#define FERMI_A_ZBC_COLOR_V0_FMT_RN16_GN16_BN16_AN16 0x0c
+#define FERMI_A_ZBC_COLOR_V0_FMT_RS16_GS16_BS16_AS16 0x10
+#define FERMI_A_ZBC_COLOR_V0_FMT_RU16_GU16_BU16_AU16 0x14
+#define FERMI_A_ZBC_COLOR_V0_FMT_RF16_GF16_BF16_AF16 0x16
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8R8G8B8 0x18
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8RL8GL8BL8 0x1c
+#define FERMI_A_ZBC_COLOR_V0_FMT_A2B10G10R10 0x20
+#define FERMI_A_ZBC_COLOR_V0_FMT_AU2BU10GU10RU10 0x24
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8B8G8R8 0x28
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8BL8GL8RL8 0x2c
+#define FERMI_A_ZBC_COLOR_V0_FMT_AN8BN8GN8RN8 0x30
+#define FERMI_A_ZBC_COLOR_V0_FMT_AS8BS8GS8RS8 0x34
+#define FERMI_A_ZBC_COLOR_V0_FMT_AU8BU8GU8RU8 0x38
+#define FERMI_A_ZBC_COLOR_V0_FMT_A2R10G10B10 0x3c
+#define FERMI_A_ZBC_COLOR_V0_FMT_BF10GF11RF11 0x40
+ __u8 format;
+ __u8 index;
+ __u8 pad03[5];
+ __u32 ds[4];
+ __u32 l2[4];
+};
+
+struct fermi_a_zbc_depth_v0 {
+ __u8 version;
+#define FERMI_A_ZBC_DEPTH_V0_FMT_FP32 0x01
+ __u8 format;
+ __u8 index;
+ __u8 pad03[5];
+ __u32 ds;
+ __u32 l2;
+};
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvif/client.c b/drivers/gpu/drm/nouveau/nvif/client.c
new file mode 100644
index 000000000000..3c4df1fc26dc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/client.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2013 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "client.h"
+#include "driver.h"
+#include "ioctl.h"
+
+int
+nvif_client_ioctl(struct nvif_client *client, void *data, u32 size)
+{
+ return client->driver->ioctl(client->base.priv, client->super, data, size, NULL);
+}
+
+int
+nvif_client_suspend(struct nvif_client *client)
+{
+ return client->driver->suspend(client->base.priv);
+}
+
+int
+nvif_client_resume(struct nvif_client *client)
+{
+ return client->driver->resume(client->base.priv);
+}
+
+void
+nvif_client_fini(struct nvif_client *client)
+{
+ if (client->driver) {
+ client->driver->fini(client->base.priv);
+ client->driver = NULL;
+ client->base.parent = NULL;
+ nvif_object_fini(&client->base);
+ }
+}
+
+const struct nvif_driver *
+nvif_drivers[] = {
+#ifdef __KERNEL__
+ &nvif_driver_nvkm,
+#else
+ &nvif_driver_drm,
+ &nvif_driver_lib,
+#endif
+ NULL
+};
+
+int
+nvif_client_init(void (*dtor)(struct nvif_client *), const char *driver,
+ const char *name, u64 device, const char *cfg, const char *dbg,
+ struct nvif_client *client)
+{
+ int ret, i;
+
+ ret = nvif_object_init(NULL, (void*)dtor, 0, 0, NULL, 0, &client->base);
+ if (ret)
+ return ret;
+
+ client->base.parent = &client->base;
+ client->base.handle = ~0;
+ client->object = &client->base;
+ client->super = true;
+
+ for (i = 0, ret = -EINVAL; (client->driver = nvif_drivers[i]); i++) {
+ if (!driver || !strcmp(client->driver->name, driver)) {
+ ret = client->driver->init(name, device, cfg, dbg,
+ &client->base.priv);
+ if (!ret || driver)
+ break;
+ }
+ }
+
+ if (ret)
+ nvif_client_fini(client);
+ return ret;
+}
+
+static void
+nvif_client_del(struct nvif_client *client)
+{
+ nvif_client_fini(client);
+ kfree(client);
+}
+
+int
+nvif_client_new(const char *driver, const char *name, u64 device,
+ const char *cfg, const char *dbg,
+ struct nvif_client **pclient)
+{
+ struct nvif_client *client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (client) {
+ int ret = nvif_client_init(nvif_client_del, driver, name,
+ device, cfg, dbg, client);
+ if (ret) {
+ kfree(client);
+ client = NULL;
+ }
+ *pclient = client;
+ return ret;
+ }
+ return -ENOMEM;
+}
+
+void
+nvif_client_ref(struct nvif_client *client, struct nvif_client **pclient)
+{
+ nvif_object_ref(&client->base, (struct nvif_object **)pclient);
+}
diff --git a/drivers/gpu/drm/nouveau/nvif/client.h b/drivers/gpu/drm/nouveau/nvif/client.h
new file mode 100644
index 000000000000..28352f0882ec
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/client.h
@@ -0,0 +1,39 @@
+#ifndef __NVIF_CLIENT_H__
+#define __NVIF_CLIENT_H__
+
+#include "object.h"
+
+struct nvif_client {
+ struct nvif_object base;
+ struct nvif_object *object; /*XXX: hack for nvif_object() */
+ const struct nvif_driver *driver;
+ bool super;
+};
+
+static inline struct nvif_client *
+nvif_client(struct nvif_object *object)
+{
+ while (object && object->parent != object)
+ object = object->parent;
+ return (void *)object;
+}
+
+int nvif_client_init(void (*dtor)(struct nvif_client *), const char *,
+ const char *, u64, const char *, const char *,
+ struct nvif_client *);
+void nvif_client_fini(struct nvif_client *);
+int nvif_client_new(const char *, const char *, u64, const char *,
+ const char *, struct nvif_client **);
+void nvif_client_ref(struct nvif_client *, struct nvif_client **);
+int nvif_client_ioctl(struct nvif_client *, void *, u32);
+int nvif_client_suspend(struct nvif_client *);
+int nvif_client_resume(struct nvif_client *);
+
+/*XXX*/
+#include <core/client.h>
+#define nvkm_client(a) ({ \
+ struct nvif_client *_client = nvif_client(nvif_object(a)); \
+ nouveau_client(_client->base.priv); \
+})
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvif/device.c b/drivers/gpu/drm/nouveau/nvif/device.c
new file mode 100644
index 000000000000..f477579725e3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/device.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "device.h"
+
+void
+nvif_device_fini(struct nvif_device *device)
+{
+ nvif_object_fini(&device->base);
+}
+
+int
+nvif_device_init(struct nvif_object *parent, void (*dtor)(struct nvif_device *),
+ u32 handle, u32 oclass, void *data, u32 size,
+ struct nvif_device *device)
+{
+ int ret = nvif_object_init(parent, (void *)dtor, handle, oclass,
+ data, size, &device->base);
+ if (ret == 0) {
+ device->object = &device->base;
+ device->info.version = 0;
+ ret = nvif_object_mthd(&device->base, NV_DEVICE_V0_INFO,
+ &device->info, sizeof(device->info));
+ }
+ return ret;
+}
+
+static void
+nvif_device_del(struct nvif_device *device)
+{
+ nvif_device_fini(device);
+ kfree(device);
+}
+
+int
+nvif_device_new(struct nvif_object *parent, u32 handle, u32 oclass,
+ void *data, u32 size, struct nvif_device **pdevice)
+{
+ struct nvif_device *device = kzalloc(sizeof(*device), GFP_KERNEL);
+ if (device) {
+ int ret = nvif_device_init(parent, nvif_device_del, handle,
+ oclass, data, size, device);
+ if (ret) {
+ kfree(device);
+ device = NULL;
+ }
+ *pdevice = device;
+ return ret;
+ }
+ return -ENOMEM;
+}
+
+void
+nvif_device_ref(struct nvif_device *device, struct nvif_device **pdevice)
+{
+ nvif_object_ref(&device->base, (struct nvif_object **)pdevice);
+}
diff --git a/drivers/gpu/drm/nouveau/nvif/device.h b/drivers/gpu/drm/nouveau/nvif/device.h
new file mode 100644
index 000000000000..43180f9fe630
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/device.h
@@ -0,0 +1,62 @@
+#ifndef __NVIF_DEVICE_H__
+#define __NVIF_DEVICE_H__
+
+#include "object.h"
+#include "class.h"
+
+struct nvif_device {
+ struct nvif_object base;
+ struct nvif_object *object; /*XXX: hack for nvif_object() */
+ struct nv_device_info_v0 info;
+};
+
+static inline struct nvif_device *
+nvif_device(struct nvif_object *object)
+{
+ while (object && object->oclass != 0x0080 /*XXX: NV_DEVICE_CLASS*/ )
+ object = object->parent;
+ return (void *)object;
+}
+
+int nvif_device_init(struct nvif_object *, void (*dtor)(struct nvif_device *),
+ u32 handle, u32 oclass, void *, u32,
+ struct nvif_device *);
+void nvif_device_fini(struct nvif_device *);
+int nvif_device_new(struct nvif_object *, u32 handle, u32 oclass,
+ void *, u32, struct nvif_device **);
+void nvif_device_ref(struct nvif_device *, struct nvif_device **);
+
+/*XXX*/
+#include <subdev/bios.h>
+#include <subdev/fb.h>
+#include <subdev/vm.h>
+#include <subdev/bar.h>
+#include <subdev/gpio.h>
+#include <subdev/clock.h>
+#include <subdev/i2c.h>
+#include <subdev/timer.h>
+#include <subdev/therm.h>
+
+#define nvkm_device(a) nv_device(nvkm_object((a)))
+#define nvkm_bios(a) nouveau_bios(nvkm_device(a))
+#define nvkm_fb(a) nouveau_fb(nvkm_device(a))
+#define nvkm_vmmgr(a) nouveau_vmmgr(nvkm_device(a))
+#define nvkm_bar(a) nouveau_bar(nvkm_device(a))
+#define nvkm_gpio(a) nouveau_gpio(nvkm_device(a))
+#define nvkm_clock(a) nouveau_clock(nvkm_device(a))
+#define nvkm_i2c(a) nouveau_i2c(nvkm_device(a))
+#define nvkm_timer(a) nouveau_timer(nvkm_device(a))
+#define nvkm_wait(a,b,c,d) nv_wait(nvkm_timer(a), (b), (c), (d))
+#define nvkm_wait_cb(a,b,c) nv_wait_cb(nvkm_timer(a), (b), (c))
+#define nvkm_therm(a) nouveau_therm(nvkm_device(a))
+
+#include <engine/device.h>
+#include <engine/fifo.h>
+#include <engine/graph.h>
+#include <engine/software.h>
+
+#define nvkm_fifo(a) nouveau_fifo(nvkm_device(a))
+#define nvkm_fifo_chan(a) ((struct nouveau_fifo_chan *)nvkm_object(a))
+#define nvkm_gr(a) ((struct nouveau_graph *)nouveau_engine(nvkm_object(a), NVDEV_ENGINE_GR))
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvif/driver.h b/drivers/gpu/drm/nouveau/nvif/driver.h
new file mode 100644
index 000000000000..ac4bdb3ea506
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/driver.h
@@ -0,0 +1,21 @@
+#ifndef __NVIF_DRIVER_H__
+#define __NVIF_DRIVER_H__
+
+struct nvif_driver {
+ const char *name;
+ int (*init)(const char *name, u64 device, const char *cfg,
+ const char *dbg, void **priv);
+ void (*fini)(void *priv);
+ int (*suspend)(void *priv);
+ int (*resume)(void *priv);
+ int (*ioctl)(void *priv, bool super, void *data, u32 size, void **hack);
+ void __iomem *(*map)(void *priv, u64 handle, u32 size);
+ void (*unmap)(void *priv, void __iomem *ptr, u32 size);
+ bool keep;
+};
+
+extern const struct nvif_driver nvif_driver_nvkm;
+extern const struct nvif_driver nvif_driver_drm;
+extern const struct nvif_driver nvif_driver_lib;
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvif/event.h b/drivers/gpu/drm/nouveau/nvif/event.h
new file mode 100644
index 000000000000..21764499b4be
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/event.h
@@ -0,0 +1,62 @@
+#ifndef __NVIF_EVENT_H__
+#define __NVIF_EVENT_H__
+
+struct nvif_notify_req_v0 {
+ __u8 version;
+ __u8 reply;
+ __u8 pad02[5];
+#define NVIF_NOTIFY_V0_ROUTE_NVIF 0x00
+ __u8 route;
+ __u64 token; /* must be unique */
+ __u8 data[]; /* request data (below) */
+};
+
+struct nvif_notify_rep_v0 {
+ __u8 version;
+ __u8 pad01[6];
+ __u8 route;
+ __u64 token;
+ __u8 data[]; /* reply data (below) */
+};
+
+struct nvif_notify_head_req_v0 {
+ /* nvif_notify_req ... */
+ __u8 version;
+ __u8 head;
+ __u8 pad02[6];
+};
+
+struct nvif_notify_head_rep_v0 {
+ /* nvif_notify_rep ... */
+ __u8 version;
+ __u8 pad01[7];
+};
+
+struct nvif_notify_conn_req_v0 {
+ /* nvif_notify_req ... */
+ __u8 version;
+#define NVIF_NOTIFY_CONN_V0_PLUG 0x01
+#define NVIF_NOTIFY_CONN_V0_UNPLUG 0x02
+#define NVIF_NOTIFY_CONN_V0_IRQ 0x04
+#define NVIF_NOTIFY_CONN_V0_ANY 0x07
+ __u8 mask;
+ __u8 conn;
+ __u8 pad03[5];
+};
+
+struct nvif_notify_conn_rep_v0 {
+ /* nvif_notify_rep ... */
+ __u8 version;
+ __u8 mask;
+ __u8 pad02[6];
+};
+
+struct nvif_notify_uevent_req {
+ /* nvif_notify_req ... */
+};
+
+struct nvif_notify_uevent_rep {
+ /* nvif_notify_rep ... */
+};
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvif/ioctl.h b/drivers/gpu/drm/nouveau/nvif/ioctl.h
new file mode 100644
index 000000000000..4cd8e323b23d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/ioctl.h
@@ -0,0 +1,128 @@
+#ifndef __NVIF_IOCTL_H__
+#define __NVIF_IOCTL_H__
+
+struct nvif_ioctl_v0 {
+ __u8 version;
+#define NVIF_IOCTL_V0_OWNER_NVIF 0x00
+#define NVIF_IOCTL_V0_OWNER_ANY 0xff
+ __u8 owner;
+#define NVIF_IOCTL_V0_NOP 0x00
+#define NVIF_IOCTL_V0_SCLASS 0x01
+#define NVIF_IOCTL_V0_NEW 0x02
+#define NVIF_IOCTL_V0_DEL 0x03
+#define NVIF_IOCTL_V0_MTHD 0x04
+#define NVIF_IOCTL_V0_RD 0x05
+#define NVIF_IOCTL_V0_WR 0x06
+#define NVIF_IOCTL_V0_MAP 0x07
+#define NVIF_IOCTL_V0_UNMAP 0x08
+#define NVIF_IOCTL_V0_NTFY_NEW 0x09
+#define NVIF_IOCTL_V0_NTFY_DEL 0x0a
+#define NVIF_IOCTL_V0_NTFY_GET 0x0b
+#define NVIF_IOCTL_V0_NTFY_PUT 0x0c
+ __u8 type;
+ __u8 path_nr;
+#define NVIF_IOCTL_V0_ROUTE_NVIF 0x00
+#define NVIF_IOCTL_V0_ROUTE_HIDDEN 0xff
+ __u8 pad04[3];
+ __u8 route;
+ __u64 token;
+ __u32 path[8]; /* in reverse */
+ __u8 data[]; /* ioctl data (below) */
+};
+
+struct nvif_ioctl_nop {
+};
+
+struct nvif_ioctl_sclass_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 count;
+ __u8 pad02[6];
+ __u32 oclass[];
+};
+
+struct nvif_ioctl_new_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 pad01[6];
+ __u8 route;
+ __u64 token;
+ __u32 handle;
+/* these class numbers are made up by us, and not nvidia-assigned */
+#define NVIF_IOCTL_NEW_V0_PERFCTR 0x0000ffff
+#define NVIF_IOCTL_NEW_V0_CONTROL 0x0000fffe
+ __u32 oclass;
+ __u8 data[]; /* class data (class.h) */
+};
+
+struct nvif_ioctl_del {
+};
+
+struct nvif_ioctl_rd_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 size;
+ __u8 pad02[2];
+ __u32 data;
+ __u64 addr;
+};
+
+struct nvif_ioctl_wr_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 size;
+ __u8 pad02[2];
+ __u32 data;
+ __u64 addr;
+};
+
+struct nvif_ioctl_map_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 pad01[3];
+ __u32 length;
+ __u64 handle;
+};
+
+struct nvif_ioctl_unmap {
+};
+
+struct nvif_ioctl_ntfy_new_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 event;
+ __u8 index;
+ __u8 pad03[5];
+ __u8 data[]; /* event request data (event.h) */
+};
+
+struct nvif_ioctl_ntfy_del_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 index;
+ __u8 pad02[6];
+};
+
+struct nvif_ioctl_ntfy_get_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 index;
+ __u8 pad02[6];
+};
+
+struct nvif_ioctl_ntfy_put_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 index;
+ __u8 pad02[6];
+};
+
+struct nvif_ioctl_mthd_v0 {
+ /* nvif_ioctl ... */
+ __u8 version;
+ __u8 method;
+ __u8 pad02[6];
+ __u8 data[]; /* method data (class.h) */
+};
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvif/list.h b/drivers/gpu/drm/nouveau/nvif/list.h
new file mode 100644
index 000000000000..8af5d144ecb0
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/list.h
@@ -0,0 +1,353 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2010 Francisco Jerez <currojerez@riseup.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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.
+ *
+ */
+
+/* Modified by Ben Skeggs <bskeggs@redhat.com> to match kernel list APIs */
+
+#ifndef _XORG_LIST_H_
+#define _XORG_LIST_H_
+
+/**
+ * @file Classic doubly-link circular list implementation.
+ * For real usage examples of the linked list, see the file test/list.c
+ *
+ * Example:
+ * We need to keep a list of struct foo in the parent struct bar, i.e. what
+ * we want is something like this.
+ *
+ * struct bar {
+ * ...
+ * struct foo *list_of_foos; -----> struct foo {}, struct foo {}, struct foo{}
+ * ...
+ * }
+ *
+ * We need one list head in bar and a list element in all list_of_foos (both are of
+ * data type 'struct list_head').
+ *
+ * struct bar {
+ * ...
+ * struct list_head list_of_foos;
+ * ...
+ * }
+ *
+ * struct foo {
+ * ...
+ * struct list_head entry;
+ * ...
+ * }
+ *
+ * Now we initialize the list head:
+ *
+ * struct bar bar;
+ * ...
+ * INIT_LIST_HEAD(&bar.list_of_foos);
+ *
+ * Then we create the first element and add it to this list:
+ *
+ * struct foo *foo = malloc(...);
+ * ....
+ * list_add(&foo->entry, &bar.list_of_foos);
+ *
+ * Repeat the above for each element you want to add to the list. Deleting
+ * works with the element itself.
+ * list_del(&foo->entry);
+ * free(foo);
+ *
+ * Note: calling list_del(&bar.list_of_foos) will set bar.list_of_foos to an empty
+ * list again.
+ *
+ * Looping through the list requires a 'struct foo' as iterator and the
+ * name of the field the subnodes use.
+ *
+ * struct foo *iterator;
+ * list_for_each_entry(iterator, &bar.list_of_foos, entry) {
+ * if (iterator->something == ...)
+ * ...
+ * }
+ *
+ * Note: You must not call list_del() on the iterator if you continue the
+ * loop. You need to run the safe for-each loop instead:
+ *
+ * struct foo *iterator, *next;
+ * list_for_each_entry_safe(iterator, next, &bar.list_of_foos, entry) {
+ * if (...)
+ * list_del(&iterator->entry);
+ * }
+ *
+ */
+
+/**
+ * The linkage struct for list nodes. This struct must be part of your
+ * to-be-linked struct. struct list_head is required for both the head of the
+ * list and for each list node.
+ *
+ * Position and name of the struct list_head field is irrelevant.
+ * There are no requirements that elements of a list are of the same type.
+ * There are no requirements for a list head, any struct list_head can be a list
+ * head.
+ */
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+/**
+ * Initialize the list as an empty list.
+ *
+ * Example:
+ * INIT_LIST_HEAD(&bar->list_of_foos);
+ *
+ * @param The list to initialized.
+ */
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void
+INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list->prev = list;
+}
+
+static inline void
+__list_add(struct list_head *entry,
+ struct list_head *prev, struct list_head *next)
+{
+ next->prev = entry;
+ entry->next = next;
+ entry->prev = prev;
+ prev->next = entry;
+}
+
+/**
+ * Insert a new element after the given list head. The new element does not
+ * need to be initialised as empty list.
+ * The list changes from:
+ * head → some element → ...
+ * to
+ * head → new element → older element → ...
+ *
+ * Example:
+ * struct foo *newfoo = malloc(...);
+ * list_add(&newfoo->entry, &bar->list_of_foos);
+ *
+ * @param entry The new element to prepend to the list.
+ * @param head The existing list.
+ */
+static inline void
+list_add(struct list_head *entry, struct list_head *head)
+{
+ __list_add(entry, head, head->next);
+}
+
+/**
+ * Append a new element to the end of the list given with this list head.
+ *
+ * The list changes from:
+ * head → some element → ... → lastelement
+ * to
+ * head → some element → ... → lastelement → new element
+ *
+ * Example:
+ * struct foo *newfoo = malloc(...);
+ * list_add_tail(&newfoo->entry, &bar->list_of_foos);
+ *
+ * @param entry The new element to prepend to the list.
+ * @param head The existing list.
+ */
+static inline void
+list_add_tail(struct list_head *entry, struct list_head *head)
+{
+ __list_add(entry, head->prev, head);
+}
+
+static inline void
+__list_del(struct list_head *prev, struct list_head *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * Remove the element from the list it is in. Using this function will reset
+ * the pointers to/from this element so it is removed from the list. It does
+ * NOT free the element itself or manipulate it otherwise.
+ *
+ * Using list_del on a pure list head (like in the example at the top of
+ * this file) will NOT remove the first element from
+ * the list but rather reset the list as empty list.
+ *
+ * Example:
+ * list_del(&foo->entry);
+ *
+ * @param entry The element to remove.
+ */
+static inline void
+list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+static inline void
+list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * Check if the list is empty.
+ *
+ * Example:
+ * list_empty(&bar->list_of_foos);
+ *
+ * @return True if the list contains one or more elements or False otherwise.
+ */
+static inline bool
+list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * Returns a pointer to the container of this list element.
+ *
+ * Example:
+ * struct foo* f;
+ * f = container_of(&foo->entry, struct foo, entry);
+ * assert(f == foo);
+ *
+ * @param ptr Pointer to the struct list_head.
+ * @param type Data type of the list element.
+ * @param member Member name of the struct list_head field in the list element.
+ * @return A pointer to the data struct containing the list head.
+ */
+#ifndef container_of
+#define container_of(ptr, type, member) \
+ (type *)((char *)(ptr) - (char *) &((type *)0)->member)
+#endif
+
+/**
+ * Alias of container_of
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * Retrieve the first list entry for the given list pointer.
+ *
+ * Example:
+ * struct foo *first;
+ * first = list_first_entry(&bar->list_of_foos, struct foo, list_of_foos);
+ *
+ * @param ptr The list head
+ * @param type Data type of the list element to retrieve
+ * @param member Member name of the struct list_head field in the list element.
+ * @return A pointer to the first list element.
+ */
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/**
+ * Retrieve the last list entry for the given listpointer.
+ *
+ * Example:
+ * struct foo *first;
+ * first = list_last_entry(&bar->list_of_foos, struct foo, list_of_foos);
+ *
+ * @param ptr The list head
+ * @param type Data type of the list element to retrieve
+ * @param member Member name of the struct list_head field in the list element.
+ * @return A pointer to the last list element.
+ */
+#define list_last_entry(ptr, type, member) \
+ list_entry((ptr)->prev, type, member)
+
+#define __container_of(ptr, sample, member) \
+ (void *)container_of((ptr), typeof(*(sample)), member)
+
+/**
+ * Loop through the list given by head and set pos to struct in the list.
+ *
+ * Example:
+ * struct foo *iterator;
+ * list_for_each_entry(iterator, &bar->list_of_foos, entry) {
+ * [modify iterator]
+ * }
+ *
+ * This macro is not safe for node deletion. Use list_for_each_entry_safe
+ * instead.
+ *
+ * @param pos Iterator variable of the type of the list elements.
+ * @param head List head
+ * @param member Member name of the struct list_head in the list elements.
+ *
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = __container_of((head)->next, pos, member); \
+ &pos->member != (head); \
+ pos = __container_of(pos->member.next, pos, member))
+
+/**
+ * Loop through the list, keeping a backup pointer to the element. This
+ * macro allows for the deletion of a list element while looping through the
+ * list.
+ *
+ * See list_for_each_entry for more details.
+ */
+#define list_for_each_entry_safe(pos, tmp, head, member) \
+ for (pos = __container_of((head)->next, pos, member), \
+ tmp = __container_of(pos->member.next, pos, member); \
+ &pos->member != (head); \
+ pos = tmp, tmp = __container_of(pos->member.next, tmp, member))
+
+
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = __container_of((head)->prev, pos, member); \
+ &pos->member != (head); \
+ pos = __container_of(pos->member.prev, pos, member))
+
+#define list_for_each_entry_continue(pos, head, member) \
+ for (pos = __container_of(pos->member.next, pos, member); \
+ &pos->member != (head); \
+ pos = __container_of(pos->member.next, pos, member))
+
+#define list_for_each_entry_continue_reverse(pos, head, member) \
+ for (pos = __container_of(pos->member.prev, pos, member); \
+ &pos->member != (head); \
+ pos = __container_of(pos->member.prev, pos, member))
+
+#define list_for_each_entry_from(pos, head, member) \
+ for (; \
+ &pos->member != (head); \
+ pos = __container_of(pos->member.next, pos, member))
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvif/notify.c b/drivers/gpu/drm/nouveau/nvif/notify.c
new file mode 100644
index 000000000000..0898c3155292
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/notify.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include <nvif/client.h>
+#include <nvif/driver.h>
+#include <nvif/notify.h>
+#include <nvif/object.h>
+#include <nvif/ioctl.h>
+#include <nvif/event.h>
+
+static inline int
+nvif_notify_put_(struct nvif_notify *notify)
+{
+ struct nvif_object *object = notify->object;
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_ntfy_put_v0 ntfy;
+ } args = {
+ .ioctl.type = NVIF_IOCTL_V0_NTFY_PUT,
+ .ntfy.index = notify->index,
+ };
+
+ if (atomic_inc_return(&notify->putcnt) != 1)
+ return 0;
+
+ return nvif_object_ioctl(object, &args, sizeof(args), NULL);
+}
+
+int
+nvif_notify_put(struct nvif_notify *notify)
+{
+ if (likely(notify->object) &&
+ test_and_clear_bit(NVIF_NOTIFY_USER, &notify->flags)) {
+ int ret = nvif_notify_put_(notify);
+ if (test_bit(NVIF_NOTIFY_WORK, &notify->flags))
+ flush_work(&notify->work);
+ return ret;
+ }
+ return 0;
+}
+
+static inline int
+nvif_notify_get_(struct nvif_notify *notify)
+{
+ struct nvif_object *object = notify->object;
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_ntfy_get_v0 ntfy;
+ } args = {
+ .ioctl.type = NVIF_IOCTL_V0_NTFY_GET,
+ .ntfy.index = notify->index,
+ };
+
+ if (atomic_dec_return(&notify->putcnt) != 0)
+ return 0;
+
+ return nvif_object_ioctl(object, &args, sizeof(args), NULL);
+}
+
+int
+nvif_notify_get(struct nvif_notify *notify)
+{
+ if (likely(notify->object) &&
+ !test_and_set_bit(NVIF_NOTIFY_USER, &notify->flags))
+ return nvif_notify_get_(notify);
+ return 0;
+}
+
+static inline int
+nvif_notify_func(struct nvif_notify *notify, bool keep)
+{
+ int ret = notify->func(notify);
+ if (ret == NVIF_NOTIFY_KEEP ||
+ !test_and_clear_bit(NVKM_NOTIFY_USER, &notify->flags)) {
+ if (!keep)
+ atomic_dec(&notify->putcnt);
+ else
+ nvif_notify_get_(notify);
+ }
+ return ret;
+}
+
+static void
+nvif_notify_work(struct work_struct *work)
+{
+ struct nvif_notify *notify = container_of(work, typeof(*notify), work);
+ nvif_notify_func(notify, true);
+}
+
+int
+nvif_notify(const void *header, u32 length, const void *data, u32 size)
+{
+ struct nvif_notify *notify = NULL;
+ const union {
+ struct nvif_notify_rep_v0 v0;
+ } *args = header;
+ int ret = NVIF_NOTIFY_DROP;
+
+ if (length == sizeof(args->v0) && args->v0.version == 0) {
+ if (WARN_ON(args->v0.route))
+ return NVIF_NOTIFY_DROP;
+ notify = (void *)(unsigned long)args->v0.token;
+ }
+
+ if (!WARN_ON(notify == NULL)) {
+ struct nvif_client *client = nvif_client(notify->object);
+ if (!WARN_ON(notify->size != size)) {
+ atomic_inc(&notify->putcnt);
+ if (test_bit(NVIF_NOTIFY_WORK, &notify->flags)) {
+ memcpy((void *)notify->data, data, size);
+ schedule_work(&notify->work);
+ return NVIF_NOTIFY_DROP;
+ }
+ notify->data = data;
+ ret = nvif_notify_func(notify, client->driver->keep);
+ notify->data = NULL;
+ }
+ }
+
+ return ret;
+}
+
+int
+nvif_notify_fini(struct nvif_notify *notify)
+{
+ struct nvif_object *object = notify->object;
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_ntfy_del_v0 ntfy;
+ } args = {
+ .ioctl.type = NVIF_IOCTL_V0_NTFY_DEL,
+ .ntfy.index = notify->index,
+ };
+ int ret = nvif_notify_put(notify);
+ if (ret >= 0 && object) {
+ ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
+ if (ret == 0) {
+ nvif_object_ref(NULL, &notify->object);
+ kfree((void *)notify->data);
+ }
+ }
+ return ret;
+}
+
+int
+nvif_notify_init(struct nvif_object *object, void (*dtor)(struct nvif_notify *),
+ int (*func)(struct nvif_notify *), bool work, u8 event,
+ void *data, u32 size, u32 reply, struct nvif_notify *notify)
+{
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_ntfy_new_v0 ntfy;
+ struct nvif_notify_req_v0 req;
+ } *args;
+ int ret = -ENOMEM;
+
+ notify->object = NULL;
+ nvif_object_ref(object, &notify->object);
+ notify->flags = 0;
+ atomic_set(&notify->putcnt, 1);
+ notify->dtor = dtor;
+ notify->func = func;
+ notify->data = NULL;
+ notify->size = reply;
+ if (work) {
+ INIT_WORK(&notify->work, nvif_notify_work);
+ set_bit(NVIF_NOTIFY_WORK, &notify->flags);
+ notify->data = kmalloc(notify->size, GFP_KERNEL);
+ if (!notify->data)
+ goto done;
+ }
+
+ if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL)))
+ goto done;
+ args->ioctl.version = 0;
+ args->ioctl.type = NVIF_IOCTL_V0_NTFY_NEW;
+ args->ntfy.version = 0;
+ args->ntfy.event = event;
+ args->req.version = 0;
+ args->req.reply = notify->size;
+ args->req.route = 0;
+ args->req.token = (unsigned long)(void *)notify;
+
+ memcpy(args->req.data, data, size);
+ ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL);
+ notify->index = args->ntfy.index;
+ kfree(args);
+done:
+ if (ret)
+ nvif_notify_fini(notify);
+ return ret;
+}
+
+static void
+nvif_notify_del(struct nvif_notify *notify)
+{
+ nvif_notify_fini(notify);
+ kfree(notify);
+}
+
+void
+nvif_notify_ref(struct nvif_notify *notify, struct nvif_notify **pnotify)
+{
+ BUG_ON(notify != NULL);
+ if (*pnotify)
+ (*pnotify)->dtor(*pnotify);
+ *pnotify = notify;
+}
+
+int
+nvif_notify_new(struct nvif_object *object, int (*func)(struct nvif_notify *),
+ bool work, u8 type, void *data, u32 size, u32 reply,
+ struct nvif_notify **pnotify)
+{
+ struct nvif_notify *notify = kzalloc(sizeof(*notify), GFP_KERNEL);
+ if (notify) {
+ int ret = nvif_notify_init(object, nvif_notify_del, func, work,
+ type, data, size, reply, notify);
+ if (ret) {
+ kfree(notify);
+ notify = NULL;
+ }
+ *pnotify = notify;
+ return ret;
+ }
+ return -ENOMEM;
+}
diff --git a/drivers/gpu/drm/nouveau/nvif/notify.h b/drivers/gpu/drm/nouveau/nvif/notify.h
new file mode 100644
index 000000000000..9ebfa3b45e76
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/notify.h
@@ -0,0 +1,39 @@
+#ifndef __NVIF_NOTIFY_H__
+#define __NVIF_NOTIFY_H__
+
+struct nvif_notify {
+ struct nvif_object *object;
+ int index;
+
+#define NVIF_NOTIFY_USER 0
+#define NVIF_NOTIFY_WORK 1
+ unsigned long flags;
+ atomic_t putcnt;
+ void (*dtor)(struct nvif_notify *);
+#define NVIF_NOTIFY_DROP 0
+#define NVIF_NOTIFY_KEEP 1
+ int (*func)(struct nvif_notify *);
+
+ /* this is const for a *very* good reason - the data might be on the
+ * stack from an irq handler. if you're not nvif/notify.c then you
+ * should probably think twice before casting it away...
+ */
+ const void *data;
+ u32 size;
+ struct work_struct work;
+};
+
+int nvif_notify_init(struct nvif_object *, void (*dtor)(struct nvif_notify *),
+ int (*func)(struct nvif_notify *), bool work, u8 type,
+ void *data, u32 size, u32 reply, struct nvif_notify *);
+int nvif_notify_fini(struct nvif_notify *);
+int nvif_notify_get(struct nvif_notify *);
+int nvif_notify_put(struct nvif_notify *);
+int nvif_notify(const void *, u32, const void *, u32);
+
+int nvif_notify_new(struct nvif_object *, int (*func)(struct nvif_notify *),
+ bool work, u8 type, void *data, u32 size, u32 reply,
+ struct nvif_notify **);
+void nvif_notify_ref(struct nvif_notify *, struct nvif_notify **);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvif/object.c b/drivers/gpu/drm/nouveau/nvif/object.c
new file mode 100644
index 000000000000..dd85b56f6aa5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/object.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2014 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include "object.h"
+#include "client.h"
+#include "driver.h"
+#include "ioctl.h"
+
+int
+nvif_object_ioctl(struct nvif_object *object, void *data, u32 size, void **hack)
+{
+ struct nvif_client *client = nvif_client(object);
+ union {
+ struct nvif_ioctl_v0 v0;
+ } *args = data;
+
+ if (size >= sizeof(*args) && args->v0.version == 0) {
+ args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY;
+ args->v0.path_nr = 0;
+ while (args->v0.path_nr < ARRAY_SIZE(args->v0.path)) {
+ args->v0.path[args->v0.path_nr++] = object->handle;
+ if (object->parent == object)
+ break;
+ object = object->parent;
+ }
+ } else
+ return -ENOSYS;
+
+ return client->driver->ioctl(client->base.priv, client->super, data, size, hack);
+}
+
+int
+nvif_object_sclass(struct nvif_object *object, u32 *oclass, int count)
+{
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_sclass_v0 sclass;
+ } *args;
+ u32 size = count * sizeof(args->sclass.oclass[0]);
+ int ret;
+
+ if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL)))
+ return -ENOMEM;
+ args->ioctl.version = 0;
+ args->ioctl.type = NVIF_IOCTL_V0_SCLASS;
+ args->sclass.version = 0;
+ args->sclass.count = count;
+
+ memcpy(args->sclass.oclass, oclass, size);
+ ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL);
+ ret = ret ? ret : args->sclass.count;
+ memcpy(oclass, args->sclass.oclass, size);
+ kfree(args);
+ return ret;
+}
+
+u32
+nvif_object_rd(struct nvif_object *object, int size, u64 addr)
+{
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_rd_v0 rd;
+ } args = {
+ .ioctl.type = NVIF_IOCTL_V0_RD,
+ .rd.size = size,
+ .rd.addr = addr,
+ };
+ int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
+ if (ret) {
+ /*XXX: warn? */
+ return 0;
+ }
+ return args.rd.data;
+}
+
+void
+nvif_object_wr(struct nvif_object *object, int size, u64 addr, u32 data)
+{
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_wr_v0 wr;
+ } args = {
+ .ioctl.type = NVIF_IOCTL_V0_WR,
+ .wr.size = size,
+ .wr.addr = addr,
+ .wr.data = data,
+ };
+ int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
+ if (ret) {
+ /*XXX: warn? */
+ }
+}
+
+int
+nvif_object_mthd(struct nvif_object *object, u32 mthd, void *data, u32 size)
+{
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_mthd_v0 mthd;
+ } *args;
+ u8 stack[128];
+ int ret;
+
+ if (sizeof(*args) + size > sizeof(stack)) {
+ if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL)))
+ return -ENOMEM;
+ } else {
+ args = (void *)stack;
+ }
+ args->ioctl.version = 0;
+ args->ioctl.type = NVIF_IOCTL_V0_MTHD;
+ args->mthd.version = 0;
+ args->mthd.method = mthd;
+
+ memcpy(args->mthd.data, data, size);
+ ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL);
+ memcpy(data, args->mthd.data, size);
+ if (args != (void *)stack)
+ kfree(args);
+ return ret;
+}
+
+void
+nvif_object_unmap(struct nvif_object *object)
+{
+ if (object->map.size) {
+ struct nvif_client *client = nvif_client(object);
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_unmap unmap;
+ } args = {
+ .ioctl.type = NVIF_IOCTL_V0_UNMAP,
+ };
+
+ if (object->map.ptr) {
+ client->driver->unmap(client, object->map.ptr,
+ object->map.size);
+ object->map.ptr = NULL;
+ }
+
+ nvif_object_ioctl(object, &args, sizeof(args), NULL);
+ object->map.size = 0;
+ }
+}
+
+int
+nvif_object_map(struct nvif_object *object)
+{
+ struct nvif_client *client = nvif_client(object);
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_map_v0 map;
+ } args = {
+ .ioctl.type = NVIF_IOCTL_V0_MAP,
+ };
+ int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
+ if (ret == 0) {
+ object->map.size = args.map.length;
+ object->map.ptr = client->driver->map(client, args.map.handle,
+ object->map.size);
+ if (ret = -ENOMEM, object->map.ptr)
+ return 0;
+ nvif_object_unmap(object);
+ }
+ return ret;
+}
+
+struct ctor {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_new_v0 new;
+};
+
+void
+nvif_object_fini(struct nvif_object *object)
+{
+ struct ctor *ctor = container_of(object->data, typeof(*ctor), new.data);
+ if (object->parent) {
+ struct {
+ struct nvif_ioctl_v0 ioctl;
+ struct nvif_ioctl_del del;
+ } args = {
+ .ioctl.type = NVIF_IOCTL_V0_DEL,
+ };
+
+ nvif_object_unmap(object);
+ nvif_object_ioctl(object, &args, sizeof(args), NULL);
+ if (object->data) {
+ object->size = 0;
+ object->data = NULL;
+ kfree(ctor);
+ }
+ nvif_object_ref(NULL, &object->parent);
+ }
+}
+
+int
+nvif_object_init(struct nvif_object *parent, void (*dtor)(struct nvif_object *),
+ u32 handle, u32 oclass, void *data, u32 size,
+ struct nvif_object *object)
+{
+ struct ctor *ctor;
+ int ret = 0;
+
+ object->parent = NULL;
+ object->object = object;
+ nvif_object_ref(parent, &object->parent);
+ kref_init(&object->refcount);
+ object->handle = handle;
+ object->oclass = oclass;
+ object->data = NULL;
+ object->size = 0;
+ object->dtor = dtor;
+ object->map.ptr = NULL;
+ object->map.size = 0;
+
+ if (object->parent) {
+ if (!(ctor = kmalloc(sizeof(*ctor) + size, GFP_KERNEL))) {
+ nvif_object_fini(object);
+ return -ENOMEM;
+ }
+ object->data = ctor->new.data;
+ object->size = size;
+ memcpy(object->data, data, size);
+
+ ctor->ioctl.version = 0;
+ ctor->ioctl.type = NVIF_IOCTL_V0_NEW;
+ ctor->new.version = 0;
+ ctor->new.route = NVIF_IOCTL_V0_ROUTE_NVIF;
+ ctor->new.token = (unsigned long)(void *)object;
+ ctor->new.handle = handle;
+ ctor->new.oclass = oclass;
+
+ ret = nvif_object_ioctl(parent, ctor, sizeof(*ctor) +
+ object->size, &object->priv);
+ }
+
+ if (ret)
+ nvif_object_fini(object);
+ return ret;
+}
+
+static void
+nvif_object_del(struct nvif_object *object)
+{
+ nvif_object_fini(object);
+ kfree(object);
+}
+
+int
+nvif_object_new(struct nvif_object *parent, u32 handle, u32 oclass,
+ void *data, u32 size, struct nvif_object **pobject)
+{
+ struct nvif_object *object = kzalloc(sizeof(*object), GFP_KERNEL);
+ if (object) {
+ int ret = nvif_object_init(parent, nvif_object_del, handle,
+ oclass, data, size, object);
+ if (ret) {
+ kfree(object);
+ object = NULL;
+ }
+ *pobject = object;
+ return ret;
+ }
+ return -ENOMEM;
+}
+
+static void
+nvif_object_put(struct kref *kref)
+{
+ struct nvif_object *object =
+ container_of(kref, typeof(*object), refcount);
+ object->dtor(object);
+}
+
+void
+nvif_object_ref(struct nvif_object *object, struct nvif_object **pobject)
+{
+ if (object)
+ kref_get(&object->refcount);
+ if (*pobject)
+ kref_put(&(*pobject)->refcount, nvif_object_put);
+ *pobject = object;
+}
diff --git a/drivers/gpu/drm/nouveau/nvif/object.h b/drivers/gpu/drm/nouveau/nvif/object.h
new file mode 100644
index 000000000000..fe519179b76c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/object.h
@@ -0,0 +1,75 @@
+#ifndef __NVIF_OBJECT_H__
+#define __NVIF_OBJECT_H__
+
+#include <nvif/os.h>
+
+struct nvif_object {
+ struct nvif_object *parent;
+ struct nvif_object *object; /*XXX: hack for nvif_object() */
+ struct kref refcount;
+ u32 handle;
+ u32 oclass;
+ void *data;
+ u32 size;
+ void *priv; /*XXX: hack */
+ void (*dtor)(struct nvif_object *);
+ struct {
+ void __iomem *ptr;
+ u32 size;
+ } map;
+};
+
+int nvif_object_init(struct nvif_object *, void (*dtor)(struct nvif_object *),
+ u32 handle, u32 oclass, void *, u32,
+ struct nvif_object *);
+void nvif_object_fini(struct nvif_object *);
+int nvif_object_new(struct nvif_object *, u32 handle, u32 oclass,
+ void *, u32, struct nvif_object **);
+void nvif_object_ref(struct nvif_object *, struct nvif_object **);
+int nvif_object_ioctl(struct nvif_object *, void *, u32, void **);
+int nvif_object_sclass(struct nvif_object *, u32 *, int);
+u32 nvif_object_rd(struct nvif_object *, int, u64);
+void nvif_object_wr(struct nvif_object *, int, u64, u32);
+int nvif_object_mthd(struct nvif_object *, u32, void *, u32);
+int nvif_object_map(struct nvif_object *);
+void nvif_object_unmap(struct nvif_object *);
+
+#define nvif_object(a) (a)->object
+
+#define ioread8_native ioread8
+#define iowrite8_native iowrite8
+#define nvif_rd(a,b,c) ({ \
+ struct nvif_object *_object = nvif_object(a); \
+ u32 _data; \
+ if (likely(_object->map.ptr)) \
+ _data = ioread##b##_native((u8 __iomem *)_object->map.ptr + (c)); \
+ else \
+ _data = nvif_object_rd(_object, (b) / 8, (c)); \
+ _data; \
+})
+#define nvif_wr(a,b,c,d) ({ \
+ struct nvif_object *_object = nvif_object(a); \
+ if (likely(_object->map.ptr)) \
+ iowrite##b##_native((d), (u8 __iomem *)_object->map.ptr + (c)); \
+ else \
+ nvif_object_wr(_object, (b) / 8, (c), (d)); \
+})
+#define nvif_rd08(a,b) ({ u8 _v = nvif_rd((a), 8, (b)); _v; })
+#define nvif_rd16(a,b) ({ u16 _v = nvif_rd((a), 16, (b)); _v; })
+#define nvif_rd32(a,b) ({ u32 _v = nvif_rd((a), 32, (b)); _v; })
+#define nvif_wr08(a,b,c) nvif_wr((a), 8, (b), (u8)(c))
+#define nvif_wr16(a,b,c) nvif_wr((a), 16, (b), (u16)(c))
+#define nvif_wr32(a,b,c) nvif_wr((a), 32, (b), (u32)(c))
+#define nvif_mask(a,b,c,d) ({ \
+ u32 _v = nvif_rd32(nvif_object(a), (b)); \
+ nvif_wr32(nvif_object(a), (b), (_v & ~(c)) | (d)); \
+ _v; \
+})
+
+#define nvif_mthd(a,b,c,d) nvif_object_mthd(nvif_object(a), (b), (c), (d))
+
+/*XXX*/
+#include <core/object.h>
+#define nvkm_object(a) ((struct nouveau_object *)nvif_object(a)->priv)
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvif/os.h b/drivers/gpu/drm/nouveau/nvif/os.h
new file mode 120000
index 000000000000..bd744b2cf5cf
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/os.h
@@ -0,0 +1 @@
+../core/os.h \ No newline at end of file
diff --git a/drivers/gpu/drm/nouveau/nvif/unpack.h b/drivers/gpu/drm/nouveau/nvif/unpack.h
new file mode 100644
index 000000000000..5933188b4a77
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvif/unpack.h
@@ -0,0 +1,24 @@
+#ifndef __NVIF_UNPACK_H__
+#define __NVIF_UNPACK_H__
+
+#define nvif_unvers(d) ({ \
+ ret = (size == sizeof(d)) ? 0 : -ENOSYS; \
+ (ret == 0); \
+})
+
+#define nvif_unpack(d,vl,vh,m) ({ \
+ if ((vl) == 0 || ret == -ENOSYS) { \
+ int _size = sizeof(d); \
+ if (_size <= size && (d).version >= (vl) && \
+ (d).version <= (vh)) { \
+ data = (u8 *)data + _size; \
+ size = size - _size; \
+ ret = ((m) || !size) ? 0 : -E2BIG; \
+ } else { \
+ ret = -ENOSYS; \
+ } \
+ } \
+ (ret == 0); \
+})
+
+#endif
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
index 86f4ead0441d..a94b11f7859d 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -32,8 +32,16 @@ struct omap_connector {
struct drm_connector base;
struct omap_dss_device *dssdev;
struct drm_encoder *encoder;
+ bool hdmi_mode;
};
+bool omap_connector_get_hdmi_mode(struct drm_connector *connector)
+{
+ struct omap_connector *omap_connector = to_omap_connector(connector);
+
+ return omap_connector->hdmi_mode;
+}
+
void copy_timings_omap_to_drm(struct drm_display_mode *mode,
struct omap_video_timings *timings)
{
@@ -130,7 +138,7 @@ static void omap_connector_destroy(struct drm_connector *connector)
struct omap_dss_device *dssdev = omap_connector->dssdev;
DBG("%s", omap_connector->dssdev->name);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(omap_connector);
@@ -162,10 +170,14 @@ static int omap_connector_get_modes(struct drm_connector *connector)
drm_mode_connector_update_edid_property(
connector, edid);
n = drm_add_edid_modes(connector, edid);
+
+ omap_connector->hdmi_mode =
+ drm_detect_hdmi_monitor(edid);
} else {
drm_mode_connector_update_edid_property(
connector, NULL);
}
+
kfree(edid);
} else {
struct drm_display_mode *mode = drm_mode_create(dev);
@@ -307,7 +319,7 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
connector->interlace_allowed = 1;
connector->doublescan_allowed = 0;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return connector;
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
index f926b4caf449..56c60552abba 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
@@ -199,7 +199,7 @@ static struct dmm_txn *dmm_txn_init(struct dmm *dmm, struct tcm *tcm)
static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
struct page **pages, uint32_t npages, uint32_t roll)
{
- dma_addr_t pat_pa = 0;
+ dma_addr_t pat_pa = 0, data_pa = 0;
uint32_t *data;
struct pat *pat;
struct refill_engine *engine = txn->engine_handle;
@@ -223,7 +223,9 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
.lut_id = engine->tcm->lut_id,
};
- data = alloc_dma(txn, 4*i, &pat->data_pa);
+ data = alloc_dma(txn, 4*i, &data_pa);
+ /* FIXME: what if data_pa is more than 32-bit ? */
+ pat->data_pa = data_pa;
while (i--) {
int n = i + roll;
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 002b9721e85a..862ba03c236c 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -629,6 +629,7 @@ static struct drm_driver omap_drm_driver = {
.lastclose = dev_lastclose,
.preclose = dev_preclose,
.postclose = dev_postclose,
+ .set_busid = drm_platform_set_busid,
.get_vblank_counter = drm_vblank_count,
.enable_vblank = omap_irq_enable_vblank,
.disable_vblank = omap_irq_disable_vblank,
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h
index 284b80fc3c54..60e47b33c801 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.h
+++ b/drivers/gpu/drm/omapdrm/omap_drv.h
@@ -26,6 +26,7 @@
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/omap_drm.h>
+#include <drm/drm_gem.h>
#include <linux/platform_data/omap_drm.h>
@@ -119,13 +120,6 @@ struct omap_drm_private {
struct omap_drm_irq error_handler;
};
-/* this should probably be in drm-core to standardize amongst drivers */
-#define DRM_ROTATE_0 0
-#define DRM_ROTATE_90 1
-#define DRM_ROTATE_180 2
-#define DRM_ROTATE_270 3
-#define DRM_REFLECT_X 4
-#define DRM_REFLECT_Y 5
#ifdef CONFIG_DEBUG_FS
int omap_debugfs_init(struct drm_minor *minor);
@@ -194,6 +188,7 @@ struct drm_encoder *omap_connector_attached_encoder(
struct drm_connector *connector);
void omap_connector_flush(struct drm_connector *connector,
int x, int y, int w, int h);
+bool omap_connector_get_hdmi_mode(struct drm_connector *connector);
void copy_timings_omap_to_drm(struct drm_display_mode *mode,
struct omap_video_timings *timings);
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c
index 5290a88c681d..7445fb1491ae 100644
--- a/drivers/gpu/drm/omapdrm/omap_encoder.c
+++ b/drivers/gpu/drm/omapdrm/omap_encoder.c
@@ -17,6 +17,8 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <drm/drm_edid.h>
+
#include "omap_drv.h"
#include "drm_crtc.h"
@@ -89,6 +91,31 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
+ struct drm_device *dev = encoder->dev;
+ struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+ struct omap_dss_device *dssdev = omap_encoder->dssdev;
+ struct drm_connector *connector;
+ bool hdmi_mode;
+ int r;
+
+ hdmi_mode = false;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder == encoder) {
+ hdmi_mode = omap_connector_get_hdmi_mode(connector);
+ break;
+ }
+ }
+
+ if (dssdev->driver->set_hdmi_mode)
+ dssdev->driver->set_hdmi_mode(dssdev, hdmi_mode);
+
+ if (hdmi_mode && dssdev->driver->set_hdmi_infoframe) {
+ struct hdmi_avi_infoframe avi;
+
+ r = drm_hdmi_avi_infoframe_from_display_mode(&avi, adjusted_mode);
+ if (r == 0)
+ dssdev->driver->set_hdmi_infoframe(dssdev, &avi);
+ }
}
static void omap_encoder_prepare(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index 1388ca7f87e8..8436c6857cda 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -281,7 +281,7 @@ fail:
return ret;
}
-static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs omap_fb_helper_funcs = {
.fb_probe = omap_fbdev_create,
};
@@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
helper = &fbdev->base;
- helper->funcs = &omap_fb_helper_funcs;
+ drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs);
ret = drm_fb_helper_init(dev, helper,
priv->num_crtcs, priv->num_connectors);
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index 95dbce286a41..e4849413ee80 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -233,11 +233,7 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj)
WARN_ON(omap_obj->pages);
- /* TODO: __GFP_DMA32 .. but somehow GFP_HIGHMEM is coming from the
- * mapping_gfp_mask(mapping) which conflicts w/ GFP_DMA32.. probably
- * we actually want CMA memory for it all anyways..
- */
- pages = drm_gem_get_pages(obj, GFP_KERNEL);
+ pages = drm_gem_get_pages(obj);
if (IS_ERR(pages)) {
dev_err(obj->dev->dev, "could not get pages: %ld\n", PTR_ERR(pages));
return PTR_ERR(pages);
@@ -791,7 +787,7 @@ int omap_gem_get_paddr(struct drm_gem_object *obj,
omap_obj->paddr = tiler_ssptr(block);
omap_obj->block = block;
- DBG("got paddr: %08x", omap_obj->paddr);
+ DBG("got paddr: %pad", &omap_obj->paddr);
}
omap_obj->paddr_cnt++;
@@ -985,9 +981,9 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
off = drm_vma_node_start(&obj->vma_node);
- seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
+ seq_printf(m, "%08x: %2d (%2d) %08llx %pad (%2d) %p %4d",
omap_obj->flags, obj->name, obj->refcount.refcount.counter,
- off, omap_obj->paddr, omap_obj->paddr_cnt,
+ off, &omap_obj->paddr, omap_obj->paddr_cnt,
omap_obj->vaddr, omap_obj->roll);
if (omap_obj->flags & OMAP_BO_TILED) {
@@ -1183,9 +1179,7 @@ int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op)
}
}
spin_unlock(&sync_lock);
-
- if (waiter)
- kfree(waiter);
+ kfree(waiter);
}
return ret;
}
@@ -1347,6 +1341,7 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
struct omap_drm_private *priv = dev->dev_private;
struct omap_gem_object *omap_obj;
struct drm_gem_object *obj = NULL;
+ struct address_space *mapping;
size_t size;
int ret;
@@ -1404,14 +1399,16 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
omap_obj->height = gsize.tiled.height;
}
- ret = 0;
- if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM))
+ if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) {
drm_gem_private_object_init(dev, obj, size);
- else
+ } else {
ret = drm_gem_object_init(dev, obj, size);
+ if (ret)
+ goto fail;
- if (ret)
- goto fail;
+ mapping = file_inode(obj->filp)->i_mapping;
+ mapping_set_gfp_mask(mapping, GFP_USER | __GFP_DMA32);
+ }
return obj;
@@ -1467,8 +1464,8 @@ void omap_gem_init(struct drm_device *dev)
entry->paddr = tiler_ssptr(block);
entry->block = block;
- DBG("%d:%d: %dx%d: paddr=%08x stride=%d", i, j, w, h,
- entry->paddr,
+ DBG("%d:%d: %dx%d: paddr=%pad stride=%d", i, j, w, h,
+ &entry->paddr,
usergart[i].stride_pfn << PAGE_SHIFT);
}
}
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
index 3cf31ee59aac..891a4dc608af 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -142,8 +142,8 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
info->out_width, info->out_height,
info->screen_width);
- DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
- info->paddr, info->p_uv_addr);
+ DBG("%d,%d %pad %pad", info->pos_x, info->pos_y,
+ &info->paddr, &info->p_uv_addr);
/* TODO: */
ilace = false;
@@ -308,16 +308,13 @@ void omap_plane_install_properties(struct drm_plane *plane,
if (priv->has_dmm) {
prop = priv->rotation_prop;
if (!prop) {
- const struct drm_prop_enum_list props[] = {
- { DRM_ROTATE_0, "rotate-0" },
- { DRM_ROTATE_90, "rotate-90" },
- { DRM_ROTATE_180, "rotate-180" },
- { DRM_ROTATE_270, "rotate-270" },
- { DRM_REFLECT_X, "reflect-x" },
- { DRM_REFLECT_Y, "reflect-y" },
- };
- prop = drm_property_create_bitmask(dev, 0, "rotation",
- props, ARRAY_SIZE(props));
+ prop = drm_mode_create_rotation_property(dev,
+ BIT(DRM_ROTATE_0) |
+ BIT(DRM_ROTATE_90) |
+ BIT(DRM_ROTATE_180) |
+ BIT(DRM_ROTATE_270) |
+ BIT(DRM_REFLECT_X) |
+ BIT(DRM_REFLECT_Y));
if (prop == NULL)
return;
priv->rotation_prop = prop;
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 4ec874da5668..bee9f72b3a93 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -5,7 +5,7 @@ config DRM_PANEL
Panel registration and lookup framework.
menu "Display Panels"
- depends on DRM_PANEL
+ depends on DRM && DRM_PANEL
config DRM_PANEL_SIMPLE
tristate "support for simple panels"
@@ -18,14 +18,11 @@ config DRM_PANEL_SIMPLE
config DRM_PANEL_LD9040
tristate "LD9040 RGB/SPI panel"
- depends on DRM && DRM_PANEL
- depends on OF
- select SPI
+ depends on OF && SPI
select VIDEOMODE_HELPERS
config DRM_PANEL_S6E8AA0
tristate "S6E8AA0 DSI video mode panel"
- depends on DRM && DRM_PANEL
depends on OF
select DRM_MIPI_DSI
select VIDEOMODE_HELPERS
diff --git a/drivers/gpu/drm/panel/panel-ld9040.c b/drivers/gpu/drm/panel/panel-ld9040.c
index db1601fdbe29..42ac67b21e9f 100644
--- a/drivers/gpu/drm/panel/panel-ld9040.c
+++ b/drivers/gpu/drm/panel/panel-ld9040.c
@@ -110,7 +110,10 @@ struct ld9040 {
int error;
};
-#define panel_to_ld9040(p) container_of(p, struct ld9040, panel)
+static inline struct ld9040 *panel_to_ld9040(struct drm_panel *panel)
+{
+ return container_of(panel, struct ld9040, panel);
+}
static int ld9040_clear_error(struct ld9040 *ctx)
{
@@ -216,6 +219,11 @@ static int ld9040_power_off(struct ld9040 *ctx)
static int ld9040_disable(struct drm_panel *panel)
{
+ return 0;
+}
+
+static int ld9040_unprepare(struct drm_panel *panel)
+{
struct ld9040 *ctx = panel_to_ld9040(panel);
msleep(120);
@@ -228,7 +236,7 @@ static int ld9040_disable(struct drm_panel *panel)
return ld9040_power_off(ctx);
}
-static int ld9040_enable(struct drm_panel *panel)
+static int ld9040_prepare(struct drm_panel *panel)
{
struct ld9040 *ctx = panel_to_ld9040(panel);
int ret;
@@ -242,11 +250,16 @@ static int ld9040_enable(struct drm_panel *panel)
ret = ld9040_clear_error(ctx);
if (ret < 0)
- ld9040_disable(panel);
+ ld9040_unprepare(panel);
return ret;
}
+static int ld9040_enable(struct drm_panel *panel)
+{
+ return 0;
+}
+
static int ld9040_get_modes(struct drm_panel *panel)
{
struct drm_connector *connector = panel->connector;
@@ -273,6 +286,8 @@ static int ld9040_get_modes(struct drm_panel *panel)
static const struct drm_panel_funcs ld9040_drm_funcs = {
.disable = ld9040_disable,
+ .unprepare = ld9040_unprepare,
+ .prepare = ld9040_prepare,
.enable = ld9040_enable,
.get_modes = ld9040_get_modes,
};
diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c
index 06e57a26db7a..b5217fe37f02 100644
--- a/drivers/gpu/drm/panel/panel-s6e8aa0.c
+++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c
@@ -120,7 +120,10 @@ struct s6e8aa0 {
int error;
};
-#define panel_to_s6e8aa0(p) container_of(p, struct s6e8aa0, panel)
+static inline struct s6e8aa0 *panel_to_s6e8aa0(struct drm_panel *panel)
+{
+ return container_of(panel, struct s6e8aa0, panel);
+}
static int s6e8aa0_clear_error(struct s6e8aa0 *ctx)
{
@@ -133,14 +136,14 @@ static int s6e8aa0_clear_error(struct s6e8aa0 *ctx)
static void s6e8aa0_dcs_write(struct s6e8aa0 *ctx, const void *data, size_t len)
{
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
- int ret;
+ ssize_t ret;
if (ctx->error < 0)
return;
- ret = mipi_dsi_dcs_write(dsi, dsi->channel, data, len);
+ ret = mipi_dsi_dcs_write(dsi, data, len);
if (ret < 0) {
- dev_err(ctx->dev, "error %d writing dcs seq: %*ph\n", ret, len,
+ dev_err(ctx->dev, "error %zd writing dcs seq: %*ph\n", ret, len,
data);
ctx->error = ret;
}
@@ -154,7 +157,7 @@ static int s6e8aa0_dcs_read(struct s6e8aa0 *ctx, u8 cmd, void *data, size_t len)
if (ctx->error < 0)
return ctx->error;
- ret = mipi_dsi_dcs_read(dsi, dsi->channel, cmd, data, len);
+ ret = mipi_dsi_dcs_read(dsi, cmd, data, len);
if (ret < 0) {
dev_err(ctx->dev, "error %d reading dcs seq(%#x)\n", ret, cmd);
ctx->error = ret;
@@ -889,6 +892,11 @@ static int s6e8aa0_power_off(struct s6e8aa0 *ctx)
static int s6e8aa0_disable(struct drm_panel *panel)
{
+ return 0;
+}
+
+static int s6e8aa0_unprepare(struct drm_panel *panel)
+{
struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel);
s6e8aa0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE);
@@ -900,7 +908,7 @@ static int s6e8aa0_disable(struct drm_panel *panel)
return s6e8aa0_power_off(ctx);
}
-static int s6e8aa0_enable(struct drm_panel *panel)
+static int s6e8aa0_prepare(struct drm_panel *panel)
{
struct s6e8aa0 *ctx = panel_to_s6e8aa0(panel);
int ret;
@@ -913,11 +921,16 @@ static int s6e8aa0_enable(struct drm_panel *panel)
ret = ctx->error;
if (ret < 0)
- s6e8aa0_disable(panel);
+ s6e8aa0_unprepare(panel);
return ret;
}
+static int s6e8aa0_enable(struct drm_panel *panel)
+{
+ return 0;
+}
+
static int s6e8aa0_get_modes(struct drm_panel *panel)
{
struct drm_connector *connector = panel->connector;
@@ -944,6 +957,8 @@ static int s6e8aa0_get_modes(struct drm_panel *panel)
static const struct drm_panel_funcs s6e8aa0_drm_funcs = {
.disable = s6e8aa0_disable,
+ .unprepare = s6e8aa0_unprepare,
+ .prepare = s6e8aa0_prepare,
.enable = s6e8aa0_enable,
.get_modes = s6e8aa0_get_modes,
};
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index a25136132c31..23de22f8c820 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -37,14 +37,35 @@ struct panel_desc {
const struct drm_display_mode *modes;
unsigned int num_modes;
+ unsigned int bpc;
+
struct {
unsigned int width;
unsigned int height;
} size;
+
+ /**
+ * @prepare: the time (in milliseconds) that it takes for the panel to
+ * become ready and start receiving video data
+ * @enable: the time (in milliseconds) that it takes for the panel to
+ * display the first valid frame after starting to receive
+ * video data
+ * @disable: the time (in milliseconds) that it takes for the panel to
+ * turn the display off (no content is visible)
+ * @unprepare: the time (in milliseconds) that it takes for the panel
+ * to power itself down completely
+ */
+ struct {
+ unsigned int prepare;
+ unsigned int enable;
+ unsigned int disable;
+ unsigned int unprepare;
+ } delay;
};
struct panel_simple {
struct drm_panel base;
+ bool prepared;
bool enabled;
const struct panel_desc *desc;
@@ -87,6 +108,7 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel)
num++;
}
+ connector->display_info.bpc = panel->desc->bpc;
connector->display_info.width_mm = panel->desc->size.width;
connector->display_info.height_mm = panel->desc->size.height;
@@ -105,21 +127,40 @@ static int panel_simple_disable(struct drm_panel *panel)
backlight_update_status(p->backlight);
}
+ if (p->desc->delay.disable)
+ msleep(p->desc->delay.disable);
+
+ p->enabled = false;
+
+ return 0;
+}
+
+static int panel_simple_unprepare(struct drm_panel *panel)
+{
+ struct panel_simple *p = to_panel_simple(panel);
+
+ if (!p->prepared)
+ return 0;
+
if (p->enable_gpio)
gpiod_set_value_cansleep(p->enable_gpio, 0);
regulator_disable(p->supply);
- p->enabled = false;
+
+ if (p->desc->delay.unprepare)
+ msleep(p->desc->delay.unprepare);
+
+ p->prepared = false;
return 0;
}
-static int panel_simple_enable(struct drm_panel *panel)
+static int panel_simple_prepare(struct drm_panel *panel)
{
struct panel_simple *p = to_panel_simple(panel);
int err;
- if (p->enabled)
+ if (p->prepared)
return 0;
err = regulator_enable(p->supply);
@@ -131,6 +172,24 @@ static int panel_simple_enable(struct drm_panel *panel)
if (p->enable_gpio)
gpiod_set_value_cansleep(p->enable_gpio, 1);
+ if (p->desc->delay.prepare)
+ msleep(p->desc->delay.prepare);
+
+ p->prepared = true;
+
+ return 0;
+}
+
+static int panel_simple_enable(struct drm_panel *panel)
+{
+ struct panel_simple *p = to_panel_simple(panel);
+
+ if (p->enabled)
+ return 0;
+
+ if (p->desc->delay.enable)
+ msleep(p->desc->delay.enable);
+
if (p->backlight) {
p->backlight->props.power = FB_BLANK_UNBLANK;
backlight_update_status(p->backlight);
@@ -164,6 +223,8 @@ static int panel_simple_get_modes(struct drm_panel *panel)
static const struct drm_panel_funcs panel_simple_funcs = {
.disable = panel_simple_disable,
+ .unprepare = panel_simple_unprepare,
+ .prepare = panel_simple_prepare,
.enable = panel_simple_enable,
.get_modes = panel_simple_get_modes,
};
@@ -179,22 +240,21 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
return -ENOMEM;
panel->enabled = false;
+ panel->prepared = false;
panel->desc = desc;
panel->supply = devm_regulator_get(dev, "power");
if (IS_ERR(panel->supply))
return PTR_ERR(panel->supply);
- panel->enable_gpio = devm_gpiod_get(dev, "enable");
+ panel->enable_gpio = devm_gpiod_get_optional(dev, "enable");
if (IS_ERR(panel->enable_gpio)) {
err = PTR_ERR(panel->enable_gpio);
- if (err != -ENOENT) {
- dev_err(dev, "failed to request GPIO: %d\n", err);
- return err;
- }
+ dev_err(dev, "failed to request GPIO: %d\n", err);
+ return err;
+ }
- panel->enable_gpio = NULL;
- } else {
+ if (panel->enable_gpio) {
err = gpiod_direction_output(panel->enable_gpio, 0);
if (err < 0) {
dev_err(dev, "failed to setup GPIO: %d\n", err);
@@ -285,6 +345,31 @@ static const struct drm_display_mode auo_b101aw03_mode = {
static const struct panel_desc auo_b101aw03 = {
.modes = &auo_b101aw03_mode,
.num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 223,
+ .height = 125,
+ },
+};
+
+static const struct drm_display_mode auo_b101xtn01_mode = {
+ .clock = 72000,
+ .hdisplay = 1366,
+ .hsync_start = 1366 + 20,
+ .hsync_end = 1366 + 20 + 70,
+ .htotal = 1366 + 20 + 70,
+ .vdisplay = 768,
+ .vsync_start = 768 + 14,
+ .vsync_end = 768 + 14 + 42,
+ .vtotal = 768 + 14 + 42,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc auo_b101xtn01 = {
+ .modes = &auo_b101xtn01_mode,
+ .num_modes = 1,
+ .bpc = 6,
.size = {
.width = 223,
.height = 125,
@@ -307,12 +392,40 @@ static const struct drm_display_mode auo_b133xtn01_mode = {
static const struct panel_desc auo_b133xtn01 = {
.modes = &auo_b133xtn01_mode,
.num_modes = 1,
+ .bpc = 6,
.size = {
.width = 293,
.height = 165,
},
};
+static const struct drm_display_mode auo_b133htn01_mode = {
+ .clock = 150660,
+ .hdisplay = 1920,
+ .hsync_start = 1920 + 172,
+ .hsync_end = 1920 + 172 + 80,
+ .htotal = 1920 + 172 + 80 + 60,
+ .vdisplay = 1080,
+ .vsync_start = 1080 + 25,
+ .vsync_end = 1080 + 25 + 10,
+ .vtotal = 1080 + 25 + 10 + 10,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc auo_b133htn01 = {
+ .modes = &auo_b133htn01_mode,
+ .num_modes = 1,
+ .size = {
+ .width = 293,
+ .height = 165,
+ },
+ .delay = {
+ .prepare = 105,
+ .enable = 20,
+ .unprepare = 50,
+ },
+};
+
static const struct drm_display_mode chunghwa_claa101wa01a_mode = {
.clock = 72070,
.hdisplay = 1366,
@@ -329,6 +442,7 @@ static const struct drm_display_mode chunghwa_claa101wa01a_mode = {
static const struct panel_desc chunghwa_claa101wa01a = {
.modes = &chunghwa_claa101wa01a_mode,
.num_modes = 1,
+ .bpc = 6,
.size = {
.width = 220,
.height = 120,
@@ -351,6 +465,7 @@ static const struct drm_display_mode chunghwa_claa101wb01_mode = {
static const struct panel_desc chunghwa_claa101wb01 = {
.modes = &chunghwa_claa101wb01_mode,
.num_modes = 1,
+ .bpc = 6,
.size = {
.width = 223,
.height = 125,
@@ -374,6 +489,7 @@ static const struct drm_display_mode edt_et057090dhu_mode = {
static const struct panel_desc edt_et057090dhu = {
.modes = &edt_et057090dhu_mode,
.num_modes = 1,
+ .bpc = 6,
.size = {
.width = 115,
.height = 86,
@@ -397,12 +513,82 @@ static const struct drm_display_mode edt_etm0700g0dh6_mode = {
static const struct panel_desc edt_etm0700g0dh6 = {
.modes = &edt_etm0700g0dh6_mode,
.num_modes = 1,
+ .bpc = 6,
.size = {
.width = 152,
.height = 91,
},
};
+static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = {
+ .clock = 32260,
+ .hdisplay = 800,
+ .hsync_start = 800 + 168,
+ .hsync_end = 800 + 168 + 64,
+ .htotal = 800 + 168 + 64 + 88,
+ .vdisplay = 480,
+ .vsync_start = 480 + 37,
+ .vsync_end = 480 + 37 + 2,
+ .vtotal = 480 + 37 + 2 + 8,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc foxlink_fl500wvr00_a0t = {
+ .modes = &foxlink_fl500wvr00_a0t_mode,
+ .num_modes = 1,
+ .size = {
+ .width = 108,
+ .height = 65,
+ },
+};
+
+static const struct drm_display_mode innolux_n116bge_mode = {
+ .clock = 71000,
+ .hdisplay = 1366,
+ .hsync_start = 1366 + 64,
+ .hsync_end = 1366 + 64 + 6,
+ .htotal = 1366 + 64 + 6 + 64,
+ .vdisplay = 768,
+ .vsync_start = 768 + 8,
+ .vsync_end = 768 + 8 + 4,
+ .vtotal = 768 + 8 + 4 + 8,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+};
+
+static const struct panel_desc innolux_n116bge = {
+ .modes = &innolux_n116bge_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 256,
+ .height = 144,
+ },
+};
+
+static const struct drm_display_mode innolux_n156bge_l21_mode = {
+ .clock = 69300,
+ .hdisplay = 1366,
+ .hsync_start = 1366 + 16,
+ .hsync_end = 1366 + 16 + 34,
+ .htotal = 1366 + 16 + 34 + 50,
+ .vdisplay = 768,
+ .vsync_start = 768 + 2,
+ .vsync_end = 768 + 2 + 6,
+ .vtotal = 768 + 2 + 6 + 12,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc innolux_n156bge_l21 = {
+ .modes = &innolux_n156bge_l21_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 344,
+ .height = 193,
+ },
+};
+
static const struct drm_display_mode lg_lp129qe_mode = {
.clock = 285250,
.hdisplay = 2560,
@@ -419,6 +605,7 @@ static const struct drm_display_mode lg_lp129qe_mode = {
static const struct panel_desc lg_lp129qe = {
.modes = &lg_lp129qe_mode,
.num_modes = 1,
+ .bpc = 8,
.size = {
.width = 272,
.height = 181,
@@ -441,6 +628,7 @@ static const struct drm_display_mode samsung_ltn101nt05_mode = {
static const struct panel_desc samsung_ltn101nt05 = {
.modes = &samsung_ltn101nt05_mode,
.num_modes = 1,
+ .bpc = 6,
.size = {
.width = 1024,
.height = 600,
@@ -452,6 +640,12 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "auo,b101aw03",
.data = &auo_b101aw03,
}, {
+ .compatible = "auo,b101xtn01",
+ .data = &auo_b101xtn01,
+ }, {
+ .compatible = "auo,b133htn01",
+ .data = &auo_b133htn01,
+ }, {
.compatible = "auo,b133xtn01",
.data = &auo_b133xtn01,
}, {
@@ -470,14 +664,21 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "edt,etm0700g0dh6",
.data = &edt_etm0700g0dh6,
}, {
+ .compatible = "foxlink,fl500wvr00-a0t",
+ .data = &foxlink_fl500wvr00_a0t,
+ }, {
+ .compatible = "innolux,n116bge",
+ .data = &innolux_n116bge,
+ }, {
+ .compatible = "innolux,n156bge-l21",
+ .data = &innolux_n156bge_l21,
+ }, {
.compatible = "lg,lp129qe",
.data = &lg_lp129qe,
}, {
.compatible = "samsung,ltn101nt05",
.data = &samsung_ltn101nt05,
}, {
- .compatible = "simple-panel",
- }, {
/* sentinel */
}
};
@@ -545,7 +746,7 @@ static const struct panel_desc_dsi lg_ld070wx3_sl01 = {
.height = 151,
},
},
- .flags = MIPI_DSI_MODE_VIDEO,
+ .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
.format = MIPI_DSI_FMT_RGB888,
.lanes = 4,
};
@@ -599,7 +800,8 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = {
.height = 136,
},
},
- .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
+ .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS,
.format = MIPI_DSI_FMT_RGB888,
.lanes = 4,
};
diff --git a/drivers/gpu/drm/qxl/Makefile b/drivers/gpu/drm/qxl/Makefile
index ea046ba691d2..bacc4aff1201 100644
--- a/drivers/gpu/drm/qxl/Makefile
+++ b/drivers/gpu/drm/qxl/Makefile
@@ -4,6 +4,6 @@
ccflags-y := -Iinclude/drm
-qxl-y := qxl_drv.o qxl_kms.o qxl_display.o qxl_ttm.o qxl_fb.o qxl_object.o qxl_gem.o qxl_cmd.o qxl_image.o qxl_draw.o qxl_debugfs.o qxl_irq.o qxl_dumb.o qxl_ioctl.o qxl_fence.o qxl_release.o
+qxl-y := qxl_drv.o qxl_kms.o qxl_display.o qxl_ttm.o qxl_fb.o qxl_object.o qxl_gem.o qxl_cmd.o qxl_image.o qxl_draw.o qxl_debugfs.o qxl_irq.o qxl_dumb.o qxl_ioctl.o qxl_release.o qxl_prime.o
obj-$(CONFIG_DRM_QXL)+= qxl.o
diff --git a/drivers/gpu/drm/qxl/qxl_cmd.c b/drivers/gpu/drm/qxl/qxl_cmd.c
index eb89653a7a17..97823644d347 100644
--- a/drivers/gpu/drm/qxl/qxl_cmd.c
+++ b/drivers/gpu/drm/qxl/qxl_cmd.c
@@ -620,17 +620,10 @@ static int qxl_reap_surf(struct qxl_device *qdev, struct qxl_bo *surf, bool stal
if (ret == -EBUSY)
return -EBUSY;
- if (surf->fence.num_active_releases > 0 && stall == false) {
- qxl_bo_unreserve(surf);
- return -EBUSY;
- }
-
if (stall)
mutex_unlock(&qdev->surf_evict_mutex);
- spin_lock(&surf->tbo.bdev->fence_lock);
ret = ttm_bo_wait(&surf->tbo, true, true, !stall);
- spin_unlock(&surf->tbo.bdev->fence_lock);
if (stall)
mutex_lock(&qdev->surf_evict_mutex);
diff --git a/drivers/gpu/drm/qxl/qxl_debugfs.c b/drivers/gpu/drm/qxl/qxl_debugfs.c
index c3c2bbdc6674..6911b8c44492 100644
--- a/drivers/gpu/drm/qxl/qxl_debugfs.c
+++ b/drivers/gpu/drm/qxl/qxl_debugfs.c
@@ -58,9 +58,17 @@ qxl_debugfs_buffers_info(struct seq_file *m, void *data)
struct qxl_bo *bo;
list_for_each_entry(bo, &qdev->gem.objects, list) {
- seq_printf(m, "size %ld, pc %d, sync obj %p, num releases %d\n",
- (unsigned long)bo->gem_base.size, bo->pin_count,
- bo->tbo.sync_obj, bo->fence.num_active_releases);
+ struct reservation_object_list *fobj;
+ int rel;
+
+ rcu_read_lock();
+ fobj = rcu_dereference(bo->tbo.resv->fence);
+ rel = fobj ? fobj->shared_count : 0;
+ rcu_read_unlock();
+
+ seq_printf(m, "size %ld, pc %d, num releases %d\n",
+ (unsigned long)bo->gem_base.size,
+ bo->pin_count, rel);
}
return 0;
}
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index 5d7ea2461852..0d1396266857 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -187,6 +187,54 @@ static void qxl_crtc_destroy(struct drm_crtc *crtc)
kfree(qxl_crtc);
}
+static int qxl_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event,
+ uint32_t page_flip_flags)
+{
+ struct drm_device *dev = crtc->dev;
+ struct qxl_device *qdev = dev->dev_private;
+ struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
+ struct qxl_framebuffer *qfb_src = to_qxl_framebuffer(fb);
+ struct qxl_framebuffer *qfb_old = to_qxl_framebuffer(crtc->primary->fb);
+ struct qxl_bo *bo_old = gem_to_qxl_bo(qfb_old->obj);
+ struct qxl_bo *bo = gem_to_qxl_bo(qfb_src->obj);
+ unsigned long flags;
+ struct drm_clip_rect norect = {
+ .x1 = 0,
+ .y1 = 0,
+ .x2 = fb->width,
+ .y2 = fb->height
+ };
+ int inc = 1;
+ int one_clip_rect = 1;
+ int ret = 0;
+
+ crtc->primary->fb = fb;
+ bo_old->is_primary = false;
+ bo->is_primary = true;
+
+ ret = qxl_bo_reserve(bo, false);
+ if (ret)
+ return ret;
+
+ qxl_draw_dirty_fb(qdev, qfb_src, bo, 0, 0,
+ &norect, one_clip_rect, inc);
+
+ drm_vblank_get(dev, qcrtc->index);
+
+ if (event) {
+ spin_lock_irqsave(&dev->event_lock, flags);
+ drm_send_vblank_event(dev, qcrtc->index, event);
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ }
+ drm_vblank_put(dev, qcrtc->index);
+
+ qxl_bo_unreserve(bo);
+
+ return 0;
+}
+
static int
qxl_hide_cursor(struct qxl_device *qdev)
{
@@ -374,6 +422,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = {
.cursor_move = qxl_crtc_cursor_move,
.set_config = drm_crtc_helper_set_config,
.destroy = qxl_crtc_destroy,
+ .page_flip = qxl_crtc_page_flip,
};
static void qxl_user_framebuffer_destroy(struct drm_framebuffer *fb)
@@ -523,7 +572,6 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
struct qxl_framebuffer *qfb;
struct qxl_bo *bo, *old_bo = NULL;
struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
- uint32_t width, height, base_offset;
bool recreate_primary = false;
int ret;
int surf_id;
@@ -553,9 +601,10 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
if (qcrtc->index == 0)
recreate_primary = true;
- width = mode->hdisplay;
- height = mode->vdisplay;
- base_offset = 0;
+ if (bo->surf.stride * bo->surf.height > qdev->vram_size) {
+ DRM_ERROR("Mode doesn't fit in vram size (vgamem)");
+ return -EINVAL;
+ }
ret = qxl_bo_reserve(bo, false);
if (ret != 0)
@@ -569,10 +618,10 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
if (recreate_primary) {
qxl_io_destroy_primary(qdev);
qxl_io_log(qdev,
- "recreate primary: %dx%d (was %dx%d,%d,%d)\n",
- width, height, bo->surf.width,
- bo->surf.height, bo->surf.stride, bo->surf.format);
- qxl_io_create_primary(qdev, base_offset, bo);
+ "recreate primary: %dx%d,%d,%d\n",
+ bo->surf.width, bo->surf.height,
+ bo->surf.stride, bo->surf.format);
+ qxl_io_create_primary(qdev, 0, bo);
bo->is_primary = true;
}
@@ -835,7 +884,7 @@ static void qxl_conn_destroy(struct drm_connector *connector)
struct qxl_output *qxl_output =
drm_connector_to_qxl_output(connector);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(qxl_output);
}
@@ -902,7 +951,7 @@ static int qdev_output_init(struct drm_device *dev, int num_output)
drm_object_attach_property(&connector->base,
qdev->hotplug_mode_update_property, 0);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return 0;
}
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index 6e936634d65c..1d9b80c91a15 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -38,7 +38,7 @@
#include "qxl_object.h"
extern int qxl_max_ioctls;
-static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
+static const struct pci_device_id pciidlist[] = {
{ 0x1b36, 0x100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8,
0xffff00, 0 },
{ 0x1b36, 0x100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_OTHER << 8,
@@ -84,6 +84,7 @@ static const struct file_operations qxl_fops = {
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
.poll = drm_poll,
+ .read = drm_read,
.mmap = qxl_mmap,
};
@@ -195,6 +196,20 @@ static int qxl_pm_restore(struct device *dev)
return qxl_drm_resume(drm_dev, false);
}
+static u32 qxl_noop_get_vblank_counter(struct drm_device *dev, int crtc)
+{
+ return dev->vblank[crtc].count.counter;
+}
+
+static int qxl_noop_enable_vblank(struct drm_device *dev, int crtc)
+{
+ return 0;
+}
+
+static void qxl_noop_disable_vblank(struct drm_device *dev, int crtc)
+{
+}
+
static const struct dev_pm_ops qxl_pm_ops = {
.suspend = qxl_pm_suspend,
.resume = qxl_pm_resume,
@@ -212,10 +227,15 @@ static struct pci_driver qxl_pci_driver = {
};
static struct drm_driver qxl_driver = {
- .driver_features = DRIVER_GEM | DRIVER_MODESET |
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
.load = qxl_driver_load,
.unload = qxl_driver_unload,
+ .get_vblank_counter = qxl_noop_get_vblank_counter,
+ .enable_vblank = qxl_noop_enable_vblank,
+ .disable_vblank = qxl_noop_disable_vblank,
+
+ .set_busid = drm_pci_set_busid,
.dumb_create = qxl_mode_dumb_create,
.dumb_map_offset = qxl_mode_dumb_mmap,
@@ -224,6 +244,17 @@ static struct drm_driver qxl_driver = {
.debugfs_init = qxl_debugfs_init,
.debugfs_cleanup = qxl_debugfs_takedown,
#endif
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .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_pin = qxl_gem_prime_pin,
+ .gem_prime_unpin = qxl_gem_prime_unpin,
+ .gem_prime_get_sg_table = qxl_gem_prime_get_sg_table,
+ .gem_prime_import_sg_table = qxl_gem_prime_import_sg_table,
+ .gem_prime_vmap = qxl_gem_prime_vmap,
+ .gem_prime_vunmap = qxl_gem_prime_vunmap,
+ .gem_prime_mmap = qxl_gem_prime_mmap,
.gem_free_object = qxl_gem_object_free,
.gem_open_object = qxl_gem_object_open,
.gem_close_object = qxl_gem_object_close,
diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h
index 36ed40ba773f..7c6cafe21f5f 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.h
+++ b/drivers/gpu/drm/qxl/qxl_drv.h
@@ -31,6 +31,7 @@
* Definitions taken from spice-protocol, plus kernel driver specific bits.
*/
+#include <linux/fence.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
@@ -42,6 +43,8 @@
#include <ttm/ttm_placement.h>
#include <ttm/ttm_module.h>
+#include <drm/drm_gem.h>
+
/* just for ttm_validate_buffer */
#include <ttm/ttm_execbuf_util.h>
@@ -95,31 +98,24 @@ enum {
QXL_INTERRUPT_IO_CMD |\
QXL_INTERRUPT_CLIENT_MONITORS_CONFIG)
-struct qxl_fence {
- struct qxl_device *qdev;
- uint32_t num_active_releases;
- uint32_t *release_ids;
- struct radix_tree_root tree;
-};
-
struct qxl_bo {
/* Protected by gem.mutex */
struct list_head list;
/* Protected by tbo.reserved */
- u32 placements[3];
+ struct ttm_place placements[3];
struct ttm_placement placement;
struct ttm_buffer_object tbo;
struct ttm_bo_kmap_obj kmap;
unsigned pin_count;
void *kptr;
int type;
+
/* Constant after initialization */
struct drm_gem_object gem_base;
bool is_primary; /* is this now a primary surface */
bool hw_surf_alloc;
struct qxl_surface surf;
uint32_t surface_id;
- struct qxl_fence fence; /* per bo fence - list of releases */
struct qxl_release *surf_create;
};
#define gem_to_qxl_bo(gobj) container_of((gobj), struct qxl_bo, gem_base)
@@ -191,6 +187,8 @@ enum {
* spice-protocol/qxl_dev.h */
#define QXL_MAX_RES 96
struct qxl_release {
+ struct fence base;
+
int id;
int type;
uint32_t release_offset;
@@ -284,7 +282,9 @@ struct qxl_device {
uint8_t slot_gen_bits;
uint64_t va_slot_mask;
+ spinlock_t release_lock;
struct idr release_idr;
+ uint32_t release_seqno;
spinlock_t release_idr_lock;
struct mutex async_io_mutex;
unsigned int last_sent_io_cmd;
@@ -532,6 +532,18 @@ int qxl_garbage_collect(struct qxl_device *qdev);
int qxl_debugfs_init(struct drm_minor *minor);
void qxl_debugfs_takedown(struct drm_minor *minor);
+/* qxl_prime.c */
+int qxl_gem_prime_pin(struct drm_gem_object *obj);
+void qxl_gem_prime_unpin(struct drm_gem_object *obj);
+struct sg_table *qxl_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *qxl_gem_prime_import_sg_table(
+ struct drm_device *dev, struct dma_buf_attachment *attach,
+ struct sg_table *sgt);
+void *qxl_gem_prime_vmap(struct drm_gem_object *obj);
+void qxl_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+int qxl_gem_prime_mmap(struct drm_gem_object *obj,
+ struct vm_area_struct *vma);
+
/* qxl_irq.c */
int qxl_irq_init(struct qxl_device *qdev);
irqreturn_t qxl_irq_handler(int irq, void *arg);
@@ -561,10 +573,4 @@ qxl_surface_lookup(struct drm_device *dev, int surface_id);
void qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool freeing);
int qxl_update_surface(struct qxl_device *qdev, struct qxl_bo *surf);
-/* qxl_fence.c */
-void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id);
-int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id);
-int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence);
-void qxl_fence_fini(struct qxl_fence *qfence);
-
#endif
diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
index f437b30ce689..3d7c1d00a424 100644
--- a/drivers/gpu/drm/qxl/qxl_fb.c
+++ b/drivers/gpu/drm/qxl/qxl_fb.c
@@ -625,7 +625,8 @@ static int qxl_fb_find_or_create_single(
struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
- struct qxl_fbdev *qfbdev = (struct qxl_fbdev *)helper;
+ struct qxl_fbdev *qfbdev =
+ container_of(helper, struct qxl_fbdev, helper);
int new_fb = 0;
int ret;
@@ -660,7 +661,7 @@ static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev)
return 0;
}
-static struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
.fb_probe = qxl_fb_find_or_create_single,
};
@@ -676,9 +677,12 @@ int qxl_fbdev_init(struct qxl_device *qdev)
qfbdev->qdev = qdev;
qdev->mode_info.qfbdev = qfbdev;
- qfbdev->helper.funcs = &qxl_fb_helper_funcs;
spin_lock_init(&qfbdev->delayed_ops_lock);
INIT_LIST_HEAD(&qfbdev->delayed_ops);
+
+ drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper,
+ &qxl_fb_helper_funcs);
+
ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
qxl_num_crtc /* num_crtc - QXL supports just 1 */,
QXLFB_CONN_LIMIT);
diff --git a/drivers/gpu/drm/qxl/qxl_fence.c b/drivers/gpu/drm/qxl/qxl_fence.c
deleted file mode 100644
index ae59e91cfb9a..000000000000
--- a/drivers/gpu/drm/qxl/qxl_fence.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2013 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
- *
- * Authors: Dave Airlie
- * Alon Levy
- */
-
-
-#include "qxl_drv.h"
-
-/* QXL fencing-
-
- When we submit operations to the GPU we pass a release reference to the GPU
- with them, the release reference is then added to the release ring when
- the GPU is finished with that particular operation and has removed it from
- its tree.
-
- So we have can have multiple outstanding non linear fences per object.
-
- From a TTM POV we only care if the object has any outstanding releases on
- it.
-
- we wait until all outstanding releases are processeed.
-
- sync object is just a list of release ids that represent that fence on
- that buffer.
-
- we just add new releases onto the sync object attached to the object.
-
- This currently uses a radix tree to store the list of release ids.
-
- For some reason every so often qxl hw fails to release, things go wrong.
-*/
-/* must be called with the fence lock held */
-void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id)
-{
- radix_tree_insert(&qfence->tree, rel_id, qfence);
- qfence->num_active_releases++;
-}
-
-int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id)
-{
- void *ret;
- int retval = 0;
- struct qxl_bo *bo = container_of(qfence, struct qxl_bo, fence);
-
- spin_lock(&bo->tbo.bdev->fence_lock);
-
- ret = radix_tree_delete(&qfence->tree, rel_id);
- if (ret == qfence)
- qfence->num_active_releases--;
- else {
- DRM_DEBUG("didn't find fence in radix tree for %d\n", rel_id);
- retval = -ENOENT;
- }
- spin_unlock(&bo->tbo.bdev->fence_lock);
- return retval;
-}
-
-
-int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence)
-{
- qfence->qdev = qdev;
- qfence->num_active_releases = 0;
- INIT_RADIX_TREE(&qfence->tree, GFP_ATOMIC);
- return 0;
-}
-
-void qxl_fence_fini(struct qxl_fence *qfence)
-{
- kfree(qfence->release_ids);
- qfence->num_active_releases = 0;
-}
diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c
index fd88eb4a3f79..b2977a181935 100644
--- a/drivers/gpu/drm/qxl/qxl_kms.c
+++ b/drivers/gpu/drm/qxl/qxl_kms.c
@@ -223,6 +223,7 @@ static int qxl_device_init(struct qxl_device *qdev,
idr_init(&qdev->release_idr);
spin_lock_init(&qdev->release_idr_lock);
+ spin_lock_init(&qdev->release_lock);
idr_init(&qdev->surf_id_idr);
spin_lock_init(&qdev->surf_id_idr_lock);
@@ -297,6 +298,9 @@ int qxl_driver_unload(struct drm_device *dev)
if (qdev == NULL)
return 0;
+
+ drm_vblank_cleanup(dev);
+
qxl_modeset_fini(qdev);
qxl_device_fini(qdev);
@@ -324,15 +328,20 @@ int qxl_driver_load(struct drm_device *dev, unsigned long flags)
if (r)
goto out;
+ r = drm_vblank_init(dev, 1);
+ if (r)
+ goto unload;
+
r = qxl_modeset_init(qdev);
- if (r) {
- qxl_driver_unload(dev);
- goto out;
- }
+ if (r)
+ goto unload;
drm_kms_helper_poll_init(qdev->ddev);
return 0;
+unload:
+ qxl_driver_unload(dev);
+
out:
kfree(qdev);
return r;
diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c
index b95f144f0b49..cdeaf08fdc74 100644
--- a/drivers/gpu/drm/qxl/qxl_object.c
+++ b/drivers/gpu/drm/qxl/qxl_object.c
@@ -36,7 +36,6 @@ static void qxl_ttm_bo_destroy(struct ttm_buffer_object *tbo)
qdev = (struct qxl_device *)bo->gem_base.dev->dev_private;
qxl_surface_evict(qdev, bo, false);
- qxl_fence_fini(&bo->fence);
mutex_lock(&qdev->gem.mutex);
list_del_init(&bo->list);
mutex_unlock(&qdev->gem.mutex);
@@ -55,21 +54,24 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned)
{
u32 c = 0;
u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0;
+ unsigned i;
- qbo->placement.fpfn = 0;
- qbo->placement.lpfn = 0;
qbo->placement.placement = qbo->placements;
qbo->placement.busy_placement = qbo->placements;
if (domain == QXL_GEM_DOMAIN_VRAM)
- qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag;
+ qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag;
if (domain == QXL_GEM_DOMAIN_SURFACE)
- qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV0 | pflag;
+ qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV0 | pflag;
if (domain == QXL_GEM_DOMAIN_CPU)
- qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM | pflag;
+ qbo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM | pflag;
if (!c)
- qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ qbo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
qbo->placement.num_placement = c;
qbo->placement.num_busy_placement = c;
+ for (i = 0; i < c; ++i) {
+ qbo->placements[i].fpfn = 0;
+ qbo->placements[i].lpfn = 0;
+ }
}
@@ -99,7 +101,6 @@ int qxl_bo_create(struct qxl_device *qdev,
bo->type = domain;
bo->pin_count = pinned ? 1 : 0;
bo->surface_id = 0;
- qxl_fence_init(qdev, &bo->fence);
INIT_LIST_HEAD(&bo->list);
if (surf)
@@ -109,7 +110,7 @@ int qxl_bo_create(struct qxl_device *qdev,
r = ttm_bo_init(&qdev->mman.bdev, &bo->tbo, size, type,
&bo->placement, 0, !kernel, NULL, size,
- NULL, &qxl_ttm_bo_destroy);
+ NULL, NULL, &qxl_ttm_bo_destroy);
if (unlikely(r != 0)) {
if (r != -ERESTARTSYS)
dev_err(qdev->dev,
@@ -259,7 +260,7 @@ int qxl_bo_unpin(struct qxl_bo *bo)
if (bo->pin_count)
return 0;
for (i = 0; i < bo->placement.num_placement; i++)
- bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
+ bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT;
r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
if (unlikely(r != 0))
dev_err(qdev->dev, "%p validate failed for unpin\n", bo);
diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h
index d458a140c024..37af1bc0dd00 100644
--- a/drivers/gpu/drm/qxl/qxl_object.h
+++ b/drivers/gpu/drm/qxl/qxl_object.h
@@ -31,7 +31,7 @@ static inline int qxl_bo_reserve(struct qxl_bo *bo, bool no_wait)
{
int r;
- r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
+ r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL);
if (unlikely(r != 0)) {
if (r != -ERESTARTSYS) {
struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private;
@@ -67,7 +67,7 @@ static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type,
{
int r;
- r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
+ r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL);
if (unlikely(r != 0)) {
if (r != -ERESTARTSYS) {
struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private;
@@ -76,12 +76,10 @@ static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type,
}
return r;
}
- spin_lock(&bo->tbo.bdev->fence_lock);
if (mem_type)
*mem_type = bo->tbo.mem.mem_type;
- if (bo->tbo.sync_obj)
- r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
- spin_unlock(&bo->tbo.bdev->fence_lock);
+
+ r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
ttm_bo_unreserve(&bo->tbo);
return r;
}
diff --git a/drivers/gpu/drm/qxl/qxl_prime.c b/drivers/gpu/drm/qxl/qxl_prime.c
new file mode 100644
index 000000000000..3d031b50a8fd
--- /dev/null
+++ b/drivers/gpu/drm/qxl/qxl_prime.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 Canonical
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Andreas Pokorny
+ */
+
+#include "qxl_drv.h"
+
+/* Empty Implementations as there should not be any other driver for a virtual
+ * device that might share buffers with qxl */
+
+int qxl_gem_prime_pin(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+ return -ENOSYS;
+}
+
+void qxl_gem_prime_unpin(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+}
+
+
+struct sg_table *qxl_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+ return ERR_PTR(-ENOSYS);
+}
+
+struct drm_gem_object *qxl_gem_prime_import_sg_table(
+ struct drm_device *dev, struct dma_buf_attachment *attach,
+ struct sg_table *table)
+{
+ WARN_ONCE(1, "not implemented");
+ return ERR_PTR(-ENOSYS);
+}
+
+void *qxl_gem_prime_vmap(struct drm_gem_object *obj)
+{
+ WARN_ONCE(1, "not implemented");
+ return ERR_PTR(-ENOSYS);
+}
+
+void qxl_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+ WARN_ONCE(1, "not implemented");
+}
+
+int qxl_gem_prime_mmap(struct drm_gem_object *obj,
+ struct vm_area_struct *area)
+{
+ WARN_ONCE(1, "not implemented");
+ return ENOSYS;
+}
diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
index 14e776f1d14e..446e71ca36cb 100644
--- a/drivers/gpu/drm/qxl/qxl_release.c
+++ b/drivers/gpu/drm/qxl/qxl_release.c
@@ -21,6 +21,7 @@
*/
#include "qxl_drv.h"
#include "qxl_object.h"
+#include <trace/events/fence.h>
/*
* drawable cmd cache - allocate a bunch of VRAM pages, suballocate
@@ -39,6 +40,88 @@
static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE };
static const int releases_per_bo[] = { RELEASES_PER_BO, SURFACE_RELEASES_PER_BO, RELEASES_PER_BO };
+static const char *qxl_get_driver_name(struct fence *fence)
+{
+ return "qxl";
+}
+
+static const char *qxl_get_timeline_name(struct fence *fence)
+{
+ return "release";
+}
+
+static bool qxl_nop_signaling(struct fence *fence)
+{
+ /* fences are always automatically signaled, so just pretend we did this.. */
+ return true;
+}
+
+static long qxl_fence_wait(struct fence *fence, bool intr, signed long timeout)
+{
+ struct qxl_device *qdev;
+ struct qxl_release *release;
+ int count = 0, sc = 0;
+ bool have_drawable_releases;
+ unsigned long cur, end = jiffies + timeout;
+
+ qdev = container_of(fence->lock, struct qxl_device, release_lock);
+ release = container_of(fence, struct qxl_release, base);
+ have_drawable_releases = release->type == QXL_RELEASE_DRAWABLE;
+
+retry:
+ sc++;
+
+ if (fence_is_signaled(fence))
+ goto signaled;
+
+ qxl_io_notify_oom(qdev);
+
+ for (count = 0; count < 11; count++) {
+ if (!qxl_queue_garbage_collect(qdev, true))
+ break;
+
+ if (fence_is_signaled(fence))
+ goto signaled;
+ }
+
+ if (fence_is_signaled(fence))
+ goto signaled;
+
+ if (have_drawable_releases || sc < 4) {
+ if (sc > 2)
+ /* back off */
+ usleep_range(500, 1000);
+
+ if (time_after(jiffies, end))
+ return 0;
+
+ if (have_drawable_releases && sc > 300) {
+ FENCE_WARN(fence, "failed to wait on release %d "
+ "after spincount %d\n",
+ fence->context & ~0xf0000000, sc);
+ goto signaled;
+ }
+ goto retry;
+ }
+ /*
+ * yeah, original sync_obj_wait gave up after 3 spins when
+ * have_drawable_releases is not set.
+ */
+
+signaled:
+ cur = jiffies;
+ if (time_after(cur, end))
+ return 0;
+ return end - cur;
+}
+
+static const struct fence_ops qxl_fence_ops = {
+ .get_driver_name = qxl_get_driver_name,
+ .get_timeline_name = qxl_get_timeline_name,
+ .enable_signaling = qxl_nop_signaling,
+ .wait = qxl_fence_wait,
+};
+
static uint64_t
qxl_release_alloc(struct qxl_device *qdev, int type,
struct qxl_release **ret)
@@ -46,13 +129,13 @@ qxl_release_alloc(struct qxl_device *qdev, int type,
struct qxl_release *release;
int handle;
size_t size = sizeof(*release);
- int idr_ret;
release = kmalloc(size, GFP_KERNEL);
if (!release) {
DRM_ERROR("Out of memory\n");
return 0;
}
+ release->base.ops = NULL;
release->type = type;
release->release_offset = 0;
release->surface_release_id = 0;
@@ -60,44 +143,61 @@ qxl_release_alloc(struct qxl_device *qdev, int type,
idr_preload(GFP_KERNEL);
spin_lock(&qdev->release_idr_lock);
- idr_ret = idr_alloc(&qdev->release_idr, release, 1, 0, GFP_NOWAIT);
+ handle = idr_alloc(&qdev->release_idr, release, 1, 0, GFP_NOWAIT);
+ release->base.seqno = ++qdev->release_seqno;
spin_unlock(&qdev->release_idr_lock);
idr_preload_end();
- handle = idr_ret;
- if (idr_ret < 0)
- goto release_fail;
+ if (handle < 0) {
+ kfree(release);
+ *ret = NULL;
+ return handle;
+ }
*ret = release;
QXL_INFO(qdev, "allocated release %lld\n", handle);
release->id = handle;
-release_fail:
-
return handle;
}
+static void
+qxl_release_free_list(struct qxl_release *release)
+{
+ while (!list_empty(&release->bos)) {
+ struct qxl_bo_list *entry;
+ struct qxl_bo *bo;
+
+ entry = container_of(release->bos.next,
+ struct qxl_bo_list, tv.head);
+ bo = to_qxl_bo(entry->tv.bo);
+ qxl_bo_unref(&bo);
+ list_del(&entry->tv.head);
+ kfree(entry);
+ }
+}
+
void
qxl_release_free(struct qxl_device *qdev,
struct qxl_release *release)
{
- struct qxl_bo_list *entry, *tmp;
QXL_INFO(qdev, "release %d, type %d\n", release->id,
release->type);
if (release->surface_release_id)
qxl_surface_id_dealloc(qdev, release->surface_release_id);
- list_for_each_entry_safe(entry, tmp, &release->bos, tv.head) {
- struct qxl_bo *bo = to_qxl_bo(entry->tv.bo);
- QXL_INFO(qdev, "release %llx\n",
- drm_vma_node_offset_addr(&entry->tv.bo->vma_node)
- - DRM_FILE_OFFSET);
- qxl_fence_remove_release(&bo->fence, release->id);
- qxl_bo_unref(&bo);
- kfree(entry);
- }
spin_lock(&qdev->release_idr_lock);
idr_remove(&qdev->release_idr, release->id);
spin_unlock(&qdev->release_idr_lock);
- kfree(release);
+
+ if (release->base.ops) {
+ WARN_ON(list_empty(&release->bos));
+ qxl_release_free_list(release);
+
+ fence_signal(&release->base);
+ fence_put(&release->base);
+ } else {
+ qxl_release_free_list(release);
+ kfree(release);
+ }
}
static int qxl_release_bo_alloc(struct qxl_device *qdev,
@@ -126,6 +226,7 @@ int qxl_release_list_add(struct qxl_release *release, struct qxl_bo *bo)
qxl_bo_ref(bo);
entry->tv.bo = &bo->tbo;
+ entry->tv.shared = false;
list_add_tail(&entry->tv.head, &release->bos);
return 0;
}
@@ -142,6 +243,10 @@ static int qxl_release_validate_bo(struct qxl_bo *bo)
return ret;
}
+ ret = reservation_object_reserve_shared(bo->tbo.resv);
+ if (ret)
+ return ret;
+
/* allocate a surface for reserved + validated buffers */
ret = qxl_bo_check_id(bo->gem_base.dev->dev_private, bo);
if (ret)
@@ -159,7 +264,7 @@ int qxl_release_reserve_list(struct qxl_release *release, bool no_intr)
if (list_is_singular(&release->bos))
return 0;
- ret = ttm_eu_reserve_buffers(&release->ticket, &release->bos);
+ ret = ttm_eu_reserve_buffers(&release->ticket, &release->bos, !no_intr);
if (ret)
return ret;
@@ -199,6 +304,8 @@ int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
/* stash the release after the create command */
idr_ret = qxl_release_alloc(qdev, QXL_RELEASE_SURFACE_CMD, release);
+ if (idr_ret < 0)
+ return idr_ret;
bo = qxl_bo_ref(to_qxl_bo(entry->tv.bo));
(*release)->release_offset = create_rel->release_offset + 64;
@@ -239,6 +346,11 @@ int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size,
}
idr_ret = qxl_release_alloc(qdev, type, release);
+ if (idr_ret < 0) {
+ if (rbo)
+ *rbo = NULL;
+ return idr_ret;
+ }
mutex_lock(&qdev->release_mutex);
if (qdev->current_release_bo_offset[cur_idx] + 1 >= releases_per_bo[cur_idx]) {
@@ -319,40 +431,44 @@ void qxl_release_unmap(struct qxl_device *qdev,
void qxl_release_fence_buffer_objects(struct qxl_release *release)
{
- struct ttm_validate_buffer *entry;
struct ttm_buffer_object *bo;
struct ttm_bo_global *glob;
struct ttm_bo_device *bdev;
struct ttm_bo_driver *driver;
struct qxl_bo *qbo;
+ struct ttm_validate_buffer *entry;
+ struct qxl_device *qdev;
/* if only one object on the release its the release itself
since these objects are pinned no need to reserve */
- if (list_is_singular(&release->bos))
+ if (list_is_singular(&release->bos) || list_empty(&release->bos))
return;
bo = list_first_entry(&release->bos, struct ttm_validate_buffer, head)->bo;
bdev = bo->bdev;
+ qdev = container_of(bdev, struct qxl_device, mman.bdev);
+
+ /*
+ * Since we never really allocated a context and we don't want to conflict,
+ * set the highest bits. This will break if we really allow exporting of dma-bufs.
+ */
+ fence_init(&release->base, &qxl_fence_ops, &qdev->release_lock,
+ release->id | 0xf0000000, release->base.seqno);
+ trace_fence_emit(&release->base);
+
driver = bdev->driver;
glob = bo->glob;
spin_lock(&glob->lru_lock);
- spin_lock(&bdev->fence_lock);
list_for_each_entry(entry, &release->bos, head) {
bo = entry->bo;
qbo = to_qxl_bo(bo);
- if (!entry->bo->sync_obj)
- entry->bo->sync_obj = &qbo->fence;
-
- qxl_fence_add_release_locked(&qbo->fence, release->id);
-
+ reservation_object_add_shared_fence(bo->resv, &release->base);
ttm_bo_add_to_lru(bo);
__ttm_bo_unreserve(bo);
- entry->reserved = false;
}
- spin_unlock(&bdev->fence_lock);
spin_unlock(&glob->lru_lock);
ww_acquire_fini(&release->ticket);
}
diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c
index 71a1baeac14e..0cbc4c987164 100644
--- a/drivers/gpu/drm/qxl/qxl_ttm.c
+++ b/drivers/gpu/drm/qxl/qxl_ttm.c
@@ -127,7 +127,7 @@ int qxl_mmap(struct file *filp, struct vm_area_struct *vma)
if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) {
pr_info("%s: vma->vm_pgoff (%ld) < DRM_FILE_PAGE_OFFSET\n",
__func__, vma->vm_pgoff);
- return drm_mmap(filp, vma);
+ return -EINVAL;
}
file_priv = filp->private_data;
@@ -188,11 +188,13 @@ static void qxl_evict_flags(struct ttm_buffer_object *bo,
struct ttm_placement *placement)
{
struct qxl_bo *qbo;
- static u32 placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ static struct ttm_place placements = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM
+ };
if (!qxl_ttm_bo_is_qxl_bo(bo)) {
- placement->fpfn = 0;
- placement->lpfn = 0;
placement->placement = &placements;
placement->busy_placement = &placements;
placement->num_placement = 1;
@@ -355,92 +357,6 @@ static int qxl_bo_move(struct ttm_buffer_object *bo,
return ttm_bo_move_memcpy(bo, evict, no_wait_gpu, new_mem);
}
-
-static int qxl_sync_obj_wait(void *sync_obj,
- bool lazy, bool interruptible)
-{
- struct qxl_fence *qfence = (struct qxl_fence *)sync_obj;
- int count = 0, sc = 0;
- struct qxl_bo *bo = container_of(qfence, struct qxl_bo, fence);
-
- if (qfence->num_active_releases == 0)
- return 0;
-
-retry:
- if (sc == 0) {
- if (bo->type == QXL_GEM_DOMAIN_SURFACE)
- qxl_update_surface(qfence->qdev, bo);
- } else if (sc >= 1) {
- qxl_io_notify_oom(qfence->qdev);
- }
-
- sc++;
-
- for (count = 0; count < 10; count++) {
- bool ret;
- ret = qxl_queue_garbage_collect(qfence->qdev, true);
- if (ret == false)
- break;
-
- if (qfence->num_active_releases == 0)
- return 0;
- }
-
- if (qfence->num_active_releases) {
- bool have_drawable_releases = false;
- void **slot;
- struct radix_tree_iter iter;
- int release_id;
-
- radix_tree_for_each_slot(slot, &qfence->tree, &iter, 0) {
- struct qxl_release *release;
-
- release_id = iter.index;
- release = qxl_release_from_id_locked(qfence->qdev, release_id);
- if (release == NULL)
- continue;
-
- if (release->type == QXL_RELEASE_DRAWABLE)
- have_drawable_releases = true;
- }
-
- qxl_queue_garbage_collect(qfence->qdev, true);
-
- if (have_drawable_releases || sc < 4) {
- if (sc > 2)
- /* back off */
- usleep_range(500, 1000);
- if (have_drawable_releases && sc > 300) {
- WARN(1, "sync obj %d still has outstanding releases %d %d %d %ld %d\n", sc, bo->surface_id, bo->is_primary, bo->pin_count, (unsigned long)bo->gem_base.size, qfence->num_active_releases);
- return -EBUSY;
- }
- goto retry;
- }
- }
- return 0;
-}
-
-static int qxl_sync_obj_flush(void *sync_obj)
-{
- return 0;
-}
-
-static void qxl_sync_obj_unref(void **sync_obj)
-{
- *sync_obj = NULL;
-}
-
-static void *qxl_sync_obj_ref(void *sync_obj)
-{
- return sync_obj;
-}
-
-static bool qxl_sync_obj_signaled(void *sync_obj)
-{
- struct qxl_fence *qfence = (struct qxl_fence *)sync_obj;
- return (qfence->num_active_releases == 0);
-}
-
static void qxl_bo_move_notify(struct ttm_buffer_object *bo,
struct ttm_mem_reg *new_mem)
{
@@ -467,16 +383,9 @@ static struct ttm_bo_driver qxl_bo_driver = {
.verify_access = &qxl_verify_access,
.io_mem_reserve = &qxl_ttm_io_mem_reserve,
.io_mem_free = &qxl_ttm_io_mem_free,
- .sync_obj_signaled = &qxl_sync_obj_signaled,
- .sync_obj_wait = &qxl_sync_obj_wait,
- .sync_obj_flush = &qxl_sync_obj_flush,
- .sync_obj_unref = &qxl_sync_obj_unref,
- .sync_obj_ref = &qxl_sync_obj_ref,
.move_notify = &qxl_bo_move_notify,
};
-
-
int qxl_ttm_init(struct qxl_device *qdev)
{
int r;
diff --git a/drivers/gpu/drm/r128/r128_cce.c b/drivers/gpu/drm/r128/r128_cce.c
index 59459fe4e8c5..2c45ac9c1dc3 100644
--- a/drivers/gpu/drm/r128/r128_cce.c
+++ b/drivers/gpu/drm/r128/r128_cce.c
@@ -452,7 +452,7 @@ static int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init)
dev_priv->span_pitch_offset_c = (((dev_priv->depth_pitch / 8) << 21) |
(dev_priv->span_offset >> 5));
- dev_priv->sarea = drm_getsarea(dev);
+ dev_priv->sarea = drm_legacy_getsarea(dev);
if (!dev_priv->sarea) {
DRM_ERROR("could not find sarea!\n");
dev->dev_private = (void *)dev_priv;
@@ -460,21 +460,21 @@ static int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init)
return -EINVAL;
}
- dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset);
+ dev_priv->mmio = drm_legacy_findmap(dev, init->mmio_offset);
if (!dev_priv->mmio) {
DRM_ERROR("could not find mmio region!\n");
dev->dev_private = (void *)dev_priv;
r128_do_cleanup_cce(dev);
return -EINVAL;
}
- dev_priv->cce_ring = drm_core_findmap(dev, init->ring_offset);
+ dev_priv->cce_ring = drm_legacy_findmap(dev, init->ring_offset);
if (!dev_priv->cce_ring) {
DRM_ERROR("could not find cce ring region!\n");
dev->dev_private = (void *)dev_priv;
r128_do_cleanup_cce(dev);
return -EINVAL;
}
- dev_priv->ring_rptr = drm_core_findmap(dev, init->ring_rptr_offset);
+ dev_priv->ring_rptr = drm_legacy_findmap(dev, init->ring_rptr_offset);
if (!dev_priv->ring_rptr) {
DRM_ERROR("could not find ring read pointer!\n");
dev->dev_private = (void *)dev_priv;
@@ -482,7 +482,7 @@ static int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init)
return -EINVAL;
}
dev->agp_buffer_token = init->buffers_offset;
- dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset);
+ dev->agp_buffer_map = drm_legacy_findmap(dev, init->buffers_offset);
if (!dev->agp_buffer_map) {
DRM_ERROR("could not find dma buffer region!\n");
dev->dev_private = (void *)dev_priv;
@@ -492,7 +492,7 @@ static int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init)
if (!dev_priv->is_pci) {
dev_priv->agp_textures =
- drm_core_findmap(dev, init->agp_textures_offset);
+ drm_legacy_findmap(dev, init->agp_textures_offset);
if (!dev_priv->agp_textures) {
DRM_ERROR("could not find agp texture region!\n");
dev->dev_private = (void *)dev_priv;
@@ -507,9 +507,9 @@ static int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init)
#if __OS_HAS_AGP
if (!dev_priv->is_pci) {
- drm_core_ioremap_wc(dev_priv->cce_ring, dev);
- drm_core_ioremap_wc(dev_priv->ring_rptr, dev);
- drm_core_ioremap_wc(dev->agp_buffer_map, dev);
+ drm_legacy_ioremap_wc(dev_priv->cce_ring, dev);
+ drm_legacy_ioremap_wc(dev_priv->ring_rptr, dev);
+ drm_legacy_ioremap_wc(dev->agp_buffer_map, dev);
if (!dev_priv->cce_ring->handle ||
!dev_priv->ring_rptr->handle ||
!dev->agp_buffer_map->handle) {
@@ -603,11 +603,11 @@ int r128_do_cleanup_cce(struct drm_device *dev)
#if __OS_HAS_AGP
if (!dev_priv->is_pci) {
if (dev_priv->cce_ring != NULL)
- drm_core_ioremapfree(dev_priv->cce_ring, dev);
+ drm_legacy_ioremapfree(dev_priv->cce_ring, dev);
if (dev_priv->ring_rptr != NULL)
- drm_core_ioremapfree(dev_priv->ring_rptr, dev);
+ drm_legacy_ioremapfree(dev_priv->ring_rptr, dev);
if (dev->agp_buffer_map != NULL) {
- drm_core_ioremapfree(dev->agp_buffer_map, dev);
+ drm_legacy_ioremapfree(dev->agp_buffer_map, dev);
dev->agp_buffer_map = NULL;
}
} else
diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c
index 5bd307cd8da1..c57b4de63caf 100644
--- a/drivers/gpu/drm/r128/r128_drv.c
+++ b/drivers/gpu/drm/r128/r128_drv.c
@@ -46,7 +46,7 @@ static const struct file_operations r128_driver_fops = {
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
- .mmap = drm_mmap,
+ .mmap = drm_legacy_mmap,
.poll = drm_poll,
#ifdef CONFIG_COMPAT
.compat_ioctl = r128_compat_ioctl,
@@ -62,6 +62,7 @@ static struct drm_driver driver = {
.load = r128_driver_load,
.preclose = r128_driver_preclose,
.lastclose = r128_driver_lastclose,
+ .set_busid = drm_pci_set_busid,
.get_vblank_counter = r128_get_vblank_counter,
.enable_vblank = r128_enable_vblank,
.disable_vblank = r128_disable_vblank,
diff --git a/drivers/gpu/drm/r128/r128_drv.h b/drivers/gpu/drm/r128/r128_drv.h
index 5bf3f5ff805d..723e5d6f10a4 100644
--- a/drivers/gpu/drm/r128/r128_drv.h
+++ b/drivers/gpu/drm/r128/r128_drv.h
@@ -35,6 +35,9 @@
#ifndef __R128_DRV_H__
#define __R128_DRV_H__
+#include <drm/ati_pcigart.h>
+#include <drm/drm_legacy.h>
+
/* General customization:
*/
#define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc."
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
index dbcbfe80aac0..d01b87991422 100644
--- a/drivers/gpu/drm/radeon/Makefile
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -60,7 +60,7 @@ radeon-y := radeon_drv.o
# add UMS driver
radeon-$(CONFIG_DRM_RADEON_UMS)+= radeon_cp.o radeon_state.o radeon_mem.o \
- radeon_irq.o r300_cmdbuf.o r600_cp.o r600_blit.o
+ radeon_irq.o r300_cmdbuf.o r600_cp.o r600_blit.o drm_buffer.o
# add KMS driver
radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
@@ -72,15 +72,15 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \
rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \
r200.o radeon_legacy_tv.o r600_cs.o r600_blit_shaders.o \
- radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o dce3_1_afmt.o \
+ radeon_pm.o atombios_dp.o r600_hdmi.o dce3_1_afmt.o \
evergreen.o evergreen_cs.o evergreen_blit_shaders.o \
evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \
atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \
- si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \
+ si_blit_shaders.o radeon_prime.o cik.o cik_blit_shaders.o \
r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \
rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \
trinity_smc.o ni_dpm.o si_smc.o si_dpm.o kv_smc.o kv_dpm.o ci_smc.o \
- ci_dpm.o dce6_afmt.o radeon_vm.o
+ ci_dpm.o dce6_afmt.o radeon_vm.o radeon_ucode.o radeon_ib.o radeon_mn.o
# add async DMA block
radeon-y += \
diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c
index 15da7ef344a4..ec1593a6a561 100644
--- a/drivers/gpu/drm/radeon/atom.c
+++ b/drivers/gpu/drm/radeon/atom.c
@@ -1217,7 +1217,7 @@ free:
return ret;
}
-int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
+int atom_execute_table_scratch_unlocked(struct atom_context *ctx, int index, uint32_t * params)
{
int r;
@@ -1238,6 +1238,15 @@ int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
return r;
}
+int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
+{
+ int r;
+ mutex_lock(&ctx->scratch_mutex);
+ r = atom_execute_table_scratch_unlocked(ctx, index, params);
+ mutex_unlock(&ctx->scratch_mutex);
+ return r;
+}
+
static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
static void atom_index_iio(struct atom_context *ctx, int base)
diff --git a/drivers/gpu/drm/radeon/atom.h b/drivers/gpu/drm/radeon/atom.h
index feba6b8d36b3..6d014ddb6b78 100644
--- a/drivers/gpu/drm/radeon/atom.h
+++ b/drivers/gpu/drm/radeon/atom.h
@@ -125,6 +125,7 @@ struct card_info {
struct atom_context {
struct card_info *card;
struct mutex mutex;
+ struct mutex scratch_mutex;
void *bios;
uint32_t cmd_table, data_table;
uint16_t *iio;
@@ -145,6 +146,7 @@ extern int atom_debug;
struct atom_context *atom_parse(struct card_info *, void *);
int atom_execute_table(struct atom_context *, int, uint32_t *);
+int atom_execute_table_scratch_unlocked(struct atom_context *, int, uint32_t *);
int atom_asic_init(struct atom_context *);
void atom_destroy(struct atom_context *);
bool atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size,
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index b1e11f8434e2..11ba9d21b89b 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -100,6 +100,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
memset(&args, 0, sizeof(args));
mutex_lock(&chan->mutex);
+ mutex_lock(&rdev->mode_info.atom_context->scratch_mutex);
base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1);
@@ -113,7 +114,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
if (ASIC_IS_DCE4(rdev))
args.v2.ucHPD_ID = chan->rec.hpd;
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ atom_execute_table_scratch_unlocked(rdev->mode_info.atom_context, index, (uint32_t *)&args);
*ack = args.v1.ucReplyStatus;
@@ -147,6 +148,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
r = recv_bytes;
done:
+ mutex_unlock(&rdev->mode_info.atom_context->scratch_mutex);
mutex_unlock(&chan->mutex);
return r;
@@ -232,8 +234,8 @@ void radeon_dp_aux_init(struct radeon_connector *radeon_connector)
/***** general DP utility functions *****/
-#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200
-#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPHASIS_9_5
+#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_LEVEL_3
+#define DP_PRE_EMPHASIS_MAX DP_TRAIN_PRE_EMPH_LEVEL_3
static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
int lane_count,
@@ -405,16 +407,13 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector)
u8 msg[DP_DPCD_SIZE];
int ret;
- char dpcd_hex_dump[DP_DPCD_SIZE * 3];
-
ret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_DPCD_REV, msg,
DP_DPCD_SIZE);
if (ret > 0) {
memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE);
- hex_dump_to_buffer(dig_connector->dpcd, sizeof(dig_connector->dpcd),
- 32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false);
- DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump);
+ DRM_DEBUG_KMS("DPCD: %*ph\n", (int)sizeof(dig_connector->dpcd),
+ dig_connector->dpcd);
radeon_dp_probe_oui(radeon_connector);
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index 7d68203a3737..b8cd7975f797 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -291,29 +291,6 @@ static void radeon_atom_backlight_exit(struct radeon_encoder *encoder)
bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
struct drm_display_mode *mode);
-
-static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder)
-{
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_LVDS:
- case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
- case ENCODER_OBJECT_ID_INTERNAL_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_DDI:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
- return true;
- default:
- return false;
- }
-}
-
static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@@ -331,12 +308,10 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
&& (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2)))
adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2;
- /* get the native mode for LVDS */
- if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT))
+ /* get the native mode for scaling */
+ if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
radeon_panel_mode_fixup(encoder, adjusted_mode);
-
- /* get the native mode for TV */
- if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
+ } else if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv;
if (tv_dac) {
if (tv_dac->tv_std == TV_STD_NTSC ||
@@ -346,6 +321,8 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
else
radeon_atom_get_tv_timings(rdev, 1, adjusted_mode);
}
+ } else if (radeon_encoder->rmx_type != RMX_OFF) {
+ radeon_panel_mode_fixup(encoder, adjusted_mode);
}
if (ASIC_IS_DCE3(rdev) &&
@@ -716,7 +693,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
if (radeon_connector->use_digital &&
(radeon_connector->audio == RADEON_AUDIO_ENABLE))
return ATOM_ENCODER_MODE_HDMI;
- else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+ else if (drm_detect_hdmi_monitor(radeon_connector_edid(connector)) &&
(radeon_connector->audio == RADEON_AUDIO_AUTO))
return ATOM_ENCODER_MODE_HDMI;
else if (radeon_connector->use_digital)
@@ -735,7 +712,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
if (radeon_audio != 0) {
if (radeon_connector->audio == RADEON_AUDIO_ENABLE)
return ATOM_ENCODER_MODE_HDMI;
- else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+ else if (drm_detect_hdmi_monitor(radeon_connector_edid(connector)) &&
(radeon_connector->audio == RADEON_AUDIO_AUTO))
return ATOM_ENCODER_MODE_HDMI;
else
@@ -755,7 +732,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
} else if (radeon_audio != 0) {
if (radeon_connector->audio == RADEON_AUDIO_ENABLE)
return ATOM_ENCODER_MODE_HDMI;
- else if (drm_detect_hdmi_monitor(radeon_connector->edid) &&
+ else if (drm_detect_hdmi_monitor(radeon_connector_edid(connector)) &&
(radeon_connector->audio == RADEON_AUDIO_AUTO))
return ATOM_ENCODER_MODE_HDMI;
else
diff --git a/drivers/gpu/drm/radeon/atombios_i2c.c b/drivers/gpu/drm/radeon/atombios_i2c.c
index 9c570fb15b8c..4157780585a0 100644
--- a/drivers/gpu/drm/radeon/atombios_i2c.c
+++ b/drivers/gpu/drm/radeon/atombios_i2c.c
@@ -48,6 +48,7 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
memset(&args, 0, sizeof(args));
mutex_lock(&chan->mutex);
+ mutex_lock(&rdev->mode_info.atom_context->scratch_mutex);
base = (unsigned char *)rdev->mode_info.atom_context->scratch;
@@ -82,7 +83,7 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
args.ucSlaveAddr = slave_addr << 1;
args.ucLineNumber = chan->rec.i2c_id;
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ atom_execute_table_scratch_unlocked(rdev->mode_info.atom_context, index, (uint32_t *)&args);
/* error */
if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) {
@@ -95,6 +96,7 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
radeon_atom_copy_swap(buf, base, num, false);
done:
+ mutex_unlock(&rdev->mode_info.atom_context->scratch_mutex);
mutex_unlock(&chan->mutex);
return r;
diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c
index f81d7ca134db..0b2929de9f41 100644
--- a/drivers/gpu/drm/radeon/btc_dpm.c
+++ b/drivers/gpu/drm/radeon/btc_dpm.c
@@ -24,6 +24,7 @@
#include "drmP.h"
#include "radeon.h"
+#include "radeon_asic.h"
#include "btcd.h"
#include "r600_dpm.h"
#include "cypress_dpm.h"
@@ -2099,7 +2100,6 @@ static void btc_apply_state_adjust_rules(struct radeon_device *rdev,
bool disable_mclk_switching;
u32 mclk, sclk;
u16 vddc, vddci;
- u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
btc_dpm_vblank_too_short(rdev))
@@ -2141,39 +2141,6 @@ static void btc_apply_state_adjust_rules(struct radeon_device *rdev,
ps->low.vddci = max_limits->vddci;
}
- /* limit clocks to max supported clocks based on voltage dependency tables */
- btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
- &max_sclk_vddc);
- btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
- &max_mclk_vddci);
- btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
- &max_mclk_vddc);
-
- if (max_sclk_vddc) {
- if (ps->low.sclk > max_sclk_vddc)
- ps->low.sclk = max_sclk_vddc;
- if (ps->medium.sclk > max_sclk_vddc)
- ps->medium.sclk = max_sclk_vddc;
- if (ps->high.sclk > max_sclk_vddc)
- ps->high.sclk = max_sclk_vddc;
- }
- if (max_mclk_vddci) {
- if (ps->low.mclk > max_mclk_vddci)
- ps->low.mclk = max_mclk_vddci;
- if (ps->medium.mclk > max_mclk_vddci)
- ps->medium.mclk = max_mclk_vddci;
- if (ps->high.mclk > max_mclk_vddci)
- ps->high.mclk = max_mclk_vddci;
- }
- if (max_mclk_vddc) {
- if (ps->low.mclk > max_mclk_vddc)
- ps->low.mclk = max_mclk_vddc;
- if (ps->medium.mclk > max_mclk_vddc)
- ps->medium.mclk = max_mclk_vddc;
- if (ps->high.mclk > max_mclk_vddc)
- ps->high.mclk = max_mclk_vddc;
- }
-
/* XXX validate the min clocks required for display */
if (disable_mclk_switching) {
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c
index 584090ac3eb9..11a55e9dad7f 100644
--- a/drivers/gpu/drm/radeon/ci_dpm.c
+++ b/drivers/gpu/drm/radeon/ci_dpm.c
@@ -24,6 +24,7 @@
#include <linux/firmware.h>
#include "drmP.h"
#include "radeon.h"
+#include "radeon_asic.h"
#include "radeon_ucode.h"
#include "cikd.h"
#include "r600_dpm.h"
@@ -162,8 +163,6 @@ static const struct ci_pt_config_reg didt_config_ci[] =
};
extern u8 rv770_get_memory_module_index(struct radeon_device *rdev);
-extern void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table,
- u32 *max_clock);
extern int ni_copy_and_switch_arb_sets(struct radeon_device *rdev,
u32 arb_freq_src, u32 arb_freq_dest);
extern u8 si_get_ddr3_mclk_frequency_ratio(u32 memory_clock);
@@ -748,7 +747,6 @@ static void ci_apply_state_adjust_rules(struct radeon_device *rdev,
struct radeon_clock_and_voltage_limits *max_limits;
bool disable_mclk_switching;
u32 sclk, mclk;
- u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
int i;
if (rps->vce_active) {
@@ -784,29 +782,6 @@ static void ci_apply_state_adjust_rules(struct radeon_device *rdev,
}
}
- /* limit clocks to max supported clocks based on voltage dependency tables */
- btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
- &max_sclk_vddc);
- btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
- &max_mclk_vddci);
- btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
- &max_mclk_vddc);
-
- for (i = 0; i < ps->performance_level_count; i++) {
- if (max_sclk_vddc) {
- if (ps->performance_levels[i].sclk > max_sclk_vddc)
- ps->performance_levels[i].sclk = max_sclk_vddc;
- }
- if (max_mclk_vddci) {
- if (ps->performance_levels[i].mclk > max_mclk_vddci)
- ps->performance_levels[i].mclk = max_mclk_vddci;
- }
- if (max_mclk_vddc) {
- if (ps->performance_levels[i].mclk > max_mclk_vddc)
- ps->performance_levels[i].mclk = max_mclk_vddc;
- }
- }
-
/* XXX validate the min clocks required for display */
if (disable_mclk_switching) {
@@ -869,6 +844,9 @@ static int ci_set_thermal_temperature_range(struct radeon_device *rdev,
WREG32_SMC(CG_THERMAL_CTRL, tmp);
#endif
+ rdev->pm.dpm.thermal.min_temp = low_temp;
+ rdev->pm.dpm.thermal.max_temp = high_temp;
+
return 0;
}
@@ -940,7 +918,18 @@ static void ci_get_leakage_voltages(struct radeon_device *rdev)
pi->vddc_leakage.count = 0;
pi->vddci_leakage.count = 0;
- if (radeon_atom_get_leakage_id_from_vbios(rdev, &leakage_id) == 0) {
+ if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_EVV) {
+ for (i = 0; i < CISLANDS_MAX_LEAKAGE_COUNT; i++) {
+ virtual_voltage_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
+ if (radeon_atom_get_voltage_evv(rdev, virtual_voltage_id, &vddc) != 0)
+ continue;
+ if (vddc != 0 && vddc != virtual_voltage_id) {
+ pi->vddc_leakage.actual_voltage[pi->vddc_leakage.count] = vddc;
+ pi->vddc_leakage.leakage_id[pi->vddc_leakage.count] = virtual_voltage_id;
+ pi->vddc_leakage.count++;
+ }
+ }
+ } else if (radeon_atom_get_leakage_id_from_vbios(rdev, &leakage_id) == 0) {
for (i = 0; i < CISLANDS_MAX_LEAKAGE_COUNT; i++) {
virtual_voltage_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
if (radeon_atom_get_leakage_vddc_based_on_leakage_params(rdev, &vddc, &vddci,
@@ -5279,9 +5268,13 @@ int ci_dpm_init(struct radeon_device *rdev)
void ci_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
struct seq_file *m)
{
+ struct ci_power_info *pi = ci_get_pi(rdev);
+ struct radeon_ps *rps = &pi->current_rps;
u32 sclk = ci_get_average_sclk_freq(rdev);
u32 mclk = ci_get_average_mclk_freq(rdev);
+ seq_printf(m, "uvd %sabled\n", pi->uvd_enabled ? "en" : "dis");
+ seq_printf(m, "vce %sabled\n", rps->vce_active ? "en" : "dis");
seq_printf(m, "power level avg sclk: %u mclk: %u\n",
sclk, mclk);
}
diff --git a/drivers/gpu/drm/radeon/ci_smc.c b/drivers/gpu/drm/radeon/ci_smc.c
index 8debc9d47362..b630edc2fd0c 100644
--- a/drivers/gpu/drm/radeon/ci_smc.c
+++ b/drivers/gpu/drm/radeon/ci_smc.c
@@ -213,24 +213,37 @@ int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
if (!rdev->smc_fw)
return -EINVAL;
- switch (rdev->family) {
- case CHIP_BONAIRE:
- ucode_start_address = BONAIRE_SMC_UCODE_START;
- ucode_size = BONAIRE_SMC_UCODE_SIZE;
- break;
- case CHIP_HAWAII:
- ucode_start_address = HAWAII_SMC_UCODE_START;
- ucode_size = HAWAII_SMC_UCODE_SIZE;
- break;
- default:
- DRM_ERROR("unknown asic in smc ucode loader\n");
- BUG();
+ if (rdev->new_fw) {
+ const struct smc_firmware_header_v1_0 *hdr =
+ (const struct smc_firmware_header_v1_0 *)rdev->smc_fw->data;
+
+ radeon_ucode_print_smc_hdr(&hdr->header);
+
+ ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
+ ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
+ src = (const u8 *)
+ (rdev->smc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ } else {
+ switch (rdev->family) {
+ case CHIP_BONAIRE:
+ ucode_start_address = BONAIRE_SMC_UCODE_START;
+ ucode_size = BONAIRE_SMC_UCODE_SIZE;
+ break;
+ case CHIP_HAWAII:
+ ucode_start_address = HAWAII_SMC_UCODE_START;
+ ucode_size = HAWAII_SMC_UCODE_SIZE;
+ break;
+ default:
+ DRM_ERROR("unknown asic in smc ucode loader\n");
+ BUG();
+ }
+
+ src = (const u8 *)rdev->smc_fw->data;
}
if (ucode_size & 3)
return -EINVAL;
- src = (const u8 *)rdev->smc_fw->data;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
WREG32(SMC_IND_INDEX_0, ucode_start_address);
WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
index c0ea66192fe0..89c01fa6dd8e 100644
--- a/drivers/gpu/drm/radeon/cik.c
+++ b/drivers/gpu/drm/radeon/cik.c
@@ -42,6 +42,16 @@ MODULE_FIRMWARE("radeon/BONAIRE_mc2.bin");
MODULE_FIRMWARE("radeon/BONAIRE_rlc.bin");
MODULE_FIRMWARE("radeon/BONAIRE_sdma.bin");
MODULE_FIRMWARE("radeon/BONAIRE_smc.bin");
+
+MODULE_FIRMWARE("radeon/bonaire_pfp.bin");
+MODULE_FIRMWARE("radeon/bonaire_me.bin");
+MODULE_FIRMWARE("radeon/bonaire_ce.bin");
+MODULE_FIRMWARE("radeon/bonaire_mec.bin");
+MODULE_FIRMWARE("radeon/bonaire_mc.bin");
+MODULE_FIRMWARE("radeon/bonaire_rlc.bin");
+MODULE_FIRMWARE("radeon/bonaire_sdma.bin");
+MODULE_FIRMWARE("radeon/bonaire_smc.bin");
+
MODULE_FIRMWARE("radeon/HAWAII_pfp.bin");
MODULE_FIRMWARE("radeon/HAWAII_me.bin");
MODULE_FIRMWARE("radeon/HAWAII_ce.bin");
@@ -51,18 +61,45 @@ MODULE_FIRMWARE("radeon/HAWAII_mc2.bin");
MODULE_FIRMWARE("radeon/HAWAII_rlc.bin");
MODULE_FIRMWARE("radeon/HAWAII_sdma.bin");
MODULE_FIRMWARE("radeon/HAWAII_smc.bin");
+
+MODULE_FIRMWARE("radeon/hawaii_pfp.bin");
+MODULE_FIRMWARE("radeon/hawaii_me.bin");
+MODULE_FIRMWARE("radeon/hawaii_ce.bin");
+MODULE_FIRMWARE("radeon/hawaii_mec.bin");
+MODULE_FIRMWARE("radeon/hawaii_mc.bin");
+MODULE_FIRMWARE("radeon/hawaii_rlc.bin");
+MODULE_FIRMWARE("radeon/hawaii_sdma.bin");
+MODULE_FIRMWARE("radeon/hawaii_smc.bin");
+
MODULE_FIRMWARE("radeon/KAVERI_pfp.bin");
MODULE_FIRMWARE("radeon/KAVERI_me.bin");
MODULE_FIRMWARE("radeon/KAVERI_ce.bin");
MODULE_FIRMWARE("radeon/KAVERI_mec.bin");
MODULE_FIRMWARE("radeon/KAVERI_rlc.bin");
MODULE_FIRMWARE("radeon/KAVERI_sdma.bin");
+
+MODULE_FIRMWARE("radeon/kaveri_pfp.bin");
+MODULE_FIRMWARE("radeon/kaveri_me.bin");
+MODULE_FIRMWARE("radeon/kaveri_ce.bin");
+MODULE_FIRMWARE("radeon/kaveri_mec.bin");
+MODULE_FIRMWARE("radeon/kaveri_mec2.bin");
+MODULE_FIRMWARE("radeon/kaveri_rlc.bin");
+MODULE_FIRMWARE("radeon/kaveri_sdma.bin");
+
MODULE_FIRMWARE("radeon/KABINI_pfp.bin");
MODULE_FIRMWARE("radeon/KABINI_me.bin");
MODULE_FIRMWARE("radeon/KABINI_ce.bin");
MODULE_FIRMWARE("radeon/KABINI_mec.bin");
MODULE_FIRMWARE("radeon/KABINI_rlc.bin");
MODULE_FIRMWARE("radeon/KABINI_sdma.bin");
+
+MODULE_FIRMWARE("radeon/kabini_pfp.bin");
+MODULE_FIRMWARE("radeon/kabini_me.bin");
+MODULE_FIRMWARE("radeon/kabini_ce.bin");
+MODULE_FIRMWARE("radeon/kabini_mec.bin");
+MODULE_FIRMWARE("radeon/kabini_rlc.bin");
+MODULE_FIRMWARE("radeon/kabini_sdma.bin");
+
MODULE_FIRMWARE("radeon/MULLINS_pfp.bin");
MODULE_FIRMWARE("radeon/MULLINS_me.bin");
MODULE_FIRMWARE("radeon/MULLINS_ce.bin");
@@ -70,6 +107,13 @@ MODULE_FIRMWARE("radeon/MULLINS_mec.bin");
MODULE_FIRMWARE("radeon/MULLINS_rlc.bin");
MODULE_FIRMWARE("radeon/MULLINS_sdma.bin");
+MODULE_FIRMWARE("radeon/mullins_pfp.bin");
+MODULE_FIRMWARE("radeon/mullins_me.bin");
+MODULE_FIRMWARE("radeon/mullins_ce.bin");
+MODULE_FIRMWARE("radeon/mullins_mec.bin");
+MODULE_FIRMWARE("radeon/mullins_rlc.bin");
+MODULE_FIRMWARE("radeon/mullins_sdma.bin");
+
extern int r600_ih_ring_alloc(struct radeon_device *rdev);
extern void r600_ih_ring_fini(struct radeon_device *rdev);
extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save);
@@ -1760,27 +1804,44 @@ static void cik_srbm_select(struct radeon_device *rdev,
*/
int ci_mc_load_microcode(struct radeon_device *rdev)
{
- const __be32 *fw_data;
+ const __be32 *fw_data = NULL;
+ const __le32 *new_fw_data = NULL;
u32 running, blackout = 0;
- u32 *io_mc_regs;
+ u32 *io_mc_regs = NULL;
+ const __le32 *new_io_mc_regs = NULL;
int i, regs_size, ucode_size;
if (!rdev->mc_fw)
return -EINVAL;
- ucode_size = rdev->mc_fw->size / 4;
+ if (rdev->new_fw) {
+ const struct mc_firmware_header_v1_0 *hdr =
+ (const struct mc_firmware_header_v1_0 *)rdev->mc_fw->data;
- switch (rdev->family) {
- case CHIP_BONAIRE:
- io_mc_regs = (u32 *)&bonaire_io_mc_regs;
- regs_size = BONAIRE_IO_MC_REGS_SIZE;
- break;
- case CHIP_HAWAII:
- io_mc_regs = (u32 *)&hawaii_io_mc_regs;
- regs_size = HAWAII_IO_MC_REGS_SIZE;
- break;
- default:
- return -EINVAL;
+ radeon_ucode_print_mc_hdr(&hdr->header);
+
+ regs_size = le32_to_cpu(hdr->io_debug_size_bytes) / (4 * 2);
+ new_io_mc_regs = (const __le32 *)
+ (rdev->mc_fw->data + le32_to_cpu(hdr->io_debug_array_offset_bytes));
+ ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
+ new_fw_data = (const __le32 *)
+ (rdev->mc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ } else {
+ ucode_size = rdev->mc_fw->size / 4;
+
+ switch (rdev->family) {
+ case CHIP_BONAIRE:
+ io_mc_regs = (u32 *)&bonaire_io_mc_regs;
+ regs_size = BONAIRE_IO_MC_REGS_SIZE;
+ break;
+ case CHIP_HAWAII:
+ io_mc_regs = (u32 *)&hawaii_io_mc_regs;
+ regs_size = HAWAII_IO_MC_REGS_SIZE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ fw_data = (const __be32 *)rdev->mc_fw->data;
}
running = RREG32(MC_SEQ_SUP_CNTL) & RUN_MASK;
@@ -1797,13 +1858,21 @@ int ci_mc_load_microcode(struct radeon_device *rdev)
/* load mc io regs */
for (i = 0; i < regs_size; i++) {
- WREG32(MC_SEQ_IO_DEBUG_INDEX, io_mc_regs[(i << 1)]);
- WREG32(MC_SEQ_IO_DEBUG_DATA, io_mc_regs[(i << 1) + 1]);
+ if (rdev->new_fw) {
+ WREG32(MC_SEQ_IO_DEBUG_INDEX, le32_to_cpup(new_io_mc_regs++));
+ WREG32(MC_SEQ_IO_DEBUG_DATA, le32_to_cpup(new_io_mc_regs++));
+ } else {
+ WREG32(MC_SEQ_IO_DEBUG_INDEX, io_mc_regs[(i << 1)]);
+ WREG32(MC_SEQ_IO_DEBUG_DATA, io_mc_regs[(i << 1) + 1]);
+ }
}
/* load the MC ucode */
- fw_data = (const __be32 *)rdev->mc_fw->data;
- for (i = 0; i < ucode_size; i++)
- WREG32(MC_SEQ_SUP_PGM, be32_to_cpup(fw_data++));
+ for (i = 0; i < ucode_size; i++) {
+ if (rdev->new_fw)
+ WREG32(MC_SEQ_SUP_PGM, le32_to_cpup(new_fw_data++));
+ else
+ WREG32(MC_SEQ_SUP_PGM, be32_to_cpup(fw_data++));
+ }
/* put the engine back into the active state */
WREG32(MC_SEQ_SUP_CNTL, 0x00000008);
@@ -1841,17 +1910,21 @@ int ci_mc_load_microcode(struct radeon_device *rdev)
static int cik_init_microcode(struct radeon_device *rdev)
{
const char *chip_name;
+ const char *new_chip_name;
size_t pfp_req_size, me_req_size, ce_req_size,
mec_req_size, rlc_req_size, mc_req_size = 0,
sdma_req_size, smc_req_size = 0, mc2_req_size = 0;
char fw_name[30];
+ int new_fw = 0;
int err;
+ int num_fw;
DRM_DEBUG("\n");
switch (rdev->family) {
case CHIP_BONAIRE:
chip_name = "BONAIRE";
+ new_chip_name = "bonaire";
pfp_req_size = CIK_PFP_UCODE_SIZE * 4;
me_req_size = CIK_ME_UCODE_SIZE * 4;
ce_req_size = CIK_CE_UCODE_SIZE * 4;
@@ -1861,9 +1934,11 @@ static int cik_init_microcode(struct radeon_device *rdev)
mc2_req_size = BONAIRE_MC2_UCODE_SIZE * 4;
sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
smc_req_size = ALIGN(BONAIRE_SMC_UCODE_SIZE, 4);
+ num_fw = 8;
break;
case CHIP_HAWAII:
chip_name = "HAWAII";
+ new_chip_name = "hawaii";
pfp_req_size = CIK_PFP_UCODE_SIZE * 4;
me_req_size = CIK_ME_UCODE_SIZE * 4;
ce_req_size = CIK_CE_UCODE_SIZE * 4;
@@ -1873,142 +1948,285 @@ static int cik_init_microcode(struct radeon_device *rdev)
mc2_req_size = HAWAII_MC2_UCODE_SIZE * 4;
sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
smc_req_size = ALIGN(HAWAII_SMC_UCODE_SIZE, 4);
+ num_fw = 8;
break;
case CHIP_KAVERI:
chip_name = "KAVERI";
+ new_chip_name = "kaveri";
pfp_req_size = CIK_PFP_UCODE_SIZE * 4;
me_req_size = CIK_ME_UCODE_SIZE * 4;
ce_req_size = CIK_CE_UCODE_SIZE * 4;
mec_req_size = CIK_MEC_UCODE_SIZE * 4;
rlc_req_size = KV_RLC_UCODE_SIZE * 4;
sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
+ num_fw = 7;
break;
case CHIP_KABINI:
chip_name = "KABINI";
+ new_chip_name = "kabini";
pfp_req_size = CIK_PFP_UCODE_SIZE * 4;
me_req_size = CIK_ME_UCODE_SIZE * 4;
ce_req_size = CIK_CE_UCODE_SIZE * 4;
mec_req_size = CIK_MEC_UCODE_SIZE * 4;
rlc_req_size = KB_RLC_UCODE_SIZE * 4;
sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
+ num_fw = 6;
break;
case CHIP_MULLINS:
chip_name = "MULLINS";
+ new_chip_name = "mullins";
pfp_req_size = CIK_PFP_UCODE_SIZE * 4;
me_req_size = CIK_ME_UCODE_SIZE * 4;
ce_req_size = CIK_CE_UCODE_SIZE * 4;
mec_req_size = CIK_MEC_UCODE_SIZE * 4;
rlc_req_size = ML_RLC_UCODE_SIZE * 4;
sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
+ num_fw = 6;
break;
default: BUG();
}
- DRM_INFO("Loading %s Microcode\n", chip_name);
+ DRM_INFO("Loading %s Microcode\n", new_chip_name);
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", new_chip_name);
err = request_firmware(&rdev->pfp_fw, fw_name, rdev->dev);
- if (err)
- goto out;
- if (rdev->pfp_fw->size != pfp_req_size) {
- printk(KERN_ERR
- "cik_cp: Bogus length %zu in firmware \"%s\"\n",
- rdev->pfp_fw->size, fw_name);
- err = -EINVAL;
- goto out;
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", chip_name);
+ err = request_firmware(&rdev->pfp_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ if (rdev->pfp_fw->size != pfp_req_size) {
+ printk(KERN_ERR
+ "cik_cp: Bogus length %zu in firmware \"%s\"\n",
+ rdev->pfp_fw->size, fw_name);
+ err = -EINVAL;
+ goto out;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->pfp_fw);
+ if (err) {
+ printk(KERN_ERR
+ "cik_fw: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_me.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_me.bin", new_chip_name);
err = request_firmware(&rdev->me_fw, fw_name, rdev->dev);
- if (err)
- goto out;
- if (rdev->me_fw->size != me_req_size) {
- printk(KERN_ERR
- "cik_cp: Bogus length %zu in firmware \"%s\"\n",
- rdev->me_fw->size, fw_name);
- err = -EINVAL;
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_me.bin", chip_name);
+ err = request_firmware(&rdev->me_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ if (rdev->me_fw->size != me_req_size) {
+ printk(KERN_ERR
+ "cik_cp: Bogus length %zu in firmware \"%s\"\n",
+ rdev->me_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->me_fw);
+ if (err) {
+ printk(KERN_ERR
+ "cik_fw: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_ce.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_ce.bin", new_chip_name);
err = request_firmware(&rdev->ce_fw, fw_name, rdev->dev);
- if (err)
- goto out;
- if (rdev->ce_fw->size != ce_req_size) {
- printk(KERN_ERR
- "cik_cp: Bogus length %zu in firmware \"%s\"\n",
- rdev->ce_fw->size, fw_name);
- err = -EINVAL;
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_ce.bin", chip_name);
+ err = request_firmware(&rdev->ce_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ if (rdev->ce_fw->size != ce_req_size) {
+ printk(KERN_ERR
+ "cik_cp: Bogus length %zu in firmware \"%s\"\n",
+ rdev->ce_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->ce_fw);
+ if (err) {
+ printk(KERN_ERR
+ "cik_fw: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_mec.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_mec.bin", new_chip_name);
err = request_firmware(&rdev->mec_fw, fw_name, rdev->dev);
- if (err)
- goto out;
- if (rdev->mec_fw->size != mec_req_size) {
- printk(KERN_ERR
- "cik_cp: Bogus length %zu in firmware \"%s\"\n",
- rdev->mec_fw->size, fw_name);
- err = -EINVAL;
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_mec.bin", chip_name);
+ err = request_firmware(&rdev->mec_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ if (rdev->mec_fw->size != mec_req_size) {
+ printk(KERN_ERR
+ "cik_cp: Bogus length %zu in firmware \"%s\"\n",
+ rdev->mec_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->mec_fw);
+ if (err) {
+ printk(KERN_ERR
+ "cik_fw: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", chip_name);
+ if (rdev->family == CHIP_KAVERI) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_mec2.bin", new_chip_name);
+ err = request_firmware(&rdev->mec2_fw, fw_name, rdev->dev);
+ if (err) {
+ goto out;
+ } else {
+ err = radeon_ucode_validate(rdev->mec2_fw);
+ if (err) {
+ goto out;
+ } else {
+ new_fw++;
+ }
+ }
+ }
+
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", new_chip_name);
err = request_firmware(&rdev->rlc_fw, fw_name, rdev->dev);
- if (err)
- goto out;
- if (rdev->rlc_fw->size != rlc_req_size) {
- printk(KERN_ERR
- "cik_rlc: Bogus length %zu in firmware \"%s\"\n",
- rdev->rlc_fw->size, fw_name);
- err = -EINVAL;
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", chip_name);
+ err = request_firmware(&rdev->rlc_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ if (rdev->rlc_fw->size != rlc_req_size) {
+ printk(KERN_ERR
+ "cik_rlc: Bogus length %zu in firmware \"%s\"\n",
+ rdev->rlc_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->rlc_fw);
+ if (err) {
+ printk(KERN_ERR
+ "cik_fw: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_sdma.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_sdma.bin", new_chip_name);
err = request_firmware(&rdev->sdma_fw, fw_name, rdev->dev);
- if (err)
- goto out;
- if (rdev->sdma_fw->size != sdma_req_size) {
- printk(KERN_ERR
- "cik_sdma: Bogus length %zu in firmware \"%s\"\n",
- rdev->sdma_fw->size, fw_name);
- err = -EINVAL;
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_sdma.bin", chip_name);
+ err = request_firmware(&rdev->sdma_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ if (rdev->sdma_fw->size != sdma_req_size) {
+ printk(KERN_ERR
+ "cik_sdma: Bogus length %zu in firmware \"%s\"\n",
+ rdev->sdma_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->sdma_fw);
+ if (err) {
+ printk(KERN_ERR
+ "cik_fw: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
/* No SMC, MC ucode on APUs */
if (!(rdev->flags & RADEON_IS_IGP)) {
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc2.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", new_chip_name);
err = request_firmware(&rdev->mc_fw, fw_name, rdev->dev);
if (err) {
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc2.bin", chip_name);
err = request_firmware(&rdev->mc_fw, fw_name, rdev->dev);
- if (err)
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
+ err = request_firmware(&rdev->mc_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ }
+ if ((rdev->mc_fw->size != mc_req_size) &&
+ (rdev->mc_fw->size != mc2_req_size)){
+ printk(KERN_ERR
+ "cik_mc: Bogus length %zu in firmware \"%s\"\n",
+ rdev->mc_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ DRM_INFO("%s: %zu bytes\n", fw_name, rdev->mc_fw->size);
+ } else {
+ err = radeon_ucode_validate(rdev->mc_fw);
+ if (err) {
+ printk(KERN_ERR
+ "cik_fw: validation failed for firmware \"%s\"\n",
+ fw_name);
goto out;
+ } else {
+ new_fw++;
+ }
}
- if ((rdev->mc_fw->size != mc_req_size) &&
- (rdev->mc_fw->size != mc2_req_size)){
- printk(KERN_ERR
- "cik_mc: Bogus length %zu in firmware \"%s\"\n",
- rdev->mc_fw->size, fw_name);
- err = -EINVAL;
- }
- DRM_INFO("%s: %zu bytes\n", fw_name, rdev->mc_fw->size);
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", new_chip_name);
err = request_firmware(&rdev->smc_fw, fw_name, rdev->dev);
if (err) {
- printk(KERN_ERR
- "smc: error loading firmware \"%s\"\n",
- fw_name);
- release_firmware(rdev->smc_fw);
- rdev->smc_fw = NULL;
- err = 0;
- } else if (rdev->smc_fw->size != smc_req_size) {
- printk(KERN_ERR
- "cik_smc: Bogus length %zu in firmware \"%s\"\n",
- rdev->smc_fw->size, fw_name);
- err = -EINVAL;
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name);
+ err = request_firmware(&rdev->smc_fw, fw_name, rdev->dev);
+ if (err) {
+ printk(KERN_ERR
+ "smc: error loading firmware \"%s\"\n",
+ fw_name);
+ release_firmware(rdev->smc_fw);
+ rdev->smc_fw = NULL;
+ err = 0;
+ } else if (rdev->smc_fw->size != smc_req_size) {
+ printk(KERN_ERR
+ "cik_smc: Bogus length %zu in firmware \"%s\"\n",
+ rdev->smc_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->smc_fw);
+ if (err) {
+ printk(KERN_ERR
+ "cik_fw: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
}
+ if (new_fw == 0) {
+ rdev->new_fw = false;
+ } else if (new_fw < num_fw) {
+ printk(KERN_ERR "ci_fw: mixing new and old firmware!\n");
+ err = -EINVAL;
+ } else {
+ rdev->new_fw = true;
+ }
+
out:
if (err) {
if (err != -EINVAL)
@@ -2021,8 +2239,14 @@ out:
rdev->me_fw = NULL;
release_firmware(rdev->ce_fw);
rdev->ce_fw = NULL;
+ release_firmware(rdev->mec_fw);
+ rdev->mec_fw = NULL;
+ release_firmware(rdev->mec2_fw);
+ rdev->mec2_fw = NULL;
release_firmware(rdev->rlc_fw);
rdev->rlc_fw = NULL;
+ release_firmware(rdev->sdma_fw);
+ rdev->sdma_fw = NULL;
release_firmware(rdev->mc_fw);
rdev->mc_fw = NULL;
release_firmware(rdev->smc_fw);
@@ -3259,7 +3483,7 @@ static void cik_gpu_init(struct radeon_device *rdev)
u32 mc_shared_chmap, mc_arb_ramcfg;
u32 hdp_host_path_cntl;
u32 tmp;
- int i, j, k;
+ int i, j;
switch (rdev->family) {
case CHIP_BONAIRE:
@@ -3320,6 +3544,7 @@ static void cik_gpu_init(struct radeon_device *rdev)
(rdev->pdev->device == 0x130B) ||
(rdev->pdev->device == 0x130E) ||
(rdev->pdev->device == 0x1315) ||
+ (rdev->pdev->device == 0x1318) ||
(rdev->pdev->device == 0x131B)) {
rdev->config.cik.max_cu_per_sh = 4;
rdev->config.cik.max_backends_per_se = 1;
@@ -3448,12 +3673,11 @@ static void cik_gpu_init(struct radeon_device *rdev)
rdev->config.cik.max_sh_per_se,
rdev->config.cik.max_backends_per_se);
+ rdev->config.cik.active_cus = 0;
for (i = 0; i < rdev->config.cik.max_shader_engines; i++) {
for (j = 0; j < rdev->config.cik.max_sh_per_se; j++) {
- for (k = 0; k < rdev->config.cik.max_cu_per_sh; k++) {
- rdev->config.cik.active_cus +=
- hweight32(cik_get_cu_active_bitmap(rdev, i, j));
- }
+ rdev->config.cik.active_cus +=
+ hweight32(cik_get_cu_active_bitmap(rdev, i, j));
}
}
@@ -3577,7 +3801,7 @@ int cik_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
radeon_ring_write(ring, PACKET3(PACKET3_SET_UCONFIG_REG, 1));
radeon_ring_write(ring, ((scratch - PACKET3_SET_UCONFIG_REG_START) >> 2));
radeon_ring_write(ring, 0xDEADBEEF);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
for (i = 0; i < rdev->usec_timeout; i++) {
tmp = RREG32(scratch);
@@ -3666,8 +3890,6 @@ void cik_fence_gfx_ring_emit(struct radeon_device *rdev,
radeon_ring_write(ring, (upper_32_bits(addr) & 0xffff) | DATA_SEL(1) | INT_SEL(2));
radeon_ring_write(ring, fence->seq);
radeon_ring_write(ring, 0);
- /* HDP flush */
- cik_hdp_flush_cp_ring_emit(rdev, fence->ring);
}
/**
@@ -3696,10 +3918,19 @@ void cik_fence_compute_ring_emit(struct radeon_device *rdev,
radeon_ring_write(ring, upper_32_bits(addr));
radeon_ring_write(ring, fence->seq);
radeon_ring_write(ring, 0);
- /* HDP flush */
- cik_hdp_flush_cp_ring_emit(rdev, fence->ring);
}
+/**
+ * cik_semaphore_ring_emit - emit a semaphore on the CP ring
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon ring buffer object
+ * @semaphore: radeon semaphore object
+ * @emit_wait: Is this a sempahore wait?
+ *
+ * Emits a semaphore signal/wait packet to the CP ring and prevents the PFP
+ * from running ahead of semaphore waits.
+ */
bool cik_semaphore_ring_emit(struct radeon_device *rdev,
struct radeon_ring *ring,
struct radeon_semaphore *semaphore,
@@ -3712,6 +3943,12 @@ bool cik_semaphore_ring_emit(struct radeon_device *rdev,
radeon_ring_write(ring, lower_32_bits(addr));
radeon_ring_write(ring, (upper_32_bits(addr) & 0xffff) | sel);
+ if (emit_wait && ring->idx == RADEON_RING_TYPE_GFX_INDEX) {
+ /* Prevent the PFP from running ahead of the semaphore wait */
+ radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0));
+ radeon_ring_write(ring, 0x0);
+ }
+
return true;
}
@@ -3722,18 +3959,19 @@ bool cik_semaphore_ring_emit(struct radeon_device *rdev,
* @src_offset: src GPU address
* @dst_offset: dst GPU address
* @num_gpu_pages: number of GPU pages to xfer
- * @fence: radeon fence object
+ * @resv: reservation object to sync to
*
* Copy GPU paging using the CP DMA engine (CIK+).
* Used by the radeon ttm implementation to move pages if
* registered as the asic copy callback.
*/
-int cik_copy_cpdma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence)
+struct radeon_fence *cik_copy_cpdma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv)
{
struct radeon_semaphore *sem = NULL;
+ struct radeon_fence *fence;
int ring_index = rdev->asic->copy.blit_ring_index;
struct radeon_ring *ring = &rdev->ring[ring_index];
u32 size_in_bytes, cur_size_in_bytes, control;
@@ -3743,7 +3981,7 @@ int cik_copy_cpdma(struct radeon_device *rdev,
r = radeon_semaphore_create(rdev, &sem);
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
- return r;
+ return ERR_PTR(r);
}
size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT);
@@ -3752,10 +3990,10 @@ int cik_copy_cpdma(struct radeon_device *rdev,
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_semaphore_sync_to(sem, *fence);
+ radeon_semaphore_sync_resv(rdev, sem, resv, false);
radeon_semaphore_sync_rings(rdev, sem, ring->idx);
for (i = 0; i < num_loops; i++) {
@@ -3777,17 +4015,17 @@ int cik_copy_cpdma(struct radeon_device *rdev,
dst_offset += cur_size_in_bytes;
}
- r = radeon_fence_emit(rdev, fence, ring->idx);
+ r = radeon_fence_emit(rdev, &fence, ring->idx);
if (r) {
radeon_ring_unlock_undo(rdev, ring);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_ring_unlock_commit(rdev, ring);
- radeon_semaphore_free(rdev, &sem, *fence);
+ radeon_ring_unlock_commit(rdev, ring, false);
+ radeon_semaphore_free(rdev, &sem, fence);
- return r;
+ return fence;
}
/*
@@ -3883,7 +4121,7 @@ int cik_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
ib.ptr[1] = ((scratch - PACKET3_SET_UCONFIG_REG_START) >> 2);
ib.ptr[2] = 0xDEADBEEF;
ib.length_dw = 3;
- r = radeon_ib_schedule(rdev, &ib, NULL);
+ r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r) {
radeon_scratch_free(rdev, scratch);
radeon_ib_free(rdev, &ib);
@@ -3969,7 +4207,6 @@ static void cik_cp_gfx_enable(struct radeon_device *rdev, bool enable)
*/
static int cik_cp_gfx_load_microcode(struct radeon_device *rdev)
{
- const __be32 *fw_data;
int i;
if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw)
@@ -3977,31 +4214,72 @@ static int cik_cp_gfx_load_microcode(struct radeon_device *rdev)
cik_cp_gfx_enable(rdev, false);
- /* PFP */
- fw_data = (const __be32 *)rdev->pfp_fw->data;
- WREG32(CP_PFP_UCODE_ADDR, 0);
- for (i = 0; i < CIK_PFP_UCODE_SIZE; i++)
- WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++));
- WREG32(CP_PFP_UCODE_ADDR, 0);
-
- /* CE */
- fw_data = (const __be32 *)rdev->ce_fw->data;
- WREG32(CP_CE_UCODE_ADDR, 0);
- for (i = 0; i < CIK_CE_UCODE_SIZE; i++)
- WREG32(CP_CE_UCODE_DATA, be32_to_cpup(fw_data++));
- WREG32(CP_CE_UCODE_ADDR, 0);
-
- /* ME */
- fw_data = (const __be32 *)rdev->me_fw->data;
- WREG32(CP_ME_RAM_WADDR, 0);
- for (i = 0; i < CIK_ME_UCODE_SIZE; i++)
- WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++));
- WREG32(CP_ME_RAM_WADDR, 0);
-
- WREG32(CP_PFP_UCODE_ADDR, 0);
- WREG32(CP_CE_UCODE_ADDR, 0);
- WREG32(CP_ME_RAM_WADDR, 0);
- WREG32(CP_ME_RAM_RADDR, 0);
+ if (rdev->new_fw) {
+ const struct gfx_firmware_header_v1_0 *pfp_hdr =
+ (const struct gfx_firmware_header_v1_0 *)rdev->pfp_fw->data;
+ const struct gfx_firmware_header_v1_0 *ce_hdr =
+ (const struct gfx_firmware_header_v1_0 *)rdev->ce_fw->data;
+ const struct gfx_firmware_header_v1_0 *me_hdr =
+ (const struct gfx_firmware_header_v1_0 *)rdev->me_fw->data;
+ const __le32 *fw_data;
+ u32 fw_size;
+
+ radeon_ucode_print_gfx_hdr(&pfp_hdr->header);
+ radeon_ucode_print_gfx_hdr(&ce_hdr->header);
+ radeon_ucode_print_gfx_hdr(&me_hdr->header);
+
+ /* PFP */
+ fw_data = (const __le32 *)
+ (rdev->pfp_fw->data + le32_to_cpu(pfp_hdr->header.ucode_array_offset_bytes));
+ fw_size = le32_to_cpu(pfp_hdr->header.ucode_size_bytes) / 4;
+ WREG32(CP_PFP_UCODE_ADDR, 0);
+ for (i = 0; i < fw_size; i++)
+ WREG32(CP_PFP_UCODE_DATA, le32_to_cpup(fw_data++));
+ WREG32(CP_PFP_UCODE_ADDR, le32_to_cpu(pfp_hdr->header.ucode_version));
+
+ /* CE */
+ fw_data = (const __le32 *)
+ (rdev->ce_fw->data + le32_to_cpu(ce_hdr->header.ucode_array_offset_bytes));
+ fw_size = le32_to_cpu(ce_hdr->header.ucode_size_bytes) / 4;
+ WREG32(CP_CE_UCODE_ADDR, 0);
+ for (i = 0; i < fw_size; i++)
+ WREG32(CP_CE_UCODE_DATA, le32_to_cpup(fw_data++));
+ WREG32(CP_CE_UCODE_ADDR, le32_to_cpu(ce_hdr->header.ucode_version));
+
+ /* ME */
+ fw_data = (const __be32 *)
+ (rdev->me_fw->data + le32_to_cpu(me_hdr->header.ucode_array_offset_bytes));
+ fw_size = le32_to_cpu(me_hdr->header.ucode_size_bytes) / 4;
+ WREG32(CP_ME_RAM_WADDR, 0);
+ for (i = 0; i < fw_size; i++)
+ WREG32(CP_ME_RAM_DATA, le32_to_cpup(fw_data++));
+ WREG32(CP_ME_RAM_WADDR, le32_to_cpu(me_hdr->header.ucode_version));
+ WREG32(CP_ME_RAM_RADDR, le32_to_cpu(me_hdr->header.ucode_version));
+ } else {
+ const __be32 *fw_data;
+
+ /* PFP */
+ fw_data = (const __be32 *)rdev->pfp_fw->data;
+ WREG32(CP_PFP_UCODE_ADDR, 0);
+ for (i = 0; i < CIK_PFP_UCODE_SIZE; i++)
+ WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++));
+ WREG32(CP_PFP_UCODE_ADDR, 0);
+
+ /* CE */
+ fw_data = (const __be32 *)rdev->ce_fw->data;
+ WREG32(CP_CE_UCODE_ADDR, 0);
+ for (i = 0; i < CIK_CE_UCODE_SIZE; i++)
+ WREG32(CP_CE_UCODE_DATA, be32_to_cpup(fw_data++));
+ WREG32(CP_CE_UCODE_ADDR, 0);
+
+ /* ME */
+ fw_data = (const __be32 *)rdev->me_fw->data;
+ WREG32(CP_ME_RAM_WADDR, 0);
+ for (i = 0; i < CIK_ME_UCODE_SIZE; i++)
+ WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++));
+ WREG32(CP_ME_RAM_WADDR, 0);
+ }
+
return 0;
}
@@ -4035,8 +4313,8 @@ static int cik_cp_gfx_start(struct radeon_device *rdev)
/* init the CE partitions. CE only used for gfx on CIK */
radeon_ring_write(ring, PACKET3(PACKET3_SET_BASE, 2));
radeon_ring_write(ring, PACKET3_BASE_INDEX(CE_PARTITION_BASE));
- radeon_ring_write(ring, 0xc000);
- radeon_ring_write(ring, 0xc000);
+ radeon_ring_write(ring, 0x8000);
+ radeon_ring_write(ring, 0x8000);
/* setup clear context state */
radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
@@ -4061,7 +4339,7 @@ static int cik_cp_gfx_start(struct radeon_device *rdev)
radeon_ring_write(ring, 0x0000000e); /* VGT_VERTEX_REUSE_BLOCK_CNTL */
radeon_ring_write(ring, 0x00000010); /* VGT_OUT_DEALLOC_CNTL */
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
return 0;
}
@@ -4261,7 +4539,6 @@ static void cik_cp_compute_enable(struct radeon_device *rdev, bool enable)
*/
static int cik_cp_compute_load_microcode(struct radeon_device *rdev)
{
- const __be32 *fw_data;
int i;
if (!rdev->mec_fw)
@@ -4269,20 +4546,55 @@ static int cik_cp_compute_load_microcode(struct radeon_device *rdev)
cik_cp_compute_enable(rdev, false);
- /* MEC1 */
- fw_data = (const __be32 *)rdev->mec_fw->data;
- WREG32(CP_MEC_ME1_UCODE_ADDR, 0);
- for (i = 0; i < CIK_MEC_UCODE_SIZE; i++)
- WREG32(CP_MEC_ME1_UCODE_DATA, be32_to_cpup(fw_data++));
- WREG32(CP_MEC_ME1_UCODE_ADDR, 0);
+ if (rdev->new_fw) {
+ const struct gfx_firmware_header_v1_0 *mec_hdr =
+ (const struct gfx_firmware_header_v1_0 *)rdev->mec_fw->data;
+ const __le32 *fw_data;
+ u32 fw_size;
+
+ radeon_ucode_print_gfx_hdr(&mec_hdr->header);
+
+ /* MEC1 */
+ fw_data = (const __le32 *)
+ (rdev->mec_fw->data + le32_to_cpu(mec_hdr->header.ucode_array_offset_bytes));
+ fw_size = le32_to_cpu(mec_hdr->header.ucode_size_bytes) / 4;
+ WREG32(CP_MEC_ME1_UCODE_ADDR, 0);
+ for (i = 0; i < fw_size; i++)
+ WREG32(CP_MEC_ME1_UCODE_DATA, le32_to_cpup(fw_data++));
+ WREG32(CP_MEC_ME1_UCODE_ADDR, le32_to_cpu(mec_hdr->header.ucode_version));
- if (rdev->family == CHIP_KAVERI) {
/* MEC2 */
+ if (rdev->family == CHIP_KAVERI) {
+ const struct gfx_firmware_header_v1_0 *mec2_hdr =
+ (const struct gfx_firmware_header_v1_0 *)rdev->mec2_fw->data;
+
+ fw_data = (const __le32 *)
+ (rdev->mec2_fw->data +
+ le32_to_cpu(mec2_hdr->header.ucode_array_offset_bytes));
+ fw_size = le32_to_cpu(mec2_hdr->header.ucode_size_bytes) / 4;
+ WREG32(CP_MEC_ME2_UCODE_ADDR, 0);
+ for (i = 0; i < fw_size; i++)
+ WREG32(CP_MEC_ME2_UCODE_DATA, le32_to_cpup(fw_data++));
+ WREG32(CP_MEC_ME2_UCODE_ADDR, le32_to_cpu(mec2_hdr->header.ucode_version));
+ }
+ } else {
+ const __be32 *fw_data;
+
+ /* MEC1 */
fw_data = (const __be32 *)rdev->mec_fw->data;
- WREG32(CP_MEC_ME2_UCODE_ADDR, 0);
+ WREG32(CP_MEC_ME1_UCODE_ADDR, 0);
for (i = 0; i < CIK_MEC_UCODE_SIZE; i++)
- WREG32(CP_MEC_ME2_UCODE_DATA, be32_to_cpup(fw_data++));
- WREG32(CP_MEC_ME2_UCODE_ADDR, 0);
+ WREG32(CP_MEC_ME1_UCODE_DATA, be32_to_cpup(fw_data++));
+ WREG32(CP_MEC_ME1_UCODE_ADDR, 0);
+
+ if (rdev->family == CHIP_KAVERI) {
+ /* MEC2 */
+ fw_data = (const __be32 *)rdev->mec_fw->data;
+ WREG32(CP_MEC_ME2_UCODE_ADDR, 0);
+ for (i = 0; i < CIK_MEC_UCODE_SIZE; i++)
+ WREG32(CP_MEC_ME2_UCODE_DATA, be32_to_cpup(fw_data++));
+ WREG32(CP_MEC_ME2_UCODE_ADDR, 0);
+ }
}
return 0;
@@ -4375,7 +4687,7 @@ static int cik_mec_init(struct radeon_device *rdev)
r = radeon_bo_create(rdev,
rdev->mec.num_mec *rdev->mec.num_pipe * MEC_HPD_SIZE * 2,
PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_GTT, NULL,
+ RADEON_GEM_DOMAIN_GTT, 0, NULL, NULL,
&rdev->mec.hpd_eop_obj);
if (r) {
dev_warn(rdev->dev, "(%d) create HDP EOP bo failed\n", r);
@@ -4489,7 +4801,7 @@ struct bonaire_mqd
*/
static int cik_cp_compute_resume(struct radeon_device *rdev)
{
- int r, i, idx;
+ int r, i, j, idx;
u32 tmp;
bool use_doorbell = true;
u64 hqd_gpu_addr;
@@ -4545,8 +4857,8 @@ static int cik_cp_compute_resume(struct radeon_device *rdev)
r = radeon_bo_create(rdev,
sizeof(struct bonaire_mqd),
PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_GTT, NULL,
- &rdev->ring[idx].mqd_obj);
+ RADEON_GEM_DOMAIN_GTT, 0, NULL,
+ NULL, &rdev->ring[idx].mqd_obj);
if (r) {
dev_warn(rdev->dev, "(%d) create MQD bo failed\n", r);
return r;
@@ -4608,7 +4920,7 @@ static int cik_cp_compute_resume(struct radeon_device *rdev)
mqd->queue_state.cp_hqd_pq_wptr= 0;
if (RREG32(CP_HQD_ACTIVE) & 1) {
WREG32(CP_HQD_DEQUEUE_REQUEST, 1);
- for (i = 0; i < rdev->usec_timeout; i++) {
+ for (j = 0; j < rdev->usec_timeout; j++) {
if (!(RREG32(CP_HQD_ACTIVE) & 1))
break;
udelay(1);
@@ -5402,7 +5714,6 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev)
r = radeon_gart_table_vram_pin(rdev);
if (r)
return r;
- radeon_gart_restore(rdev);
/* Setup TLB control */
WREG32(MC_VM_MX_L1_TLB_CNTL,
(0xA << 7) |
@@ -5436,20 +5747,17 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev)
WREG32(0x15D8, 0);
WREG32(0x15DC, 0);
- /* empty context1-15 */
- /* FIXME start with 4G, once using 2 level pt switch to full
- * vm size space
- */
+ /* restore context1-15 */
/* set vm size, must be a multiple of 4 */
WREG32(VM_CONTEXT1_PAGE_TABLE_START_ADDR, 0);
WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, rdev->vm_manager.max_pfn);
for (i = 1; i < 16; i++) {
if (i < 8)
WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2),
- rdev->gart.table_addr >> 12);
+ rdev->vm_manager.saved_table_addr[i]);
else
WREG32(VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2),
- rdev->gart.table_addr >> 12);
+ rdev->vm_manager.saved_table_addr[i]);
}
/* enable context1-15 */
@@ -5514,6 +5822,17 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev)
*/
static void cik_pcie_gart_disable(struct radeon_device *rdev)
{
+ unsigned i;
+
+ for (i = 1; i < 16; ++i) {
+ uint32_t reg;
+ if (i < 8)
+ reg = VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2);
+ else
+ reg = VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2);
+ rdev->vm_manager.saved_table_addr[i] = RREG32(reg);
+ }
+
/* Disable all tables */
WREG32(VM_CONTEXT0_CNTL, 0);
WREG32(VM_CONTEXT1_CNTL, 0);
@@ -5642,12 +5961,13 @@ static void cik_vm_decode_fault(struct radeon_device *rdev,
void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
{
struct radeon_ring *ring = &rdev->ring[ridx];
+ int usepfp = (ridx == RADEON_RING_TYPE_GFX_INDEX);
if (vm == NULL)
return;
radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
- radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+ radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(usepfp) |
WRITE_DATA_DST_SEL(0)));
if (vm->id < 8) {
radeon_ring_write(ring,
@@ -5661,14 +5981,14 @@ void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
/* update SH_MEM_* regs */
radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
- radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+ radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(usepfp) |
WRITE_DATA_DST_SEL(0)));
radeon_ring_write(ring, SRBM_GFX_CNTL >> 2);
radeon_ring_write(ring, 0);
radeon_ring_write(ring, VMID(vm->id));
radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 6));
- radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+ radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(usepfp) |
WRITE_DATA_DST_SEL(0)));
radeon_ring_write(ring, SH_MEM_BASES >> 2);
radeon_ring_write(ring, 0);
@@ -5679,7 +5999,7 @@ void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
radeon_ring_write(ring, 0); /* SH_MEM_APE1_LIMIT */
radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
- radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+ radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(usepfp) |
WRITE_DATA_DST_SEL(0)));
radeon_ring_write(ring, SRBM_GFX_CNTL >> 2);
radeon_ring_write(ring, 0);
@@ -5690,14 +6010,14 @@ void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
/* bits 0-15 are the VM contexts0-15 */
radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
- radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+ radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(usepfp) |
WRITE_DATA_DST_SEL(0)));
radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2);
radeon_ring_write(ring, 0);
radeon_ring_write(ring, 1 << vm->id);
/* compute doesn't have PFP */
- if (ridx == RADEON_RING_TYPE_GFX_INDEX) {
+ if (usepfp) {
/* sync PFP to ME, otherwise we might get invalid PFP reads */
radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0));
radeon_ring_write(ring, 0x0);
@@ -5865,28 +6185,10 @@ static void cik_rlc_start(struct radeon_device *rdev)
static int cik_rlc_resume(struct radeon_device *rdev)
{
u32 i, size, tmp;
- const __be32 *fw_data;
if (!rdev->rlc_fw)
return -EINVAL;
- switch (rdev->family) {
- case CHIP_BONAIRE:
- case CHIP_HAWAII:
- default:
- size = BONAIRE_RLC_UCODE_SIZE;
- break;
- case CHIP_KAVERI:
- size = KV_RLC_UCODE_SIZE;
- break;
- case CHIP_KABINI:
- size = KB_RLC_UCODE_SIZE;
- break;
- case CHIP_MULLINS:
- size = ML_RLC_UCODE_SIZE;
- break;
- }
-
cik_rlc_stop(rdev);
/* disable CG */
@@ -5910,11 +6212,45 @@ static int cik_rlc_resume(struct radeon_device *rdev)
WREG32(RLC_MC_CNTL, 0);
WREG32(RLC_UCODE_CNTL, 0);
- fw_data = (const __be32 *)rdev->rlc_fw->data;
+ if (rdev->new_fw) {
+ const struct rlc_firmware_header_v1_0 *hdr =
+ (const struct rlc_firmware_header_v1_0 *)rdev->rlc_fw->data;
+ const __le32 *fw_data = (const __le32 *)
+ (rdev->rlc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+
+ radeon_ucode_print_rlc_hdr(&hdr->header);
+
+ size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
WREG32(RLC_GPM_UCODE_ADDR, 0);
- for (i = 0; i < size; i++)
- WREG32(RLC_GPM_UCODE_DATA, be32_to_cpup(fw_data++));
- WREG32(RLC_GPM_UCODE_ADDR, 0);
+ for (i = 0; i < size; i++)
+ WREG32(RLC_GPM_UCODE_DATA, le32_to_cpup(fw_data++));
+ WREG32(RLC_GPM_UCODE_ADDR, le32_to_cpu(hdr->header.ucode_version));
+ } else {
+ const __be32 *fw_data;
+
+ switch (rdev->family) {
+ case CHIP_BONAIRE:
+ case CHIP_HAWAII:
+ default:
+ size = BONAIRE_RLC_UCODE_SIZE;
+ break;
+ case CHIP_KAVERI:
+ size = KV_RLC_UCODE_SIZE;
+ break;
+ case CHIP_KABINI:
+ size = KB_RLC_UCODE_SIZE;
+ break;
+ case CHIP_MULLINS:
+ size = ML_RLC_UCODE_SIZE;
+ break;
+ }
+
+ fw_data = (const __be32 *)rdev->rlc_fw->data;
+ WREG32(RLC_GPM_UCODE_ADDR, 0);
+ for (i = 0; i < size; i++)
+ WREG32(RLC_GPM_UCODE_DATA, be32_to_cpup(fw_data++));
+ WREG32(RLC_GPM_UCODE_ADDR, 0);
+ }
/* XXX - find out what chips support lbpw */
cik_enable_lbpw(rdev, false);
@@ -6348,11 +6684,10 @@ static void cik_enable_gds_pg(struct radeon_device *rdev, bool enable)
void cik_init_cp_pg_table(struct radeon_device *rdev)
{
- const __be32 *fw_data;
volatile u32 *dst_ptr;
int me, i, max_me = 4;
u32 bo_offset = 0;
- u32 table_offset;
+ u32 table_offset, table_size;
if (rdev->family == CHIP_KAVERI)
max_me = 5;
@@ -6363,24 +6698,71 @@ void cik_init_cp_pg_table(struct radeon_device *rdev)
/* write the cp table buffer */
dst_ptr = rdev->rlc.cp_table_ptr;
for (me = 0; me < max_me; me++) {
- if (me == 0) {
- fw_data = (const __be32 *)rdev->ce_fw->data;
- table_offset = CP_ME_TABLE_OFFSET;
- } else if (me == 1) {
- fw_data = (const __be32 *)rdev->pfp_fw->data;
- table_offset = CP_ME_TABLE_OFFSET;
- } else if (me == 2) {
- fw_data = (const __be32 *)rdev->me_fw->data;
- table_offset = CP_ME_TABLE_OFFSET;
+ if (rdev->new_fw) {
+ const __le32 *fw_data;
+ const struct gfx_firmware_header_v1_0 *hdr;
+
+ if (me == 0) {
+ hdr = (const struct gfx_firmware_header_v1_0 *)rdev->ce_fw->data;
+ fw_data = (const __le32 *)
+ (rdev->ce_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ table_offset = le32_to_cpu(hdr->jt_offset);
+ table_size = le32_to_cpu(hdr->jt_size);
+ } else if (me == 1) {
+ hdr = (const struct gfx_firmware_header_v1_0 *)rdev->pfp_fw->data;
+ fw_data = (const __le32 *)
+ (rdev->pfp_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ table_offset = le32_to_cpu(hdr->jt_offset);
+ table_size = le32_to_cpu(hdr->jt_size);
+ } else if (me == 2) {
+ hdr = (const struct gfx_firmware_header_v1_0 *)rdev->me_fw->data;
+ fw_data = (const __le32 *)
+ (rdev->me_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ table_offset = le32_to_cpu(hdr->jt_offset);
+ table_size = le32_to_cpu(hdr->jt_size);
+ } else if (me == 3) {
+ hdr = (const struct gfx_firmware_header_v1_0 *)rdev->mec_fw->data;
+ fw_data = (const __le32 *)
+ (rdev->mec_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ table_offset = le32_to_cpu(hdr->jt_offset);
+ table_size = le32_to_cpu(hdr->jt_size);
+ } else {
+ hdr = (const struct gfx_firmware_header_v1_0 *)rdev->mec2_fw->data;
+ fw_data = (const __le32 *)
+ (rdev->mec2_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ table_offset = le32_to_cpu(hdr->jt_offset);
+ table_size = le32_to_cpu(hdr->jt_size);
+ }
+
+ for (i = 0; i < table_size; i ++) {
+ dst_ptr[bo_offset + i] =
+ cpu_to_le32(le32_to_cpu(fw_data[table_offset + i]));
+ }
+ bo_offset += table_size;
} else {
- fw_data = (const __be32 *)rdev->mec_fw->data;
- table_offset = CP_MEC_TABLE_OFFSET;
- }
+ const __be32 *fw_data;
+ table_size = CP_ME_TABLE_SIZE;
+
+ if (me == 0) {
+ fw_data = (const __be32 *)rdev->ce_fw->data;
+ table_offset = CP_ME_TABLE_OFFSET;
+ } else if (me == 1) {
+ fw_data = (const __be32 *)rdev->pfp_fw->data;
+ table_offset = CP_ME_TABLE_OFFSET;
+ } else if (me == 2) {
+ fw_data = (const __be32 *)rdev->me_fw->data;
+ table_offset = CP_ME_TABLE_OFFSET;
+ } else {
+ fw_data = (const __be32 *)rdev->mec_fw->data;
+ table_offset = CP_MEC_TABLE_OFFSET;
+ }
- for (i = 0; i < CP_ME_TABLE_SIZE; i ++) {
- dst_ptr[bo_offset + i] = cpu_to_le32(be32_to_cpu(fw_data[table_offset + i]));
+ for (i = 0; i < table_size; i ++) {
+ dst_ptr[bo_offset + i] =
+ cpu_to_le32(be32_to_cpu(fw_data[table_offset + i]));
+ }
+ bo_offset += table_size;
}
- bo_offset += CP_ME_TABLE_SIZE;
}
}
@@ -7367,17 +7749,17 @@ static inline u32 cik_get_ih_wptr(struct radeon_device *rdev)
wptr = RREG32(IH_RB_WPTR);
if (wptr & RB_OVERFLOW) {
+ wptr &= ~RB_OVERFLOW;
/* When a ring buffer overflow happen start parsing interrupt
* from the last not overwritten vector (wptr + 16). Hopefully
* this should allow us to catchup.
*/
- dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n",
- wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask);
+ dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
+ wptr, rdev->ih.rptr, (wptr + 16) & rdev->ih.ptr_mask);
rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask;
tmp = RREG32(IH_RB_CNTL);
tmp |= IH_WPTR_OVERFLOW_CLEAR;
WREG32(IH_RB_CNTL, tmp);
- wptr &= ~RB_OVERFLOW;
}
return (wptr & rdev->ih.ptr_mask);
}
@@ -7618,7 +8000,8 @@ restart_ih:
case 16: /* D5 page flip */
case 18: /* D6 page flip */
DRM_DEBUG("IH: D%d flip\n", ((src_id - 8) >> 1) + 1);
- radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1);
+ if (radeon_use_pflipirq > 0)
+ radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1);
break;
case 42: /* HPD hotplug */
switch (src_data) {
@@ -7866,15 +8249,17 @@ restart_ih:
/* wptr/rptr are in bytes! */
rptr += 16;
rptr &= rdev->ih.ptr_mask;
+ WREG32(IH_RB_RPTR, rptr);
}
if (queue_hotplug)
schedule_work(&rdev->hotplug_work);
- if (queue_reset)
- schedule_work(&rdev->reset_work);
+ if (queue_reset) {
+ rdev->needs_reset = true;
+ wake_up_all(&rdev->fence_queue);
+ }
if (queue_thermal)
schedule_work(&rdev->pm.dpm.thermal.work);
rdev->ih.rptr = rptr;
- WREG32(IH_RB_RPTR, rdev->ih.rptr);
atomic_set(&rdev->ih.lock, 0);
/* make sure wptr hasn't changed while processing */
@@ -7900,6 +8285,7 @@ restart_ih:
static int cik_startup(struct radeon_device *rdev)
{
struct radeon_ring *ring;
+ u32 nop;
int r;
/* enable pcie gen2/3 link */
@@ -8033,9 +8419,18 @@ static int cik_startup(struct radeon_device *rdev)
}
cik_irq_set(rdev);
+ if (rdev->family == CHIP_HAWAII) {
+ if (rdev->new_fw)
+ nop = PACKET3(PACKET3_NOP, 0x3FFF);
+ else
+ nop = RADEON_CP_PACKET2;
+ } else {
+ nop = PACKET3(PACKET3_NOP, 0x3FFF);
+ }
+
ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET,
- PACKET3(PACKET3_NOP, 0x3FFF));
+ nop);
if (r)
return r;
@@ -8043,7 +8438,7 @@ static int cik_startup(struct radeon_device *rdev)
/* type-2 packets are deprecated on MEC, use type-3 instead */
ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX];
r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP1_RPTR_OFFSET,
- PACKET3(PACKET3_NOP, 0x3FFF));
+ nop);
if (r)
return r;
ring->me = 1; /* first MEC */
@@ -8054,7 +8449,7 @@ static int cik_startup(struct radeon_device *rdev)
/* type-2 packets are deprecated on MEC, use type-3 instead */
ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX];
r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP2_RPTR_OFFSET,
- PACKET3(PACKET3_NOP, 0x3FFF));
+ nop);
if (r)
return r;
/* dGPU only have 1 MEC */
@@ -9052,6 +9447,9 @@ void dce8_bandwidth_update(struct radeon_device *rdev)
u32 num_heads = 0, lb_size;
int i;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
for (i = 0; i < rdev->num_crtc; i++) {
@@ -9168,6 +9566,9 @@ static void cik_pcie_gen3_enable(struct radeon_device *rdev)
int ret, i;
u16 tmp16;
+ if (pci_is_root_bus(rdev->pdev->bus))
+ return;
+
if (radeon_pcie_gen2 == 0)
return;
@@ -9394,7 +9795,8 @@ static void cik_program_aspm(struct radeon_device *rdev)
if (orig != data)
WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, data);
- if (!disable_clkreq) {
+ if (!disable_clkreq &&
+ !pci_is_root_bus(rdev->pdev->bus)) {
struct pci_dev *root = rdev->pdev->bus->self;
u32 lnkcap;
diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c
index 8e9d0f1d858e..d748963af08b 100644
--- a/drivers/gpu/drm/radeon/cik_sdma.c
+++ b/drivers/gpu/drm/radeon/cik_sdma.c
@@ -24,6 +24,7 @@
#include <linux/firmware.h>
#include <drm/drmP.h>
#include "radeon.h"
+#include "radeon_ucode.h"
#include "radeon_asic.h"
#include "radeon_trace.h"
#include "cikd.h"
@@ -118,6 +119,7 @@ void cik_sdma_set_wptr(struct radeon_device *rdev,
reg = SDMA0_GFX_RB_WPTR + SDMA1_REGISTER_OFFSET;
WREG32(reg, (ring->wptr << 2) & 0x3fffc);
+ (void)RREG32(reg);
}
/**
@@ -419,7 +421,6 @@ static int cik_sdma_rlc_resume(struct radeon_device *rdev)
*/
static int cik_sdma_load_microcode(struct radeon_device *rdev)
{
- const __be32 *fw_data;
int i;
if (!rdev->sdma_fw)
@@ -428,19 +429,48 @@ static int cik_sdma_load_microcode(struct radeon_device *rdev)
/* halt the MEs */
cik_sdma_enable(rdev, false);
- /* sdma0 */
- fw_data = (const __be32 *)rdev->sdma_fw->data;
- WREG32(SDMA0_UCODE_ADDR + SDMA0_REGISTER_OFFSET, 0);
- for (i = 0; i < CIK_SDMA_UCODE_SIZE; i++)
- WREG32(SDMA0_UCODE_DATA + SDMA0_REGISTER_OFFSET, be32_to_cpup(fw_data++));
- WREG32(SDMA0_UCODE_DATA + SDMA0_REGISTER_OFFSET, CIK_SDMA_UCODE_VERSION);
-
- /* sdma1 */
- fw_data = (const __be32 *)rdev->sdma_fw->data;
- WREG32(SDMA0_UCODE_ADDR + SDMA1_REGISTER_OFFSET, 0);
- for (i = 0; i < CIK_SDMA_UCODE_SIZE; i++)
- WREG32(SDMA0_UCODE_DATA + SDMA1_REGISTER_OFFSET, be32_to_cpup(fw_data++));
- WREG32(SDMA0_UCODE_DATA + SDMA1_REGISTER_OFFSET, CIK_SDMA_UCODE_VERSION);
+ if (rdev->new_fw) {
+ const struct sdma_firmware_header_v1_0 *hdr =
+ (const struct sdma_firmware_header_v1_0 *)rdev->sdma_fw->data;
+ const __le32 *fw_data;
+ u32 fw_size;
+
+ radeon_ucode_print_sdma_hdr(&hdr->header);
+
+ /* sdma0 */
+ fw_data = (const __le32 *)
+ (rdev->sdma_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ fw_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
+ WREG32(SDMA0_UCODE_ADDR + SDMA0_REGISTER_OFFSET, 0);
+ for (i = 0; i < fw_size; i++)
+ WREG32(SDMA0_UCODE_DATA + SDMA0_REGISTER_OFFSET, le32_to_cpup(fw_data++));
+ WREG32(SDMA0_UCODE_DATA + SDMA0_REGISTER_OFFSET, CIK_SDMA_UCODE_VERSION);
+
+ /* sdma1 */
+ fw_data = (const __le32 *)
+ (rdev->sdma_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ fw_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
+ WREG32(SDMA0_UCODE_ADDR + SDMA1_REGISTER_OFFSET, 0);
+ for (i = 0; i < fw_size; i++)
+ WREG32(SDMA0_UCODE_DATA + SDMA1_REGISTER_OFFSET, le32_to_cpup(fw_data++));
+ WREG32(SDMA0_UCODE_DATA + SDMA1_REGISTER_OFFSET, CIK_SDMA_UCODE_VERSION);
+ } else {
+ const __be32 *fw_data;
+
+ /* sdma0 */
+ fw_data = (const __be32 *)rdev->sdma_fw->data;
+ WREG32(SDMA0_UCODE_ADDR + SDMA0_REGISTER_OFFSET, 0);
+ for (i = 0; i < CIK_SDMA_UCODE_SIZE; i++)
+ WREG32(SDMA0_UCODE_DATA + SDMA0_REGISTER_OFFSET, be32_to_cpup(fw_data++));
+ WREG32(SDMA0_UCODE_DATA + SDMA0_REGISTER_OFFSET, CIK_SDMA_UCODE_VERSION);
+
+ /* sdma1 */
+ fw_data = (const __be32 *)rdev->sdma_fw->data;
+ WREG32(SDMA0_UCODE_ADDR + SDMA1_REGISTER_OFFSET, 0);
+ for (i = 0; i < CIK_SDMA_UCODE_SIZE; i++)
+ WREG32(SDMA0_UCODE_DATA + SDMA1_REGISTER_OFFSET, be32_to_cpup(fw_data++));
+ WREG32(SDMA0_UCODE_DATA + SDMA1_REGISTER_OFFSET, CIK_SDMA_UCODE_VERSION);
+ }
WREG32(SDMA0_UCODE_ADDR + SDMA0_REGISTER_OFFSET, 0);
WREG32(SDMA0_UCODE_ADDR + SDMA1_REGISTER_OFFSET, 0);
@@ -459,13 +489,6 @@ int cik_sdma_resume(struct radeon_device *rdev)
{
int r;
- /* Reset dma */
- WREG32(SRBM_SOFT_RESET, SOFT_RESET_SDMA | SOFT_RESET_SDMA1);
- RREG32(SRBM_SOFT_RESET);
- udelay(50);
- WREG32(SRBM_SOFT_RESET, 0);
- RREG32(SRBM_SOFT_RESET);
-
r = cik_sdma_load_microcode(rdev);
if (r)
return r;
@@ -507,18 +530,19 @@ void cik_sdma_fini(struct radeon_device *rdev)
* @src_offset: src GPU address
* @dst_offset: dst GPU address
* @num_gpu_pages: number of GPU pages to xfer
- * @fence: radeon fence object
+ * @resv: reservation object to sync to
*
* Copy GPU paging using the DMA engine (CIK).
* Used by the radeon ttm implementation to move pages if
* registered as the asic copy callback.
*/
-int cik_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence)
+struct radeon_fence *cik_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv)
{
struct radeon_semaphore *sem = NULL;
+ struct radeon_fence *fence;
int ring_index = rdev->asic->copy.dma_ring_index;
struct radeon_ring *ring = &rdev->ring[ring_index];
u32 size_in_bytes, cur_size_in_bytes;
@@ -528,7 +552,7 @@ int cik_copy_dma(struct radeon_device *rdev,
r = radeon_semaphore_create(rdev, &sem);
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
- return r;
+ return ERR_PTR(r);
}
size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT);
@@ -537,10 +561,10 @@ int cik_copy_dma(struct radeon_device *rdev,
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_semaphore_sync_to(sem, *fence);
+ radeon_semaphore_sync_resv(rdev, sem, resv, false);
radeon_semaphore_sync_rings(rdev, sem, ring->idx);
for (i = 0; i < num_loops; i++) {
@@ -559,17 +583,17 @@ int cik_copy_dma(struct radeon_device *rdev,
dst_offset += cur_size_in_bytes;
}
- r = radeon_fence_emit(rdev, fence, ring->idx);
+ r = radeon_fence_emit(rdev, &fence, ring->idx);
if (r) {
radeon_ring_unlock_undo(rdev, ring);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_ring_unlock_commit(rdev, ring);
- radeon_semaphore_free(rdev, &sem, *fence);
+ radeon_ring_unlock_commit(rdev, ring, false);
+ radeon_semaphore_free(rdev, &sem, fence);
- return r;
+ return fence;
}
/**
@@ -587,16 +611,19 @@ int cik_sdma_ring_test(struct radeon_device *rdev,
{
unsigned i;
int r;
- void __iomem *ptr = (void *)rdev->vram_scratch.ptr;
+ unsigned index;
u32 tmp;
+ u64 gpu_addr;
- if (!ptr) {
- DRM_ERROR("invalid vram scratch pointer\n");
- return -EINVAL;
- }
+ if (ring->idx == R600_RING_TYPE_DMA_INDEX)
+ index = R600_WB_DMA_RING_TEST_OFFSET;
+ else
+ index = CAYMAN_WB_DMA1_RING_TEST_OFFSET;
+
+ gpu_addr = rdev->wb.gpu_addr + index;
tmp = 0xCAFEDEAD;
- writel(tmp, ptr);
+ rdev->wb.wb[index/4] = cpu_to_le32(tmp);
r = radeon_ring_lock(rdev, ring, 5);
if (r) {
@@ -604,14 +631,14 @@ int cik_sdma_ring_test(struct radeon_device *rdev,
return r;
}
radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0));
- radeon_ring_write(ring, rdev->vram_scratch.gpu_addr & 0xfffffffc);
- radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr));
+ radeon_ring_write(ring, lower_32_bits(gpu_addr));
+ radeon_ring_write(ring, upper_32_bits(gpu_addr));
radeon_ring_write(ring, 1); /* number of DWs to follow */
radeon_ring_write(ring, 0xDEADBEEF);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
for (i = 0; i < rdev->usec_timeout; i++) {
- tmp = readl(ptr);
+ tmp = le32_to_cpu(rdev->wb.wb[index/4]);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
@@ -640,17 +667,20 @@ int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
{
struct radeon_ib ib;
unsigned i;
+ unsigned index;
int r;
- void __iomem *ptr = (void *)rdev->vram_scratch.ptr;
u32 tmp = 0;
+ u64 gpu_addr;
- if (!ptr) {
- DRM_ERROR("invalid vram scratch pointer\n");
- return -EINVAL;
- }
+ if (ring->idx == R600_RING_TYPE_DMA_INDEX)
+ index = R600_WB_DMA_RING_TEST_OFFSET;
+ else
+ index = CAYMAN_WB_DMA1_RING_TEST_OFFSET;
+
+ gpu_addr = rdev->wb.gpu_addr + index;
tmp = 0xCAFEDEAD;
- writel(tmp, ptr);
+ rdev->wb.wb[index/4] = cpu_to_le32(tmp);
r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256);
if (r) {
@@ -659,13 +689,13 @@ int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
}
ib.ptr[0] = SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
- ib.ptr[1] = rdev->vram_scratch.gpu_addr & 0xfffffffc;
- ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr);
+ ib.ptr[1] = lower_32_bits(gpu_addr);
+ ib.ptr[2] = upper_32_bits(gpu_addr);
ib.ptr[3] = 1;
ib.ptr[4] = 0xDEADBEEF;
ib.length_dw = 5;
- r = radeon_ib_schedule(rdev, &ib, NULL);
+ r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r) {
radeon_ib_free(rdev, &ib);
DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
@@ -677,7 +707,7 @@ int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
return r;
}
for (i = 0; i < rdev->usec_timeout; i++) {
- tmp = readl(ptr);
+ tmp = le32_to_cpu(rdev->wb.wb[index/4]);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
@@ -719,7 +749,43 @@ bool cik_sdma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
}
/**
- * cik_sdma_vm_set_page - update the page tables using sDMA
+ * cik_sdma_vm_copy_pages - update PTEs by copying them from the GART
+ *
+ * @rdev: radeon_device pointer
+ * @ib: indirect buffer to fill with commands
+ * @pe: addr of the page entry
+ * @src: src addr to copy from
+ * @count: number of page entries to update
+ *
+ * Update PTEs by copying them from the GART using sDMA (CIK).
+ */
+void cik_sdma_vm_copy_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe, uint64_t src,
+ unsigned count)
+{
+ while (count) {
+ unsigned bytes = count * 8;
+ if (bytes > 0x1FFFF8)
+ bytes = 0x1FFFF8;
+
+ ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_COPY,
+ SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
+ ib->ptr[ib->length_dw++] = bytes;
+ ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
+ ib->ptr[ib->length_dw++] = lower_32_bits(src);
+ ib->ptr[ib->length_dw++] = upper_32_bits(src);
+ ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+ ib->ptr[ib->length_dw++] = upper_32_bits(pe);
+
+ pe += bytes;
+ src += bytes;
+ count -= bytes / 8;
+ }
+}
+
+/**
+ * cik_sdma_vm_write_pages - update PTEs by writing them manually
*
* @rdev: radeon_device pointer
* @ib: indirect buffer to fill with commands
@@ -729,84 +795,103 @@ bool cik_sdma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
* @incr: increase next addr by incr bytes
* @flags: access flags
*
- * Update the page tables using sDMA (CIK).
+ * Update PTEs by writing them manually using sDMA (CIK).
*/
-void cik_sdma_vm_set_page(struct radeon_device *rdev,
- struct radeon_ib *ib,
- uint64_t pe,
- uint64_t addr, unsigned count,
- uint32_t incr, uint32_t flags)
+void cik_sdma_vm_write_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags)
{
uint64_t value;
unsigned ndw;
- trace_radeon_vm_set_page(pe, addr, count, incr, flags);
-
- if (flags == R600_PTE_GART) {
- uint64_t src = rdev->gart.table_addr + (addr >> 12) * 8;
- while (count) {
- unsigned bytes = count * 8;
- if (bytes > 0x1FFFF8)
- bytes = 0x1FFFF8;
-
- ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_COPY, SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
- ib->ptr[ib->length_dw++] = bytes;
- ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
- ib->ptr[ib->length_dw++] = lower_32_bits(src);
- ib->ptr[ib->length_dw++] = upper_32_bits(src);
- ib->ptr[ib->length_dw++] = lower_32_bits(pe);
- ib->ptr[ib->length_dw++] = upper_32_bits(pe);
-
- pe += bytes;
- src += bytes;
- count -= bytes / 8;
- }
- } else if (flags & R600_PTE_SYSTEM) {
- while (count) {
- ndw = count * 2;
- if (ndw > 0xFFFFE)
- ndw = 0xFFFFE;
-
- /* for non-physically contiguous pages (system) */
- ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
- ib->ptr[ib->length_dw++] = pe;
- ib->ptr[ib->length_dw++] = upper_32_bits(pe);
- ib->ptr[ib->length_dw++] = ndw;
- for (; ndw > 0; ndw -= 2, --count, pe += 8) {
+ while (count) {
+ ndw = count * 2;
+ if (ndw > 0xFFFFE)
+ ndw = 0xFFFFE;
+
+ /* for non-physically contiguous pages (system) */
+ ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_WRITE,
+ SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
+ ib->ptr[ib->length_dw++] = pe;
+ ib->ptr[ib->length_dw++] = upper_32_bits(pe);
+ ib->ptr[ib->length_dw++] = ndw;
+ for (; ndw > 0; ndw -= 2, --count, pe += 8) {
+ if (flags & R600_PTE_SYSTEM) {
value = radeon_vm_map_gart(rdev, addr);
value &= 0xFFFFFFFFFFFFF000ULL;
- addr += incr;
- value |= flags;
- ib->ptr[ib->length_dw++] = value;
- ib->ptr[ib->length_dw++] = upper_32_bits(value);
- }
- }
- } else {
- while (count) {
- ndw = count;
- if (ndw > 0x7FFFF)
- ndw = 0x7FFFF;
-
- if (flags & R600_PTE_VALID)
+ } else if (flags & R600_PTE_VALID) {
value = addr;
- else
+ } else {
value = 0;
- /* for physically contiguous pages (vram) */
- ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_GENERATE_PTE_PDE, 0, 0);
- ib->ptr[ib->length_dw++] = pe; /* dst addr */
- ib->ptr[ib->length_dw++] = upper_32_bits(pe);
- ib->ptr[ib->length_dw++] = flags; /* mask */
- ib->ptr[ib->length_dw++] = 0;
- ib->ptr[ib->length_dw++] = value; /* value */
+ }
+ addr += incr;
+ value |= flags;
+ ib->ptr[ib->length_dw++] = value;
ib->ptr[ib->length_dw++] = upper_32_bits(value);
- ib->ptr[ib->length_dw++] = incr; /* increment size */
- ib->ptr[ib->length_dw++] = 0;
- ib->ptr[ib->length_dw++] = ndw; /* number of entries */
- pe += ndw * 8;
- addr += ndw * incr;
- count -= ndw;
}
}
+}
+
+/**
+ * cik_sdma_vm_set_pages - update the page tables using sDMA
+ *
+ * @rdev: radeon_device pointer
+ * @ib: indirect buffer to fill with commands
+ * @pe: addr of the page entry
+ * @addr: dst addr to write into pe
+ * @count: number of page entries to update
+ * @incr: increase next addr by incr bytes
+ * @flags: access flags
+ *
+ * Update the page tables using sDMA (CIK).
+ */
+void cik_sdma_vm_set_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags)
+{
+ uint64_t value;
+ unsigned ndw;
+
+ while (count) {
+ ndw = count;
+ if (ndw > 0x7FFFF)
+ ndw = 0x7FFFF;
+
+ if (flags & R600_PTE_VALID)
+ value = addr;
+ else
+ value = 0;
+
+ /* for physically contiguous pages (vram) */
+ ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_GENERATE_PTE_PDE, 0, 0);
+ ib->ptr[ib->length_dw++] = pe; /* dst addr */
+ ib->ptr[ib->length_dw++] = upper_32_bits(pe);
+ ib->ptr[ib->length_dw++] = flags; /* mask */
+ ib->ptr[ib->length_dw++] = 0;
+ ib->ptr[ib->length_dw++] = value; /* value */
+ ib->ptr[ib->length_dw++] = upper_32_bits(value);
+ ib->ptr[ib->length_dw++] = incr; /* increment size */
+ ib->ptr[ib->length_dw++] = 0;
+ ib->ptr[ib->length_dw++] = ndw; /* number of entries */
+
+ pe += ndw * 8;
+ addr += ndw * incr;
+ count -= ndw;
+ }
+}
+
+/**
+ * cik_sdma_vm_pad_ib - pad the IB to the required number of dw
+ *
+ * @ib: indirect buffer to fill with padding
+ *
+ */
+void cik_sdma_vm_pad_ib(struct radeon_ib *ib)
+{
while (ib->length_dw & 0x7)
ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0);
}
diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c
index 47d31e915758..9aad0327e4d1 100644
--- a/drivers/gpu/drm/radeon/cypress_dpm.c
+++ b/drivers/gpu/drm/radeon/cypress_dpm.c
@@ -24,6 +24,7 @@
#include "drmP.h"
#include "radeon.h"
+#include "radeon_asic.h"
#include "evergreend.h"
#include "r600_dpm.h"
#include "cypress_dpm.h"
diff --git a/drivers/gpu/drm/radeon/dce3_1_afmt.c b/drivers/gpu/drm/radeon/dce3_1_afmt.c
index 51800e340a57..2fe8cfc966d9 100644
--- a/drivers/gpu/drm/radeon/dce3_1_afmt.c
+++ b/drivers/gpu/drm/radeon/dce3_1_afmt.c
@@ -32,7 +32,7 @@ static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder)
struct drm_connector *connector;
struct radeon_connector *radeon_connector = NULL;
u32 tmp;
- u8 *sadb;
+ u8 *sadb = NULL;
int sad_count;
list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
@@ -49,8 +49,8 @@ static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder)
sad_count = drm_edid_to_speaker_allocation(radeon_connector->edid, &sadb);
if (sad_count < 0) {
- DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sad_count);
- return;
+ DRM_DEBUG("Couldn't read Speaker Allocation Data Block: %d\n", sad_count);
+ sad_count = 0;
}
/* program the speaker allocation */
@@ -165,7 +165,7 @@ void dce3_1_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *m
/* disable audio prior to setting up hw */
dig->afmt->pin = r600_audio_get_pin(rdev);
- r600_audio_enable(rdev, dig->afmt->pin, false);
+ r600_audio_enable(rdev, dig->afmt->pin, 0);
r600_audio_set_dto(encoder, mode->clock);
@@ -240,5 +240,5 @@ void dce3_1_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *m
r600_hdmi_audio_workaround(encoder);
/* enable audio after to setting up hw */
- r600_audio_enable(rdev, dig->afmt->pin, true);
+ r600_audio_enable(rdev, dig->afmt->pin, 0xf);
}
diff --git a/drivers/gpu/drm/radeon/dce6_afmt.c b/drivers/gpu/drm/radeon/dce6_afmt.c
index 0a65dc7e93e7..f312edf4d50e 100644
--- a/drivers/gpu/drm/radeon/dce6_afmt.c
+++ b/drivers/gpu/drm/radeon/dce6_afmt.c
@@ -136,13 +136,13 @@ void dce6_afmt_write_latency_fields(struct drm_encoder *encoder,
tmp = VIDEO_LIPSYNC(connector->video_latency[1]) |
AUDIO_LIPSYNC(connector->audio_latency[1]);
else
- tmp = VIDEO_LIPSYNC(255) | AUDIO_LIPSYNC(255);
+ tmp = VIDEO_LIPSYNC(0) | AUDIO_LIPSYNC(0);
} else {
if (connector->latency_present[0])
tmp = VIDEO_LIPSYNC(connector->video_latency[0]) |
AUDIO_LIPSYNC(connector->audio_latency[0]);
else
- tmp = VIDEO_LIPSYNC(255) | AUDIO_LIPSYNC(255);
+ tmp = VIDEO_LIPSYNC(0) | AUDIO_LIPSYNC(0);
}
WREG32_ENDPOINT(offset, AZ_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC, tmp);
}
@@ -155,7 +155,7 @@ void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder)
struct drm_connector *connector;
struct radeon_connector *radeon_connector = NULL;
u32 offset, tmp;
- u8 *sadb;
+ u8 *sadb = NULL;
int sad_count;
if (!dig || !dig->afmt || !dig->afmt->pin)
@@ -164,8 +164,10 @@ void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder)
offset = dig->afmt->pin->offset;
list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder)
+ if (connector->encoder == encoder) {
radeon_connector = to_radeon_connector(connector);
+ break;
+ }
}
if (!radeon_connector) {
@@ -173,10 +175,10 @@ void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder)
return;
}
- sad_count = drm_edid_to_speaker_allocation(radeon_connector->edid, &sadb);
- if (sad_count <= 0) {
- DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sad_count);
- return;
+ sad_count = drm_edid_to_speaker_allocation(radeon_connector_edid(connector), &sadb);
+ if (sad_count < 0) {
+ DRM_DEBUG("Couldn't read Speaker Allocation Data Block: %d\n", sad_count);
+ sad_count = 0;
}
/* program the speaker allocation */
@@ -225,8 +227,10 @@ void dce6_afmt_write_sad_regs(struct drm_encoder *encoder)
offset = dig->afmt->pin->offset;
list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder)
+ if (connector->encoder == encoder) {
radeon_connector = to_radeon_connector(connector);
+ break;
+ }
}
if (!radeon_connector) {
@@ -234,7 +238,7 @@ void dce6_afmt_write_sad_regs(struct drm_encoder *encoder)
return;
}
- sad_count = drm_edid_to_sad(radeon_connector->edid, &sads);
+ sad_count = drm_edid_to_sad(radeon_connector_edid(connector), &sads);
if (sad_count <= 0) {
DRM_ERROR("Couldn't read SADs: %d\n", sad_count);
return;
@@ -280,13 +284,13 @@ static int dce6_audio_chipset_supported(struct radeon_device *rdev)
void dce6_audio_enable(struct radeon_device *rdev,
struct r600_audio_pin *pin,
- bool enable)
+ u8 enable_mask)
{
if (!pin)
return;
- WREG32_ENDPOINT(pin->offset, AZ_F0_CODEC_PIN_CONTROL_HOTPLUG_CONTROL,
- enable ? AUDIO_ENABLED : 0);
+ WREG32_ENDPOINT(pin->offset, AZ_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL,
+ enable_mask ? AUDIO_ENABLED : 0);
}
static const u32 pin_offsets[7] =
diff --git a/drivers/gpu/drm/drm_buffer.c b/drivers/gpu/drm/radeon/drm_buffer.c
index 0406110f83ed..f4e0f3a3d7b1 100644
--- a/drivers/gpu/drm/drm_buffer.c
+++ b/drivers/gpu/drm/radeon/drm_buffer.c
@@ -33,7 +33,7 @@
*/
#include <linux/export.h>
-#include <drm/drm_buffer.h>
+#include "drm_buffer.h"
/**
* Allocate the drm buffer object.
@@ -80,17 +80,12 @@ int drm_buffer_alloc(struct drm_buffer **buf, int size)
error_out:
- /* Only last element can be null pointer so check for it first. */
- if ((*buf)->data[idx])
- kfree((*buf)->data[idx]);
-
- for (--idx; idx >= 0; --idx)
+ for (; idx >= 0; --idx)
kfree((*buf)->data[idx]);
kfree(*buf);
return -ENOMEM;
}
-EXPORT_SYMBOL(drm_buffer_alloc);
/**
* Copy the user data to the begin of the buffer and reset the processing
@@ -127,7 +122,6 @@ int drm_buffer_copy_from_user(struct drm_buffer *buf,
buf->iterator = 0;
return 0;
}
-EXPORT_SYMBOL(drm_buffer_copy_from_user);
/**
* Free the drm buffer object
@@ -145,7 +139,6 @@ void drm_buffer_free(struct drm_buffer *buf)
kfree(buf);
}
}
-EXPORT_SYMBOL(drm_buffer_free);
/**
* Read an object from buffer that may be split to multiple parts. If object
@@ -182,4 +175,3 @@ void *drm_buffer_read_object(struct drm_buffer *buf,
drm_buffer_advance(buf, objsize);
return obj;
}
-EXPORT_SYMBOL(drm_buffer_read_object);
diff --git a/drivers/gpu/drm/radeon/drm_buffer.h b/drivers/gpu/drm/radeon/drm_buffer.h
new file mode 100644
index 000000000000..c80d3a340b94
--- /dev/null
+++ b/drivers/gpu/drm/radeon/drm_buffer.h
@@ -0,0 +1,148 @@
+/**************************************************************************
+ *
+ * Copyright 2010 Pauli Nieminen.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ *
+ **************************************************************************/
+/*
+ * Multipart buffer for coping data which is larger than the page size.
+ *
+ * Authors:
+ * Pauli Nieminen <suokkos-at-gmail-dot-com>
+ */
+
+#ifndef _DRM_BUFFER_H_
+#define _DRM_BUFFER_H_
+
+#include <drm/drmP.h>
+
+struct drm_buffer {
+ int iterator;
+ int size;
+ char *data[];
+};
+
+
+/**
+ * Return the index of page that buffer is currently pointing at.
+ */
+static inline int drm_buffer_page(struct drm_buffer *buf)
+{
+ return buf->iterator / PAGE_SIZE;
+}
+/**
+ * Return the index of the current byte in the page
+ */
+static inline int drm_buffer_index(struct drm_buffer *buf)
+{
+ return buf->iterator & (PAGE_SIZE - 1);
+}
+/**
+ * Return number of bytes that is left to process
+ */
+static inline int drm_buffer_unprocessed(struct drm_buffer *buf)
+{
+ return buf->size - buf->iterator;
+}
+
+/**
+ * Advance the buffer iterator number of bytes that is given.
+ */
+static inline void drm_buffer_advance(struct drm_buffer *buf, int bytes)
+{
+ buf->iterator += bytes;
+}
+
+/**
+ * Allocate the drm buffer object.
+ *
+ * buf: A pointer to a pointer where the object is stored.
+ * size: The number of bytes to allocate.
+ */
+extern int drm_buffer_alloc(struct drm_buffer **buf, int size);
+
+/**
+ * Copy the user data to the begin of the buffer and reset the processing
+ * iterator.
+ *
+ * user_data: A pointer the data that is copied to the buffer.
+ * size: The Number of bytes to copy.
+ */
+extern int drm_buffer_copy_from_user(struct drm_buffer *buf,
+ void __user *user_data, int size);
+
+/**
+ * Free the drm buffer object
+ */
+extern void drm_buffer_free(struct drm_buffer *buf);
+
+/**
+ * Read an object from buffer that may be split to multiple parts. If object
+ * is not split function just returns the pointer to object in buffer. But in
+ * case of split object data is copied to given stack object that is suplied
+ * by caller.
+ *
+ * The processing location of the buffer is also advanced to the next byte
+ * after the object.
+ *
+ * objsize: The size of the objet in bytes.
+ * stack_obj: A pointer to a memory location where object can be copied.
+ */
+extern void *drm_buffer_read_object(struct drm_buffer *buf,
+ int objsize, void *stack_obj);
+
+/**
+ * Returns the pointer to the dword which is offset number of elements from the
+ * current processing location.
+ *
+ * Caller must make sure that dword is not split in the buffer. This
+ * requirement is easily met if all the sizes of objects in buffer are
+ * multiples of dword and PAGE_SIZE is multiple dword.
+ *
+ * Call to this function doesn't change the processing location.
+ *
+ * offset: The index of the dword relative to the internat iterator.
+ */
+static inline void *drm_buffer_pointer_to_dword(struct drm_buffer *buffer,
+ int offset)
+{
+ int iter = buffer->iterator + offset * 4;
+ return &buffer->data[iter / PAGE_SIZE][iter & (PAGE_SIZE - 1)];
+}
+/**
+ * Returns the pointer to the dword which is offset number of elements from
+ * the current processing location.
+ *
+ * Call to this function doesn't change the processing location.
+ *
+ * offset: The index of the byte relative to the internat iterator.
+ */
+static inline void *drm_buffer_pointer_to_byte(struct drm_buffer *buffer,
+ int offset)
+{
+ int iter = buffer->iterator + offset;
+ return &buffer->data[iter / PAGE_SIZE][iter & (PAGE_SIZE - 1)];
+}
+
+#endif
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 15e4f28015e1..85995b4e3338 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -22,7 +22,6 @@
* Authors: Alex Deucher
*/
#include <linux/firmware.h>
-#include <linux/platform_device.h>
#include <linux/slab.h>
#include <drm/drmP.h>
#include "radeon.h"
@@ -2346,6 +2345,9 @@ void evergreen_bandwidth_update(struct radeon_device *rdev)
u32 num_heads = 0, lb_size;
int i;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
for (i = 0; i < rdev->num_crtc; i++) {
@@ -2424,7 +2426,6 @@ static int evergreen_pcie_gart_enable(struct radeon_device *rdev)
r = radeon_gart_table_vram_pin(rdev);
if (r)
return r;
- radeon_gart_restore(rdev);
/* Setup L2 cache */
WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING |
ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE |
@@ -2554,6 +2555,7 @@ void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *sav
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
tmp |= EVERGREEN_CRTC_BLANK_DATA_EN;
WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp);
+ WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
}
} else {
tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]);
@@ -2677,7 +2679,7 @@ void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *s
if (save->crtc_enabled[i]) {
if (ASIC_IS_DCE6(rdev)) {
tmp = RREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i]);
- tmp |= EVERGREEN_CRTC_BLANK_DATA_EN;
+ tmp &= ~EVERGREEN_CRTC_BLANK_DATA_EN;
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp);
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
@@ -2870,7 +2872,7 @@ static int evergreen_cp_start(struct radeon_device *rdev)
radeon_ring_write(ring, PACKET3_ME_INITIALIZE_DEVICE_ID(1));
radeon_ring_write(ring, 0);
radeon_ring_write(ring, 0);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
cp_me = 0xff;
WREG32(CP_ME_CNTL, cp_me);
@@ -2913,7 +2915,7 @@ static int evergreen_cp_start(struct radeon_device *rdev)
radeon_ring_write(ring, 0x0000000e); /* VGT_VERTEX_REUSE_BLOCK_CNTL */
radeon_ring_write(ring, 0x00000010); /* */
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
return 0;
}
@@ -3007,7 +3009,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
u32 vgt_cache_invalidation;
u32 hdp_host_path_cntl, tmp;
u32 disabled_rb_mask;
- int i, j, num_shader_engines, ps_thread_count;
+ int i, j, ps_thread_count;
switch (rdev->family) {
case CHIP_CYPRESS:
@@ -3305,8 +3307,6 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
rdev->config.evergreen.tile_config |=
((gb_addr_config & 0x30000000) >> 28) << 12;
- num_shader_engines = (gb_addr_config & NUM_SHADER_ENGINES(3) >> 12) + 1;
-
if ((rdev->family >= CHIP_CEDAR) && (rdev->family <= CHIP_HEMLOCK)) {
u32 efuse_straps_4;
u32 efuse_straps_3;
@@ -4023,7 +4023,8 @@ int sumo_rlc_init(struct radeon_device *rdev)
/* save restore block */
if (rdev->rlc.save_restore_obj == NULL) {
r = radeon_bo_create(rdev, dws * 4, PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->rlc.save_restore_obj);
+ RADEON_GEM_DOMAIN_VRAM, 0, NULL,
+ NULL, &rdev->rlc.save_restore_obj);
if (r) {
dev_warn(rdev->dev, "(%d) create RLC sr bo failed\n", r);
return r;
@@ -4101,7 +4102,8 @@ int sumo_rlc_init(struct radeon_device *rdev)
if (rdev->rlc.clear_state_obj == NULL) {
r = radeon_bo_create(rdev, dws * 4, PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->rlc.clear_state_obj);
+ RADEON_GEM_DOMAIN_VRAM, 0, NULL,
+ NULL, &rdev->rlc.clear_state_obj);
if (r) {
dev_warn(rdev->dev, "(%d) create RLC c bo failed\n", r);
sumo_rlc_fini(rdev);
@@ -4175,8 +4177,10 @@ int sumo_rlc_init(struct radeon_device *rdev)
if (rdev->rlc.cp_table_size) {
if (rdev->rlc.cp_table_obj == NULL) {
- r = radeon_bo_create(rdev, rdev->rlc.cp_table_size, PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->rlc.cp_table_obj);
+ r = radeon_bo_create(rdev, rdev->rlc.cp_table_size,
+ PAGE_SIZE, true,
+ RADEON_GEM_DOMAIN_VRAM, 0, NULL,
+ NULL, &rdev->rlc.cp_table_obj);
if (r) {
dev_warn(rdev->dev, "(%d) create RLC cp table bo failed\n", r);
sumo_rlc_fini(rdev);
@@ -4746,17 +4750,17 @@ static u32 evergreen_get_ih_wptr(struct radeon_device *rdev)
wptr = RREG32(IH_RB_WPTR);
if (wptr & RB_OVERFLOW) {
+ wptr &= ~RB_OVERFLOW;
/* When a ring buffer overflow happen start parsing interrupt
* from the last not overwritten vector (wptr + 16). Hopefully
* this should allow us to catchup.
*/
- dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n",
- wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask);
+ dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
+ wptr, rdev->ih.rptr, (wptr + 16) & rdev->ih.ptr_mask);
rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask;
tmp = RREG32(IH_RB_CNTL);
tmp |= IH_WPTR_OVERFLOW_CLEAR;
WREG32(IH_RB_CNTL, tmp);
- wptr &= ~RB_OVERFLOW;
}
return (wptr & rdev->ih.ptr_mask);
}
@@ -4961,7 +4965,8 @@ restart_ih:
case 16: /* D5 page flip */
case 18: /* D6 page flip */
DRM_DEBUG("IH: D%d flip\n", ((src_id - 8) >> 1) + 1);
- radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1);
+ if (radeon_use_pflipirq > 0)
+ radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1);
break;
case 42: /* HPD hotplug */
switch (src_data) {
@@ -5133,6 +5138,7 @@ restart_ih:
/* wptr/rptr are in bytes! */
rptr += 16;
rptr &= rdev->ih.ptr_mask;
+ WREG32(IH_RB_RPTR, rptr);
}
if (queue_hotplug)
schedule_work(&rdev->hotplug_work);
@@ -5141,7 +5147,6 @@ restart_ih:
if (queue_thermal && rdev->pm.dpm_enabled)
schedule_work(&rdev->pm.dpm.thermal.work);
rdev->ih.rptr = rptr;
- WREG32(IH_RB_RPTR, rdev->ih.rptr);
atomic_set(&rdev->ih.lock, 0);
/* make sure wptr hasn't changed while processing */
diff --git a/drivers/gpu/drm/radeon/evergreen_dma.c b/drivers/gpu/drm/radeon/evergreen_dma.c
index 478caefe0fef..66bcfadeedd1 100644
--- a/drivers/gpu/drm/radeon/evergreen_dma.c
+++ b/drivers/gpu/drm/radeon/evergreen_dma.c
@@ -104,12 +104,14 @@ void evergreen_dma_ring_ib_execute(struct radeon_device *rdev,
* Used by the radeon ttm implementation to move pages if
* registered as the asic copy callback.
*/
-int evergreen_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence)
+struct radeon_fence *evergreen_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv)
{
struct radeon_semaphore *sem = NULL;
+ struct radeon_fence *fence;
int ring_index = rdev->asic->copy.dma_ring_index;
struct radeon_ring *ring = &rdev->ring[ring_index];
u32 size_in_dw, cur_size_in_dw;
@@ -119,7 +121,7 @@ int evergreen_copy_dma(struct radeon_device *rdev,
r = radeon_semaphore_create(rdev, &sem);
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
- return r;
+ return ERR_PTR(r);
}
size_in_dw = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT) / 4;
@@ -128,10 +130,10 @@ int evergreen_copy_dma(struct radeon_device *rdev,
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_semaphore_sync_to(sem, *fence);
+ radeon_semaphore_sync_resv(rdev, sem, resv, false);
radeon_semaphore_sync_rings(rdev, sem, ring->idx);
for (i = 0; i < num_loops; i++) {
@@ -148,17 +150,17 @@ int evergreen_copy_dma(struct radeon_device *rdev,
dst_offset += cur_size_in_dw * 4;
}
- r = radeon_fence_emit(rdev, fence, ring->idx);
+ r = radeon_fence_emit(rdev, &fence, ring->idx);
if (r) {
radeon_ring_unlock_undo(rdev, ring);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_ring_unlock_commit(rdev, ring);
- radeon_semaphore_free(rdev, &sem, *fence);
+ radeon_ring_unlock_commit(rdev, ring, false);
+ radeon_semaphore_free(rdev, &sem, fence);
- return r;
+ return fence;
}
/**
diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c
index 1ec0e6e83f9f..53abd9b17a50 100644
--- a/drivers/gpu/drm/radeon/evergreen_hdmi.c
+++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c
@@ -38,6 +38,37 @@ extern void dce6_afmt_select_pin(struct drm_encoder *encoder);
extern void dce6_afmt_write_latency_fields(struct drm_encoder *encoder,
struct drm_display_mode *mode);
+/* enable the audio stream */
+static void dce4_audio_enable(struct radeon_device *rdev,
+ struct r600_audio_pin *pin,
+ u8 enable_mask)
+{
+ u32 tmp = RREG32(AZ_HOT_PLUG_CONTROL);
+
+ if (!pin)
+ return;
+
+ if (enable_mask) {
+ tmp |= AUDIO_ENABLED;
+ if (enable_mask & 1)
+ tmp |= PIN0_AUDIO_ENABLED;
+ if (enable_mask & 2)
+ tmp |= PIN1_AUDIO_ENABLED;
+ if (enable_mask & 4)
+ tmp |= PIN2_AUDIO_ENABLED;
+ if (enable_mask & 8)
+ tmp |= PIN3_AUDIO_ENABLED;
+ } else {
+ tmp &= ~(AUDIO_ENABLED |
+ PIN0_AUDIO_ENABLED |
+ PIN1_AUDIO_ENABLED |
+ PIN2_AUDIO_ENABLED |
+ PIN3_AUDIO_ENABLED);
+ }
+
+ WREG32(AZ_HOT_PLUG_CONTROL, tmp);
+}
+
/*
* update the N and CTS parameters for a given pixel clock rate
*/
@@ -102,7 +133,7 @@ static void dce4_afmt_write_speaker_allocation(struct drm_encoder *encoder)
struct drm_connector *connector;
struct radeon_connector *radeon_connector = NULL;
u32 tmp;
- u8 *sadb;
+ u8 *sadb = NULL;
int sad_count;
list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
@@ -117,10 +148,10 @@ static void dce4_afmt_write_speaker_allocation(struct drm_encoder *encoder)
return;
}
- sad_count = drm_edid_to_speaker_allocation(radeon_connector->edid, &sadb);
- if (sad_count <= 0) {
- DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sad_count);
- return;
+ sad_count = drm_edid_to_speaker_allocation(radeon_connector_edid(connector), &sadb);
+ if (sad_count < 0) {
+ DRM_DEBUG("Couldn't read Speaker Allocation Data Block: %d\n", sad_count);
+ sad_count = 0;
}
/* program the speaker allocation */
@@ -172,7 +203,7 @@ static void evergreen_hdmi_write_sad_regs(struct drm_encoder *encoder)
return;
}
- sad_count = drm_edid_to_sad(radeon_connector->edid, &sads);
+ sad_count = drm_edid_to_sad(radeon_connector_edid(connector), &sads);
if (sad_count <= 0) {
DRM_ERROR("Couldn't read SADs: %d\n", sad_count);
return;
@@ -318,10 +349,10 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
/* disable audio prior to setting up hw */
if (ASIC_IS_DCE6(rdev)) {
dig->afmt->pin = dce6_audio_get_pin(rdev);
- dce6_audio_enable(rdev, dig->afmt->pin, false);
+ dce6_audio_enable(rdev, dig->afmt->pin, 0);
} else {
dig->afmt->pin = r600_audio_get_pin(rdev);
- r600_audio_enable(rdev, dig->afmt->pin, false);
+ dce4_audio_enable(rdev, dig->afmt->pin, 0);
}
evergreen_audio_set_dto(encoder, mode->clock);
@@ -463,13 +494,15 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
/* enable audio after to setting up hw */
if (ASIC_IS_DCE6(rdev))
- dce6_audio_enable(rdev, dig->afmt->pin, true);
+ dce6_audio_enable(rdev, dig->afmt->pin, 1);
else
- r600_audio_enable(rdev, dig->afmt->pin, true);
+ dce4_audio_enable(rdev, dig->afmt->pin, 0xf);
}
void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable)
{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
@@ -482,6 +515,14 @@ void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable)
if (!enable && !dig->afmt->enabled)
return;
+ if (!enable && dig->afmt->pin) {
+ if (ASIC_IS_DCE6(rdev))
+ dce6_audio_enable(rdev, dig->afmt->pin, 0);
+ else
+ dce4_audio_enable(rdev, dig->afmt->pin, 0);
+ dig->afmt->pin = NULL;
+ }
+
dig->afmt->enabled = enable;
DRM_DEBUG("%sabling HDMI interface @ 0x%04X for encoder 0x%x\n",
diff --git a/drivers/gpu/drm/radeon/kv_dpm.c b/drivers/gpu/drm/radeon/kv_dpm.c
index 9ef8c38f2d66..9b42001295ba 100644
--- a/drivers/gpu/drm/radeon/kv_dpm.c
+++ b/drivers/gpu/drm/radeon/kv_dpm.c
@@ -33,6 +33,8 @@
#define KV_MINIMUM_ENGINE_CLOCK 800
#define SMC_RAM_END 0x40000
+static int kv_enable_nb_dpm(struct radeon_device *rdev,
+ bool enable);
static void kv_init_graphics_levels(struct radeon_device *rdev);
static int kv_calculate_ds_divider(struct radeon_device *rdev);
static int kv_calculate_nbps_level_settings(struct radeon_device *rdev);
@@ -1295,6 +1297,9 @@ void kv_dpm_disable(struct radeon_device *rdev)
{
kv_smc_bapm_enable(rdev, false);
+ if (rdev->family == CHIP_MULLINS)
+ kv_enable_nb_dpm(rdev, false);
+
/* powerup blocks */
kv_dpm_powergate_acp(rdev, false);
kv_dpm_powergate_samu(rdev, false);
@@ -1438,14 +1443,14 @@ static int kv_update_uvd_dpm(struct radeon_device *rdev, bool gate)
return kv_enable_uvd_dpm(rdev, !gate);
}
-static u8 kv_get_vce_boot_level(struct radeon_device *rdev)
+static u8 kv_get_vce_boot_level(struct radeon_device *rdev, u32 evclk)
{
u8 i;
struct radeon_vce_clock_voltage_dependency_table *table =
&rdev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
for (i = 0; i < table->count; i++) {
- if (table->entries[i].evclk >= 0) /* XXX */
+ if (table->entries[i].evclk >= evclk)
break;
}
@@ -1468,7 +1473,7 @@ static int kv_update_vce_dpm(struct radeon_device *rdev,
if (pi->caps_stable_p_state)
pi->vce_boot_level = table->count - 1;
else
- pi->vce_boot_level = kv_get_vce_boot_level(rdev);
+ pi->vce_boot_level = kv_get_vce_boot_level(rdev, radeon_new_state->evclk);
ret = kv_copy_bytes_to_smc(rdev,
pi->dpm_table_start +
@@ -1769,15 +1774,24 @@ static int kv_update_dfs_bypass_settings(struct radeon_device *rdev,
return ret;
}
-static int kv_enable_nb_dpm(struct radeon_device *rdev)
+static int kv_enable_nb_dpm(struct radeon_device *rdev,
+ bool enable)
{
struct kv_power_info *pi = kv_get_pi(rdev);
int ret = 0;
- if (pi->enable_nb_dpm && !pi->nb_dpm_enabled) {
- ret = kv_notify_message_to_smu(rdev, PPSMC_MSG_NBDPM_Enable);
- if (ret == 0)
- pi->nb_dpm_enabled = true;
+ if (enable) {
+ if (pi->enable_nb_dpm && !pi->nb_dpm_enabled) {
+ ret = kv_notify_message_to_smu(rdev, PPSMC_MSG_NBDPM_Enable);
+ if (ret == 0)
+ pi->nb_dpm_enabled = true;
+ }
+ } else {
+ if (pi->enable_nb_dpm && pi->nb_dpm_enabled) {
+ ret = kv_notify_message_to_smu(rdev, PPSMC_MSG_NBDPM_Disable);
+ if (ret == 0)
+ pi->nb_dpm_enabled = false;
+ }
}
return ret;
@@ -1864,7 +1878,7 @@ int kv_dpm_set_power_state(struct radeon_device *rdev)
}
kv_update_sclk_t(rdev);
if (rdev->family == CHIP_MULLINS)
- kv_enable_nb_dpm(rdev);
+ kv_enable_nb_dpm(rdev, true);
}
} else {
if (pi->enable_dpm) {
@@ -1889,7 +1903,7 @@ int kv_dpm_set_power_state(struct radeon_device *rdev)
}
kv_update_acp_boot_level(rdev);
kv_update_sclk_t(rdev);
- kv_enable_nb_dpm(rdev);
+ kv_enable_nb_dpm(rdev, true);
}
}
@@ -2711,7 +2725,11 @@ int kv_dpm_init(struct radeon_device *rdev)
pi->sram_end = SMC_RAM_END;
- pi->enable_nb_dpm = true;
+ /* Enabling nb dpm on an asrock system prevents dpm from working */
+ if (rdev->pdev->subsystem_vendor == 0x1849)
+ pi->enable_nb_dpm = false;
+ else
+ pi->enable_nb_dpm = true;
pi->caps_power_containment = true;
pi->caps_cac = true;
@@ -2726,7 +2744,19 @@ int kv_dpm_init(struct radeon_device *rdev)
pi->caps_sclk_ds = true;
pi->enable_auto_thermal_throttling = true;
pi->disable_nb_ps3_in_battery = false;
- pi->bapm_enable = true;
+ if (radeon_bapm == -1) {
+ /* There are stability issues reported on with
+ * bapm enabled on an asrock system.
+ */
+ if (rdev->pdev->subsystem_vendor == 0x1849)
+ pi->bapm_enable = false;
+ else
+ pi->bapm_enable = true;
+ } else if (radeon_bapm == 0) {
+ pi->bapm_enable = false;
+ } else {
+ pi->bapm_enable = true;
+ }
pi->voltage_drop_t = 0;
pi->caps_sclk_throttle_low_notification = false;
pi->caps_fps = false; /* true? */
@@ -2770,6 +2800,8 @@ void kv_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev,
tmp = (RREG32_SMC(SMU_VOLTAGE_STATUS) & SMU_VOLTAGE_CURRENT_LEVEL_MASK) >>
SMU_VOLTAGE_CURRENT_LEVEL_SHIFT;
vddc = kv_convert_8bit_index_to_voltage(rdev, (u16)tmp);
+ seq_printf(m, "uvd %sabled\n", pi->uvd_power_gated ? "dis" : "en");
+ seq_printf(m, "vce %sabled\n", pi->vce_power_gated ? "dis" : "en");
seq_printf(m, "power level %d sclk: %u vddc: %u\n",
current_index, sclk, vddc);
}
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 5a33ca681867..3faee58946dd 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -1229,7 +1229,6 @@ static int cayman_pcie_gart_enable(struct radeon_device *rdev)
r = radeon_gart_table_vram_pin(rdev);
if (r)
return r;
- radeon_gart_restore(rdev);
/* Setup TLB control */
WREG32(MC_VM_MX_L1_TLB_CNTL,
(0xA << 7) |
@@ -1272,7 +1271,7 @@ static int cayman_pcie_gart_enable(struct radeon_device *rdev)
WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR + (i << 2), 0);
WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR + (i << 2), rdev->vm_manager.max_pfn);
WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2),
- rdev->gart.table_addr >> 12);
+ rdev->vm_manager.saved_table_addr[i]);
}
/* enable context1-7 */
@@ -1304,6 +1303,13 @@ static int cayman_pcie_gart_enable(struct radeon_device *rdev)
static void cayman_pcie_gart_disable(struct radeon_device *rdev)
{
+ unsigned i;
+
+ for (i = 1; i < 8; ++i) {
+ rdev->vm_manager.saved_table_addr[i] = RREG32(
+ VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2));
+ }
+
/* Disable all tables */
WREG32(VM_CONTEXT0_CNTL, 0);
WREG32(VM_CONTEXT1_CNTL, 0);
@@ -1506,7 +1512,7 @@ static int cayman_cp_start(struct radeon_device *rdev)
radeon_ring_write(ring, PACKET3_ME_INITIALIZE_DEVICE_ID(1));
radeon_ring_write(ring, 0);
radeon_ring_write(ring, 0);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
cayman_cp_enable(rdev, true);
@@ -1548,7 +1554,7 @@ static int cayman_cp_start(struct radeon_device *rdev)
radeon_ring_write(ring, 0x0000000e); /* VGT_VERTEX_REUSE_BLOCK_CNTL */
radeon_ring_write(ring, 0x00000010); /* */
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
/* XXX init other rings */
diff --git a/drivers/gpu/drm/radeon/ni_dma.c b/drivers/gpu/drm/radeon/ni_dma.c
index 6378e0276691..f26f0a9fb522 100644
--- a/drivers/gpu/drm/radeon/ni_dma.c
+++ b/drivers/gpu/drm/radeon/ni_dma.c
@@ -191,12 +191,6 @@ int cayman_dma_resume(struct radeon_device *rdev)
u32 reg_offset, wb_offset;
int i, r;
- /* Reset dma */
- WREG32(SRBM_SOFT_RESET, SOFT_RESET_DMA | SOFT_RESET_DMA1);
- RREG32(SRBM_SOFT_RESET);
- udelay(50);
- WREG32(SRBM_SOFT_RESET, 0);
-
for (i = 0; i < 2; i++) {
if (i == 0) {
ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX];
@@ -307,7 +301,43 @@ bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
}
/**
- * cayman_dma_vm_set_page - update the page tables using the DMA
+ * cayman_dma_vm_copy_pages - update PTEs by copying them from the GART
+ *
+ * @rdev: radeon_device pointer
+ * @ib: indirect buffer to fill with commands
+ * @pe: addr of the page entry
+ * @src: src addr where to copy from
+ * @count: number of page entries to update
+ *
+ * Update PTEs by copying them from the GART using the DMA (cayman/TN).
+ */
+void cayman_dma_vm_copy_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe, uint64_t src,
+ unsigned count)
+{
+ unsigned ndw;
+
+ while (count) {
+ ndw = count * 2;
+ if (ndw > 0xFFFFE)
+ ndw = 0xFFFFE;
+
+ ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_COPY,
+ 0, 0, ndw);
+ ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+ ib->ptr[ib->length_dw++] = lower_32_bits(src);
+ ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
+ ib->ptr[ib->length_dw++] = upper_32_bits(src) & 0xff;
+
+ pe += ndw * 4;
+ src += ndw * 4;
+ count -= ndw / 2;
+ }
+}
+
+/**
+ * cayman_dma_vm_write_pages - update PTEs by writing them manually
*
* @rdev: radeon_device pointer
* @ib: indirect buffer to fill with commands
@@ -315,71 +345,103 @@ bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
* @addr: dst addr to write into pe
* @count: number of page entries to update
* @incr: increase next addr by incr bytes
- * @flags: hw access flags
+ * @flags: hw access flags
*
- * Update the page tables using the DMA (cayman/TN).
+ * Update PTEs by writing them manually using the DMA (cayman/TN).
*/
-void cayman_dma_vm_set_page(struct radeon_device *rdev,
- struct radeon_ib *ib,
- uint64_t pe,
- uint64_t addr, unsigned count,
- uint32_t incr, uint32_t flags)
+void cayman_dma_vm_write_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags)
{
uint64_t value;
unsigned ndw;
- trace_radeon_vm_set_page(pe, addr, count, incr, flags);
-
- if ((flags & R600_PTE_SYSTEM) || (count == 1)) {
- while (count) {
- ndw = count * 2;
- if (ndw > 0xFFFFE)
- ndw = 0xFFFFE;
-
- /* for non-physically contiguous pages (system) */
- ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_WRITE, 0, 0, ndw);
- ib->ptr[ib->length_dw++] = pe;
- ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
- for (; ndw > 0; ndw -= 2, --count, pe += 8) {
- if (flags & R600_PTE_SYSTEM) {
- value = radeon_vm_map_gart(rdev, addr);
- value &= 0xFFFFFFFFFFFFF000ULL;
- } else if (flags & R600_PTE_VALID) {
- value = addr;
- } else {
- value = 0;
- }
- addr += incr;
- value |= flags;
- ib->ptr[ib->length_dw++] = value;
- ib->ptr[ib->length_dw++] = upper_32_bits(value);
- }
- }
- } else {
- while (count) {
- ndw = count * 2;
- if (ndw > 0xFFFFE)
- ndw = 0xFFFFE;
-
- if (flags & R600_PTE_VALID)
+ while (count) {
+ ndw = count * 2;
+ if (ndw > 0xFFFFE)
+ ndw = 0xFFFFE;
+
+ /* for non-physically contiguous pages (system) */
+ ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_WRITE,
+ 0, 0, ndw);
+ ib->ptr[ib->length_dw++] = pe;
+ ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
+ for (; ndw > 0; ndw -= 2, --count, pe += 8) {
+ if (flags & R600_PTE_SYSTEM) {
+ value = radeon_vm_map_gart(rdev, addr);
+ value &= 0xFFFFFFFFFFFFF000ULL;
+ } else if (flags & R600_PTE_VALID) {
value = addr;
- else
+ } else {
value = 0;
- /* for physically contiguous pages (vram) */
- ib->ptr[ib->length_dw++] = DMA_PTE_PDE_PACKET(ndw);
- ib->ptr[ib->length_dw++] = pe; /* dst addr */
- ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
- ib->ptr[ib->length_dw++] = flags; /* mask */
- ib->ptr[ib->length_dw++] = 0;
- ib->ptr[ib->length_dw++] = value; /* value */
+ }
+ addr += incr;
+ value |= flags;
+ ib->ptr[ib->length_dw++] = value;
ib->ptr[ib->length_dw++] = upper_32_bits(value);
- ib->ptr[ib->length_dw++] = incr; /* increment size */
- ib->ptr[ib->length_dw++] = 0;
- pe += ndw * 4;
- addr += (ndw / 2) * incr;
- count -= ndw / 2;
}
}
+}
+
+/**
+ * cayman_dma_vm_set_pages - update the page tables using the DMA
+ *
+ * @rdev: radeon_device pointer
+ * @ib: indirect buffer to fill with commands
+ * @pe: addr of the page entry
+ * @addr: dst addr to write into pe
+ * @count: number of page entries to update
+ * @incr: increase next addr by incr bytes
+ * @flags: hw access flags
+ *
+ * Update the page tables using the DMA (cayman/TN).
+ */
+void cayman_dma_vm_set_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags)
+{
+ uint64_t value;
+ unsigned ndw;
+
+ while (count) {
+ ndw = count * 2;
+ if (ndw > 0xFFFFE)
+ ndw = 0xFFFFE;
+
+ if (flags & R600_PTE_VALID)
+ value = addr;
+ else
+ value = 0;
+
+ /* for physically contiguous pages (vram) */
+ ib->ptr[ib->length_dw++] = DMA_PTE_PDE_PACKET(ndw);
+ ib->ptr[ib->length_dw++] = pe; /* dst addr */
+ ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
+ ib->ptr[ib->length_dw++] = flags; /* mask */
+ ib->ptr[ib->length_dw++] = 0;
+ ib->ptr[ib->length_dw++] = value; /* value */
+ ib->ptr[ib->length_dw++] = upper_32_bits(value);
+ ib->ptr[ib->length_dw++] = incr; /* increment size */
+ ib->ptr[ib->length_dw++] = 0;
+
+ pe += ndw * 4;
+ addr += (ndw / 2) * incr;
+ count -= ndw / 2;
+ }
+}
+
+/**
+ * cayman_dma_vm_pad_ib - pad the IB to the required number of dw
+ *
+ * @ib: indirect buffer to fill with padding
+ *
+ */
+void cayman_dma_vm_pad_ib(struct radeon_ib *ib)
+{
while (ib->length_dw & 0x7)
ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0);
}
diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c
index 01fc4888e6fe..6d2f16cf2c1c 100644
--- a/drivers/gpu/drm/radeon/ni_dpm.c
+++ b/drivers/gpu/drm/radeon/ni_dpm.c
@@ -23,6 +23,7 @@
#include "drmP.h"
#include "radeon.h"
+#include "radeon_asic.h"
#include "nid.h"
#include "r600_dpm.h"
#include "ni_dpm.h"
@@ -789,7 +790,6 @@ static void ni_apply_state_adjust_rules(struct radeon_device *rdev,
bool disable_mclk_switching;
u32 mclk;
u16 vddci;
- u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
int i;
if ((rdev->pm.dpm.new_active_crtc_count > 1) ||
@@ -816,29 +816,6 @@ static void ni_apply_state_adjust_rules(struct radeon_device *rdev,
}
}
- /* limit clocks to max supported clocks based on voltage dependency tables */
- btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
- &max_sclk_vddc);
- btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
- &max_mclk_vddci);
- btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
- &max_mclk_vddc);
-
- for (i = 0; i < ps->performance_level_count; i++) {
- if (max_sclk_vddc) {
- if (ps->performance_levels[i].sclk > max_sclk_vddc)
- ps->performance_levels[i].sclk = max_sclk_vddc;
- }
- if (max_mclk_vddci) {
- if (ps->performance_levels[i].mclk > max_mclk_vddci)
- ps->performance_levels[i].mclk = max_mclk_vddci;
- }
- if (max_mclk_vddc) {
- if (ps->performance_levels[i].mclk > max_mclk_vddc)
- ps->performance_levels[i].mclk = max_mclk_vddc;
- }
- }
-
/* XXX validate the min clocks required for display */
/* adjust low state */
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 1544efcf1c3a..b53b31a7b76f 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -652,7 +652,6 @@ int r100_pci_gart_enable(struct radeon_device *rdev)
{
uint32_t tmp;
- radeon_gart_restore(rdev);
/* discard memory request outside of configured range */
tmp = RREG32(RADEON_AIC_CNTL) | RADEON_DIS_OUT_OF_PCI_GART_ACCESS;
WREG32(RADEON_AIC_CNTL, tmp);
@@ -683,7 +682,7 @@ void r100_pci_gart_disable(struct radeon_device *rdev)
}
void r100_pci_gart_set_page(struct radeon_device *rdev, unsigned i,
- uint64_t addr)
+ uint64_t addr, uint32_t flags)
{
u32 *gtt = rdev->gart.ptr;
gtt[i] = cpu_to_le32(lower_32_bits(addr));
@@ -822,6 +821,20 @@ u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc)
return RREG32(RADEON_CRTC2_CRNT_FRAME);
}
+/**
+ * r100_ring_hdp_flush - flush Host Data Path via the ring buffer
+ * rdev: radeon device structure
+ * ring: ring buffer struct for emitting packets
+ */
+static void r100_ring_hdp_flush(struct radeon_device *rdev, struct radeon_ring *ring)
+{
+ radeon_ring_write(ring, PACKET0(RADEON_HOST_PATH_CNTL, 0));
+ radeon_ring_write(ring, rdev->config.r100.hdp_cntl |
+ RADEON_HDP_READ_BUFFER_INVALIDATE);
+ radeon_ring_write(ring, PACKET0(RADEON_HOST_PATH_CNTL, 0));
+ radeon_ring_write(ring, rdev->config.r100.hdp_cntl);
+}
+
/* Who ever call radeon_fence_emit should call ring_lock and ask
* for enough space (today caller are ib schedule and buffer move) */
void r100_fence_ring_emit(struct radeon_device *rdev,
@@ -838,11 +851,7 @@ void r100_fence_ring_emit(struct radeon_device *rdev,
/* Wait until IDLE & CLEAN */
radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0));
radeon_ring_write(ring, RADEON_WAIT_2D_IDLECLEAN | RADEON_WAIT_3D_IDLECLEAN);
- radeon_ring_write(ring, PACKET0(RADEON_HOST_PATH_CNTL, 0));
- radeon_ring_write(ring, rdev->config.r100.hdp_cntl |
- RADEON_HDP_READ_BUFFER_INVALIDATE);
- radeon_ring_write(ring, PACKET0(RADEON_HOST_PATH_CNTL, 0));
- radeon_ring_write(ring, rdev->config.r100.hdp_cntl);
+ r100_ring_hdp_flush(rdev, ring);
/* Emit fence sequence & fire IRQ */
radeon_ring_write(ring, PACKET0(rdev->fence_drv[fence->ring].scratch_reg, 0));
radeon_ring_write(ring, fence->seq);
@@ -860,13 +869,14 @@ bool r100_semaphore_ring_emit(struct radeon_device *rdev,
return false;
}
-int r100_copy_blit(struct radeon_device *rdev,
- uint64_t src_offset,
- uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence)
+struct radeon_fence *r100_copy_blit(struct radeon_device *rdev,
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv)
{
struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
+ struct radeon_fence *fence;
uint32_t cur_pages;
uint32_t stride_bytes = RADEON_GPU_PAGE_SIZE;
uint32_t pitch;
@@ -887,7 +897,7 @@ int r100_copy_blit(struct radeon_device *rdev,
r = radeon_ring_lock(rdev, ring, ndw);
if (r) {
DRM_ERROR("radeon: moving bo (%d) asking for %u dw.\n", r, ndw);
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
while (num_gpu_pages > 0) {
cur_pages = num_gpu_pages;
@@ -927,11 +937,13 @@ int r100_copy_blit(struct radeon_device *rdev,
RADEON_WAIT_2D_IDLECLEAN |
RADEON_WAIT_HOST_IDLECLEAN |
RADEON_WAIT_DMA_GUI_IDLE);
- if (fence) {
- r = radeon_fence_emit(rdev, fence, RADEON_RING_TYPE_GFX_INDEX);
+ r = radeon_fence_emit(rdev, &fence, RADEON_RING_TYPE_GFX_INDEX);
+ if (r) {
+ radeon_ring_unlock_undo(rdev, ring);
+ return ERR_PTR(r);
}
- radeon_ring_unlock_commit(rdev, ring);
- return r;
+ radeon_ring_unlock_commit(rdev, ring, false);
+ return fence;
}
static int r100_cp_wait_for_idle(struct radeon_device *rdev)
@@ -963,7 +975,7 @@ void r100_ring_start(struct radeon_device *rdev, struct radeon_ring *ring)
RADEON_ISYNC_ANY3D_IDLE2D |
RADEON_ISYNC_WAIT_IDLEGUI |
RADEON_ISYNC_CPSCRATCH_IDLEGUI);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
}
@@ -1401,7 +1413,6 @@ int r100_cs_parse_packet0(struct radeon_cs_parser *p,
*/
int r100_cs_packet_parse_vline(struct radeon_cs_parser *p)
{
- struct drm_mode_object *obj;
struct drm_crtc *crtc;
struct radeon_crtc *radeon_crtc;
struct radeon_cs_packet p3reloc, waitreloc;
@@ -1441,12 +1452,11 @@ int r100_cs_packet_parse_vline(struct radeon_cs_parser *p)
header = radeon_get_ib_value(p, h_idx);
crtc_id = radeon_get_ib_value(p, h_idx + 5);
reg = R100_CP_PACKET0_GET_REG(header);
- obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC);
- if (!obj) {
+ crtc = drm_crtc_find(p->rdev->ddev, crtc_id);
+ if (!crtc) {
DRM_ERROR("cannot find crtc %d\n", crtc_id);
return -ENOENT;
}
- crtc = obj_to_crtc(obj);
radeon_crtc = to_radeon_crtc(crtc);
crtc_id = radeon_crtc->crtc_id;
@@ -3197,6 +3207,9 @@ void r100_bandwidth_update(struct radeon_device *rdev)
uint32_t pixel_bytes1 = 0;
uint32_t pixel_bytes2 = 0;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled) {
@@ -3631,7 +3644,7 @@ int r100_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
}
radeon_ring_write(ring, PACKET0(scratch, 0));
radeon_ring_write(ring, 0xDEADBEEF);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
for (i = 0; i < rdev->usec_timeout; i++) {
tmp = RREG32(scratch);
if (tmp == 0xDEADBEEF) {
@@ -3693,7 +3706,7 @@ int r100_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
ib.ptr[6] = PACKET2(0);
ib.ptr[7] = PACKET2(0);
ib.length_dw = 8;
- r = radeon_ib_schedule(rdev, &ib, NULL);
+ r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r) {
DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
goto free_ib;
@@ -4067,39 +4080,6 @@ int r100_init(struct radeon_device *rdev)
return 0;
}
-uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg,
- bool always_indirect)
-{
- if (reg < rdev->rmmio_size && !always_indirect)
- return readl(((void __iomem *)rdev->rmmio) + reg);
- else {
- unsigned long flags;
- uint32_t ret;
-
- spin_lock_irqsave(&rdev->mmio_idx_lock, flags);
- writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
- ret = readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
- spin_unlock_irqrestore(&rdev->mmio_idx_lock, flags);
-
- return ret;
- }
-}
-
-void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v,
- bool always_indirect)
-{
- if (reg < rdev->rmmio_size && !always_indirect)
- writel(v, ((void __iomem *)rdev->rmmio) + reg);
- else {
- unsigned long flags;
-
- spin_lock_irqsave(&rdev->mmio_idx_lock, flags);
- writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
- writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
- spin_unlock_irqrestore(&rdev->mmio_idx_lock, flags);
- }
-}
-
u32 r100_io_rreg(struct radeon_device *rdev, u32 reg)
{
if (reg < rdev->rio_mem_size)
diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c
index 58f0473aa73f..732d4938aab7 100644
--- a/drivers/gpu/drm/radeon/r200.c
+++ b/drivers/gpu/drm/radeon/r200.c
@@ -80,13 +80,14 @@ static int r200_get_vtx_size_0(uint32_t vtx_fmt_0)
return vtx_size;
}
-int r200_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset,
- uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence)
+struct radeon_fence *r200_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv)
{
struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
+ struct radeon_fence *fence;
uint32_t size;
uint32_t cur_size;
int i, num_loops;
@@ -98,7 +99,7 @@ int r200_copy_dma(struct radeon_device *rdev,
r = radeon_ring_lock(rdev, ring, num_loops * 4 + 64);
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
- return r;
+ return ERR_PTR(r);
}
/* Must wait for 2D idle & clean before DMA or hangs might happen */
radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0));
@@ -118,11 +119,13 @@ int r200_copy_dma(struct radeon_device *rdev,
}
radeon_ring_write(ring, PACKET0(RADEON_WAIT_UNTIL, 0));
radeon_ring_write(ring, RADEON_WAIT_DMA_GUI_IDLE);
- if (fence) {
- r = radeon_fence_emit(rdev, fence, RADEON_RING_TYPE_GFX_INDEX);
+ r = radeon_fence_emit(rdev, &fence, RADEON_RING_TYPE_GFX_INDEX);
+ if (r) {
+ radeon_ring_unlock_undo(rdev, ring);
+ return ERR_PTR(r);
}
- radeon_ring_unlock_commit(rdev, ring);
- return r;
+ radeon_ring_unlock_commit(rdev, ring, false);
+ return fence;
}
diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
index 3c21d77a483d..1bc4704034ce 100644
--- a/drivers/gpu/drm/radeon/r300.c
+++ b/drivers/gpu/drm/radeon/r300.c
@@ -69,17 +69,23 @@ void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev)
mb();
}
+#define R300_PTE_UNSNOOPED (1 << 0)
#define R300_PTE_WRITEABLE (1 << 2)
#define R300_PTE_READABLE (1 << 3)
void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i,
- uint64_t addr)
+ uint64_t addr, uint32_t flags)
{
void __iomem *ptr = rdev->gart.ptr;
addr = (lower_32_bits(addr) >> 8) |
- ((upper_32_bits(addr) & 0xff) << 24) |
- R300_PTE_WRITEABLE | R300_PTE_READABLE;
+ ((upper_32_bits(addr) & 0xff) << 24);
+ if (flags & RADEON_GART_PAGE_READ)
+ addr |= R300_PTE_READABLE;
+ if (flags & RADEON_GART_PAGE_WRITE)
+ addr |= R300_PTE_WRITEABLE;
+ if (!(flags & RADEON_GART_PAGE_SNOOP))
+ addr |= R300_PTE_UNSNOOPED;
/* on x86 we want this to be CPU endian, on powerpc
* on powerpc without HW swappers, it'll get swapped on way
* into VRAM - so no need for cpu_to_le32 on VRAM tables */
@@ -120,7 +126,6 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev)
r = radeon_gart_table_vram_pin(rdev);
if (r)
return r;
- radeon_gart_restore(rdev);
/* discard memory request outside of configured range */
tmp = RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD;
WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp);
@@ -290,7 +295,7 @@ void r300_ring_start(struct radeon_device *rdev, struct radeon_ring *ring)
radeon_ring_write(ring,
R300_GEOMETRY_ROUND_NEAREST |
R300_COLOR_ROUND_NEAREST);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
}
static void r300_errata(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/r300_cmdbuf.c b/drivers/gpu/drm/radeon/r300_cmdbuf.c
index 84b1d5367a11..9418e388b045 100644
--- a/drivers/gpu/drm/radeon/r300_cmdbuf.c
+++ b/drivers/gpu/drm/radeon/r300_cmdbuf.c
@@ -34,10 +34,10 @@
*/
#include <drm/drmP.h>
-#include <drm/drm_buffer.h>
#include <drm/radeon_drm.h>
#include "radeon_drv.h"
#include "r300_reg.h"
+#include "drm_buffer.h"
#include <asm/unaligned.h>
diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c
index 802b19220a21..2828605aef3f 100644
--- a/drivers/gpu/drm/radeon/r420.c
+++ b/drivers/gpu/drm/radeon/r420.c
@@ -219,7 +219,7 @@ static void r420_cp_errata_init(struct radeon_device *rdev)
radeon_ring_write(ring, PACKET0(R300_CP_RESYNC_ADDR, 1));
radeon_ring_write(ring, rdev->config.r300.resync_scratch);
radeon_ring_write(ring, 0xDEADBEEF);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
}
static void r420_cp_errata_fini(struct radeon_device *rdev)
@@ -232,7 +232,7 @@ static void r420_cp_errata_fini(struct radeon_device *rdev)
radeon_ring_lock(rdev, ring, 8);
radeon_ring_write(ring, PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0));
radeon_ring_write(ring, R300_RB3D_DC_FINISH);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
radeon_scratch_free(rdev, rdev->config.r300.resync_scratch);
}
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 3c69f58e46ef..56b02927cd3d 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -122,6 +122,94 @@ u32 r600_get_xclk(struct radeon_device *rdev)
int r600_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
{
+ unsigned fb_div = 0, ref_div, vclk_div = 0, dclk_div = 0;
+ int r;
+
+ /* bypass vclk and dclk with bclk */
+ WREG32_P(CG_UPLL_FUNC_CNTL_2,
+ VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1),
+ ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
+
+ /* assert BYPASS_EN, deassert UPLL_RESET, UPLL_SLEEP and UPLL_CTLREQ */
+ WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~(
+ UPLL_RESET_MASK | UPLL_SLEEP_MASK | UPLL_CTLREQ_MASK));
+
+ if (rdev->family >= CHIP_RS780)
+ WREG32_P(GFX_MACRO_BYPASS_CNTL, UPLL_BYPASS_CNTL,
+ ~UPLL_BYPASS_CNTL);
+
+ if (!vclk || !dclk) {
+ /* keep the Bypass mode, put PLL to sleep */
+ WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
+ return 0;
+ }
+
+ if (rdev->clock.spll.reference_freq == 10000)
+ ref_div = 34;
+ else
+ ref_div = 4;
+
+ r = radeon_uvd_calc_upll_dividers(rdev, vclk, dclk, 50000, 160000,
+ ref_div + 1, 0xFFF, 2, 30, ~0,
+ &fb_div, &vclk_div, &dclk_div);
+ if (r)
+ return r;
+
+ if (rdev->family >= CHIP_RV670 && rdev->family < CHIP_RS780)
+ fb_div >>= 1;
+ else
+ fb_div |= 1;
+
+ r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL);
+ if (r)
+ return r;
+
+ /* assert PLL_RESET */
+ WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_RESET_MASK, ~UPLL_RESET_MASK);
+
+ /* For RS780 we have to choose ref clk */
+ if (rdev->family >= CHIP_RS780)
+ WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_REFCLK_SRC_SEL_MASK,
+ ~UPLL_REFCLK_SRC_SEL_MASK);
+
+ /* set the required fb, ref and post divder values */
+ WREG32_P(CG_UPLL_FUNC_CNTL,
+ UPLL_FB_DIV(fb_div) |
+ UPLL_REF_DIV(ref_div),
+ ~(UPLL_FB_DIV_MASK | UPLL_REF_DIV_MASK));
+ WREG32_P(CG_UPLL_FUNC_CNTL_2,
+ UPLL_SW_HILEN(vclk_div >> 1) |
+ UPLL_SW_LOLEN((vclk_div >> 1) + (vclk_div & 1)) |
+ UPLL_SW_HILEN2(dclk_div >> 1) |
+ UPLL_SW_LOLEN2((dclk_div >> 1) + (dclk_div & 1)) |
+ UPLL_DIVEN_MASK | UPLL_DIVEN2_MASK,
+ ~UPLL_SW_MASK);
+
+ /* give the PLL some time to settle */
+ mdelay(15);
+
+ /* deassert PLL_RESET */
+ WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);
+
+ mdelay(15);
+
+ /* deassert BYPASS EN */
+ WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK);
+
+ if (rdev->family >= CHIP_RS780)
+ WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~UPLL_BYPASS_CNTL);
+
+ r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL);
+ if (r)
+ return r;
+
+ /* switch VCLK and DCLK selection */
+ WREG32_P(CG_UPLL_FUNC_CNTL_2,
+ VCLK_SRC_SEL(2) | DCLK_SRC_SEL(2),
+ ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
+
+ mdelay(100);
+
return 0;
}
@@ -968,7 +1056,6 @@ static int r600_pcie_gart_enable(struct radeon_device *rdev)
r = radeon_gart_table_vram_pin(rdev);
if (r)
return r;
- radeon_gart_restore(rdev);
/* Setup L2 cache */
WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING |
@@ -993,6 +1080,8 @@ static int r600_pcie_gart_enable(struct radeon_device *rdev)
WREG32(MC_VM_L1_TLB_MCB_WR_GFX_CNTL, tmp);
WREG32(MC_VM_L1_TLB_MCB_RD_PDMA_CNTL, tmp);
WREG32(MC_VM_L1_TLB_MCB_WR_PDMA_CNTL, tmp);
+ WREG32(MC_VM_L1_TLB_MCB_RD_UVD_CNTL, tmp);
+ WREG32(MC_VM_L1_TLB_MCB_WR_UVD_CNTL, tmp);
WREG32(MC_VM_L1_TLB_MCB_RD_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE);
WREG32(MC_VM_L1_TLB_MCB_WR_SEM_CNTL, tmp | ENABLE_SEMAPHORE_MODE);
WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12);
@@ -1043,6 +1132,8 @@ static void r600_pcie_gart_disable(struct radeon_device *rdev)
WREG32(MC_VM_L1_TLB_MCB_WR_SYS_CNTL, tmp);
WREG32(MC_VM_L1_TLB_MCB_RD_HDP_CNTL, tmp);
WREG32(MC_VM_L1_TLB_MCB_WR_HDP_CNTL, tmp);
+ WREG32(MC_VM_L1_TLB_MCB_RD_UVD_CNTL, tmp);
+ WREG32(MC_VM_L1_TLB_MCB_WR_UVD_CNTL, tmp);
radeon_gart_table_vram_unpin(rdev);
}
@@ -1339,7 +1430,7 @@ int r600_vram_scratch_init(struct radeon_device *rdev)
if (rdev->vram_scratch.robj == NULL) {
r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE,
PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM,
- NULL, &rdev->vram_scratch.robj);
+ 0, NULL, NULL, &rdev->vram_scratch.robj);
if (r) {
return r;
}
@@ -1813,7 +1904,6 @@ static void r600_gpu_init(struct radeon_device *rdev)
{
u32 tiling_config;
u32 ramcfg;
- u32 cc_rb_backend_disable;
u32 cc_gc_shader_pipe_config;
u32 tmp;
int i, j;
@@ -1940,29 +2030,20 @@ static void r600_gpu_init(struct radeon_device *rdev)
}
tiling_config |= BANK_SWAPS(1);
- cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000;
- tmp = R6XX_MAX_BACKENDS -
- r600_count_pipe_bits((cc_rb_backend_disable >> 16) & R6XX_MAX_BACKENDS_MASK);
- if (tmp < rdev->config.r600.max_backends) {
- rdev->config.r600.max_backends = tmp;
- }
-
cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0x00ffff00;
- tmp = R6XX_MAX_PIPES -
- r600_count_pipe_bits((cc_gc_shader_pipe_config >> 8) & R6XX_MAX_PIPES_MASK);
- if (tmp < rdev->config.r600.max_pipes) {
- rdev->config.r600.max_pipes = tmp;
- }
- tmp = R6XX_MAX_SIMDS -
- r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R6XX_MAX_SIMDS_MASK);
- if (tmp < rdev->config.r600.max_simds) {
- rdev->config.r600.max_simds = tmp;
- }
tmp = rdev->config.r600.max_simds -
r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R6XX_MAX_SIMDS_MASK);
rdev->config.r600.active_simds = tmp;
disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R6XX_MAX_BACKENDS_MASK;
+ tmp = 0;
+ for (i = 0; i < rdev->config.r600.max_backends; i++)
+ tmp |= (1 << i);
+ /* if all the backends are disabled, fix it up here */
+ if ((disabled_rb_mask & tmp) == tmp) {
+ for (i = 0; i < rdev->config.r600.max_backends; i++)
+ disabled_rb_mask &= ~(1 << i);
+ }
tmp = (tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT;
tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.r600.max_backends,
R6XX_MAX_BACKENDS, disabled_rb_mask);
@@ -2548,7 +2629,7 @@ int r600_cp_start(struct radeon_device *rdev)
radeon_ring_write(ring, PACKET3_ME_INITIALIZE_DEVICE_ID(1));
radeon_ring_write(ring, 0);
radeon_ring_write(ring, 0);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
cp_me = 0xff;
WREG32(R_0086D8_CP_ME_CNTL, cp_me);
@@ -2684,7 +2765,7 @@ int r600_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1));
radeon_ring_write(ring, ((scratch - PACKET3_SET_CONFIG_REG_OFFSET) >> 2));
radeon_ring_write(ring, 0xDEADBEEF);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
for (i = 0; i < rdev->usec_timeout; i++) {
tmp = RREG32(scratch);
if (tmp == 0xDEADBEEF)
@@ -2754,6 +2835,17 @@ void r600_fence_ring_emit(struct radeon_device *rdev,
}
}
+/**
+ * r600_semaphore_ring_emit - emit a semaphore on the CP ring
+ *
+ * @rdev: radeon_device pointer
+ * @ring: radeon ring buffer object
+ * @semaphore: radeon semaphore object
+ * @emit_wait: Is this a sempahore wait?
+ *
+ * Emits a semaphore signal/wait packet to the CP ring and prevents the PFP
+ * from running ahead of semaphore waits.
+ */
bool r600_semaphore_ring_emit(struct radeon_device *rdev,
struct radeon_ring *ring,
struct radeon_semaphore *semaphore,
@@ -2769,6 +2861,13 @@ bool r600_semaphore_ring_emit(struct radeon_device *rdev,
radeon_ring_write(ring, lower_32_bits(addr));
radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | sel);
+ /* PFP_SYNC_ME packet only exists on 7xx+, only enable it on eg+ */
+ if (emit_wait && (rdev->family >= CHIP_CEDAR)) {
+ /* Prevent the PFP from running ahead of the semaphore wait */
+ radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0));
+ radeon_ring_write(ring, 0x0);
+ }
+
return true;
}
@@ -2785,12 +2884,13 @@ bool r600_semaphore_ring_emit(struct radeon_device *rdev,
* Used by the radeon ttm implementation to move pages if
* registered as the asic copy callback.
*/
-int r600_copy_cpdma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence)
+struct radeon_fence *r600_copy_cpdma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv)
{
struct radeon_semaphore *sem = NULL;
+ struct radeon_fence *fence;
int ring_index = rdev->asic->copy.blit_ring_index;
struct radeon_ring *ring = &rdev->ring[ring_index];
u32 size_in_bytes, cur_size_in_bytes, tmp;
@@ -2800,7 +2900,7 @@ int r600_copy_cpdma(struct radeon_device *rdev,
r = radeon_semaphore_create(rdev, &sem);
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
- return r;
+ return ERR_PTR(r);
}
size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT);
@@ -2809,10 +2909,10 @@ int r600_copy_cpdma(struct radeon_device *rdev,
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_semaphore_sync_to(sem, *fence);
+ radeon_semaphore_sync_resv(rdev, sem, resv, false);
radeon_semaphore_sync_rings(rdev, sem, ring->idx);
radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1));
@@ -2839,17 +2939,17 @@ int r600_copy_cpdma(struct radeon_device *rdev,
radeon_ring_write(ring, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
radeon_ring_write(ring, WAIT_CP_DMA_IDLE_bit);
- r = radeon_fence_emit(rdev, fence, ring->idx);
+ r = radeon_fence_emit(rdev, &fence, ring->idx);
if (r) {
radeon_ring_unlock_undo(rdev, ring);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_ring_unlock_commit(rdev, ring);
- radeon_semaphore_free(rdev, &sem, *fence);
+ radeon_ring_unlock_commit(rdev, ring, false);
+ radeon_semaphore_free(rdev, &sem, fence);
- return r;
+ return fence;
}
int r600_set_surface_reg(struct radeon_device *rdev, int reg,
@@ -2900,6 +3000,18 @@ static int r600_startup(struct radeon_device *rdev)
return r;
}
+ if (rdev->has_uvd) {
+ r = uvd_v1_0_resume(rdev);
+ if (!r) {
+ r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_UVD_INDEX);
+ if (r) {
+ dev_err(rdev->dev, "failed initializing UVD fences (%d).\n", r);
+ }
+ }
+ if (r)
+ rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_size = 0;
+ }
+
/* Enable IRQ */
if (!rdev->irq.installed) {
r = radeon_irq_kms_init(rdev);
@@ -2928,6 +3040,18 @@ static int r600_startup(struct radeon_device *rdev)
if (r)
return r;
+ if (rdev->has_uvd) {
+ ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX];
+ if (ring->ring_size) {
+ r = radeon_ring_init(rdev, ring, ring->ring_size, 0,
+ RADEON_CP_PACKET2);
+ if (!r)
+ r = uvd_v1_0_init(rdev);
+ if (r)
+ DRM_ERROR("radeon: failed initializing UVD (%d).\n", r);
+ }
+ }
+
r = radeon_ib_pool_init(rdev);
if (r) {
dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
@@ -2987,6 +3111,10 @@ int r600_suspend(struct radeon_device *rdev)
radeon_pm_suspend(rdev);
r600_audio_fini(rdev);
r600_cp_stop(rdev);
+ if (rdev->has_uvd) {
+ uvd_v1_0_fini(rdev);
+ radeon_uvd_suspend(rdev);
+ }
r600_irq_suspend(rdev);
radeon_wb_disable(rdev);
r600_pcie_gart_disable(rdev);
@@ -3066,6 +3194,14 @@ int r600_init(struct radeon_device *rdev)
rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ring_obj = NULL;
r600_ring_init(rdev, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX], 1024 * 1024);
+ if (rdev->has_uvd) {
+ r = radeon_uvd_init(rdev);
+ if (!r) {
+ rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_obj = NULL;
+ r600_ring_init(rdev, &rdev->ring[R600_RING_TYPE_UVD_INDEX], 4096);
+ }
+ }
+
rdev->ih.ring_obj = NULL;
r600_ih_ring_init(rdev, 64 * 1024);
@@ -3095,6 +3231,10 @@ void r600_fini(struct radeon_device *rdev)
r600_audio_fini(rdev);
r600_cp_fini(rdev);
r600_irq_fini(rdev);
+ if (rdev->has_uvd) {
+ uvd_v1_0_fini(rdev);
+ radeon_uvd_fini(rdev);
+ }
radeon_wb_fini(rdev);
radeon_ib_pool_fini(rdev);
radeon_irq_kms_fini(rdev);
@@ -3166,7 +3306,7 @@ int r600_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
ib.ptr[1] = ((scratch - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
ib.ptr[2] = 0xDEADBEEF;
ib.length_dw = 3;
- r = radeon_ib_schedule(rdev, &ib, NULL);
+ r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r) {
DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
goto free_ib;
@@ -3227,8 +3367,8 @@ int r600_ih_ring_alloc(struct radeon_device *rdev)
if (rdev->ih.ring_obj == NULL) {
r = radeon_bo_create(rdev, rdev->ih.ring_size,
PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_GTT,
- NULL, &rdev->ih.ring_obj);
+ RADEON_GEM_DOMAIN_GTT, 0,
+ NULL, NULL, &rdev->ih.ring_obj);
if (r) {
DRM_ERROR("radeon: failed to create ih ring buffer (%d).\n", r);
return r;
@@ -3785,17 +3925,17 @@ static u32 r600_get_ih_wptr(struct radeon_device *rdev)
wptr = RREG32(IH_RB_WPTR);
if (wptr & RB_OVERFLOW) {
+ wptr &= ~RB_OVERFLOW;
/* When a ring buffer overflow happen start parsing interrupt
* from the last not overwritten vector (wptr + 16). Hopefully
* this should allow us to catchup.
*/
- dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n",
- wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask);
+ dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
+ wptr, rdev->ih.rptr, (wptr + 16) & rdev->ih.ptr_mask);
rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask;
tmp = RREG32(IH_RB_CNTL);
tmp |= IH_WPTR_OVERFLOW_CLEAR;
WREG32(IH_RB_CNTL, tmp);
- wptr &= ~RB_OVERFLOW;
}
return (wptr & rdev->ih.ptr_mask);
}
@@ -3924,11 +4064,13 @@ restart_ih:
break;
case 9: /* D1 pflip */
DRM_DEBUG("IH: D1 flip\n");
- radeon_crtc_handle_flip(rdev, 0);
+ if (radeon_use_pflipirq > 0)
+ radeon_crtc_handle_flip(rdev, 0);
break;
case 11: /* D2 pflip */
DRM_DEBUG("IH: D2 flip\n");
- radeon_crtc_handle_flip(rdev, 1);
+ if (radeon_use_pflipirq > 0)
+ radeon_crtc_handle_flip(rdev, 1);
break;
case 19: /* HPD/DAC hotplug */
switch (src_data) {
@@ -4039,6 +4181,7 @@ restart_ih:
/* wptr/rptr are in bytes! */
rptr += 16;
rptr &= rdev->ih.ptr_mask;
+ WREG32(IH_RB_RPTR, rptr);
}
if (queue_hotplug)
schedule_work(&rdev->hotplug_work);
@@ -4047,7 +4190,6 @@ restart_ih:
if (queue_thermal && rdev->pm.dpm_enabled)
schedule_work(&rdev->pm.dpm.thermal.work);
rdev->ih.rptr = rptr;
- WREG32(IH_RB_RPTR, rdev->ih.rptr);
atomic_set(&rdev->ih.lock, 0);
/* make sure wptr hasn't changed while processing */
@@ -4089,16 +4231,15 @@ int r600_debugfs_mc_info_init(struct radeon_device *rdev)
}
/**
- * r600_ioctl_wait_idle - flush host path cache on wait idle ioctl
+ * r600_mmio_hdp_flush - flush Host Data Path cache via MMIO
* rdev: radeon device structure
- * bo: buffer object struct which userspace is waiting for idle
*
- * Some R6XX/R7XX doesn't seems to take into account HDP flush performed
- * through ring buffer, this leads to corruption in rendering, see
- * http://bugzilla.kernel.org/show_bug.cgi?id=15186 to avoid this we
- * directly perform HDP flush by writing register through MMIO.
+ * Some R6XX/R7XX don't seem to take into account HDP flushes performed
+ * through the ring buffer. This leads to corruption in rendering, see
+ * http://bugzilla.kernel.org/show_bug.cgi?id=15186 . To avoid this, we
+ * directly perform the HDP flush by writing the register through MMIO.
*/
-void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo)
+void r600_mmio_hdp_flush(struct radeon_device *rdev)
{
/* r7xx hw bug. write to HDP_DEBUG1 followed by fb read
* rather than write to HDP_REG_COHERENCY_FLUSH_CNTL.
diff --git a/drivers/gpu/drm/radeon/r600_audio.c b/drivers/gpu/drm/radeon/r600_audio.c
deleted file mode 100644
index bffac10c4296..000000000000
--- a/drivers/gpu/drm/radeon/r600_audio.c
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright 2008 Advanced Micro Devices, Inc.
- * Copyright 2008 Red Hat Inc.
- * Copyright 2009 Christian König.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
- *
- * Authors: Christian König
- */
-#include <drm/drmP.h>
-#include "radeon.h"
-#include "radeon_reg.h"
-#include "radeon_asic.h"
-#include "atom.h"
-
-/*
- * check if enc_priv stores radeon_encoder_atom_dig
- */
-static bool radeon_dig_encoder(struct drm_encoder *encoder)
-{
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- switch (radeon_encoder->encoder_id) {
- case ENCODER_OBJECT_ID_INTERNAL_LVDS:
- case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
- case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
- case ENCODER_OBJECT_ID_INTERNAL_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
- case ENCODER_OBJECT_ID_INTERNAL_DDI:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
- return true;
- }
- return false;
-}
-
-/*
- * check if the chipset is supported
- */
-static int r600_audio_chipset_supported(struct radeon_device *rdev)
-{
- return ASIC_IS_DCE2(rdev) && !ASIC_IS_NODCE(rdev);
-}
-
-struct r600_audio_pin r600_audio_status(struct radeon_device *rdev)
-{
- struct r600_audio_pin status;
- uint32_t value;
-
- value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL);
-
- /* number of channels */
- status.channels = (value & 0x7) + 1;
-
- /* bits per sample */
- switch ((value & 0xF0) >> 4) {
- case 0x0:
- status.bits_per_sample = 8;
- break;
- case 0x1:
- status.bits_per_sample = 16;
- break;
- case 0x2:
- status.bits_per_sample = 20;
- break;
- case 0x3:
- status.bits_per_sample = 24;
- break;
- case 0x4:
- status.bits_per_sample = 32;
- break;
- default:
- dev_err(rdev->dev, "Unknown bits per sample 0x%x, using 16\n",
- (int)value);
- status.bits_per_sample = 16;
- }
-
- /* current sampling rate in HZ */
- if (value & 0x4000)
- status.rate = 44100;
- else
- status.rate = 48000;
- status.rate *= ((value >> 11) & 0x7) + 1;
- status.rate /= ((value >> 8) & 0x7) + 1;
-
- value = RREG32(R600_AUDIO_STATUS_BITS);
-
- /* iec 60958 status bits */
- status.status_bits = value & 0xff;
-
- /* iec 60958 category code */
- status.category_code = (value >> 8) & 0xff;
-
- return status;
-}
-
-/*
- * update all hdmi interfaces with current audio parameters
- */
-void r600_audio_update_hdmi(struct work_struct *work)
-{
- struct radeon_device *rdev = container_of(work, struct radeon_device,
- audio_work);
- struct drm_device *dev = rdev->ddev;
- struct r600_audio_pin audio_status = r600_audio_status(rdev);
- struct drm_encoder *encoder;
- bool changed = false;
-
- if (rdev->audio.pin[0].channels != audio_status.channels ||
- rdev->audio.pin[0].rate != audio_status.rate ||
- rdev->audio.pin[0].bits_per_sample != audio_status.bits_per_sample ||
- rdev->audio.pin[0].status_bits != audio_status.status_bits ||
- rdev->audio.pin[0].category_code != audio_status.category_code) {
- rdev->audio.pin[0] = audio_status;
- changed = true;
- }
-
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- if (!radeon_dig_encoder(encoder))
- continue;
- if (changed || r600_hdmi_buffer_status_changed(encoder))
- r600_hdmi_update_audio_settings(encoder);
- }
-}
-
-/* enable the audio stream */
-void r600_audio_enable(struct radeon_device *rdev,
- struct r600_audio_pin *pin,
- bool enable)
-{
- u32 value = 0;
-
- if (!pin)
- return;
-
- if (ASIC_IS_DCE4(rdev)) {
- if (enable) {
- value |= 0x81000000; /* Required to enable audio */
- value |= 0x0e1000f0; /* fglrx sets that too */
- }
- WREG32(EVERGREEN_AUDIO_ENABLE, value);
- } else {
- WREG32_P(R600_AUDIO_ENABLE,
- enable ? 0x81000000 : 0x0, ~0x81000000);
- }
-}
-
-/*
- * initialize the audio vars
- */
-int r600_audio_init(struct radeon_device *rdev)
-{
- if (!radeon_audio || !r600_audio_chipset_supported(rdev))
- return 0;
-
- rdev->audio.enabled = true;
-
- rdev->audio.num_pins = 1;
- rdev->audio.pin[0].channels = -1;
- rdev->audio.pin[0].rate = -1;
- rdev->audio.pin[0].bits_per_sample = -1;
- rdev->audio.pin[0].status_bits = 0;
- rdev->audio.pin[0].category_code = 0;
- rdev->audio.pin[0].id = 0;
- /* disable audio. it will be set up later */
- r600_audio_enable(rdev, &rdev->audio.pin[0], false);
-
- return 0;
-}
-
-/*
- * release the audio timer
- * TODO: How to do this correctly on SMP systems?
- */
-void r600_audio_fini(struct radeon_device *rdev)
-{
- if (!rdev->audio.enabled)
- return;
-
- r600_audio_enable(rdev, &rdev->audio.pin[0], false);
-
- rdev->audio.enabled = false;
-}
-
-struct r600_audio_pin *r600_audio_get_pin(struct radeon_device *rdev)
-{
- /* only one pin on 6xx-NI */
- return &rdev->audio.pin[0];
-}
diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c
index 8c9b7e26533c..09e3f39925fa 100644
--- a/drivers/gpu/drm/radeon/r600_cp.c
+++ b/drivers/gpu/drm/radeon/r600_cp.c
@@ -1949,15 +1949,15 @@ int r600_do_cleanup_cp(struct drm_device *dev)
#if __OS_HAS_AGP
if (dev_priv->flags & RADEON_IS_AGP) {
if (dev_priv->cp_ring != NULL) {
- drm_core_ioremapfree(dev_priv->cp_ring, dev);
+ drm_legacy_ioremapfree(dev_priv->cp_ring, dev);
dev_priv->cp_ring = NULL;
}
if (dev_priv->ring_rptr != NULL) {
- drm_core_ioremapfree(dev_priv->ring_rptr, dev);
+ drm_legacy_ioremapfree(dev_priv->ring_rptr, dev);
dev_priv->ring_rptr = NULL;
}
if (dev->agp_buffer_map != NULL) {
- drm_core_ioremapfree(dev->agp_buffer_map, dev);
+ drm_legacy_ioremapfree(dev->agp_buffer_map, dev);
dev->agp_buffer_map = NULL;
}
} else
@@ -1968,7 +1968,7 @@ int r600_do_cleanup_cp(struct drm_device *dev)
r600_page_table_cleanup(dev, &dev_priv->gart_info);
if (dev_priv->gart_info.gart_table_location == DRM_ATI_GART_FB) {
- drm_core_ioremapfree(&dev_priv->gart_info.mapping, dev);
+ drm_legacy_ioremapfree(&dev_priv->gart_info.mapping, dev);
dev_priv->gart_info.addr = NULL;
}
}
@@ -2052,27 +2052,27 @@ int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
dev_priv->buffers_offset = init->buffers_offset;
dev_priv->gart_textures_offset = init->gart_textures_offset;
- master_priv->sarea = drm_getsarea(dev);
+ master_priv->sarea = drm_legacy_getsarea(dev);
if (!master_priv->sarea) {
DRM_ERROR("could not find sarea!\n");
r600_do_cleanup_cp(dev);
return -EINVAL;
}
- dev_priv->cp_ring = drm_core_findmap(dev, init->ring_offset);
+ dev_priv->cp_ring = drm_legacy_findmap(dev, init->ring_offset);
if (!dev_priv->cp_ring) {
DRM_ERROR("could not find cp ring region!\n");
r600_do_cleanup_cp(dev);
return -EINVAL;
}
- dev_priv->ring_rptr = drm_core_findmap(dev, init->ring_rptr_offset);
+ dev_priv->ring_rptr = drm_legacy_findmap(dev, init->ring_rptr_offset);
if (!dev_priv->ring_rptr) {
DRM_ERROR("could not find ring read pointer!\n");
r600_do_cleanup_cp(dev);
return -EINVAL;
}
dev->agp_buffer_token = init->buffers_offset;
- dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset);
+ dev->agp_buffer_map = drm_legacy_findmap(dev, init->buffers_offset);
if (!dev->agp_buffer_map) {
DRM_ERROR("could not find dma buffer region!\n");
r600_do_cleanup_cp(dev);
@@ -2081,7 +2081,7 @@ int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
if (init->gart_textures_offset) {
dev_priv->gart_textures =
- drm_core_findmap(dev, init->gart_textures_offset);
+ drm_legacy_findmap(dev, init->gart_textures_offset);
if (!dev_priv->gart_textures) {
DRM_ERROR("could not find GART texture region!\n");
r600_do_cleanup_cp(dev);
@@ -2092,9 +2092,9 @@ int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
#if __OS_HAS_AGP
/* XXX */
if (dev_priv->flags & RADEON_IS_AGP) {
- drm_core_ioremap_wc(dev_priv->cp_ring, dev);
- drm_core_ioremap_wc(dev_priv->ring_rptr, dev);
- drm_core_ioremap_wc(dev->agp_buffer_map, dev);
+ drm_legacy_ioremap_wc(dev_priv->cp_ring, dev);
+ drm_legacy_ioremap_wc(dev_priv->ring_rptr, dev);
+ drm_legacy_ioremap_wc(dev->agp_buffer_map, dev);
if (!dev_priv->cp_ring->handle ||
!dev_priv->ring_rptr->handle ||
!dev->agp_buffer_map->handle) {
@@ -2235,7 +2235,7 @@ int r600_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
dev_priv->gart_info.mapping.size =
dev_priv->gart_info.table_size;
- drm_core_ioremap_wc(&dev_priv->gart_info.mapping, dev);
+ drm_legacy_ioremap_wc(&dev_priv->gart_info.mapping, dev);
if (!dev_priv->gart_info.mapping.handle) {
DRM_ERROR("ioremap failed.\n");
r600_do_cleanup_cp(dev);
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index 12511bb5fd6f..c47537a1ddba 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -825,7 +825,6 @@ int r600_cs_common_vline_parse(struct radeon_cs_parser *p,
uint32_t *vline_start_end,
uint32_t *vline_status)
{
- struct drm_mode_object *obj;
struct drm_crtc *crtc;
struct radeon_crtc *radeon_crtc;
struct radeon_cs_packet p3reloc, wait_reg_mem;
@@ -887,12 +886,11 @@ int r600_cs_common_vline_parse(struct radeon_cs_parser *p,
crtc_id = radeon_get_ib_value(p, h_idx + 2 + 7 + 1);
reg = R600_CP_PACKET0_GET_REG(header);
- obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC);
- if (!obj) {
+ crtc = drm_crtc_find(p->rdev->ddev, crtc_id);
+ if (!crtc) {
DRM_ERROR("cannot find crtc %d\n", crtc_id);
return -ENOENT;
}
- crtc = obj_to_crtc(obj);
radeon_crtc = to_radeon_crtc(crtc);
crtc_id = radeon_crtc->crtc_id;
diff --git a/drivers/gpu/drm/radeon/r600_dma.c b/drivers/gpu/drm/radeon/r600_dma.c
index 4969cef44a19..cf0df45d455e 100644
--- a/drivers/gpu/drm/radeon/r600_dma.c
+++ b/drivers/gpu/drm/radeon/r600_dma.c
@@ -124,15 +124,6 @@ int r600_dma_resume(struct radeon_device *rdev)
u32 rb_bufsz;
int r;
- /* Reset dma */
- if (rdev->family >= CHIP_RV770)
- WREG32(SRBM_SOFT_RESET, RV770_SOFT_RESET_DMA);
- else
- WREG32(SRBM_SOFT_RESET, SOFT_RESET_DMA);
- RREG32(SRBM_SOFT_RESET);
- udelay(50);
- WREG32(SRBM_SOFT_RESET, 0);
-
WREG32(DMA_SEM_INCOMPLETE_TIMER_CNTL, 0);
WREG32(DMA_SEM_WAIT_FAIL_TIMER_CNTL, 0);
@@ -241,16 +232,19 @@ int r600_dma_ring_test(struct radeon_device *rdev,
{
unsigned i;
int r;
- void __iomem *ptr = (void *)rdev->vram_scratch.ptr;
+ unsigned index;
u32 tmp;
+ u64 gpu_addr;
- if (!ptr) {
- DRM_ERROR("invalid vram scratch pointer\n");
- return -EINVAL;
- }
+ if (ring->idx == R600_RING_TYPE_DMA_INDEX)
+ index = R600_WB_DMA_RING_TEST_OFFSET;
+ else
+ index = CAYMAN_WB_DMA1_RING_TEST_OFFSET;
+
+ gpu_addr = rdev->wb.gpu_addr + index;
tmp = 0xCAFEDEAD;
- writel(tmp, ptr);
+ rdev->wb.wb[index/4] = cpu_to_le32(tmp);
r = radeon_ring_lock(rdev, ring, 4);
if (r) {
@@ -258,13 +252,13 @@ int r600_dma_ring_test(struct radeon_device *rdev,
return r;
}
radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1));
- radeon_ring_write(ring, rdev->vram_scratch.gpu_addr & 0xfffffffc);
- radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xff);
+ radeon_ring_write(ring, lower_32_bits(gpu_addr));
+ radeon_ring_write(ring, upper_32_bits(gpu_addr) & 0xff);
radeon_ring_write(ring, 0xDEADBEEF);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
for (i = 0; i < rdev->usec_timeout; i++) {
- tmp = readl(ptr);
+ tmp = le32_to_cpu(rdev->wb.wb[index/4]);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
@@ -344,17 +338,17 @@ int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
{
struct radeon_ib ib;
unsigned i;
+ unsigned index;
int r;
- void __iomem *ptr = (void *)rdev->vram_scratch.ptr;
u32 tmp = 0;
+ u64 gpu_addr;
- if (!ptr) {
- DRM_ERROR("invalid vram scratch pointer\n");
- return -EINVAL;
- }
+ if (ring->idx == R600_RING_TYPE_DMA_INDEX)
+ index = R600_WB_DMA_RING_TEST_OFFSET;
+ else
+ index = CAYMAN_WB_DMA1_RING_TEST_OFFSET;
- tmp = 0xCAFEDEAD;
- writel(tmp, ptr);
+ gpu_addr = rdev->wb.gpu_addr + index;
r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256);
if (r) {
@@ -363,12 +357,12 @@ int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
}
ib.ptr[0] = DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1);
- ib.ptr[1] = rdev->vram_scratch.gpu_addr & 0xfffffffc;
- ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xff;
+ ib.ptr[1] = lower_32_bits(gpu_addr);
+ ib.ptr[2] = upper_32_bits(gpu_addr) & 0xff;
ib.ptr[3] = 0xDEADBEEF;
ib.length_dw = 4;
- r = radeon_ib_schedule(rdev, &ib, NULL);
+ r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r) {
radeon_ib_free(rdev, &ib);
DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
@@ -380,7 +374,7 @@ int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
return r;
}
for (i = 0; i < rdev->usec_timeout; i++) {
- tmp = readl(ptr);
+ tmp = le32_to_cpu(rdev->wb.wb[index/4]);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
@@ -436,18 +430,19 @@ void r600_dma_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
* @src_offset: src GPU address
* @dst_offset: dst GPU address
* @num_gpu_pages: number of GPU pages to xfer
- * @fence: radeon fence object
+ * @resv: reservation object to sync to
*
* Copy GPU paging using the DMA engine (r6xx).
* Used by the radeon ttm implementation to move pages if
* registered as the asic copy callback.
*/
-int r600_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence)
+struct radeon_fence *r600_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv)
{
struct radeon_semaphore *sem = NULL;
+ struct radeon_fence *fence;
int ring_index = rdev->asic->copy.dma_ring_index;
struct radeon_ring *ring = &rdev->ring[ring_index];
u32 size_in_dw, cur_size_in_dw;
@@ -457,7 +452,7 @@ int r600_copy_dma(struct radeon_device *rdev,
r = radeon_semaphore_create(rdev, &sem);
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
- return r;
+ return ERR_PTR(r);
}
size_in_dw = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT) / 4;
@@ -466,10 +461,10 @@ int r600_copy_dma(struct radeon_device *rdev,
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_semaphore_sync_to(sem, *fence);
+ radeon_semaphore_sync_resv(rdev, sem, resv, false);
radeon_semaphore_sync_rings(rdev, sem, ring->idx);
for (i = 0; i < num_loops; i++) {
@@ -486,15 +481,15 @@ int r600_copy_dma(struct radeon_device *rdev,
dst_offset += cur_size_in_dw * 4;
}
- r = radeon_fence_emit(rdev, fence, ring->idx);
+ r = radeon_fence_emit(rdev, &fence, ring->idx);
if (r) {
radeon_ring_unlock_undo(rdev, ring);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_ring_unlock_commit(rdev, ring);
- radeon_semaphore_free(rdev, &sem, *fence);
+ radeon_ring_unlock_commit(rdev, ring, false);
+ radeon_semaphore_free(rdev, &sem, fence);
- return r;
+ return fence;
}
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c
index 9c61b74ef441..f6309bd23e01 100644
--- a/drivers/gpu/drm/radeon/r600_dpm.c
+++ b/drivers/gpu/drm/radeon/r600_dpm.c
@@ -24,6 +24,7 @@
#include "drmP.h"
#include "radeon.h"
+#include "radeon_asic.h"
#include "r600d.h"
#include "r600_dpm.h"
#include "atom.h"
diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c
index 26ef8ced6f89..b90dc0eb08e6 100644
--- a/drivers/gpu/drm/radeon/r600_hdmi.c
+++ b/drivers/gpu/drm/radeon/r600_hdmi.c
@@ -72,6 +72,169 @@ static const struct radeon_hdmi_acr r600_hdmi_predefined_acr[] = {
/*
+ * check if the chipset is supported
+ */
+static int r600_audio_chipset_supported(struct radeon_device *rdev)
+{
+ return ASIC_IS_DCE2(rdev) && !ASIC_IS_NODCE(rdev);
+}
+
+static struct r600_audio_pin r600_audio_status(struct radeon_device *rdev)
+{
+ struct r600_audio_pin status;
+ uint32_t value;
+
+ value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL);
+
+ /* number of channels */
+ status.channels = (value & 0x7) + 1;
+
+ /* bits per sample */
+ switch ((value & 0xF0) >> 4) {
+ case 0x0:
+ status.bits_per_sample = 8;
+ break;
+ case 0x1:
+ status.bits_per_sample = 16;
+ break;
+ case 0x2:
+ status.bits_per_sample = 20;
+ break;
+ case 0x3:
+ status.bits_per_sample = 24;
+ break;
+ case 0x4:
+ status.bits_per_sample = 32;
+ break;
+ default:
+ dev_err(rdev->dev, "Unknown bits per sample 0x%x, using 16\n",
+ (int)value);
+ status.bits_per_sample = 16;
+ }
+
+ /* current sampling rate in HZ */
+ if (value & 0x4000)
+ status.rate = 44100;
+ else
+ status.rate = 48000;
+ status.rate *= ((value >> 11) & 0x7) + 1;
+ status.rate /= ((value >> 8) & 0x7) + 1;
+
+ value = RREG32(R600_AUDIO_STATUS_BITS);
+
+ /* iec 60958 status bits */
+ status.status_bits = value & 0xff;
+
+ /* iec 60958 category code */
+ status.category_code = (value >> 8) & 0xff;
+
+ return status;
+}
+
+/*
+ * update all hdmi interfaces with current audio parameters
+ */
+void r600_audio_update_hdmi(struct work_struct *work)
+{
+ struct radeon_device *rdev = container_of(work, struct radeon_device,
+ audio_work);
+ struct drm_device *dev = rdev->ddev;
+ struct r600_audio_pin audio_status = r600_audio_status(rdev);
+ struct drm_encoder *encoder;
+ bool changed = false;
+
+ if (rdev->audio.pin[0].channels != audio_status.channels ||
+ rdev->audio.pin[0].rate != audio_status.rate ||
+ rdev->audio.pin[0].bits_per_sample != audio_status.bits_per_sample ||
+ rdev->audio.pin[0].status_bits != audio_status.status_bits ||
+ rdev->audio.pin[0].category_code != audio_status.category_code) {
+ rdev->audio.pin[0] = audio_status;
+ changed = true;
+ }
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (!radeon_encoder_is_digital(encoder))
+ continue;
+ if (changed || r600_hdmi_buffer_status_changed(encoder))
+ r600_hdmi_update_audio_settings(encoder);
+ }
+}
+
+/* enable the audio stream */
+void r600_audio_enable(struct radeon_device *rdev,
+ struct r600_audio_pin *pin,
+ u8 enable_mask)
+{
+ u32 tmp = RREG32(AZ_HOT_PLUG_CONTROL);
+
+ if (!pin)
+ return;
+
+ if (enable_mask) {
+ tmp |= AUDIO_ENABLED;
+ if (enable_mask & 1)
+ tmp |= PIN0_AUDIO_ENABLED;
+ if (enable_mask & 2)
+ tmp |= PIN1_AUDIO_ENABLED;
+ if (enable_mask & 4)
+ tmp |= PIN2_AUDIO_ENABLED;
+ if (enable_mask & 8)
+ tmp |= PIN3_AUDIO_ENABLED;
+ } else {
+ tmp &= ~(AUDIO_ENABLED |
+ PIN0_AUDIO_ENABLED |
+ PIN1_AUDIO_ENABLED |
+ PIN2_AUDIO_ENABLED |
+ PIN3_AUDIO_ENABLED);
+ }
+
+ WREG32(AZ_HOT_PLUG_CONTROL, tmp);
+}
+
+/*
+ * initialize the audio vars
+ */
+int r600_audio_init(struct radeon_device *rdev)
+{
+ if (!radeon_audio || !r600_audio_chipset_supported(rdev))
+ return 0;
+
+ rdev->audio.enabled = true;
+
+ rdev->audio.num_pins = 1;
+ rdev->audio.pin[0].channels = -1;
+ rdev->audio.pin[0].rate = -1;
+ rdev->audio.pin[0].bits_per_sample = -1;
+ rdev->audio.pin[0].status_bits = 0;
+ rdev->audio.pin[0].category_code = 0;
+ rdev->audio.pin[0].id = 0;
+ /* disable audio. it will be set up later */
+ r600_audio_enable(rdev, &rdev->audio.pin[0], 0);
+
+ return 0;
+}
+
+/*
+ * release the audio timer
+ * TODO: How to do this correctly on SMP systems?
+ */
+void r600_audio_fini(struct radeon_device *rdev)
+{
+ if (!rdev->audio.enabled)
+ return;
+
+ r600_audio_enable(rdev, &rdev->audio.pin[0], 0);
+
+ rdev->audio.enabled = false;
+}
+
+struct r600_audio_pin *r600_audio_get_pin(struct radeon_device *rdev)
+{
+ /* only one pin on 6xx-NI */
+ return &rdev->audio.pin[0];
+}
+
+/*
* calculate CTS and N values if they are not found in the table
*/
static void r600_hdmi_calc_cts(uint32_t clock, int *CTS, int *N, int freq)
@@ -357,7 +520,7 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
/* disable audio prior to setting up hw */
dig->afmt->pin = r600_audio_get_pin(rdev);
- r600_audio_enable(rdev, dig->afmt->pin, false);
+ r600_audio_enable(rdev, dig->afmt->pin, 0xf);
r600_audio_set_dto(encoder, mode->clock);
@@ -443,7 +606,7 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
WREG32(HDMI0_RAMP_CONTROL3 + offset, 0x00000001);
/* enable audio after to setting up hw */
- r600_audio_enable(rdev, dig->afmt->pin, true);
+ r600_audio_enable(rdev, dig->afmt->pin, 0xf);
}
/**
@@ -528,6 +691,11 @@ void r600_hdmi_enable(struct drm_encoder *encoder, bool enable)
if (!enable && !dig->afmt->enabled)
return;
+ if (!enable && dig->afmt->pin) {
+ r600_audio_enable(rdev, dig->afmt->pin, 0);
+ dig->afmt->pin = NULL;
+ }
+
/* Older chipsets require setting HDMI and routing manually */
if (!ASIC_IS_DCE3(rdev)) {
if (enable)
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index f94e7a9afe75..1e8495cca41e 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -44,13 +44,6 @@
#define R6XX_MAX_PIPES 8
#define R6XX_MAX_PIPES_MASK 0xff
-/* PTE flags */
-#define PTE_VALID (1 << 0)
-#define PTE_SYSTEM (1 << 1)
-#define PTE_SNOOPED (1 << 2)
-#define PTE_READABLE (1 << 5)
-#define PTE_WRITEABLE (1 << 6)
-
/* tiling bits */
#define ARRAY_LINEAR_GENERAL 0x00000000
#define ARRAY_LINEAR_ALIGNED 0x00000001
@@ -330,11 +323,12 @@
#define HDP_TILING_CONFIG 0x2F3C
#define HDP_DEBUG1 0x2F34
+#define MC_CONFIG 0x2000
#define MC_VM_AGP_TOP 0x2184
#define MC_VM_AGP_BOT 0x2188
#define MC_VM_AGP_BASE 0x218C
#define MC_VM_FB_LOCATION 0x2180
-#define MC_VM_L1_TLB_MCD_RD_A_CNTL 0x219C
+#define MC_VM_L1_TLB_MCB_RD_UVD_CNTL 0x2124
#define ENABLE_L1_TLB (1 << 0)
#define ENABLE_L1_FRAGMENT_PROCESSING (1 << 1)
#define ENABLE_L1_STRICT_ORDERING (1 << 2)
@@ -354,12 +348,14 @@
#define EFFECTIVE_L1_QUEUE_SIZE(x) (((x) & 7) << 15)
#define EFFECTIVE_L1_QUEUE_SIZE_MASK 0x00038000
#define EFFECTIVE_L1_QUEUE_SIZE_SHIFT 15
+#define MC_VM_L1_TLB_MCD_RD_A_CNTL 0x219C
#define MC_VM_L1_TLB_MCD_RD_B_CNTL 0x21A0
#define MC_VM_L1_TLB_MCB_RD_GFX_CNTL 0x21FC
#define MC_VM_L1_TLB_MCB_RD_HDP_CNTL 0x2204
#define MC_VM_L1_TLB_MCB_RD_PDMA_CNTL 0x2208
#define MC_VM_L1_TLB_MCB_RD_SEM_CNTL 0x220C
#define MC_VM_L1_TLB_MCB_RD_SYS_CNTL 0x2200
+#define MC_VM_L1_TLB_MCB_WR_UVD_CNTL 0x212c
#define MC_VM_L1_TLB_MCD_WR_A_CNTL 0x21A4
#define MC_VM_L1_TLB_MCD_WR_B_CNTL 0x21A8
#define MC_VM_L1_TLB_MCB_WR_GFX_CNTL 0x2210
@@ -373,6 +369,8 @@
#define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2194
#define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x2198
+#define RS_DQ_RD_RET_CONF 0x2348
+
#define PA_CL_ENHANCE 0x8A14
#define CLIP_VTX_REORDER_ENA (1 << 0)
#define NUM_CLIP_SEQ(x) ((x) << 1)
@@ -929,6 +927,23 @@
# define TARGET_LINK_SPEED_MASK (0xf << 0)
# define SELECTABLE_DEEMPHASIS (1 << 6)
+/* Audio */
+#define AZ_HOT_PLUG_CONTROL 0x7300
+# define AZ_FORCE_CODEC_WAKE (1 << 0)
+# define JACK_DETECTION_ENABLE (1 << 4)
+# define UNSOLICITED_RESPONSE_ENABLE (1 << 8)
+# define CODEC_HOT_PLUG_ENABLE (1 << 12)
+# define AUDIO_ENABLED (1 << 31)
+/* DCE3 adds */
+# define PIN0_JACK_DETECTION_ENABLE (1 << 4)
+# define PIN1_JACK_DETECTION_ENABLE (1 << 5)
+# define PIN2_JACK_DETECTION_ENABLE (1 << 6)
+# define PIN3_JACK_DETECTION_ENABLE (1 << 7)
+# define PIN0_AUDIO_ENABLED (1 << 24)
+# define PIN1_AUDIO_ENABLED (1 << 25)
+# define PIN2_AUDIO_ENABLED (1 << 26)
+# define PIN3_AUDIO_ENABLED (1 << 27)
+
/* Audio clocks DCE 2.0/3.0 */
#define AUDIO_DTO 0x7340
# define AUDIO_DTO_PHASE(x) (((x) & 0xffff) << 0)
@@ -1483,6 +1498,7 @@
#define UVD_CGC_GATE 0xf4a8
#define UVD_LMI_CTRL2 0xf4f4
#define UVD_MASTINT_EN 0xf500
+#define UVD_FW_START 0xf51C
#define UVD_LMI_ADDR_EXT 0xf594
#define UVD_LMI_CTRL 0xf598
#define UVD_LMI_SWAP_CNTL 0xf5b4
@@ -1495,6 +1511,13 @@
#define UVD_MPC_SET_MUX 0xf5f4
#define UVD_MPC_SET_ALU 0xf5f8
+#define UVD_VCPU_CACHE_OFFSET0 0xf608
+#define UVD_VCPU_CACHE_SIZE0 0xf60c
+#define UVD_VCPU_CACHE_OFFSET1 0xf610
+#define UVD_VCPU_CACHE_SIZE1 0xf614
+#define UVD_VCPU_CACHE_OFFSET2 0xf618
+#define UVD_VCPU_CACHE_SIZE2 0xf61c
+
#define UVD_VCPU_CNTL 0xf660
#define UVD_SOFT_RESET 0xf680
#define RBC_SOFT_RESET (1<<0)
@@ -1524,9 +1547,35 @@
#define UVD_CONTEXT_ID 0xf6f4
+/* rs780 only */
+#define GFX_MACRO_BYPASS_CNTL 0x30c0
+#define SPLL_BYPASS_CNTL (1 << 0)
+#define UPLL_BYPASS_CNTL (1 << 1)
+
+#define CG_UPLL_FUNC_CNTL 0x7e0
+# define UPLL_RESET_MASK 0x00000001
+# define UPLL_SLEEP_MASK 0x00000002
+# define UPLL_BYPASS_EN_MASK 0x00000004
# define UPLL_CTLREQ_MASK 0x00000008
+# define UPLL_FB_DIV(x) ((x) << 4)
+# define UPLL_FB_DIV_MASK 0x0000FFF0
+# define UPLL_REF_DIV(x) ((x) << 16)
+# define UPLL_REF_DIV_MASK 0x003F0000
+# define UPLL_REFCLK_SRC_SEL_MASK 0x20000000
# define UPLL_CTLACK_MASK 0x40000000
# define UPLL_CTLACK2_MASK 0x80000000
+#define CG_UPLL_FUNC_CNTL_2 0x7e4
+# define UPLL_SW_HILEN(x) ((x) << 0)
+# define UPLL_SW_LOLEN(x) ((x) << 4)
+# define UPLL_SW_HILEN2(x) ((x) << 8)
+# define UPLL_SW_LOLEN2(x) ((x) << 12)
+# define UPLL_DIVEN_MASK 0x00010000
+# define UPLL_DIVEN2_MASK 0x00020000
+# define UPLL_SW_MASK 0x0003FFFF
+# define VCLK_SRC_SEL(x) ((x) << 20)
+# define VCLK_SRC_SEL_MASK 0x01F00000
+# define DCLK_SRC_SEL(x) ((x) << 25)
+# define DCLK_SRC_SEL_MASK 0x3E000000
/*
* PM4
@@ -1597,6 +1646,7 @@
*/
# define PACKET3_CP_DMA_CMD_SAIC (1 << 28)
# define PACKET3_CP_DMA_CMD_DAIC (1 << 29)
+#define PACKET3_PFP_SYNC_ME 0x42 /* r7xx+ only */
#define PACKET3_SURFACE_SYNC 0x43
# define PACKET3_CB0_DEST_BASE_ENA (1 << 6)
# define PACKET3_FULL_CACHE_ENA (1 << 20) /* r7xx+ only */
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 60c47f829122..a9717b3fbf1b 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -64,6 +64,9 @@
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/kref.h>
+#include <linux/interval_tree.h>
+#include <linux/hashtable.h>
+#include <linux/fence.h>
#include <ttm/ttm_bo_api.h>
#include <ttm/ttm_bo_driver.h>
@@ -71,6 +74,8 @@
#include <ttm/ttm_module.h>
#include <ttm/ttm_execbuf_util.h>
+#include <drm/drm_gem.h>
+
#include "radeon_family.h"
#include "radeon_mode.h"
#include "radeon_reg.h"
@@ -103,6 +108,9 @@ extern int radeon_hard_reset;
extern int radeon_vm_size;
extern int radeon_vm_block_size;
extern int radeon_deep_color;
+extern int radeon_use_pflipirq;
+extern int radeon_bapm;
+extern int radeon_backlight;
/*
* Copy from radeon_drv.h so we don't have to include both and have conflicting
@@ -116,9 +124,6 @@ extern int radeon_deep_color;
#define RADEONFB_CONN_LIMIT 4
#define RADEON_BIOS_NUM_SCRATCH 8
-/* fence seq are set to this number when signaled */
-#define RADEON_FENCE_SIGNALED_SEQ 0LL
-
/* internal ring indices */
/* r1xx+ has gfx CP ring */
#define RADEON_RING_TYPE_GFX_INDEX 0
@@ -304,6 +309,9 @@ int radeon_atom_get_leakage_vddc_based_on_leakage_params(struct radeon_device *r
u16 *vddc, u16 *vddci,
u16 virtual_voltage_id,
u16 vbios_voltage_id);
+int radeon_atom_get_voltage_evv(struct radeon_device *rdev,
+ u16 virtual_voltage_id,
+ u16 *voltage);
int radeon_atom_round_to_true_voltage(struct radeon_device *rdev,
u8 voltage_type,
u16 nominal_voltage,
@@ -317,6 +325,9 @@ int radeon_atom_get_voltage_table(struct radeon_device *rdev,
struct atom_voltage_table *voltage_table);
bool radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
u8 voltage_type, u8 voltage_mode);
+int radeon_atom_get_svi2_info(struct radeon_device *rdev,
+ u8 voltage_type,
+ u8 *svd_gpio_id, u8 *svc_gpio_id);
void radeon_atom_update_memory_dll(struct radeon_device *rdev,
u32 mem_clock);
void radeon_atom_set_ac_timing(struct radeon_device *rdev,
@@ -340,28 +351,32 @@ extern void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw,
* Fences.
*/
struct radeon_fence_driver {
+ struct radeon_device *rdev;
uint32_t scratch_reg;
uint64_t gpu_addr;
volatile uint32_t *cpu_addr;
/* sync_seq is protected by ring emission lock */
uint64_t sync_seq[RADEON_NUM_RINGS];
atomic64_t last_seq;
- bool initialized;
+ bool initialized, delayed_irq;
+ struct delayed_work lockup_work;
};
struct radeon_fence {
+ struct fence base;
+
struct radeon_device *rdev;
- struct kref kref;
- /* protected by radeon_fence.lock */
uint64_t seq;
/* RB, DMA, etc. */
unsigned ring;
+
+ wait_queue_t fence_wake;
};
int radeon_fence_driver_start_ring(struct radeon_device *rdev, int ring);
int radeon_fence_driver_init(struct radeon_device *rdev);
void radeon_fence_driver_fini(struct radeon_device *rdev);
-void radeon_fence_driver_force_completion(struct radeon_device *rdev);
+void radeon_fence_driver_force_completion(struct radeon_device *rdev, int ring);
int radeon_fence_emit(struct radeon_device *rdev, struct radeon_fence **fence, int ring);
void radeon_fence_process(struct radeon_device *rdev, int ring);
bool radeon_fence_signaled(struct radeon_fence *fence);
@@ -441,14 +456,12 @@ struct radeon_mman {
struct radeon_bo_va {
/* protected by bo being reserved */
struct list_head bo_list;
- uint64_t soffset;
- uint64_t eoffset;
uint32_t flags;
- bool valid;
+ uint64_t addr;
unsigned ref_count;
/* protected by vm mutex */
- struct list_head vm_list;
+ struct interval_tree_node it;
struct list_head vm_status;
/* constant after initialization */
@@ -461,10 +474,11 @@ struct radeon_bo {
struct list_head list;
/* Protected by tbo.reserved */
u32 initial_domain;
- u32 placements[3];
+ struct ttm_place placements[3];
struct ttm_placement placement;
struct ttm_buffer_object tbo;
struct ttm_bo_kmap_obj kmap;
+ u32 flags;
unsigned pin_count;
void *kptr;
u32 tiling_flags;
@@ -480,6 +494,9 @@ struct radeon_bo {
struct ttm_bo_kmap_obj dma_buf_vmap;
pid_t pid;
+
+ struct radeon_mn *mn;
+ struct interval_tree_node mn_it;
};
#define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, gem_base)
@@ -543,9 +560,9 @@ struct radeon_gem {
int radeon_gem_init(struct radeon_device *rdev);
void radeon_gem_fini(struct radeon_device *rdev);
-int radeon_gem_object_create(struct radeon_device *rdev, int size,
+int radeon_gem_object_create(struct radeon_device *rdev, unsigned long size,
int alignment, int initial_domain,
- bool discardable, bool kernel,
+ u32 flags, bool kernel,
struct drm_gem_object **obj);
int radeon_mode_dumb_create(struct drm_file *file_priv,
@@ -571,8 +588,12 @@ bool radeon_semaphore_emit_signal(struct radeon_device *rdev, int ring,
struct radeon_semaphore *semaphore);
bool radeon_semaphore_emit_wait(struct radeon_device *rdev, int ring,
struct radeon_semaphore *semaphore);
-void radeon_semaphore_sync_to(struct radeon_semaphore *semaphore,
- struct radeon_fence *fence);
+void radeon_semaphore_sync_fence(struct radeon_semaphore *semaphore,
+ struct radeon_fence *fence);
+int radeon_semaphore_sync_resv(struct radeon_device *rdev,
+ struct radeon_semaphore *semaphore,
+ struct reservation_object *resv,
+ bool shared);
int radeon_semaphore_sync_rings(struct radeon_device *rdev,
struct radeon_semaphore *semaphore,
int waiting_ring);
@@ -590,6 +611,12 @@ struct radeon_mc;
#define RADEON_GPU_PAGE_SHIFT 12
#define RADEON_GPU_PAGE_ALIGN(a) (((a) + RADEON_GPU_PAGE_MASK) & ~RADEON_GPU_PAGE_MASK)
+#define RADEON_GART_PAGE_DUMMY 0
+#define RADEON_GART_PAGE_VALID (1 << 0)
+#define RADEON_GART_PAGE_READ (1 << 1)
+#define RADEON_GART_PAGE_WRITE (1 << 2)
+#define RADEON_GART_PAGE_SNOOP (1 << 3)
+
struct radeon_gart {
dma_addr_t table_addr;
struct radeon_bo *robj;
@@ -614,8 +641,7 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset,
int pages);
int radeon_gart_bind(struct radeon_device *rdev, unsigned offset,
int pages, struct page **pagelist,
- dma_addr_t *dma_addr);
-void radeon_gart_restore(struct radeon_device *rdev);
+ dma_addr_t *dma_addr, uint32_t flags);
/*
@@ -688,7 +714,7 @@ struct radeon_flip_work {
uint64_t base;
struct drm_pending_vblank_event *event;
struct radeon_bo *old_rbo;
- struct radeon_fence *fence;
+ struct fence *fence;
};
struct r500_irq_stat_regs {
@@ -766,6 +792,7 @@ struct radeon_irq {
int radeon_irq_kms_init(struct radeon_device *rdev);
void radeon_irq_kms_fini(struct radeon_device *rdev);
void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev, int ring);
+bool radeon_irq_kms_sw_irq_get_delayed(struct radeon_device *rdev, int ring);
void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev, int ring);
void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc);
void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc);
@@ -855,9 +882,9 @@ struct radeon_mec {
#define R600_PTE_FRAG_64KB (4 << 7)
#define R600_PTE_FRAG_256KB (6 << 7)
-/* flags used for GART page table entries on R600+ */
-#define R600_PTE_GART ( R600_PTE_VALID | R600_PTE_SYSTEM | R600_PTE_SNOOPED \
- | R600_PTE_READABLE | R600_PTE_WRITEABLE)
+/* flags needed to be set so we can copy directly from the GART table */
+#define R600_PTE_GART_MASK ( R600_PTE_READABLE | R600_PTE_WRITEABLE | \
+ R600_PTE_SYSTEM | R600_PTE_VALID )
struct radeon_vm_pt {
struct radeon_bo *bo;
@@ -865,9 +892,12 @@ struct radeon_vm_pt {
};
struct radeon_vm {
- struct list_head va;
+ struct rb_root va;
unsigned id;
+ /* BOs moved, but not yet updated in the PT */
+ struct list_head invalidated;
+
/* BOs freed, but not yet updated in the PT */
struct list_head freed;
@@ -899,6 +929,8 @@ struct radeon_vm_manager {
u64 vram_base_offset;
/* is vm enabled? */
bool enabled;
+ /* for hw to save the PD addr on suspend/resume */
+ uint32_t saved_table_addr[RADEON_NUM_VM];
};
/*
@@ -952,7 +984,7 @@ int radeon_ib_get(struct radeon_device *rdev, int ring,
unsigned size);
void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib *ib);
int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib,
- struct radeon_ib *const_ib);
+ struct radeon_ib *const_ib, bool hdp_flush);
int radeon_ib_pool_init(struct radeon_device *rdev);
void radeon_ib_pool_fini(struct radeon_device *rdev);
int radeon_ib_ring_tests(struct radeon_device *rdev);
@@ -962,8 +994,10 @@ bool radeon_ring_supports_scratch_reg(struct radeon_device *rdev,
void radeon_ring_free_size(struct radeon_device *rdev, struct radeon_ring *cp);
int radeon_ring_alloc(struct radeon_device *rdev, struct radeon_ring *cp, unsigned ndw);
int radeon_ring_lock(struct radeon_device *rdev, struct radeon_ring *cp, unsigned ndw);
-void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *cp);
-void radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *cp);
+void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *cp,
+ bool hdp_flush);
+void radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *cp,
+ bool hdp_flush);
void radeon_ring_undo(struct radeon_ring *ring);
void radeon_ring_unlock_undo(struct radeon_device *rdev, struct radeon_ring *cp);
int radeon_ring_test(struct radeon_device *rdev, struct radeon_ring *cp);
@@ -1099,6 +1133,8 @@ struct radeon_wb {
#define R600_WB_EVENT_OFFSET 3072
#define CIK_WB_CP1_WPTR_OFFSET 3328
#define CIK_WB_CP2_WPTR_OFFSET 3584
+#define R600_WB_DMA_RING_TEST_OFFSET 3588
+#define CAYMAN_WB_DMA1_RING_TEST_OFFSET 3592
/**
* struct radeon_pm - power management datas
@@ -1621,7 +1657,8 @@ int radeon_uvd_get_create_msg(struct radeon_device *rdev, int ring,
uint32_t handle, struct radeon_fence **fence);
int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring,
uint32_t handle, struct radeon_fence **fence);
-void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo);
+void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo,
+ uint32_t allowed_domains);
void radeon_uvd_free_handles(struct radeon_device *rdev,
struct drm_file *filp);
int radeon_uvd_cs_parse(struct radeon_cs_parser *parser);
@@ -1710,6 +1747,11 @@ void radeon_test_ring_sync(struct radeon_device *rdev,
struct radeon_ring *cpB);
void radeon_test_syncing(struct radeon_device *rdev);
+/*
+ * MMU Notifier
+ */
+int radeon_mn_register(struct radeon_bo *bo, unsigned long addr);
+void radeon_mn_unregister(struct radeon_bo *bo);
/*
* Debugfs
@@ -1740,6 +1782,7 @@ struct radeon_asic_ring {
/* command emmit functions */
void (*ib_execute)(struct radeon_device *rdev, struct radeon_ib *ib);
void (*emit_fence)(struct radeon_device *rdev, struct radeon_fence *fence);
+ void (*hdp_flush)(struct radeon_device *rdev, struct radeon_ring *ring);
bool (*emit_semaphore)(struct radeon_device *rdev, struct radeon_ring *cp,
struct radeon_semaphore *semaphore, bool emit_wait);
void (*vm_flush)(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
@@ -1763,13 +1806,8 @@ struct radeon_asic {
int (*suspend)(struct radeon_device *rdev);
void (*vga_set_state)(struct radeon_device *rdev, bool state);
int (*asic_reset)(struct radeon_device *rdev);
- /* ioctl hw specific callback. Some hw might want to perform special
- * operation on specific ioctl. For instance on wait idle some hw
- * might want to perform and HDP flush through MMIO as it seems that
- * some R6XX/R7XX hw doesn't take HDP flush into account if programmed
- * through ring.
- */
- void (*ioctl_wait_idle)(struct radeon_device *rdev, struct radeon_bo *bo);
+ /* Flush the HDP cache via MMIO */
+ void (*mmio_hdp_flush)(struct radeon_device *rdev);
/* check if 3D engine is idle */
bool (*gui_idle)(struct radeon_device *rdev);
/* wait for mc_idle */
@@ -1782,16 +1820,26 @@ struct radeon_asic {
struct {
void (*tlb_flush)(struct radeon_device *rdev);
void (*set_page)(struct radeon_device *rdev, unsigned i,
- uint64_t addr);
+ uint64_t addr, uint32_t flags);
} gart;
struct {
int (*init)(struct radeon_device *rdev);
void (*fini)(struct radeon_device *rdev);
- void (*set_page)(struct radeon_device *rdev,
- struct radeon_ib *ib,
- uint64_t pe,
- uint64_t addr, unsigned count,
- uint32_t incr, uint32_t flags);
+ void (*copy_pages)(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe, uint64_t src,
+ unsigned count);
+ void (*write_pages)(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags);
+ void (*set_pages)(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags);
+ void (*pad_ib)(struct radeon_ib *ib);
} vm;
/* ring specific callbacks */
struct radeon_asic_ring *ring[RADEON_NUM_RINGS];
@@ -1818,24 +1866,24 @@ struct radeon_asic {
} display;
/* copy functions for bo handling */
struct {
- int (*blit)(struct radeon_device *rdev,
- uint64_t src_offset,
- uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence);
+ struct radeon_fence *(*blit)(struct radeon_device *rdev,
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
u32 blit_ring_index;
- int (*dma)(struct radeon_device *rdev,
- uint64_t src_offset,
- uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence);
+ struct radeon_fence *(*dma)(struct radeon_device *rdev,
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
u32 dma_ring_index;
/* method used for bo copy */
- int (*copy)(struct radeon_device *rdev,
- uint64_t src_offset,
- uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence);
+ struct radeon_fence *(*copy)(struct radeon_device *rdev,
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
/* ring used for bo copies */
u32 copy_ring_index;
} copy;
@@ -2117,6 +2165,8 @@ int radeon_gem_info_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
+int radeon_gem_userptr_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
int radeon_gem_pin_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int radeon_gem_unpin_ioctl(struct drm_device *dev, void *data,
@@ -2273,6 +2323,7 @@ struct radeon_device {
struct radeon_mman mman;
struct radeon_fence_driver fence_drv[RADEON_NUM_RINGS];
wait_queue_head_t fence_queue;
+ unsigned fence_context;
struct mutex ring_lock;
struct radeon_ring ring[RADEON_NUM_RINGS];
bool ib_pool_ready;
@@ -2291,7 +2342,7 @@ struct radeon_device {
bool need_dma32;
bool accel_working;
bool fastfb_working; /* IGP feature*/
- bool needs_reset;
+ bool needs_reset, in_reset;
struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES];
const struct firmware *me_fw; /* all family ME firmware */
const struct firmware *pfp_fw; /* r6/700 PFP firmware */
@@ -2299,10 +2350,12 @@ struct radeon_device {
const struct firmware *mc_fw; /* NI MC firmware */
const struct firmware *ce_fw; /* SI CE firmware */
const struct firmware *mec_fw; /* CIK MEC firmware */
+ const struct firmware *mec2_fw; /* KV MEC2 firmware */
const struct firmware *sdma_fw; /* CIK SDMA firmware */
const struct firmware *smc_fw; /* SMC firmware */
const struct firmware *uvd_fw; /* UVD firmware */
const struct firmware *vce_fw; /* VCE firmware */
+ bool new_fw;
struct r600_vram_scratch vram_scratch;
int msi_enabled; /* msi enabled */
struct r600_ih ih; /* r6/700 interrupt ring */
@@ -2310,7 +2363,6 @@ struct radeon_device {
struct radeon_mec mec;
struct work_struct hotplug_work;
struct work_struct audio_work;
- struct work_struct reset_work;
int num_crtc; /* number of crtcs */
struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */
bool has_uvd;
@@ -2342,6 +2394,14 @@ struct radeon_device {
struct dev_pm_domain vga_pm_domain;
bool have_disp_power_ref;
+ u32 px_quirk_flags;
+
+ /* tracking pinned memory */
+ u64 vram_pin_size;
+ u64 gart_pin_size;
+
+ struct mutex mn_lock;
+ DECLARE_HASHTABLE(mn_hash, 7);
};
bool radeon_is_px(struct drm_device *dev);
@@ -2352,10 +2412,42 @@ int radeon_device_init(struct radeon_device *rdev,
void radeon_device_fini(struct radeon_device *rdev);
int radeon_gpu_wait_for_idle(struct radeon_device *rdev);
-uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg,
- bool always_indirect);
-void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v,
- bool always_indirect);
+#define RADEON_MIN_MMIO_SIZE 0x10000
+
+static inline uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg,
+ bool always_indirect)
+{
+ /* The mmio size is 64kb at minimum. Allows the if to be optimized out. */
+ if ((reg < rdev->rmmio_size || reg < RADEON_MIN_MMIO_SIZE) && !always_indirect)
+ return readl(((void __iomem *)rdev->rmmio) + reg);
+ else {
+ unsigned long flags;
+ uint32_t ret;
+
+ spin_lock_irqsave(&rdev->mmio_idx_lock, flags);
+ writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
+ ret = readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
+ spin_unlock_irqrestore(&rdev->mmio_idx_lock, flags);
+
+ return ret;
+ }
+}
+
+static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v,
+ bool always_indirect)
+{
+ if ((reg < rdev->rmmio_size || reg < RADEON_MIN_MMIO_SIZE) && !always_indirect)
+ writel(v, ((void __iomem *)rdev->rmmio) + reg);
+ else {
+ unsigned long flags;
+
+ spin_lock_irqsave(&rdev->mmio_idx_lock, flags);
+ writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
+ writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA);
+ spin_unlock_irqrestore(&rdev->mmio_idx_lock, flags);
+ }
+}
+
u32 r100_io_rreg(struct radeon_device *rdev, u32 reg);
void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v);
@@ -2365,7 +2457,17 @@ void cik_mm_wdoorbell(struct radeon_device *rdev, u32 index, u32 v);
/*
* Cast helper
*/
-#define to_radeon_fence(p) ((struct radeon_fence *)(p))
+extern const struct fence_ops radeon_fence_ops;
+
+static inline struct radeon_fence *to_radeon_fence(struct fence *f)
+{
+ struct radeon_fence *__f = container_of(f, struct radeon_fence, base);
+
+ if (__f->base.ops == &radeon_fence_ops)
+ return __f;
+
+ return NULL;
+}
/*
* Registers read & write functions.
@@ -2685,18 +2787,25 @@ void radeon_atombios_fini(struct radeon_device *rdev);
/*
* RING helpers.
*/
-#if DRM_DEBUG_CODE == 0
+
+/**
+ * radeon_ring_write - write a value to the ring
+ *
+ * @ring: radeon_ring structure holding ring information
+ * @v: dword (dw) value to write
+ *
+ * Write a value to the requested ring buffer (all asics).
+ */
static inline void radeon_ring_write(struct radeon_ring *ring, uint32_t v)
{
+ if (ring->count_dw <= 0)
+ DRM_ERROR("radeon: writing more dwords to the ring than expected!\n");
+
ring->ring[ring->wptr++] = v;
ring->wptr &= ring->ptr_mask;
ring->count_dw--;
ring->ring_free_dw--;
}
-#else
-/* With debugging this is just too big to inline */
-void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
-#endif
/*
* ASICs macro.
@@ -2709,10 +2818,13 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
#define radeon_vga_set_state(rdev, state) (rdev)->asic->vga_set_state((rdev), (state))
#define radeon_asic_reset(rdev) (rdev)->asic->asic_reset((rdev))
#define radeon_gart_tlb_flush(rdev) (rdev)->asic->gart.tlb_flush((rdev))
-#define radeon_gart_set_page(rdev, i, p) (rdev)->asic->gart.set_page((rdev), (i), (p))
+#define radeon_gart_set_page(rdev, i, p, f) (rdev)->asic->gart.set_page((rdev), (i), (p), (f))
#define radeon_asic_vm_init(rdev) (rdev)->asic->vm.init((rdev))
#define radeon_asic_vm_fini(rdev) (rdev)->asic->vm.fini((rdev))
-#define radeon_asic_vm_set_page(rdev, ib, pe, addr, count, incr, flags) ((rdev)->asic->vm.set_page((rdev), (ib), (pe), (addr), (count), (incr), (flags)))
+#define radeon_asic_vm_copy_pages(rdev, ib, pe, src, count) ((rdev)->asic->vm.copy_pages((rdev), (ib), (pe), (src), (count)))
+#define radeon_asic_vm_write_pages(rdev, ib, pe, addr, count, incr, flags) ((rdev)->asic->vm.write_pages((rdev), (ib), (pe), (addr), (count), (incr), (flags)))
+#define radeon_asic_vm_set_pages(rdev, ib, pe, addr, count, incr, flags) ((rdev)->asic->vm.set_pages((rdev), (ib), (pe), (addr), (count), (incr), (flags)))
+#define radeon_asic_vm_pad_ib(rdev, ib) ((rdev)->asic->vm.pad_ib((ib)))
#define radeon_ring_start(rdev, r, cp) (rdev)->asic->ring[(r)]->ring_start((rdev), (cp))
#define radeon_ring_test(rdev, r, cp) (rdev)->asic->ring[(r)]->ring_test((rdev), (cp))
#define radeon_ib_test(rdev, r, cp) (rdev)->asic->ring[(r)]->ib_test((rdev), (cp))
@@ -2732,9 +2844,9 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
#define radeon_hdmi_setmode(rdev, e, m) (rdev)->asic->display.hdmi_setmode((e), (m))
#define radeon_fence_ring_emit(rdev, r, fence) (rdev)->asic->ring[(r)]->emit_fence((rdev), (fence))
#define radeon_semaphore_ring_emit(rdev, r, cp, semaphore, emit_wait) (rdev)->asic->ring[(r)]->emit_semaphore((rdev), (cp), (semaphore), (emit_wait))
-#define radeon_copy_blit(rdev, s, d, np, f) (rdev)->asic->copy.blit((rdev), (s), (d), (np), (f))
-#define radeon_copy_dma(rdev, s, d, np, f) (rdev)->asic->copy.dma((rdev), (s), (d), (np), (f))
-#define radeon_copy(rdev, s, d, np, f) (rdev)->asic->copy.copy((rdev), (s), (d), (np), (f))
+#define radeon_copy_blit(rdev, s, d, np, resv) (rdev)->asic->copy.blit((rdev), (s), (d), (np), (resv))
+#define radeon_copy_dma(rdev, s, d, np, resv) (rdev)->asic->copy.dma((rdev), (s), (d), (np), (resv))
+#define radeon_copy(rdev, s, d, np, resv) (rdev)->asic->copy.copy((rdev), (s), (d), (np), (resv))
#define radeon_copy_blit_ring_index(rdev) (rdev)->asic->copy.blit_ring_index
#define radeon_copy_dma_ring_index(rdev) (rdev)->asic->copy.dma_ring_index
#define radeon_copy_ring_index(rdev) (rdev)->asic->copy.copy_ring_index
@@ -2808,6 +2920,10 @@ extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enabl
extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable);
extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain);
extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
+extern int radeon_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
+ uint32_t flags);
+extern bool radeon_ttm_tt_has_userptr(struct ttm_tt *ttm);
+extern bool radeon_ttm_tt_is_readonly(struct ttm_tt *ttm);
extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base);
extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
extern int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
@@ -2840,6 +2956,8 @@ int radeon_vm_update_page_directory(struct radeon_device *rdev,
struct radeon_vm *vm);
int radeon_vm_clear_freed(struct radeon_device *rdev,
struct radeon_vm *vm);
+int radeon_vm_clear_invalids(struct radeon_device *rdev,
+ struct radeon_vm *vm);
int radeon_vm_bo_update(struct radeon_device *rdev,
struct radeon_bo_va *bo_va,
struct ttm_mem_reg *mem);
@@ -2863,10 +2981,10 @@ struct r600_audio_pin *r600_audio_get_pin(struct radeon_device *rdev);
struct r600_audio_pin *dce6_audio_get_pin(struct radeon_device *rdev);
void r600_audio_enable(struct radeon_device *rdev,
struct r600_audio_pin *pin,
- bool enable);
+ u8 enable_mask);
void dce6_audio_enable(struct radeon_device *rdev,
struct r600_audio_pin *pin,
- bool enable);
+ u8 enable_mask);
/*
* R600 vram scratch functions
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index 34b9aa9e3c06..850de57069be 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -194,7 +194,7 @@ static struct radeon_asic r100_asic = {
.resume = &r100_resume,
.vga_set_state = &r100_vga_set_state,
.asic_reset = &r100_asic_reset,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = NULL,
.gui_idle = &r100_gui_idle,
.mc_wait_for_idle = &r100_mc_wait_for_idle,
.gart = {
@@ -260,7 +260,7 @@ static struct radeon_asic r200_asic = {
.resume = &r100_resume,
.vga_set_state = &r100_vga_set_state,
.asic_reset = &r100_asic_reset,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = NULL,
.gui_idle = &r100_gui_idle,
.mc_wait_for_idle = &r100_mc_wait_for_idle,
.gart = {
@@ -340,7 +340,7 @@ static struct radeon_asic r300_asic = {
.resume = &r300_resume,
.vga_set_state = &r100_vga_set_state,
.asic_reset = &r300_asic_reset,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = NULL,
.gui_idle = &r100_gui_idle,
.mc_wait_for_idle = &r300_mc_wait_for_idle,
.gart = {
@@ -406,7 +406,7 @@ static struct radeon_asic r300_asic_pcie = {
.resume = &r300_resume,
.vga_set_state = &r100_vga_set_state,
.asic_reset = &r300_asic_reset,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = NULL,
.gui_idle = &r100_gui_idle,
.mc_wait_for_idle = &r300_mc_wait_for_idle,
.gart = {
@@ -472,7 +472,7 @@ static struct radeon_asic r420_asic = {
.resume = &r420_resume,
.vga_set_state = &r100_vga_set_state,
.asic_reset = &r300_asic_reset,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = NULL,
.gui_idle = &r100_gui_idle,
.mc_wait_for_idle = &r300_mc_wait_for_idle,
.gart = {
@@ -538,7 +538,7 @@ static struct radeon_asic rs400_asic = {
.resume = &rs400_resume,
.vga_set_state = &r100_vga_set_state,
.asic_reset = &r300_asic_reset,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = NULL,
.gui_idle = &r100_gui_idle,
.mc_wait_for_idle = &rs400_mc_wait_for_idle,
.gart = {
@@ -604,7 +604,7 @@ static struct radeon_asic rs600_asic = {
.resume = &rs600_resume,
.vga_set_state = &r100_vga_set_state,
.asic_reset = &rs600_asic_reset,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = NULL,
.gui_idle = &r100_gui_idle,
.mc_wait_for_idle = &rs600_mc_wait_for_idle,
.gart = {
@@ -672,7 +672,7 @@ static struct radeon_asic rs690_asic = {
.resume = &rs690_resume,
.vga_set_state = &r100_vga_set_state,
.asic_reset = &rs600_asic_reset,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = NULL,
.gui_idle = &r100_gui_idle,
.mc_wait_for_idle = &rs690_mc_wait_for_idle,
.gart = {
@@ -740,7 +740,7 @@ static struct radeon_asic rv515_asic = {
.resume = &rv515_resume,
.vga_set_state = &r100_vga_set_state,
.asic_reset = &rs600_asic_reset,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = NULL,
.gui_idle = &r100_gui_idle,
.mc_wait_for_idle = &rv515_mc_wait_for_idle,
.gart = {
@@ -806,7 +806,7 @@ static struct radeon_asic r520_asic = {
.resume = &r520_resume,
.vga_set_state = &r100_vga_set_state,
.asic_reset = &rs600_asic_reset,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = NULL,
.gui_idle = &r100_gui_idle,
.mc_wait_for_idle = &r520_mc_wait_for_idle,
.gart = {
@@ -898,7 +898,7 @@ static struct radeon_asic r600_asic = {
.resume = &r600_resume,
.vga_set_state = &r600_vga_set_state,
.asic_reset = &r600_asic_reset,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .mmio_hdp_flush = r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &r600_mc_wait_for_idle,
.get_xclk = &r600_get_xclk,
@@ -963,6 +963,19 @@ static struct radeon_asic r600_asic = {
},
};
+static struct radeon_asic_ring rv6xx_uvd_ring = {
+ .ib_execute = &uvd_v1_0_ib_execute,
+ .emit_fence = &uvd_v1_0_fence_emit,
+ .emit_semaphore = &uvd_v1_0_semaphore_emit,
+ .cs_parse = &radeon_uvd_cs_parse,
+ .ring_test = &uvd_v1_0_ring_test,
+ .ib_test = &uvd_v1_0_ib_test,
+ .is_lockup = &radeon_ring_test_lockup,
+ .get_rptr = &uvd_v1_0_get_rptr,
+ .get_wptr = &uvd_v1_0_get_wptr,
+ .set_wptr = &uvd_v1_0_set_wptr,
+};
+
static struct radeon_asic rv6xx_asic = {
.init = &r600_init,
.fini = &r600_fini,
@@ -970,7 +983,7 @@ static struct radeon_asic rv6xx_asic = {
.resume = &r600_resume,
.vga_set_state = &r600_vga_set_state,
.asic_reset = &r600_asic_reset,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .mmio_hdp_flush = r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &r600_mc_wait_for_idle,
.get_xclk = &r600_get_xclk,
@@ -982,6 +995,7 @@ static struct radeon_asic rv6xx_asic = {
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = &r600_gfx_ring,
[R600_RING_TYPE_DMA_INDEX] = &r600_dma_ring,
+ [R600_RING_TYPE_UVD_INDEX] = &rv6xx_uvd_ring,
},
.irq = {
.set = &r600_irq_set,
@@ -1060,7 +1074,7 @@ static struct radeon_asic rs780_asic = {
.resume = &r600_resume,
.vga_set_state = &r600_vga_set_state,
.asic_reset = &r600_asic_reset,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .mmio_hdp_flush = r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &r600_mc_wait_for_idle,
.get_xclk = &r600_get_xclk,
@@ -1072,6 +1086,7 @@ static struct radeon_asic rs780_asic = {
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = &r600_gfx_ring,
[R600_RING_TYPE_DMA_INDEX] = &r600_dma_ring,
+ [R600_RING_TYPE_UVD_INDEX] = &rv6xx_uvd_ring,
},
.irq = {
.set = &r600_irq_set,
@@ -1163,7 +1178,7 @@ static struct radeon_asic rv770_asic = {
.resume = &rv770_resume,
.asic_reset = &r600_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .mmio_hdp_flush = r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &r600_mc_wait_for_idle,
.get_xclk = &rv770_get_xclk,
@@ -1281,7 +1296,7 @@ static struct radeon_asic evergreen_asic = {
.resume = &evergreen_resume,
.asic_reset = &evergreen_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .mmio_hdp_flush = r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &evergreen_mc_wait_for_idle,
.get_xclk = &rv770_get_xclk,
@@ -1373,7 +1388,7 @@ static struct radeon_asic sumo_asic = {
.resume = &evergreen_resume,
.asic_reset = &evergreen_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .mmio_hdp_flush = r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &evergreen_mc_wait_for_idle,
.get_xclk = &r600_get_xclk,
@@ -1464,7 +1479,7 @@ static struct radeon_asic btc_asic = {
.resume = &evergreen_resume,
.asic_reset = &evergreen_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .mmio_hdp_flush = r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &evergreen_mc_wait_for_idle,
.get_xclk = &rv770_get_xclk,
@@ -1599,7 +1614,7 @@ static struct radeon_asic cayman_asic = {
.resume = &cayman_resume,
.asic_reset = &cayman_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .mmio_hdp_flush = r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &evergreen_mc_wait_for_idle,
.get_xclk = &rv770_get_xclk,
@@ -1611,7 +1626,10 @@ static struct radeon_asic cayman_asic = {
.vm = {
.init = &cayman_vm_init,
.fini = &cayman_vm_fini,
- .set_page = &cayman_dma_vm_set_page,
+ .copy_pages = &cayman_dma_vm_copy_pages,
+ .write_pages = &cayman_dma_vm_write_pages,
+ .set_pages = &cayman_dma_vm_set_pages,
+ .pad_ib = &cayman_dma_vm_pad_ib,
},
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = &cayman_gfx_ring,
@@ -1699,7 +1717,7 @@ static struct radeon_asic trinity_asic = {
.resume = &cayman_resume,
.asic_reset = &cayman_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .mmio_hdp_flush = r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &evergreen_mc_wait_for_idle,
.get_xclk = &r600_get_xclk,
@@ -1711,7 +1729,10 @@ static struct radeon_asic trinity_asic = {
.vm = {
.init = &cayman_vm_init,
.fini = &cayman_vm_fini,
- .set_page = &cayman_dma_vm_set_page,
+ .copy_pages = &cayman_dma_vm_copy_pages,
+ .write_pages = &cayman_dma_vm_write_pages,
+ .set_pages = &cayman_dma_vm_set_pages,
+ .pad_ib = &cayman_dma_vm_pad_ib,
},
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = &cayman_gfx_ring,
@@ -1829,7 +1850,7 @@ static struct radeon_asic si_asic = {
.resume = &si_resume,
.asic_reset = &si_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .mmio_hdp_flush = r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &evergreen_mc_wait_for_idle,
.get_xclk = &si_get_xclk,
@@ -1841,7 +1862,10 @@ static struct radeon_asic si_asic = {
.vm = {
.init = &si_vm_init,
.fini = &si_vm_fini,
- .set_page = &si_dma_vm_set_page,
+ .copy_pages = &si_dma_vm_copy_pages,
+ .write_pages = &si_dma_vm_write_pages,
+ .set_pages = &si_dma_vm_set_pages,
+ .pad_ib = &cayman_dma_vm_pad_ib,
},
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = &si_gfx_ring,
@@ -1987,7 +2011,7 @@ static struct radeon_asic ci_asic = {
.resume = &cik_resume,
.asic_reset = &cik_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = &r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &evergreen_mc_wait_for_idle,
.get_xclk = &cik_get_xclk,
@@ -1999,7 +2023,10 @@ static struct radeon_asic ci_asic = {
.vm = {
.init = &cik_vm_init,
.fini = &cik_vm_fini,
- .set_page = &cik_sdma_vm_set_page,
+ .copy_pages = &cik_sdma_vm_copy_pages,
+ .write_pages = &cik_sdma_vm_write_pages,
+ .set_pages = &cik_sdma_vm_set_pages,
+ .pad_ib = &cik_sdma_vm_pad_ib,
},
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = &ci_gfx_ring,
@@ -2091,7 +2118,7 @@ static struct radeon_asic kv_asic = {
.resume = &cik_resume,
.asic_reset = &cik_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .ioctl_wait_idle = NULL,
+ .mmio_hdp_flush = &r600_mmio_hdp_flush,
.gui_idle = &r600_gui_idle,
.mc_wait_for_idle = &evergreen_mc_wait_for_idle,
.get_xclk = &cik_get_xclk,
@@ -2103,7 +2130,10 @@ static struct radeon_asic kv_asic = {
.vm = {
.init = &cik_vm_init,
.fini = &cik_vm_fini,
- .set_page = &cik_sdma_vm_set_page,
+ .copy_pages = &cik_sdma_vm_copy_pages,
+ .write_pages = &cik_sdma_vm_write_pages,
+ .set_pages = &cik_sdma_vm_set_pages,
+ .pad_ib = &cik_sdma_vm_pad_ib,
},
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = &ci_gfx_ring,
@@ -2281,7 +2311,15 @@ int radeon_asic_init(struct radeon_device *rdev)
case CHIP_RS780:
case CHIP_RS880:
rdev->asic = &rs780_asic;
- rdev->has_uvd = true;
+ /* 760G/780V/880V don't have UVD */
+ if ((rdev->pdev->device == 0x9616)||
+ (rdev->pdev->device == 0x9611)||
+ (rdev->pdev->device == 0x9613)||
+ (rdev->pdev->device == 0x9711)||
+ (rdev->pdev->device == 0x9713))
+ rdev->has_uvd = false;
+ else
+ rdev->has_uvd = true;
break;
case CHIP_RV770:
case CHIP_RV730:
@@ -2457,7 +2495,7 @@ int radeon_asic_init(struct radeon_device *rdev)
rdev->cg_flags =
RADEON_CG_SUPPORT_GFX_MGCG |
RADEON_CG_SUPPORT_GFX_MGLS |
- RADEON_CG_SUPPORT_GFX_CGCG |
+ /*RADEON_CG_SUPPORT_GFX_CGCG |*/
RADEON_CG_SUPPORT_GFX_CGLS |
RADEON_CG_SUPPORT_GFX_CGTS |
RADEON_CG_SUPPORT_GFX_CGTS_LS |
@@ -2476,7 +2514,7 @@ int radeon_asic_init(struct radeon_device *rdev)
rdev->cg_flags =
RADEON_CG_SUPPORT_GFX_MGCG |
RADEON_CG_SUPPORT_GFX_MGLS |
- RADEON_CG_SUPPORT_GFX_CGCG |
+ /*RADEON_CG_SUPPORT_GFX_CGCG |*/
RADEON_CG_SUPPORT_GFX_CGLS |
RADEON_CG_SUPPORT_GFX_CGTS |
RADEON_CG_SUPPORT_GFX_CP_LS |
@@ -2502,7 +2540,7 @@ int radeon_asic_init(struct radeon_device *rdev)
rdev->cg_flags =
RADEON_CG_SUPPORT_GFX_MGCG |
RADEON_CG_SUPPORT_GFX_MGLS |
- RADEON_CG_SUPPORT_GFX_CGCG |
+ /*RADEON_CG_SUPPORT_GFX_CGCG |*/
RADEON_CG_SUPPORT_GFX_CGLS |
RADEON_CG_SUPPORT_GFX_CGTS |
RADEON_CG_SUPPORT_GFX_CGTS_LS |
@@ -2530,7 +2568,7 @@ int radeon_asic_init(struct radeon_device *rdev)
rdev->cg_flags =
RADEON_CG_SUPPORT_GFX_MGCG |
RADEON_CG_SUPPORT_GFX_MGLS |
- RADEON_CG_SUPPORT_GFX_CGCG |
+ /*RADEON_CG_SUPPORT_GFX_CGCG |*/
RADEON_CG_SUPPORT_GFX_CGLS |
RADEON_CG_SUPPORT_GFX_CGTS |
RADEON_CG_SUPPORT_GFX_CGTS_LS |
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index 01e7c0ad8f01..d8ace5b28a5b 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -68,7 +68,7 @@ int r100_asic_reset(struct radeon_device *rdev);
u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc);
void r100_pci_gart_tlb_flush(struct radeon_device *rdev);
void r100_pci_gart_set_page(struct radeon_device *rdev, unsigned i,
- uint64_t addr);
+ uint64_t addr, uint32_t flags);
void r100_ring_start(struct radeon_device *rdev, struct radeon_ring *ring);
int r100_irq_set(struct radeon_device *rdev);
int r100_irq_process(struct radeon_device *rdev);
@@ -81,11 +81,11 @@ bool r100_semaphore_ring_emit(struct radeon_device *rdev,
int r100_cs_parse(struct radeon_cs_parser *p);
void r100_pll_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
uint32_t r100_pll_rreg(struct radeon_device *rdev, uint32_t reg);
-int r100_copy_blit(struct radeon_device *rdev,
- uint64_t src_offset,
- uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence);
+struct radeon_fence *r100_copy_blit(struct radeon_device *rdev,
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
int r100_set_surface_reg(struct radeon_device *rdev, int reg,
uint32_t tiling_flags, uint32_t pitch,
uint32_t offset, uint32_t obj_size);
@@ -152,11 +152,11 @@ void r100_gfx_set_wptr(struct radeon_device *rdev,
/*
* r200,rv250,rs300,rv280
*/
-extern int r200_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset,
- uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence);
+struct radeon_fence *r200_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
void r200_set_safe_registers(struct radeon_device *rdev);
/*
@@ -173,7 +173,7 @@ extern void r300_fence_ring_emit(struct radeon_device *rdev,
extern int r300_cs_parse(struct radeon_cs_parser *p);
extern void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev);
extern void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i,
- uint64_t addr);
+ uint64_t addr, uint32_t flags);
extern void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes);
extern int rv370_get_pcie_lanes(struct radeon_device *rdev);
extern void r300_set_reg_safe(struct radeon_device *rdev);
@@ -209,7 +209,7 @@ extern int rs400_suspend(struct radeon_device *rdev);
extern int rs400_resume(struct radeon_device *rdev);
void rs400_gart_tlb_flush(struct radeon_device *rdev);
void rs400_gart_set_page(struct radeon_device *rdev, unsigned i,
- uint64_t addr);
+ uint64_t addr, uint32_t flags);
uint32_t rs400_mc_rreg(struct radeon_device *rdev, uint32_t reg);
void rs400_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
int rs400_gart_init(struct radeon_device *rdev);
@@ -233,7 +233,7 @@ void rs600_irq_disable(struct radeon_device *rdev);
u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc);
void rs600_gart_tlb_flush(struct radeon_device *rdev);
void rs600_gart_set_page(struct radeon_device *rdev, unsigned i,
- uint64_t addr);
+ uint64_t addr, uint32_t flags);
uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg);
void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
void rs600_bandwidth_update(struct radeon_device *rdev);
@@ -340,18 +340,20 @@ int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring);
void r600_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
int r600_ring_test(struct radeon_device *rdev, struct radeon_ring *cp);
int r600_dma_ring_test(struct radeon_device *rdev, struct radeon_ring *cp);
-int r600_copy_cpdma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages, struct radeon_fence **fence);
-int r600_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages, struct radeon_fence **fence);
+struct radeon_fence *r600_copy_cpdma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
+struct radeon_fence *r600_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
void r600_hpd_init(struct radeon_device *rdev);
void r600_hpd_fini(struct radeon_device *rdev);
bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
void r600_hpd_set_polarity(struct radeon_device *rdev,
enum radeon_hpd_id hpd);
-extern void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo);
+extern void r600_mmio_hdp_flush(struct radeon_device *rdev);
extern bool r600_gui_idle(struct radeon_device *rdev);
extern void r600_pm_misc(struct radeon_device *rdev);
extern void r600_pm_init_profile(struct radeon_device *rdev);
@@ -389,7 +391,6 @@ void r600_disable_interrupts(struct radeon_device *rdev);
void r600_rlc_stop(struct radeon_device *rdev);
/* r600 audio */
int r600_audio_init(struct radeon_device *rdev);
-struct r600_audio_pin r600_audio_status(struct radeon_device *rdev);
void r600_audio_fini(struct radeon_device *rdev);
void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock);
void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder, void *buffer,
@@ -461,10 +462,10 @@ bool rv770_page_flip_pending(struct radeon_device *rdev, int crtc);
void r700_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
void r700_cp_stop(struct radeon_device *rdev);
void r700_cp_fini(struct radeon_device *rdev);
-int rv770_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence);
+struct radeon_fence *rv770_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
u32 rv770_get_xclk(struct radeon_device *rdev);
int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
int rv770_get_temp(struct radeon_device *rdev);
@@ -535,10 +536,10 @@ void evergreen_dma_fence_ring_emit(struct radeon_device *rdev,
struct radeon_fence *fence);
void evergreen_dma_ring_ib_execute(struct radeon_device *rdev,
struct radeon_ib *ib);
-int evergreen_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence);
+struct radeon_fence *evergreen_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable);
void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode);
int evergreen_get_temp(struct radeon_device *rdev);
@@ -606,11 +607,22 @@ void cayman_dma_ring_ib_execute(struct radeon_device *rdev,
struct radeon_ib *ib);
bool cayman_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring);
bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring);
-void cayman_dma_vm_set_page(struct radeon_device *rdev,
- struct radeon_ib *ib,
- uint64_t pe,
- uint64_t addr, unsigned count,
- uint32_t incr, uint32_t flags);
+
+void cayman_dma_vm_copy_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe, uint64_t src,
+ unsigned count);
+void cayman_dma_vm_write_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags);
+void cayman_dma_vm_set_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags);
+void cayman_dma_vm_pad_ib(struct radeon_ib *ib);
void cayman_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
@@ -689,15 +701,26 @@ int si_vm_init(struct radeon_device *rdev);
void si_vm_fini(struct radeon_device *rdev);
void si_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);
-int si_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence);
-void si_dma_vm_set_page(struct radeon_device *rdev,
- struct radeon_ib *ib,
- uint64_t pe,
- uint64_t addr, unsigned count,
- uint32_t incr, uint32_t flags);
+struct radeon_fence *si_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
+
+void si_dma_vm_copy_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe, uint64_t src,
+ unsigned count);
+void si_dma_vm_write_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags);
+void si_dma_vm_set_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags);
+
void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
u32 si_get_xclk(struct radeon_device *rdev);
uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev);
@@ -737,14 +760,14 @@ bool cik_sdma_semaphore_ring_emit(struct radeon_device *rdev,
struct radeon_semaphore *semaphore,
bool emit_wait);
void cik_sdma_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
-int cik_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence);
-int cik_copy_cpdma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence);
+struct radeon_fence *cik_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
+struct radeon_fence *cik_copy_cpdma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv);
int cik_sdma_ring_test(struct radeon_device *rdev, struct radeon_ring *ring);
int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring);
bool cik_sdma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring);
@@ -771,11 +794,23 @@ int cik_irq_process(struct radeon_device *rdev);
int cik_vm_init(struct radeon_device *rdev);
void cik_vm_fini(struct radeon_device *rdev);
void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
-void cik_sdma_vm_set_page(struct radeon_device *rdev,
- struct radeon_ib *ib,
- uint64_t pe,
- uint64_t addr, unsigned count,
- uint32_t incr, uint32_t flags);
+
+void cik_sdma_vm_copy_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe, uint64_t src,
+ unsigned count);
+void cik_sdma_vm_write_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags);
+void cik_sdma_vm_set_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags);
+void cik_sdma_vm_pad_ib(struct radeon_ib *ib);
+
void cik_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
int cik_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);
u32 cik_gfx_get_rptr(struct radeon_device *rdev,
@@ -848,6 +883,7 @@ uint32_t uvd_v1_0_get_wptr(struct radeon_device *rdev,
struct radeon_ring *ring);
void uvd_v1_0_set_wptr(struct radeon_device *rdev,
struct radeon_ring *ring);
+int uvd_v1_0_resume(struct radeon_device *rdev);
int uvd_v1_0_init(struct radeon_device *rdev);
void uvd_v1_0_fini(struct radeon_device *rdev);
@@ -855,6 +891,8 @@ int uvd_v1_0_start(struct radeon_device *rdev);
void uvd_v1_0_stop(struct radeon_device *rdev);
int uvd_v1_0_ring_test(struct radeon_device *rdev, struct radeon_ring *ring);
+void uvd_v1_0_fence_emit(struct radeon_device *rdev,
+ struct radeon_fence *fence);
int uvd_v1_0_ib_test(struct radeon_device *rdev, struct radeon_ring *ring);
bool uvd_v1_0_semaphore_emit(struct radeon_device *rdev,
struct radeon_ring *ring,
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 173f378428a9..df69b92ba164 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -447,11 +447,18 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
}
}
+ /* Fujitsu D3003-S2 board lists DVI-I as DVI-I and VGA */
+ if ((dev->pdev->device == 0x9805) &&
+ (dev->pdev->subsystem_vendor == 0x1734) &&
+ (dev->pdev->subsystem_device == 0x11bd)) {
+ if (*connector_type == DRM_MODE_CONNECTOR_VGA)
+ return false;
+ }
return true;
}
-const int supported_devices_connector_convert[] = {
+static const int supported_devices_connector_convert[] = {
DRM_MODE_CONNECTOR_Unknown,
DRM_MODE_CONNECTOR_VGA,
DRM_MODE_CONNECTOR_DVII,
@@ -470,7 +477,7 @@ const int supported_devices_connector_convert[] = {
DRM_MODE_CONNECTOR_DisplayPort
};
-const uint16_t supported_devices_connector_object_id_convert[] = {
+static const uint16_t supported_devices_connector_object_id_convert[] = {
CONNECTOR_OBJECT_ID_NONE,
CONNECTOR_OBJECT_ID_VGA,
CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I, /* not all boards support DL */
@@ -487,7 +494,7 @@ const uint16_t supported_devices_connector_object_id_convert[] = {
CONNECTOR_OBJECT_ID_SVIDEO
};
-const int object_connector_convert[] = {
+static const int object_connector_convert[] = {
DRM_MODE_CONNECTOR_Unknown,
DRM_MODE_CONNECTOR_DVII,
DRM_MODE_CONNECTOR_DVII,
@@ -1963,7 +1970,7 @@ static const char *thermal_controller_names[] = {
"adm1032",
"adm1030",
"max6649",
- "lm64",
+ "lm63", /* lm64 */
"f75375",
"asc7xxx",
};
@@ -1974,7 +1981,7 @@ static const char *pp_lib_thermal_controller_names[] = {
"adm1032",
"adm1030",
"max6649",
- "lm64",
+ "lm63", /* lm64 */
"f75375",
"RV6xx",
"RV770",
@@ -2281,19 +2288,31 @@ static void radeon_atombios_add_pplib_thermal_controller(struct radeon_device *r
(controller->ucFanParameters &
ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
rdev->pm.int_thermal_type = THERMAL_TYPE_KV;
- } else if ((controller->ucType ==
- ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO) ||
- (controller->ucType ==
- ATOM_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL) ||
- (controller->ucType ==
- ATOM_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL)) {
- DRM_INFO("Special thermal controller config\n");
+ } else if (controller->ucType ==
+ ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO) {
+ DRM_INFO("External GPIO thermal controller %s fan control\n",
+ (controller->ucFanParameters &
+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
+ rdev->pm.int_thermal_type = THERMAL_TYPE_EXTERNAL_GPIO;
+ } else if (controller->ucType ==
+ ATOM_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL) {
+ DRM_INFO("ADT7473 with internal thermal controller %s fan control\n",
+ (controller->ucFanParameters &
+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
+ rdev->pm.int_thermal_type = THERMAL_TYPE_ADT7473_WITH_INTERNAL;
+ } else if (controller->ucType ==
+ ATOM_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL) {
+ DRM_INFO("EMC2103 with internal thermal controller %s fan control\n",
+ (controller->ucFanParameters &
+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
+ rdev->pm.int_thermal_type = THERMAL_TYPE_EMC2103_WITH_INTERNAL;
} else if (controller->ucType < ARRAY_SIZE(pp_lib_thermal_controller_names)) {
DRM_INFO("Possible %s thermal controller at 0x%02x %s fan control\n",
pp_lib_thermal_controller_names[controller->ucType],
controller->ucI2cAddress >> 1,
(controller->ucFanParameters &
ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
+ rdev->pm.int_thermal_type = THERMAL_TYPE_EXTERNAL;
i2c_bus = radeon_lookup_i2c_gpio(rdev, controller->ucI2cLine);
rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
if (rdev->pm.i2c_bus) {
@@ -3236,6 +3255,41 @@ int radeon_atom_get_leakage_vddc_based_on_leakage_params(struct radeon_device *r
return 0;
}
+union get_voltage_info {
+ struct _GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_2 in;
+ struct _GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_2 evv_out;
+};
+
+int radeon_atom_get_voltage_evv(struct radeon_device *rdev,
+ u16 virtual_voltage_id,
+ u16 *voltage)
+{
+ int index = GetIndexIntoMasterTable(COMMAND, GetVoltageInfo);
+ u32 entry_id;
+ u32 count = rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count;
+ union get_voltage_info args;
+
+ for (entry_id = 0; entry_id < count; entry_id++) {
+ if (rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[entry_id].v ==
+ virtual_voltage_id)
+ break;
+ }
+
+ if (entry_id >= count)
+ return -EINVAL;
+
+ args.in.ucVoltageType = VOLTAGE_TYPE_VDDC;
+ args.in.ucVoltageMode = ATOM_GET_VOLTAGE_EVV_VOLTAGE;
+ args.in.ulSCLKFreq =
+ cpu_to_le32(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[entry_id].clk);
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+ *voltage = le16_to_cpu(args.evv_out.usVoltageLevel);
+
+ return 0;
+}
+
int radeon_atom_get_voltage_gpio_settings(struct radeon_device *rdev,
u16 voltage_level, u8 voltage_type,
u32 *gpio_value, u32 *gpio_mask)
@@ -3397,6 +3451,50 @@ radeon_atom_is_voltage_gpio(struct radeon_device *rdev,
return false;
}
+int radeon_atom_get_svi2_info(struct radeon_device *rdev,
+ u8 voltage_type,
+ u8 *svd_gpio_id, u8 *svc_gpio_id)
+{
+ int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
+ u8 frev, crev;
+ u16 data_offset, size;
+ union voltage_object_info *voltage_info;
+ union voltage_object *voltage_object = NULL;
+
+ if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
+ &frev, &crev, &data_offset)) {
+ voltage_info = (union voltage_object_info *)
+ (rdev->mode_info.atom_context->bios + data_offset);
+
+ switch (frev) {
+ case 3:
+ switch (crev) {
+ case 1:
+ voltage_object = (union voltage_object *)
+ atom_lookup_voltage_object_v3(&voltage_info->v3,
+ voltage_type,
+ VOLTAGE_OBJ_SVID2);
+ if (voltage_object) {
+ *svd_gpio_id = voltage_object->v3.asSVID2Obj.ucSVDGpioId;
+ *svc_gpio_id = voltage_object->v3.asSVID2Obj.ucSVCGpioId;
+ } else {
+ return -EINVAL;
+ }
+ break;
+ default:
+ DRM_ERROR("unknown voltage object table\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ DRM_ERROR("unknown voltage object table\n");
+ return -EINVAL;
+ }
+
+ }
+ return 0;
+}
+
int radeon_atom_get_max_voltage(struct radeon_device *rdev,
u8 voltage_type, u16 *max_voltage)
{
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
index a9fb0d016d38..8bc7d0bbd3c8 100644
--- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
+++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
@@ -33,7 +33,6 @@ static struct radeon_atpx_priv {
bool atpx_detected;
/* handle for device - and atpx */
acpi_handle dhandle;
- acpi_handle other_handle;
struct radeon_atpx atpx;
} radeon_atpx_priv;
@@ -453,10 +452,9 @@ static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev)
return false;
status = acpi_get_handle(dhandle, "ATPX", &atpx_handle);
- if (ACPI_FAILURE(status)) {
- radeon_atpx_priv.other_handle = dhandle;
+ if (ACPI_FAILURE(status))
return false;
- }
+
radeon_atpx_priv.dhandle = dhandle;
radeon_atpx_priv.atpx.handle = atpx_handle;
return true;
@@ -540,16 +538,6 @@ static bool radeon_atpx_detect(void)
printk(KERN_INFO "VGA switcheroo: detected switching method %s handle\n",
acpi_method_name);
radeon_atpx_priv.atpx_detected = true;
- /*
- * On some systems hotplug events are generated for the device
- * being switched off when ATPX is executed. They cause ACPI
- * hotplug to trigger and attempt to remove the device from
- * the system, which causes it to break down. Prevent that from
- * happening by setting the no_hotplug flag for the involved
- * ACPI device objects.
- */
- acpi_bus_no_hotplug(radeon_atpx_priv.dhandle);
- acpi_bus_no_hotplug(radeon_atpx_priv.other_handle);
return true;
}
return false;
diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c
index 6e05a2e75a46..9e7f23dd14bd 100644
--- a/drivers/gpu/drm/radeon/radeon_benchmark.c
+++ b/drivers/gpu/drm/radeon/radeon_benchmark.c
@@ -45,33 +45,29 @@ static int radeon_benchmark_do_move(struct radeon_device *rdev, unsigned size,
for (i = 0; i < n; i++) {
switch (flag) {
case RADEON_BENCHMARK_COPY_DMA:
- r = radeon_copy_dma(rdev, saddr, daddr,
- size / RADEON_GPU_PAGE_SIZE,
- &fence);
+ fence = radeon_copy_dma(rdev, saddr, daddr,
+ size / RADEON_GPU_PAGE_SIZE,
+ NULL);
break;
case RADEON_BENCHMARK_COPY_BLIT:
- r = radeon_copy_blit(rdev, saddr, daddr,
- size / RADEON_GPU_PAGE_SIZE,
- &fence);
+ fence = radeon_copy_blit(rdev, saddr, daddr,
+ size / RADEON_GPU_PAGE_SIZE,
+ NULL);
break;
default:
DRM_ERROR("Unknown copy method\n");
- r = -EINVAL;
+ return -EINVAL;
}
- if (r)
- goto exit_do_move;
+ if (IS_ERR(fence))
+ return PTR_ERR(fence);
+
r = radeon_fence_wait(fence, false);
- if (r)
- goto exit_do_move;
radeon_fence_unref(&fence);
+ if (r)
+ return r;
}
end_jiffies = jiffies;
- r = jiffies_to_msecs(end_jiffies - start_jiffies);
-
-exit_do_move:
- if (fence)
- radeon_fence_unref(&fence);
- return r;
+ return jiffies_to_msecs(end_jiffies - start_jiffies);
}
@@ -97,7 +93,7 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size,
int time;
n = RADEON_BENCHMARK_ITERATIONS;
- r = radeon_bo_create(rdev, size, PAGE_SIZE, true, sdomain, NULL, &sobj);
+ r = radeon_bo_create(rdev, size, PAGE_SIZE, true, sdomain, 0, NULL, NULL, &sobj);
if (r) {
goto out_cleanup;
}
@@ -109,7 +105,7 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size,
if (r) {
goto out_cleanup;
}
- r = radeon_bo_create(rdev, size, PAGE_SIZE, true, ddomain, NULL, &dobj);
+ r = radeon_bo_create(rdev, size, PAGE_SIZE, true, ddomain, 0, NULL, NULL, &dobj);
if (r) {
goto out_cleanup;
}
diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c
index 6a03624fadaa..63ccb8fa799c 100644
--- a/drivers/gpu/drm/radeon/radeon_bios.c
+++ b/drivers/gpu/drm/radeon/radeon_bios.c
@@ -658,12 +658,10 @@ bool radeon_get_bios(struct radeon_device *rdev)
r = igp_read_bios_from_vram(rdev);
if (r == false)
r = radeon_read_bios(rdev);
- if (r == false) {
+ if (r == false)
r = radeon_read_disabled_bios(rdev);
- }
- if (r == false) {
+ if (r == false)
r = radeon_read_platform_bios(rdev);
- }
if (r == false || rdev->bios == NULL) {
DRM_ERROR("Unable to locate a BIOS ROM\n");
rdev->bios = NULL;
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index 6651177110f0..3e5f6b71f3ad 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -116,7 +116,7 @@ enum radeon_combios_connector {
CONNECTOR_UNSUPPORTED_LEGACY
};
-const int legacy_connector_convert[] = {
+static const int legacy_connector_convert[] = {
DRM_MODE_CONNECTOR_Unknown,
DRM_MODE_CONNECTOR_DVID,
DRM_MODE_CONNECTOR_VGA,
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 44831197e82e..300c4b3d4669 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -107,7 +107,7 @@ int radeon_get_monitor_bpc(struct drm_connector *connector)
case DRM_MODE_CONNECTOR_DVII:
case DRM_MODE_CONNECTOR_HDMIB:
if (radeon_connector->use_digital) {
- if (drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ if (drm_detect_hdmi_monitor(radeon_connector_edid(connector))) {
if (connector->display_info.bpc)
bpc = connector->display_info.bpc;
}
@@ -115,7 +115,7 @@ int radeon_get_monitor_bpc(struct drm_connector *connector)
break;
case DRM_MODE_CONNECTOR_DVID:
case DRM_MODE_CONNECTOR_HDMIA:
- if (drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ if (drm_detect_hdmi_monitor(radeon_connector_edid(connector))) {
if (connector->display_info.bpc)
bpc = connector->display_info.bpc;
}
@@ -124,7 +124,7 @@ int radeon_get_monitor_bpc(struct drm_connector *connector)
dig_connector = radeon_connector->con_priv;
if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
(dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) ||
- drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ drm_detect_hdmi_monitor(radeon_connector_edid(connector))) {
if (connector->display_info.bpc)
bpc = connector->display_info.bpc;
}
@@ -148,7 +148,7 @@ int radeon_get_monitor_bpc(struct drm_connector *connector)
break;
}
- if (drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ if (drm_detect_hdmi_monitor(radeon_connector_edid(connector))) {
/* hdmi deep color only implemented on DCE4+ */
if ((bpc > 8) && !ASIC_IS_DCE4(rdev)) {
DRM_DEBUG("%s: HDMI deep color %d bpc unsupported. Using 8 bpc.\n",
@@ -197,10 +197,19 @@ int radeon_get_monitor_bpc(struct drm_connector *connector)
connector->name, bpc);
}
}
+ else if (bpc > 8) {
+ /* max_tmds_clock missing, but hdmi spec mandates it for deep color. */
+ DRM_DEBUG("%s: Required max tmds clock for HDMI deep color missing. Using 8 bpc.\n",
+ connector->name);
+ bpc = 8;
+ }
}
- if ((radeon_deep_color == 0) && (bpc > 8))
+ if ((radeon_deep_color == 0) && (bpc > 8)) {
+ DRM_DEBUG("%s: Deep color disabled. Set radeon module param deep_color=1 to enable.\n",
+ connector->name);
bpc = 8;
+ }
DRM_DEBUG("%s: Display bpc=%d, returned bpc=%d\n",
connector->name, connector->display_info.bpc, bpc);
@@ -216,7 +225,6 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c
struct drm_encoder *best_encoder = NULL;
struct drm_encoder *encoder = NULL;
struct drm_connector_helper_funcs *connector_funcs = connector->helper_private;
- struct drm_mode_object *obj;
bool connected;
int i;
@@ -226,14 +234,11 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c
if (connector->encoder_ids[i] == 0)
break;
- obj = drm_mode_object_find(connector->dev,
- connector->encoder_ids[i],
- DRM_MODE_OBJECT_ENCODER);
- if (!obj)
+ encoder = drm_encoder_find(connector->dev,
+ connector->encoder_ids[i]);
+ if (!encoder)
continue;
- encoder = obj_to_encoder(obj);
-
if ((encoder == best_encoder) && (status == connector_status_connected))
connected = true;
else
@@ -249,7 +254,6 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c
static struct drm_encoder *radeon_find_encoder(struct drm_connector *connector, int encoder_type)
{
- struct drm_mode_object *obj;
struct drm_encoder *encoder;
int i;
@@ -257,34 +261,134 @@ static struct drm_encoder *radeon_find_encoder(struct drm_connector *connector,
if (connector->encoder_ids[i] == 0)
break;
- obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER);
- if (!obj)
+ encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
+ if (!encoder)
continue;
- encoder = obj_to_encoder(obj);
if (encoder->encoder_type == encoder_type)
return encoder;
}
return NULL;
}
+struct edid *radeon_connector_edid(struct drm_connector *connector)
+{
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ struct drm_property_blob *edid_blob = connector->edid_blob_ptr;
+
+ if (radeon_connector->edid) {
+ return radeon_connector->edid;
+ } else if (edid_blob) {
+ struct edid *edid = kmemdup(edid_blob->data, edid_blob->length, GFP_KERNEL);
+ if (edid)
+ radeon_connector->edid = edid;
+ }
+ return radeon_connector->edid;
+}
+
+static void radeon_connector_get_edid(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+
+ if (radeon_connector->edid)
+ return;
+
+ /* on hw with routers, select right port */
+ if (radeon_connector->router.ddc_valid)
+ radeon_router_select_ddc_port(radeon_connector);
+
+ if ((radeon_connector_encoder_get_dp_bridge_encoder_id(connector) !=
+ ENCODER_OBJECT_ID_NONE) &&
+ radeon_connector->ddc_bus->has_aux) {
+ radeon_connector->edid = drm_get_edid(connector,
+ &radeon_connector->ddc_bus->aux.ddc);
+ } else if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
+ (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
+ struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
+
+ if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT ||
+ dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) &&
+ radeon_connector->ddc_bus->has_aux)
+ radeon_connector->edid = drm_get_edid(&radeon_connector->base,
+ &radeon_connector->ddc_bus->aux.ddc);
+ else if (radeon_connector->ddc_bus)
+ radeon_connector->edid = drm_get_edid(&radeon_connector->base,
+ &radeon_connector->ddc_bus->adapter);
+ } else if (radeon_connector->ddc_bus) {
+ radeon_connector->edid = drm_get_edid(&radeon_connector->base,
+ &radeon_connector->ddc_bus->adapter);
+ }
+
+ if (!radeon_connector->edid) {
+ if (rdev->is_atom_bios) {
+ /* some laptops provide a hardcoded edid in rom for LCDs */
+ if (((connector->connector_type == DRM_MODE_CONNECTOR_LVDS) ||
+ (connector->connector_type == DRM_MODE_CONNECTOR_eDP)))
+ radeon_connector->edid = radeon_bios_get_hardcoded_edid(rdev);
+ } else {
+ /* some servers provide a hardcoded edid in rom for KVMs */
+ radeon_connector->edid = radeon_bios_get_hardcoded_edid(rdev);
+ }
+ }
+}
+
+static void radeon_connector_free_edid(struct drm_connector *connector)
+{
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+
+ if (radeon_connector->edid) {
+ kfree(radeon_connector->edid);
+ radeon_connector->edid = NULL;
+ }
+}
+
+static int radeon_ddc_get_modes(struct drm_connector *connector)
+{
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ int ret;
+
+ if (radeon_connector->edid) {
+ drm_mode_connector_update_edid_property(connector, radeon_connector->edid);
+ ret = drm_add_edid_modes(connector, radeon_connector->edid);
+ drm_edid_to_eld(connector, radeon_connector->edid);
+ return ret;
+ }
+ drm_mode_connector_update_edid_property(connector, NULL);
+ return 0;
+}
+
static struct drm_encoder *radeon_best_single_encoder(struct drm_connector *connector)
{
int enc_id = connector->encoder_ids[0];
- struct drm_mode_object *obj;
- struct drm_encoder *encoder;
-
/* pick the encoder ids */
- if (enc_id) {
- obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
- if (!obj)
- return NULL;
- encoder = obj_to_encoder(obj);
- return encoder;
- }
+ if (enc_id)
+ return drm_encoder_find(connector->dev, enc_id);
return NULL;
}
+static void radeon_get_native_mode(struct drm_connector *connector)
+{
+ struct drm_encoder *encoder = radeon_best_single_encoder(connector);
+ struct radeon_encoder *radeon_encoder;
+
+ if (encoder == NULL)
+ return;
+
+ radeon_encoder = to_radeon_encoder(encoder);
+
+ if (!list_empty(&connector->probed_modes)) {
+ struct drm_display_mode *preferred_mode =
+ list_first_entry(&connector->probed_modes,
+ struct drm_display_mode, head);
+
+ radeon_encoder->native_mode = *preferred_mode;
+ } else {
+ radeon_encoder->native_mode.clock = 0;
+ }
+}
+
/*
* radeon_connector_analog_encoder_conflict_solve
* - search for other connectors sharing this encoder
@@ -585,6 +689,35 @@ static int radeon_connector_set_property(struct drm_connector *connector, struct
radeon_property_change_mode(&radeon_encoder->base);
}
+ if (property == dev->mode_config.scaling_mode_property) {
+ enum radeon_rmx_type rmx_type;
+
+ if (connector->encoder)
+ radeon_encoder = to_radeon_encoder(connector->encoder);
+ else {
+ struct drm_connector_helper_funcs *connector_funcs = connector->helper_private;
+ radeon_encoder = to_radeon_encoder(connector_funcs->best_encoder(connector));
+ }
+
+ switch (val) {
+ default:
+ case DRM_MODE_SCALE_NONE: rmx_type = RMX_OFF; break;
+ case DRM_MODE_SCALE_CENTER: rmx_type = RMX_CENTER; break;
+ case DRM_MODE_SCALE_ASPECT: rmx_type = RMX_ASPECT; break;
+ case DRM_MODE_SCALE_FULLSCREEN: rmx_type = RMX_FULL; break;
+ }
+ if (radeon_encoder->rmx_type == rmx_type)
+ return 0;
+
+ if ((rmx_type != DRM_MODE_SCALE_NONE) &&
+ (radeon_encoder->native_mode.clock == 0))
+ return 0;
+
+ radeon_encoder->rmx_type = rmx_type;
+
+ radeon_property_change_mode(&radeon_encoder->base);
+ }
+
return 0;
}
@@ -625,22 +758,20 @@ static void radeon_fixup_lvds_native_mode(struct drm_encoder *encoder,
static int radeon_lvds_get_modes(struct drm_connector *connector)
{
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct drm_encoder *encoder;
int ret = 0;
struct drm_display_mode *mode;
- if (radeon_connector->ddc_bus) {
- ret = radeon_ddc_get_modes(radeon_connector);
- if (ret > 0) {
- encoder = radeon_best_single_encoder(connector);
- if (encoder) {
- radeon_fixup_lvds_native_mode(encoder, connector);
- /* add scaled modes */
- radeon_add_common_modes(encoder, connector);
- }
- return ret;
+ radeon_connector_get_edid(connector);
+ ret = radeon_ddc_get_modes(connector);
+ if (ret > 0) {
+ encoder = radeon_best_single_encoder(connector);
+ if (encoder) {
+ radeon_fixup_lvds_native_mode(encoder, connector);
+ /* add scaled modes */
+ radeon_add_common_modes(encoder, connector);
}
+ return ret;
}
encoder = radeon_best_single_encoder(connector);
@@ -715,16 +846,9 @@ radeon_lvds_detect(struct drm_connector *connector, bool force)
}
/* check for edid as well */
+ radeon_connector_get_edid(connector);
if (radeon_connector->edid)
ret = connector_status_connected;
- else {
- if (radeon_connector->ddc_bus) {
- radeon_connector->edid = drm_get_edid(&radeon_connector->base,
- &radeon_connector->ddc_bus->adapter);
- if (radeon_connector->edid)
- ret = connector_status_connected;
- }
- }
/* check acpi lid status ??? */
radeon_connector_update_scratch_regs(connector, ret);
@@ -737,10 +861,9 @@ static void radeon_connector_destroy(struct drm_connector *connector)
{
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- if (radeon_connector->edid)
- kfree(radeon_connector->edid);
+ radeon_connector_free_edid(connector);
kfree(radeon_connector->con_priv);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -797,10 +920,12 @@ static const struct drm_connector_funcs radeon_lvds_connector_funcs = {
static int radeon_vga_get_modes(struct drm_connector *connector)
{
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
int ret;
- ret = radeon_ddc_get_modes(radeon_connector);
+ radeon_connector_get_edid(connector);
+ ret = radeon_ddc_get_modes(connector);
+
+ radeon_get_native_mode(connector);
return ret;
}
@@ -843,28 +968,26 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
dret = radeon_ddc_probe(radeon_connector, false);
if (dret) {
radeon_connector->detected_by_load = false;
- if (radeon_connector->edid) {
- kfree(radeon_connector->edid);
- radeon_connector->edid = NULL;
- }
- radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
+ radeon_connector_free_edid(connector);
+ radeon_connector_get_edid(connector);
if (!radeon_connector->edid) {
DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
connector->name);
ret = connector_status_connected;
} else {
- radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL);
+ radeon_connector->use_digital =
+ !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL);
/* some oems have boards with separate digital and analog connectors
* with a shared ddc line (often vga + hdmi)
*/
if (radeon_connector->use_digital && radeon_connector->shared_ddc) {
- kfree(radeon_connector->edid);
- radeon_connector->edid = NULL;
+ radeon_connector_free_edid(connector);
ret = connector_status_disconnected;
- } else
+ } else {
ret = connector_status_connected;
+ }
}
} else {
@@ -999,15 +1122,6 @@ static const struct drm_connector_funcs radeon_tv_connector_funcs = {
.set_property = radeon_connector_set_property,
};
-static int radeon_dvi_get_modes(struct drm_connector *connector)
-{
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- int ret;
-
- ret = radeon_ddc_get_modes(radeon_connector);
- return ret;
-}
-
static bool radeon_check_hpd_status_unchanged(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
@@ -1048,7 +1162,6 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct drm_encoder *encoder = NULL;
struct drm_encoder_helper_funcs *encoder_funcs;
- struct drm_mode_object *obj;
int i, r;
enum drm_connector_status ret = connector_status_disconnected;
bool dret = false, broken_edid = false;
@@ -1066,18 +1179,16 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
dret = radeon_ddc_probe(radeon_connector, false);
if (dret) {
radeon_connector->detected_by_load = false;
- if (radeon_connector->edid) {
- kfree(radeon_connector->edid);
- radeon_connector->edid = NULL;
- }
- radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
+ radeon_connector_free_edid(connector);
+ radeon_connector_get_edid(connector);
if (!radeon_connector->edid) {
DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
connector->name);
/* rs690 seems to have a problem with connectors not existing and always
* return a block of 0's. If we see this just stop polling on this output */
- if ((rdev->family == CHIP_RS690 || rdev->family == CHIP_RS740) && radeon_connector->base.null_edid_counter) {
+ if ((rdev->family == CHIP_RS690 || rdev->family == CHIP_RS740) &&
+ radeon_connector->base.null_edid_counter) {
ret = connector_status_disconnected;
DRM_ERROR("%s: detected RS690 floating bus bug, stopping ddc detect\n",
connector->name);
@@ -1087,18 +1198,18 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
broken_edid = true; /* defer use_digital to later */
}
} else {
- radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL);
+ radeon_connector->use_digital =
+ !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL);
/* some oems have boards with separate digital and analog connectors
* with a shared ddc line (often vga + hdmi)
*/
if ((!radeon_connector->use_digital) && radeon_connector->shared_ddc) {
- kfree(radeon_connector->edid);
- radeon_connector->edid = NULL;
+ radeon_connector_free_edid(connector);
ret = connector_status_disconnected;
- } else
+ } else {
ret = connector_status_connected;
-
+ }
/* This gets complicated. We have boards with VGA + HDMI with a
* shared DDC line and we have boards with DVI-D + HDMI with a shared
* DDC line. The latter is more complex because with DVI<->HDMI adapters
@@ -1118,8 +1229,7 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
if (list_connector->connector_type != DRM_MODE_CONNECTOR_VGA) {
/* hpd is our only option in this case */
if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) {
- kfree(radeon_connector->edid);
- radeon_connector->edid = NULL;
+ radeon_connector_free_edid(connector);
ret = connector_status_disconnected;
}
}
@@ -1153,14 +1263,11 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
if (connector->encoder_ids[i] == 0)
break;
- obj = drm_mode_object_find(connector->dev,
- connector->encoder_ids[i],
- DRM_MODE_OBJECT_ENCODER);
- if (!obj)
+ encoder = drm_encoder_find(connector->dev,
+ connector->encoder_ids[i]);
+ if (!encoder)
continue;
- encoder = obj_to_encoder(obj);
-
if (encoder->encoder_type != DRM_MODE_ENCODER_DAC &&
encoder->encoder_type != DRM_MODE_ENCODER_TVDAC)
continue;
@@ -1225,19 +1332,16 @@ static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector)
{
int enc_id = connector->encoder_ids[0];
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- struct drm_mode_object *obj;
struct drm_encoder *encoder;
int i;
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
if (connector->encoder_ids[i] == 0)
break;
- obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER);
- if (!obj)
+ encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
+ if (!encoder)
continue;
- encoder = obj_to_encoder(obj);
-
if (radeon_connector->use_digital == true) {
if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
return encoder;
@@ -1252,13 +1356,8 @@ static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector)
/* then check use digitial */
/* pick the first one */
- if (enc_id) {
- obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
- if (!obj)
- return NULL;
- encoder = obj_to_encoder(obj);
- return encoder;
- }
+ if (enc_id)
+ return drm_encoder_find(connector->dev, enc_id);
return NULL;
}
@@ -1291,7 +1390,7 @@ static int radeon_dvi_mode_valid(struct drm_connector *connector,
(radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D) ||
(radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_B))
return MODE_OK;
- else if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ else if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector_edid(connector))) {
/* HDMI 1.3+ supports max clock of 340 Mhz */
if (mode->clock > 340000)
return MODE_CLOCK_HIGH;
@@ -1310,7 +1409,7 @@ static int radeon_dvi_mode_valid(struct drm_connector *connector,
}
static const struct drm_connector_helper_funcs radeon_dvi_connector_helper_funcs = {
- .get_modes = radeon_dvi_get_modes,
+ .get_modes = radeon_vga_get_modes,
.mode_valid = radeon_dvi_mode_valid,
.best_encoder = radeon_dvi_encoder,
};
@@ -1339,7 +1438,8 @@ static int radeon_dp_get_modes(struct drm_connector *connector)
if (!radeon_dig_connector->edp_on)
atombios_set_edp_panel_power(connector,
ATOM_TRANSMITTER_ACTION_POWER_ON);
- ret = radeon_ddc_get_modes(radeon_connector);
+ radeon_connector_get_edid(connector);
+ ret = radeon_ddc_get_modes(connector);
if (!radeon_dig_connector->edp_on)
atombios_set_edp_panel_power(connector,
ATOM_TRANSMITTER_ACTION_POWER_OFF);
@@ -1350,7 +1450,8 @@ static int radeon_dp_get_modes(struct drm_connector *connector)
if (encoder)
radeon_atom_ext_encoder_setup_ddc(encoder);
}
- ret = radeon_ddc_get_modes(radeon_connector);
+ radeon_connector_get_edid(connector);
+ ret = radeon_ddc_get_modes(connector);
}
if (ret > 0) {
@@ -1383,7 +1484,10 @@ static int radeon_dp_get_modes(struct drm_connector *connector)
if (encoder)
radeon_atom_ext_encoder_setup_ddc(encoder);
}
- ret = radeon_ddc_get_modes(radeon_connector);
+ radeon_connector_get_edid(connector);
+ ret = radeon_ddc_get_modes(connector);
+
+ radeon_get_native_mode(connector);
}
return ret;
@@ -1391,7 +1495,6 @@ static int radeon_dp_get_modes(struct drm_connector *connector)
u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector)
{
- struct drm_mode_object *obj;
struct drm_encoder *encoder;
struct radeon_encoder *radeon_encoder;
int i;
@@ -1400,11 +1503,10 @@ u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn
if (connector->encoder_ids[i] == 0)
break;
- obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER);
- if (!obj)
+ encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
+ if (!encoder)
continue;
- encoder = obj_to_encoder(obj);
radeon_encoder = to_radeon_encoder(encoder);
switch (radeon_encoder->encoder_id) {
@@ -1419,9 +1521,8 @@ u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn
return ENCODER_OBJECT_ID_NONE;
}
-bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector)
+static bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector)
{
- struct drm_mode_object *obj;
struct drm_encoder *encoder;
struct radeon_encoder *radeon_encoder;
int i;
@@ -1431,11 +1532,10 @@ bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector)
if (connector->encoder_ids[i] == 0)
break;
- obj = drm_mode_object_find(connector->dev, connector->encoder_ids[i], DRM_MODE_OBJECT_ENCODER);
- if (!obj)
+ encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
+ if (!encoder)
continue;
- encoder = obj_to_encoder(obj);
radeon_encoder = to_radeon_encoder(encoder);
if (radeon_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2)
found = true;
@@ -1478,10 +1578,7 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
goto out;
}
- if (radeon_connector->edid) {
- kfree(radeon_connector->edid);
- radeon_connector->edid = NULL;
- }
+ radeon_connector_free_edid(connector);
if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) ||
(connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) {
@@ -1587,7 +1684,7 @@ static int radeon_dp_mode_valid(struct drm_connector *connector,
(radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
return radeon_dp_mode_valid_helper(connector, mode);
} else {
- if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector_edid(connector))) {
/* HDMI 1.3+ supports max clock of 340 Mhz */
if (mode->clock > 340000)
return MODE_CLOCK_HIGH;
@@ -1747,6 +1844,9 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.load_detect_property,
1);
+ drm_object_attach_property(&radeon_connector->base.base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_NONE);
break;
case DRM_MODE_CONNECTOR_DVII:
case DRM_MODE_CONNECTOR_DVID:
@@ -1768,6 +1868,10 @@ radeon_add_atom_connector(struct drm_device *dev,
0);
drm_object_attach_property(&radeon_connector->base.base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_NONE);
+
+ drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.dither_property,
RADEON_FMT_DITHER_DISABLE);
@@ -1817,6 +1921,10 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.load_detect_property,
1);
+ if (ASIC_IS_AVIVO(rdev))
+ drm_object_attach_property(&radeon_connector->base.base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_NONE);
/* no HPD on analog connectors */
radeon_connector->hpd.hpd = RADEON_HPD_NONE;
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
@@ -1835,6 +1943,10 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.load_detect_property,
1);
+ if (ASIC_IS_AVIVO(rdev))
+ drm_object_attach_property(&radeon_connector->base.base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_NONE);
/* no HPD on analog connectors */
radeon_connector->hpd.hpd = RADEON_HPD_NONE;
connector->interlace_allowed = true;
@@ -1868,17 +1980,18 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.underscan_vborder_property,
0);
+ drm_object_attach_property(&radeon_connector->base.base,
+ rdev->mode_info.dither_property,
+ RADEON_FMT_DITHER_DISABLE);
+ drm_object_attach_property(&radeon_connector->base.base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_NONE);
}
if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.audio_property,
RADEON_AUDIO_AUTO);
}
- if (ASIC_IS_AVIVO(rdev)) {
- drm_object_attach_property(&radeon_connector->base.base,
- rdev->mode_info.dither_property,
- RADEON_FMT_DITHER_DISABLE);
- }
if (connector_type == DRM_MODE_CONNECTOR_DVII) {
radeon_connector->dac_load_detect = true;
drm_object_attach_property(&radeon_connector->base.base,
@@ -1918,17 +2031,18 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.underscan_vborder_property,
0);
+ drm_object_attach_property(&radeon_connector->base.base,
+ rdev->mode_info.dither_property,
+ RADEON_FMT_DITHER_DISABLE);
+ drm_object_attach_property(&radeon_connector->base.base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_NONE);
}
if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.audio_property,
RADEON_AUDIO_AUTO);
}
- if (ASIC_IS_AVIVO(rdev)) {
- drm_object_attach_property(&radeon_connector->base.base,
- rdev->mode_info.dither_property,
- RADEON_FMT_DITHER_DISABLE);
- }
subpixel_order = SubPixelHorizontalRGB;
connector->interlace_allowed = true;
if (connector_type == DRM_MODE_CONNECTOR_HDMIB)
@@ -1965,18 +2079,18 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.underscan_vborder_property,
0);
+ drm_object_attach_property(&radeon_connector->base.base,
+ rdev->mode_info.dither_property,
+ RADEON_FMT_DITHER_DISABLE);
+ drm_object_attach_property(&radeon_connector->base.base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_NONE);
}
if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) {
drm_object_attach_property(&radeon_connector->base.base,
rdev->mode_info.audio_property,
RADEON_AUDIO_AUTO);
}
- if (ASIC_IS_AVIVO(rdev)) {
- drm_object_attach_property(&radeon_connector->base.base,
- rdev->mode_info.dither_property,
- RADEON_FMT_DITHER_DISABLE);
-
- }
connector->interlace_allowed = true;
/* in theory with a DP to VGA converter... */
connector->doublescan_allowed = false;
@@ -2050,7 +2164,7 @@ radeon_add_atom_connector(struct drm_device *dev,
connector->polled = DRM_CONNECTOR_POLL_HPD;
connector->display_info.subpixel_order = subpixel_order;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
if (has_aux)
radeon_dp_aux_init(radeon_connector);
@@ -2211,5 +2325,5 @@ radeon_add_legacy_connector(struct drm_device *dev,
} else
connector->polled = DRM_CONNECTOR_POLL_HPD;
connector->display_info.subpixel_order = subpixel_order;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
}
diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
index bb0d5c3a8311..ea134a7d51a5 100644
--- a/drivers/gpu/drm/radeon/radeon_cp.c
+++ b/drivers/gpu/drm/radeon/radeon_cp.c
@@ -1298,27 +1298,27 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
dev_priv->buffers_offset = init->buffers_offset;
dev_priv->gart_textures_offset = init->gart_textures_offset;
- master_priv->sarea = drm_getsarea(dev);
+ master_priv->sarea = drm_legacy_getsarea(dev);
if (!master_priv->sarea) {
DRM_ERROR("could not find sarea!\n");
radeon_do_cleanup_cp(dev);
return -EINVAL;
}
- dev_priv->cp_ring = drm_core_findmap(dev, init->ring_offset);
+ dev_priv->cp_ring = drm_legacy_findmap(dev, init->ring_offset);
if (!dev_priv->cp_ring) {
DRM_ERROR("could not find cp ring region!\n");
radeon_do_cleanup_cp(dev);
return -EINVAL;
}
- dev_priv->ring_rptr = drm_core_findmap(dev, init->ring_rptr_offset);
+ dev_priv->ring_rptr = drm_legacy_findmap(dev, init->ring_rptr_offset);
if (!dev_priv->ring_rptr) {
DRM_ERROR("could not find ring read pointer!\n");
radeon_do_cleanup_cp(dev);
return -EINVAL;
}
dev->agp_buffer_token = init->buffers_offset;
- dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset);
+ dev->agp_buffer_map = drm_legacy_findmap(dev, init->buffers_offset);
if (!dev->agp_buffer_map) {
DRM_ERROR("could not find dma buffer region!\n");
radeon_do_cleanup_cp(dev);
@@ -1327,7 +1327,7 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
if (init->gart_textures_offset) {
dev_priv->gart_textures =
- drm_core_findmap(dev, init->gart_textures_offset);
+ drm_legacy_findmap(dev, init->gart_textures_offset);
if (!dev_priv->gart_textures) {
DRM_ERROR("could not find GART texture region!\n");
radeon_do_cleanup_cp(dev);
@@ -1337,9 +1337,9 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
#if __OS_HAS_AGP
if (dev_priv->flags & RADEON_IS_AGP) {
- drm_core_ioremap_wc(dev_priv->cp_ring, dev);
- drm_core_ioremap_wc(dev_priv->ring_rptr, dev);
- drm_core_ioremap_wc(dev->agp_buffer_map, dev);
+ drm_legacy_ioremap_wc(dev_priv->cp_ring, dev);
+ drm_legacy_ioremap_wc(dev_priv->ring_rptr, dev);
+ drm_legacy_ioremap_wc(dev->agp_buffer_map, dev);
if (!dev_priv->cp_ring->handle ||
!dev_priv->ring_rptr->handle ||
!dev->agp_buffer_map->handle) {
@@ -1475,7 +1475,7 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
dev_priv->gart_info.mapping.size =
dev_priv->gart_info.table_size;
- drm_core_ioremap_wc(&dev_priv->gart_info.mapping, dev);
+ drm_legacy_ioremap_wc(&dev_priv->gart_info.mapping, dev);
dev_priv->gart_info.addr =
dev_priv->gart_info.mapping.handle;
@@ -1569,15 +1569,15 @@ static int radeon_do_cleanup_cp(struct drm_device * dev)
#if __OS_HAS_AGP
if (dev_priv->flags & RADEON_IS_AGP) {
if (dev_priv->cp_ring != NULL) {
- drm_core_ioremapfree(dev_priv->cp_ring, dev);
+ drm_legacy_ioremapfree(dev_priv->cp_ring, dev);
dev_priv->cp_ring = NULL;
}
if (dev_priv->ring_rptr != NULL) {
- drm_core_ioremapfree(dev_priv->ring_rptr, dev);
+ drm_legacy_ioremapfree(dev_priv->ring_rptr, dev);
dev_priv->ring_rptr = NULL;
}
if (dev->agp_buffer_map != NULL) {
- drm_core_ioremapfree(dev->agp_buffer_map, dev);
+ drm_legacy_ioremapfree(dev->agp_buffer_map, dev);
dev->agp_buffer_map = NULL;
}
} else
@@ -1597,7 +1597,7 @@ static int radeon_do_cleanup_cp(struct drm_device * dev)
if (dev_priv->gart_info.gart_table_location == DRM_ATI_GART_FB)
{
- drm_core_ioremapfree(&dev_priv->gart_info.mapping, dev);
+ drm_legacy_ioremapfree(&dev_priv->gart_info.mapping, dev);
dev_priv->gart_info.addr = NULL;
}
}
@@ -2106,9 +2106,9 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags)
else
dev_priv->flags |= RADEON_IS_PCI;
- ret = drm_addmap(dev, pci_resource_start(dev->pdev, 2),
- pci_resource_len(dev->pdev, 2), _DRM_REGISTERS,
- _DRM_READ_ONLY | _DRM_DRIVER, &dev_priv->mmio);
+ ret = drm_legacy_addmap(dev, pci_resource_start(dev->pdev, 2),
+ pci_resource_len(dev->pdev, 2), _DRM_REGISTERS,
+ _DRM_READ_ONLY | _DRM_DRIVER, &dev_priv->mmio);
if (ret != 0)
return ret;
@@ -2135,8 +2135,8 @@ int radeon_master_create(struct drm_device *dev, struct drm_master *master)
/* prebuild the SAREA */
sareapage = max_t(unsigned long, SAREA_MAX, PAGE_SIZE);
- ret = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK,
- &master_priv->sarea);
+ ret = drm_legacy_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK,
+ &master_priv->sarea);
if (ret) {
DRM_ERROR("SAREA setup failed\n");
kfree(master_priv);
@@ -2162,7 +2162,7 @@ void radeon_master_destroy(struct drm_device *dev, struct drm_master *master)
master_priv->sarea_priv = NULL;
if (master_priv->sarea)
- drm_rmmap_locked(dev, master_priv->sarea);
+ drm_legacy_rmmap_locked(dev, master_priv->sarea);
kfree(master_priv);
@@ -2181,9 +2181,9 @@ int radeon_driver_firstopen(struct drm_device *dev)
dev_priv->gart_info.table_size = RADEON_PCIGART_TABLE_SIZE;
dev_priv->fb_aper_offset = pci_resource_start(dev->pdev, 0);
- ret = drm_addmap(dev, dev_priv->fb_aper_offset,
- pci_resource_len(dev->pdev, 0), _DRM_FRAME_BUFFER,
- _DRM_WRITE_COMBINING, &map);
+ ret = drm_legacy_addmap(dev, dev_priv->fb_aper_offset,
+ pci_resource_len(dev->pdev, 0),
+ _DRM_FRAME_BUFFER, _DRM_WRITE_COMBINING, &map);
if (ret != 0)
return ret;
@@ -2196,7 +2196,7 @@ int radeon_driver_unload(struct drm_device *dev)
DRM_DEBUG("\n");
- drm_rmmap(dev, dev_priv->mmio);
+ drm_legacy_rmmap(dev, dev_priv->mmio);
kfree(dev_priv);
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index ae763f60c8a0..a3e7aed7e680 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -78,7 +78,8 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
struct radeon_cs_chunk *chunk;
struct radeon_cs_buckets buckets;
unsigned i, j;
- bool duplicate;
+ bool duplicate, need_mmap_lock = false;
+ int r;
if (p->chunk_relocs_idx == -1) {
return 0;
@@ -132,13 +133,17 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
* the buffers used for read only, which doubles the range
* to 0 to 31. 32 is reserved for the kernel driver.
*/
- priority = (r->flags & 0xf) * 2 + !!r->write_domain;
+ priority = (r->flags & RADEON_RELOC_PRIO_MASK) * 2
+ + !!r->write_domain;
/* the first reloc of an UVD job is the msg and that must be in
- VRAM, also but everything into VRAM on AGP cards to avoid
- image corruptions */
+ VRAM, also but everything into VRAM on AGP cards and older
+ IGP chips to avoid image corruptions */
if (p->ring == R600_RING_TYPE_UVD_INDEX &&
- (i == 0 || drm_pci_device_is_agp(p->rdev->ddev))) {
+ (i == 0 || drm_pci_device_is_agp(p->rdev->ddev) ||
+ p->rdev->family == CHIP_RS780 ||
+ p->rdev->family == CHIP_RS880)) {
+
/* TODO: is this still needed for NI+ ? */
p->relocs[i].prefered_domains =
RADEON_GEM_DOMAIN_VRAM;
@@ -164,7 +169,21 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
p->relocs[i].allowed_domains = domain;
}
+ if (radeon_ttm_tt_has_userptr(p->relocs[i].robj->tbo.ttm)) {
+ uint32_t domain = p->relocs[i].prefered_domains;
+ if (!(domain & RADEON_GEM_DOMAIN_GTT)) {
+ DRM_ERROR("Only RADEON_GEM_DOMAIN_GTT is "
+ "allowed for userptr BOs\n");
+ return -EINVAL;
+ }
+ need_mmap_lock = true;
+ domain = RADEON_GEM_DOMAIN_GTT;
+ p->relocs[i].prefered_domains = domain;
+ p->relocs[i].allowed_domains = domain;
+ }
+
p->relocs[i].tv.bo = &p->relocs[i].robj->tbo;
+ p->relocs[i].tv.shared = !r->write_domain;
p->relocs[i].handle = r->handle;
radeon_cs_buckets_add(&buckets, &p->relocs[i].tv.head,
@@ -176,8 +195,15 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
if (p->cs_flags & RADEON_CS_USE_VM)
p->vm_bos = radeon_vm_get_bos(p->rdev, p->ib.vm,
&p->validated);
+ if (need_mmap_lock)
+ down_read(&current->mm->mmap_sem);
- return radeon_bo_list_validate(p->rdev, &p->ticket, &p->validated, p->ring);
+ r = radeon_bo_list_validate(p->rdev, &p->ticket, &p->validated, p->ring);
+
+ if (need_mmap_lock)
+ up_read(&current->mm->mmap_sem);
+
+ return r;
}
static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority)
@@ -223,17 +249,24 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority
return 0;
}
-static void radeon_cs_sync_rings(struct radeon_cs_parser *p)
+static int radeon_cs_sync_rings(struct radeon_cs_parser *p)
{
- int i;
+ int i, r = 0;
for (i = 0; i < p->nrelocs; i++) {
+ struct reservation_object *resv;
+
if (!p->relocs[i].robj)
continue;
- radeon_semaphore_sync_to(p->ib.semaphore,
- p->relocs[i].robj->tbo.sync_obj);
+ resv = p->relocs[i].robj->tbo.resv;
+ r = radeon_semaphore_sync_resv(p->rdev, p->ib.semaphore, resv,
+ p->relocs[i].tv.shared);
+
+ if (r)
+ break;
}
+ return r;
}
/* XXX: note that this is called from the legacy UMS CS ioctl as well */
@@ -402,7 +435,7 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bo
ttm_eu_fence_buffer_objects(&parser->ticket,
&parser->validated,
- parser->ib.fence);
+ &parser->ib.fence->base);
} else if (backoff) {
ttm_eu_backoff_reservation(&parser->ticket,
&parser->validated);
@@ -417,7 +450,7 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error, bo
kfree(parser->track);
kfree(parser->relocs);
kfree(parser->relocs_ptr);
- kfree(parser->vm_bos);
+ drm_free_large(parser->vm_bos);
for (i = 0; i < parser->nchunks; i++)
drm_free_large(parser->chunks[i].kdata);
kfree(parser->chunks);
@@ -443,14 +476,20 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev,
return r;
}
+ r = radeon_cs_sync_rings(parser);
+ if (r) {
+ if (r != -ERESTARTSYS)
+ DRM_ERROR("Failed to sync rings: %i\n", r);
+ return r;
+ }
+
if (parser->ring == R600_RING_TYPE_UVD_INDEX)
radeon_uvd_note_usage(rdev);
else if ((parser->ring == TN_RING_TYPE_VCE1_INDEX) ||
(parser->ring == TN_RING_TYPE_VCE2_INDEX))
radeon_vce_note_usage(rdev);
- radeon_cs_sync_rings(parser);
- r = radeon_ib_schedule(rdev, &parser->ib, NULL);
+ r = radeon_ib_schedule(rdev, &parser->ib, NULL, true);
if (r) {
DRM_ERROR("Failed to schedule IB !\n");
}
@@ -500,7 +539,8 @@ static int radeon_bo_vm_update_pte(struct radeon_cs_parser *p,
if (r)
return r;
}
- return 0;
+
+ return radeon_vm_clear_invalids(rdev, vm);
}
static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
@@ -535,14 +575,20 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
if (r) {
goto out;
}
- radeon_cs_sync_rings(parser);
- radeon_semaphore_sync_to(parser->ib.semaphore, vm->fence);
+
+ r = radeon_cs_sync_rings(parser);
+ if (r) {
+ if (r != -ERESTARTSYS)
+ DRM_ERROR("Failed to sync rings: %i\n", r);
+ goto out;
+ }
+ radeon_semaphore_sync_fence(parser->ib.semaphore, vm->fence);
if ((rdev->family >= CHIP_TAHITI) &&
(parser->chunk_const_ib_idx != -1)) {
- r = radeon_ib_schedule(rdev, &parser->ib, &parser->const_ib);
+ r = radeon_ib_schedule(rdev, &parser->ib, &parser->const_ib, true);
} else {
- r = radeon_ib_schedule(rdev, &parser->ib, NULL);
+ r = radeon_ib_schedule(rdev, &parser->ib, NULL, true);
}
out:
@@ -627,6 +673,13 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
up_read(&rdev->exclusive_lock);
return -EBUSY;
}
+ if (rdev->in_reset) {
+ up_read(&rdev->exclusive_lock);
+ r = radeon_gpu_reset(rdev);
+ if (!r)
+ r = -EAGAIN;
+ return r;
+ }
/* initialize parser */
memset(&parser, 0, sizeof(struct radeon_cs_parser));
parser.filp = filp;
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 697add2cd4e3..995a8b1770dd 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -103,6 +103,35 @@ static const char radeon_family_name[][16] = {
"LAST",
};
+#define RADEON_PX_QUIRK_DISABLE_PX (1 << 0)
+#define RADEON_PX_QUIRK_LONG_WAKEUP (1 << 1)
+
+struct radeon_px_quirk {
+ u32 chip_vendor;
+ u32 chip_device;
+ u32 subsys_vendor;
+ u32 subsys_device;
+ u32 px_quirk_flags;
+};
+
+static struct radeon_px_quirk radeon_px_quirk_list[] = {
+ /* Acer aspire 5560g (CPU: AMD A4-3305M; GPU: AMD Radeon HD 6480g + 7470m)
+ * https://bugzilla.kernel.org/show_bug.cgi?id=74551
+ */
+ { PCI_VENDOR_ID_ATI, 0x6760, 0x1025, 0x0672, RADEON_PX_QUIRK_DISABLE_PX },
+ /* Asus K73TA laptop with AMD A6-3400M APU and Radeon 6550 GPU
+ * https://bugzilla.kernel.org/show_bug.cgi?id=51381
+ */
+ { PCI_VENDOR_ID_ATI, 0x6741, 0x1043, 0x108c, RADEON_PX_QUIRK_DISABLE_PX },
+ /* Asus K53TK laptop with AMD A6-3420M APU and Radeon 7670m GPU
+ * https://bugzilla.kernel.org/show_bug.cgi?id=51381
+ */
+ { PCI_VENDOR_ID_ATI, 0x6840, 0x1043, 0x2122, RADEON_PX_QUIRK_DISABLE_PX },
+ /* macbook pro 8.2 */
+ { PCI_VENDOR_ID_ATI, 0x6741, PCI_VENDOR_ID_APPLE, 0x00e2, RADEON_PX_QUIRK_LONG_WAKEUP },
+ { 0, 0, 0, 0, 0 },
+};
+
bool radeon_is_px(struct drm_device *dev)
{
struct radeon_device *rdev = dev->dev_private;
@@ -112,6 +141,26 @@ bool radeon_is_px(struct drm_device *dev)
return false;
}
+static void radeon_device_handle_px_quirks(struct radeon_device *rdev)
+{
+ struct radeon_px_quirk *p = radeon_px_quirk_list;
+
+ /* Apply PX quirks */
+ while (p && p->chip_device != 0) {
+ if (rdev->pdev->vendor == p->chip_vendor &&
+ rdev->pdev->device == p->chip_device &&
+ rdev->pdev->subsystem_vendor == p->subsys_vendor &&
+ rdev->pdev->subsystem_device == p->subsys_device) {
+ rdev->px_quirk_flags = p->px_quirk_flags;
+ break;
+ }
+ ++p;
+ }
+
+ if (rdev->px_quirk_flags & RADEON_PX_QUIRK_DISABLE_PX)
+ rdev->flags &= ~RADEON_IS_PX;
+}
+
/**
* radeon_program_register_sequence - program an array of registers.
*
@@ -385,7 +434,8 @@ int radeon_wb_init(struct radeon_device *rdev)
if (rdev->wb.wb_obj == NULL) {
r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_GTT, NULL, &rdev->wb.wb_obj);
+ RADEON_GEM_DOMAIN_GTT, 0, NULL, NULL,
+ &rdev->wb.wb_obj);
if (r) {
dev_warn(rdev->dev, "(%d) create WB bo failed\n", r);
return r;
@@ -902,6 +952,7 @@ int radeon_atombios_init(struct radeon_device *rdev)
}
mutex_init(&rdev->mode_info.atom_context->mutex);
+ mutex_init(&rdev->mode_info.atom_context->scratch_mutex);
radeon_atom_initialize_bios_scratch_regs(rdev->ddev);
atom_allocate_fb_scratch(rdev->mode_info.atom_context);
return 0;
@@ -1077,7 +1128,19 @@ static void radeon_check_arguments(struct radeon_device *rdev)
/* defines number of bits in page table versus page directory,
* a page is 4KB so we have 12 bits offset, minimum 9 bits in the
* page table and the remaining bits are in the page directory */
- if (radeon_vm_block_size < 9) {
+ if (radeon_vm_block_size == -1) {
+
+ /* Total bits covered by PD + PTs */
+ unsigned bits = ilog2(radeon_vm_size) + 18;
+
+ /* Make sure the PD is 4K in size up to 8GB address space.
+ Above that split equal between PD and PTs */
+ if (radeon_vm_size <= 8)
+ radeon_vm_block_size = bits - 9;
+ else
+ radeon_vm_block_size = (bits + 3) / 2;
+
+ } else if (radeon_vm_block_size < 9) {
dev_warn(rdev->dev, "VM page table size (%d) too small\n",
radeon_vm_block_size);
radeon_vm_block_size = 9;
@@ -1092,25 +1155,6 @@ static void radeon_check_arguments(struct radeon_device *rdev)
}
/**
- * radeon_switcheroo_quirk_long_wakeup - return true if longer d3 delay is
- * needed for waking up.
- *
- * @pdev: pci dev pointer
- */
-static bool radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev)
-{
-
- /* 6600m in a macbook pro */
- if (pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE &&
- pdev->subsystem_device == 0x00e2) {
- printk(KERN_INFO "radeon: quirking longer d3 wakeup delay\n");
- return true;
- }
-
- return false;
-}
-
-/**
* radeon_switcheroo_set_state - set switcheroo state
*
* @pdev: pci dev pointer
@@ -1122,6 +1166,7 @@ static bool radeon_switcheroo_quirk_long_wakeup(struct pci_dev *pdev)
static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state)
{
struct drm_device *dev = pci_get_drvdata(pdev);
+ struct radeon_device *rdev = dev->dev_private;
if (radeon_is_px(dev) && state == VGA_SWITCHEROO_OFF)
return;
@@ -1133,7 +1178,7 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
/* don't suspend or resume card normally */
dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
- if (d3_delay < 20 && radeon_switcheroo_quirk_long_wakeup(pdev))
+ if (d3_delay < 20 && (rdev->px_quirk_flags & RADEON_PX_QUIRK_LONG_WAKEUP))
dev->pdev->d3_delay = 20;
radeon_resume_kms(dev, true, true);
@@ -1213,6 +1258,7 @@ int radeon_device_init(struct radeon_device *rdev,
for (i = 0; i < RADEON_NUM_RINGS; i++) {
rdev->ring[i].idx = i;
}
+ rdev->fence_context = fence_context_alloc(RADEON_NUM_RINGS);
DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X).\n",
radeon_family_name[rdev->family], pdev->vendor, pdev->device,
@@ -1230,6 +1276,8 @@ int radeon_device_init(struct radeon_device *rdev,
init_rwsem(&rdev->pm.mclk_lock);
init_rwsem(&rdev->exclusive_lock);
init_waitqueue_head(&rdev->irq.vblank_queue);
+ mutex_init(&rdev->mn_lock);
+ hash_init(rdev->mn_hash);
r = radeon_gem_init(rdev);
if (r)
return r;
@@ -1337,6 +1385,9 @@ int radeon_device_init(struct radeon_device *rdev,
if (rdev->rio_mem == NULL)
DRM_ERROR("Unable to find PCI I/O BAR\n");
+ if (rdev->flags & RADEON_IS_PX)
+ radeon_device_handle_px_quirks(rdev);
+
/* if we have > 1 VGA cards, then disable the radeon VGA resources */
/* this will fail for cards that aren't VGA class devices, just
* ignore it */
@@ -1350,11 +1401,7 @@ int radeon_device_init(struct radeon_device *rdev,
r = radeon_init(rdev);
if (r)
- return r;
-
- r = radeon_ib_ring_tests(rdev);
- if (r)
- DRM_ERROR("ib ring test failed (%d).\n", r);
+ goto failed;
r = radeon_gem_debugfs_init(rdev);
if (r) {
@@ -1370,9 +1417,13 @@ int radeon_device_init(struct radeon_device *rdev,
radeon_agp_disable(rdev);
r = radeon_init(rdev);
if (r)
- return r;
+ goto failed;
}
+ r = radeon_ib_ring_tests(rdev);
+ if (r)
+ DRM_ERROR("ib ring test failed (%d).\n", r);
+
if ((radeon_testing & 1)) {
if (rdev->accel_working)
radeon_test_moves(rdev);
@@ -1392,6 +1443,11 @@ int radeon_device_init(struct radeon_device *rdev,
DRM_INFO("radeon: acceleration disabled, skipping benchmarks\n");
}
return 0;
+
+failed:
+ if (runtime)
+ vga_switcheroo_fini_domain_pm_ops(rdev->dev);
+ return r;
}
static void radeon_debugfs_remove_files(struct radeon_device *rdev);
@@ -1412,6 +1468,8 @@ void radeon_device_fini(struct radeon_device *rdev)
radeon_bo_evict_vram(rdev);
radeon_fini(rdev);
vga_switcheroo_unregister_client(rdev->pdev);
+ if (rdev->flags & RADEON_IS_PX)
+ vga_switcheroo_fini_domain_pm_ops(rdev->dev);
vga_client_register(rdev->pdev, NULL, NULL, NULL);
if (rdev->rio_mem)
pci_iounmap(rdev->pdev, rdev->rio_mem);
@@ -1443,7 +1501,6 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
struct drm_crtc *crtc;
struct drm_connector *connector;
int i, r;
- bool force_completion = false;
if (dev == NULL || dev->dev_private == NULL) {
return -ENODEV;
@@ -1487,12 +1544,9 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
r = radeon_fence_wait_empty(rdev, i);
if (r) {
/* delay GPU reset to resume */
- force_completion = true;
+ radeon_fence_driver_force_completion(rdev, i);
}
}
- if (force_completion) {
- radeon_fence_driver_force_completion(rdev);
- }
radeon_save_bios_scratch_regs(rdev);
@@ -1632,13 +1686,11 @@ int radeon_gpu_reset(struct radeon_device *rdev)
return 0;
}
- rdev->needs_reset = false;
-
radeon_save_bios_scratch_regs(rdev);
/* block TTM */
resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev);
- radeon_pm_suspend(rdev);
radeon_suspend(rdev);
+ radeon_hpd_fini(rdev);
for (i = 0; i < RADEON_NUM_RINGS; ++i) {
ring_sizes[i] = radeon_ring_backup(rdev, &rdev->ring[i],
@@ -1650,7 +1702,6 @@ int radeon_gpu_reset(struct radeon_device *rdev)
}
}
-retry:
r = radeon_asic_reset(rdev);
if (!r) {
dev_info(rdev->dev, "GPU reset succeeded, trying to resume\n");
@@ -1659,40 +1710,69 @@ retry:
radeon_restore_bios_scratch_regs(rdev);
- if (!r) {
- for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+ for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+ if (!r && ring_data[i]) {
radeon_ring_restore(rdev, &rdev->ring[i],
ring_sizes[i], ring_data[i]);
- ring_sizes[i] = 0;
- ring_data[i] = NULL;
+ } else {
+ radeon_fence_driver_force_completion(rdev, i);
+ kfree(ring_data[i]);
}
+ }
- r = radeon_ib_ring_tests(rdev);
+ if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) {
+ /* do dpm late init */
+ r = radeon_pm_late_init(rdev);
if (r) {
- dev_err(rdev->dev, "ib ring test failed (%d).\n", r);
- if (saved) {
- saved = false;
- radeon_suspend(rdev);
- goto retry;
- }
+ rdev->pm.dpm_enabled = false;
+ DRM_ERROR("radeon_pm_late_init failed, disabling dpm\n");
}
} else {
- radeon_fence_driver_force_completion(rdev);
- for (i = 0; i < RADEON_NUM_RINGS; ++i) {
- kfree(ring_data[i]);
+ /* resume old pm late */
+ radeon_pm_resume(rdev);
+ }
+
+ /* init dig PHYs, disp eng pll */
+ if (rdev->is_atom_bios) {
+ radeon_atom_encoder_init(rdev);
+ radeon_atom_disp_eng_pll_init(rdev);
+ /* turn on the BL */
+ if (rdev->mode_info.bl_encoder) {
+ u8 bl_level = radeon_get_backlight_level(rdev,
+ rdev->mode_info.bl_encoder);
+ radeon_set_backlight_level(rdev, rdev->mode_info.bl_encoder,
+ bl_level);
}
}
+ /* reset hpd state */
+ radeon_hpd_init(rdev);
+
+ ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched);
+
+ rdev->in_reset = true;
+ rdev->needs_reset = false;
+
+ downgrade_write(&rdev->exclusive_lock);
- radeon_pm_resume(rdev);
drm_helper_resume_force_mode(rdev->ddev);
- ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched);
- if (r) {
+ /* set the power state here in case we are a PX system or headless */
+ if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled)
+ radeon_pm_compute_clocks(rdev);
+
+ if (!r) {
+ r = radeon_ib_ring_tests(rdev);
+ if (r && saved)
+ r = -EAGAIN;
+ } else {
/* bad news, how to tell it to userspace ? */
dev_info(rdev->dev, "GPU reset failed\n");
}
- up_write(&rdev->exclusive_lock);
+ rdev->needs_reset = r == -EAGAIN;
+ rdev->in_reset = false;
+
+ up_read(&rdev->exclusive_lock);
return r;
}
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index bf25061c8ac4..00ead8c2758a 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -293,6 +293,18 @@ void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id)
if (radeon_crtc == NULL)
return;
+ /* Skip the pageflip completion check below (based on polling) on
+ * asics which reliably support hw pageflip completion irqs. pflip
+ * irqs are a reliable and race-free method of handling pageflip
+ * completion detection. A use_pflipirq module parameter < 2 allows
+ * to override this in case of asics with faulty pflip irqs.
+ * A module parameter of 0 would only use this polling based path,
+ * a parameter of 1 would use pflip irq only as a backup to this
+ * path, as in Linux 3.16.
+ */
+ if ((radeon_use_pflipirq == 2) && ASIC_IS_DCE4(rdev))
+ return;
+
spin_lock_irqsave(&rdev->ddev->event_lock, flags);
if (radeon_crtc->flip_status != RADEON_FLIP_SUBMITTED) {
DRM_DEBUG_DRIVER("radeon_crtc->flip_status = %d != "
@@ -390,12 +402,21 @@ static void radeon_flip_work_func(struct work_struct *__work)
down_read(&rdev->exclusive_lock);
if (work->fence) {
- r = radeon_fence_wait(work->fence, false);
- if (r == -EDEADLK) {
- up_read(&rdev->exclusive_lock);
- r = radeon_gpu_reset(rdev);
- down_read(&rdev->exclusive_lock);
- }
+ struct radeon_fence *fence;
+
+ fence = to_radeon_fence(work->fence);
+ if (fence && fence->rdev == rdev) {
+ r = radeon_fence_wait(fence, false);
+ if (r == -EDEADLK) {
+ up_read(&rdev->exclusive_lock);
+ do {
+ r = radeon_gpu_reset(rdev);
+ } while (r == -EAGAIN);
+ down_read(&rdev->exclusive_lock);
+ }
+ } else
+ r = fence_wait(work->fence, false);
+
if (r)
DRM_ERROR("failed to wait on page flip fence (%d)!\n", r);
@@ -404,7 +425,8 @@ static void radeon_flip_work_func(struct work_struct *__work)
* confused about which BO the CRTC is scanning out
*/
- radeon_fence_unref(&work->fence);
+ fence_put(work->fence);
+ work->fence = NULL;
}
/* We borrow the event spin lock for protecting flip_status */
@@ -462,11 +484,6 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
obj = new_radeon_fb->obj;
new_rbo = gem_to_radeon_bo(obj);
- spin_lock(&new_rbo->tbo.bdev->fence_lock);
- if (new_rbo->tbo.sync_obj)
- work->fence = radeon_fence_ref(new_rbo->tbo.sync_obj);
- spin_unlock(&new_rbo->tbo.bdev->fence_lock);
-
/* pin the new buffer */
DRM_DEBUG_DRIVER("flip-ioctl() cur_rbo = %p, new_rbo = %p\n",
work->old_rbo, new_rbo);
@@ -485,6 +502,7 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
DRM_ERROR("failed to pin new rbo buffer before flip\n");
goto cleanup;
}
+ work->fence = fence_get(reservation_object_get_excl(new_rbo->tbo.resv));
radeon_bo_get_tiling_flags(new_rbo, &tiling_flags, NULL);
radeon_bo_unreserve(new_rbo);
@@ -566,9 +584,8 @@ pflip_cleanup:
cleanup:
drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base);
- radeon_fence_unref(&work->fence);
+ fence_put(work->fence);
kfree(work);
-
return r;
}
@@ -823,64 +840,6 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
return ret;
}
-int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
-{
- struct drm_device *dev = radeon_connector->base.dev;
- struct radeon_device *rdev = dev->dev_private;
- int ret = 0;
-
- /* don't leak the edid if we already fetched it in detect() */
- if (radeon_connector->edid)
- goto got_edid;
-
- /* on hw with routers, select right port */
- if (radeon_connector->router.ddc_valid)
- radeon_router_select_ddc_port(radeon_connector);
-
- if (radeon_connector_encoder_get_dp_bridge_encoder_id(&radeon_connector->base) !=
- ENCODER_OBJECT_ID_NONE) {
- if (radeon_connector->ddc_bus->has_aux)
- radeon_connector->edid = drm_get_edid(&radeon_connector->base,
- &radeon_connector->ddc_bus->aux.ddc);
- } else if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
- (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) {
- struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
-
- if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT ||
- dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) &&
- radeon_connector->ddc_bus->has_aux)
- radeon_connector->edid = drm_get_edid(&radeon_connector->base,
- &radeon_connector->ddc_bus->aux.ddc);
- else if (radeon_connector->ddc_bus && !radeon_connector->edid)
- radeon_connector->edid = drm_get_edid(&radeon_connector->base,
- &radeon_connector->ddc_bus->adapter);
- } else {
- if (radeon_connector->ddc_bus && !radeon_connector->edid)
- radeon_connector->edid = drm_get_edid(&radeon_connector->base,
- &radeon_connector->ddc_bus->adapter);
- }
-
- if (!radeon_connector->edid) {
- if (rdev->is_atom_bios) {
- /* some laptops provide a hardcoded edid in rom for LCDs */
- if (((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_LVDS) ||
- (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)))
- radeon_connector->edid = radeon_bios_get_hardcoded_edid(rdev);
- } else
- /* some servers provide a hardcoded edid in rom for KVMs */
- radeon_connector->edid = radeon_bios_get_hardcoded_edid(rdev);
- }
- if (radeon_connector->edid) {
-got_edid:
- drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid);
- ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid);
- drm_edid_to_eld(&radeon_connector->base, radeon_connector->edid);
- return ret;
- }
- drm_mode_connector_update_edid_property(&radeon_connector->base, NULL);
- return 0;
-}
-
/* avivo */
/**
@@ -1749,7 +1708,7 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
(!(mode->flags & DRM_MODE_FLAG_INTERLACE)) &&
((radeon_encoder->underscan_type == UNDERSCAN_ON) ||
((radeon_encoder->underscan_type == UNDERSCAN_AUTO) &&
- drm_detect_hdmi_monitor(radeon_connector->edid) &&
+ drm_detect_hdmi_monitor(radeon_connector_edid(connector)) &&
is_hdtv_mode(mode)))) {
if (radeon_encoder->underscan_hborder != 0)
radeon_crtc->h_border = radeon_encoder->underscan_hborder;
@@ -1963,7 +1922,7 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int fl
/* In vblank? */
if (in_vbl)
- ret |= DRM_SCANOUTPOS_INVBL;
+ ret |= DRM_SCANOUTPOS_IN_VBLANK;
/* Is vpos outside nominal vblank area, but less than
* 1/100 of a frame height away from start of vblank?
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 959f0866d993..dcffa30ee2db 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -38,6 +38,8 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/vga_switcheroo.h>
+#include <drm/drm_gem.h>
+
#include "drm_crtc_helper.h"
/*
* KMS wrapper.
@@ -82,9 +84,11 @@
* 2.38.0 - RADEON_GEM_OP (GET_INITIAL_DOMAIN, SET_INITIAL_DOMAIN),
* CIK: 1D and linear tiling modes contain valid PIPE_CONFIG
* 2.39.0 - Add INFO query for number of active CUs
+ * 2.40.0 - Add RADEON_GEM_GTT_WC/UC, flush HDP cache before submitting
+ * CS to GPU on >= r600
*/
#define KMS_DRIVER_MAJOR 2
-#define KMS_DRIVER_MINOR 39
+#define KMS_DRIVER_MINOR 40
#define KMS_DRIVER_PATCHLEVEL 0
int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
int radeon_driver_unload_kms(struct drm_device *dev);
@@ -112,6 +116,9 @@ int radeon_gem_object_open(struct drm_gem_object *obj,
struct drm_file *file_priv);
void radeon_gem_object_close(struct drm_gem_object *obj,
struct drm_file *file_priv);
+struct dma_buf *radeon_gem_prime_export(struct drm_device *dev,
+ struct drm_gem_object *gobj,
+ int flags);
extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc,
unsigned int flags,
int *vpos, int *hpos, ktime_t *stime,
@@ -128,7 +135,7 @@ int radeon_mode_dumb_create(struct drm_file *file_priv,
struct drm_mode_create_dumb *args);
struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj);
struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev,
- size_t size,
+ struct dma_buf_attachment *,
struct sg_table *sg);
int radeon_gem_prime_pin(struct drm_gem_object *obj);
void radeon_gem_prime_unpin(struct drm_gem_object *obj);
@@ -174,9 +181,12 @@ int radeon_dpm = -1;
int radeon_aspm = -1;
int radeon_runtime_pm = -1;
int radeon_hard_reset = 0;
-int radeon_vm_size = 4;
-int radeon_vm_block_size = 9;
+int radeon_vm_size = 8;
+int radeon_vm_block_size = -1;
int radeon_deep_color = 0;
+int radeon_use_pflipirq = 2;
+int radeon_bapm = -1;
+int radeon_backlight = -1;
MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
module_param_named(no_wb, radeon_no_wb, int, 0444);
@@ -247,12 +257,21 @@ module_param_named(hard_reset, radeon_hard_reset, int, 0444);
MODULE_PARM_DESC(vm_size, "VM address space size in gigabytes (default 4GB)");
module_param_named(vm_size, radeon_vm_size, int, 0444);
-MODULE_PARM_DESC(vm_block_size, "VM page table size in bits (default 9)");
+MODULE_PARM_DESC(vm_block_size, "VM page table size in bits (default depending on vm_size)");
module_param_named(vm_block_size, radeon_vm_block_size, int, 0444);
MODULE_PARM_DESC(deep_color, "Deep Color support (1 = enable, 0 = disable (default))");
module_param_named(deep_color, radeon_deep_color, int, 0444);
+MODULE_PARM_DESC(use_pflipirq, "Pflip irqs for pageflip completion (0 = disable, 1 = as fallback, 2 = exclusive (default))");
+module_param_named(use_pflipirq, radeon_use_pflipirq, int, 0444);
+
+MODULE_PARM_DESC(bapm, "BAPM support (1 = enable, 0 = disable, -1 = auto)");
+module_param_named(bapm, radeon_bapm, int, 0444);
+
+MODULE_PARM_DESC(backlight, "backlight support (1 = enable, 0 = disable, -1 = auto)");
+module_param_named(backlight, radeon_backlight, int, 0444);
+
static struct pci_device_id pciidlist[] = {
radeon_PCI_IDS
};
@@ -295,7 +314,7 @@ static const struct file_operations radeon_driver_old_fops = {
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
- .mmap = drm_mmap,
+ .mmap = drm_legacy_mmap,
.poll = drm_poll,
.read = drm_read,
#ifdef CONFIG_COMPAT
@@ -315,6 +334,7 @@ static struct drm_driver driver_old = {
.preclose = radeon_driver_preclose,
.postclose = radeon_driver_postclose,
.lastclose = radeon_driver_lastclose,
+ .set_busid = drm_pci_set_busid,
.unload = radeon_driver_unload,
.suspend = radeon_suspend,
.resume = radeon_resume,
@@ -430,6 +450,7 @@ static int radeon_pmops_runtime_suspend(struct device *dev)
ret = radeon_suspend_kms(drm_dev, false, false);
pci_save_state(pdev);
pci_disable_device(pdev);
+ pci_ignore_hotplug(pdev);
pci_set_power_state(pdev, PCI_D3cold);
drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
@@ -538,6 +559,7 @@ static struct drm_driver kms_driver = {
.preclose = radeon_driver_preclose_kms,
.postclose = radeon_driver_postclose_kms,
.lastclose = radeon_driver_lastclose_kms,
+ .set_busid = drm_pci_set_busid,
.unload = radeon_driver_unload_kms,
.get_vblank_counter = radeon_get_vblank_counter_kms,
.enable_vblank = radeon_enable_vblank_kms,
@@ -563,7 +585,7 @@ static struct drm_driver kms_driver = {
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
- .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_export = radeon_gem_prime_export,
.gem_prime_import = drm_gem_prime_import,
.gem_prime_pin = radeon_gem_prime_pin,
.gem_prime_unpin = radeon_gem_prime_unpin,
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
index dafd812e4571..46bd3938282c 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.h
+++ b/drivers/gpu/drm/radeon/radeon_drv.h
@@ -33,7 +33,9 @@
#include <linux/firmware.h>
#include <linux/platform_device.h>
+#include <drm/drm_legacy.h>
+#include <drm/ati_pcigart.h>
#include "radeon_family.h"
/* General customization:
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
index bd4959ca23aa..9a19e52cc655 100644
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
@@ -158,10 +158,43 @@ radeon_get_encoder_enum(struct drm_device *dev, uint32_t supported_device, uint8
return ret;
}
+static void radeon_encoder_add_backlight(struct radeon_encoder *radeon_encoder,
+ struct drm_connector *connector)
+{
+ struct drm_device *dev = radeon_encoder->base.dev;
+ struct radeon_device *rdev = dev->dev_private;
+ bool use_bl = false;
+
+ if (!(radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)))
+ return;
+
+ if (radeon_backlight == 0) {
+ return;
+ } else if (radeon_backlight == 1) {
+ use_bl = true;
+ } else if (radeon_backlight == -1) {
+ /* Quirks */
+ /* Amilo Xi 2550 only works with acpi bl */
+ if ((rdev->pdev->device == 0x9583) &&
+ (rdev->pdev->subsystem_vendor == 0x1734) &&
+ (rdev->pdev->subsystem_device == 0x1107))
+ use_bl = false;
+ else
+ use_bl = true;
+ }
+
+ if (use_bl) {
+ if (rdev->is_atom_bios)
+ radeon_atom_backlight_init(radeon_encoder, connector);
+ else
+ radeon_legacy_backlight_init(radeon_encoder, connector);
+ rdev->mode_info.bl_encoder = radeon_encoder;
+ }
+}
+
void
radeon_link_encoder_connector(struct drm_device *dev)
{
- struct radeon_device *rdev = dev->dev_private;
struct drm_connector *connector;
struct radeon_connector *radeon_connector;
struct drm_encoder *encoder;
@@ -174,13 +207,8 @@ radeon_link_encoder_connector(struct drm_device *dev)
radeon_encoder = to_radeon_encoder(encoder);
if (radeon_encoder->devices & radeon_connector->devices) {
drm_mode_connector_attach_encoder(connector, encoder);
- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
- if (rdev->is_atom_bios)
- radeon_atom_backlight_init(radeon_encoder, connector);
- else
- radeon_legacy_backlight_init(radeon_encoder, connector);
- rdev->mode_info.bl_encoder = radeon_encoder;
- }
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
+ radeon_encoder_add_backlight(radeon_encoder, connector);
}
}
}
@@ -343,7 +371,7 @@ bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder,
case DRM_MODE_CONNECTOR_HDMIB:
if (radeon_connector->use_digital) {
/* HDMI 1.3 supports up to 340 Mhz over single link */
- if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector_edid(connector))) {
if (pixel_clock > 340000)
return true;
else
@@ -365,7 +393,7 @@ bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder,
return false;
else {
/* HDMI 1.3 supports up to 340 Mhz over single link */
- if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector_edid(connector))) {
if (pixel_clock > 340000)
return true;
else
@@ -382,3 +410,24 @@ bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder,
}
}
+bool radeon_encoder_is_digital(struct drm_encoder *encoder)
+{
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_LVDS:
+ case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
+ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
+ case ENCODER_OBJECT_ID_INTERNAL_DVO1:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+ case ENCODER_OBJECT_ID_INTERNAL_DDI:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 665ced3b7313..0ea1db83d573 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -127,8 +127,7 @@ static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev,
aligned_size = ALIGN(size, PAGE_SIZE);
ret = radeon_gem_object_create(rdev, aligned_size, 0,
RADEON_GEM_DOMAIN_VRAM,
- false, true,
- &gobj);
+ 0, true, &gobj);
if (ret) {
printk(KERN_ERR "failed to allocate framebuffer (%d)\n",
aligned_size);
@@ -190,7 +189,8 @@ out_unref:
static int radeonfb_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
- struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper;
+ struct radeon_fbdev *rfbdev =
+ container_of(helper, struct radeon_fbdev, helper);
struct radeon_device *rdev = rfbdev->rdev;
struct fb_info *info;
struct drm_framebuffer *fb = NULL;
@@ -331,7 +331,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb
return 0;
}
-static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
.gamma_set = radeon_crtc_fb_gamma_set,
.gamma_get = radeon_crtc_fb_gamma_get,
.fb_probe = radeonfb_create,
@@ -353,7 +353,9 @@ int radeon_fbdev_init(struct radeon_device *rdev)
rfbdev->rdev = rdev;
rdev->mode_info.rfbdev = rfbdev;
- rfbdev->helper.funcs = &radeon_fb_helper_funcs;
+
+ drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
+ &radeon_fb_helper_funcs);
ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
rdev->num_crtc,
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
index 913787085dfa..995167025282 100644
--- a/drivers/gpu/drm/radeon/radeon_fence.c
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
@@ -98,6 +98,25 @@ static u32 radeon_fence_read(struct radeon_device *rdev, int ring)
}
/**
+ * radeon_fence_schedule_check - schedule lockup check
+ *
+ * @rdev: radeon_device pointer
+ * @ring: ring index we should work with
+ *
+ * Queues a delayed work item to check for lockups.
+ */
+static void radeon_fence_schedule_check(struct radeon_device *rdev, int ring)
+{
+ /*
+ * Do not reset the timer here with mod_delayed_work,
+ * this can livelock in an interaction with TTM delayed destroy.
+ */
+ queue_delayed_work(system_power_efficient_wq,
+ &rdev->fence_drv[ring].lockup_work,
+ RADEON_FENCE_JIFFIES_TIMEOUT);
+}
+
+/**
* radeon_fence_emit - emit a fence on the requested ring
*
* @rdev: radeon_device pointer
@@ -111,30 +130,70 @@ int radeon_fence_emit(struct radeon_device *rdev,
struct radeon_fence **fence,
int ring)
{
+ u64 seq = ++rdev->fence_drv[ring].sync_seq[ring];
+
/* we are protected by the ring emission mutex */
*fence = kmalloc(sizeof(struct radeon_fence), GFP_KERNEL);
if ((*fence) == NULL) {
return -ENOMEM;
}
- kref_init(&((*fence)->kref));
(*fence)->rdev = rdev;
- (*fence)->seq = ++rdev->fence_drv[ring].sync_seq[ring];
+ (*fence)->seq = seq;
(*fence)->ring = ring;
+ fence_init(&(*fence)->base, &radeon_fence_ops,
+ &rdev->fence_queue.lock, rdev->fence_context + ring, seq);
radeon_fence_ring_emit(rdev, ring, *fence);
trace_radeon_fence_emit(rdev->ddev, ring, (*fence)->seq);
+ radeon_fence_schedule_check(rdev, ring);
return 0;
}
/**
- * radeon_fence_process - process a fence
+ * radeon_fence_check_signaled - callback from fence_queue
+ *
+ * this function is called with fence_queue lock held, which is also used
+ * for the fence locking itself, so unlocked variants are used for
+ * fence_signal, and remove_wait_queue.
+ */
+static int radeon_fence_check_signaled(wait_queue_t *wait, unsigned mode, int flags, void *key)
+{
+ struct radeon_fence *fence;
+ u64 seq;
+
+ fence = container_of(wait, struct radeon_fence, fence_wake);
+
+ /*
+ * We cannot use radeon_fence_process here because we're already
+ * in the waitqueue, in a call from wake_up_all.
+ */
+ seq = atomic64_read(&fence->rdev->fence_drv[fence->ring].last_seq);
+ if (seq >= fence->seq) {
+ int ret = fence_signal_locked(&fence->base);
+
+ if (!ret)
+ FENCE_TRACE(&fence->base, "signaled from irq context\n");
+ else
+ FENCE_TRACE(&fence->base, "was already signaled\n");
+
+ radeon_irq_kms_sw_irq_put(fence->rdev, fence->ring);
+ __remove_wait_queue(&fence->rdev->fence_queue, &fence->fence_wake);
+ fence_put(&fence->base);
+ } else
+ FENCE_TRACE(&fence->base, "pending\n");
+ return 0;
+}
+
+/**
+ * radeon_fence_activity - check for fence activity
*
* @rdev: radeon_device pointer
* @ring: ring index the fence is associated with
*
- * Checks the current fence value and wakes the fence queue
- * if the sequence number has increased (all asics).
+ * Checks the current fence value and calculates the last
+ * signalled fence value. Returns true if activity occured
+ * on the ring, and the fence_queue should be waken up.
*/
-void radeon_fence_process(struct radeon_device *rdev, int ring)
+static bool radeon_fence_activity(struct radeon_device *rdev, int ring)
{
uint64_t seq, last_seq, last_emitted;
unsigned count_loop = 0;
@@ -190,23 +249,77 @@ void radeon_fence_process(struct radeon_device *rdev, int ring)
}
} while (atomic64_xchg(&rdev->fence_drv[ring].last_seq, seq) > seq);
- if (wake)
- wake_up_all(&rdev->fence_queue);
+ if (seq < last_emitted)
+ radeon_fence_schedule_check(rdev, ring);
+
+ return wake;
}
/**
- * radeon_fence_destroy - destroy a fence
+ * radeon_fence_check_lockup - check for hardware lockup
*
- * @kref: fence kref
+ * @work: delayed work item
*
- * Frees the fence object (all asics).
+ * Checks for fence activity and if there is none probe
+ * the hardware if a lockup occured.
*/
-static void radeon_fence_destroy(struct kref *kref)
+static void radeon_fence_check_lockup(struct work_struct *work)
{
- struct radeon_fence *fence;
+ struct radeon_fence_driver *fence_drv;
+ struct radeon_device *rdev;
+ int ring;
+
+ fence_drv = container_of(work, struct radeon_fence_driver,
+ lockup_work.work);
+ rdev = fence_drv->rdev;
+ ring = fence_drv - &rdev->fence_drv[0];
+
+ if (!down_read_trylock(&rdev->exclusive_lock)) {
+ /* just reschedule the check if a reset is going on */
+ radeon_fence_schedule_check(rdev, ring);
+ return;
+ }
+
+ if (fence_drv->delayed_irq && rdev->ddev->irq_enabled) {
+ unsigned long irqflags;
+
+ fence_drv->delayed_irq = false;
+ spin_lock_irqsave(&rdev->irq.lock, irqflags);
+ radeon_irq_set(rdev);
+ spin_unlock_irqrestore(&rdev->irq.lock, irqflags);
+ }
+
+ if (radeon_fence_activity(rdev, ring))
+ wake_up_all(&rdev->fence_queue);
- fence = container_of(kref, struct radeon_fence, kref);
- kfree(fence);
+ else if (radeon_ring_is_lockup(rdev, ring, &rdev->ring[ring])) {
+
+ /* good news we believe it's a lockup */
+ dev_warn(rdev->dev, "GPU lockup (current fence id "
+ "0x%016llx last fence id 0x%016llx on ring %d)\n",
+ (uint64_t)atomic64_read(&fence_drv->last_seq),
+ fence_drv->sync_seq[ring], ring);
+
+ /* remember that we need an reset */
+ rdev->needs_reset = true;
+ wake_up_all(&rdev->fence_queue);
+ }
+ up_read(&rdev->exclusive_lock);
+}
+
+/**
+ * radeon_fence_process - process a fence
+ *
+ * @rdev: radeon_device pointer
+ * @ring: ring index the fence is associated with
+ *
+ * Checks the current fence value and wakes the fence queue
+ * if the sequence number has increased (all asics).
+ */
+void radeon_fence_process(struct radeon_device *rdev, int ring)
+{
+ if (radeon_fence_activity(rdev, ring))
+ wake_up_all(&rdev->fence_queue);
}
/**
@@ -237,6 +350,75 @@ static bool radeon_fence_seq_signaled(struct radeon_device *rdev,
return false;
}
+static bool radeon_fence_is_signaled(struct fence *f)
+{
+ struct radeon_fence *fence = to_radeon_fence(f);
+ struct radeon_device *rdev = fence->rdev;
+ unsigned ring = fence->ring;
+ u64 seq = fence->seq;
+
+ if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq) {
+ return true;
+ }
+
+ if (down_read_trylock(&rdev->exclusive_lock)) {
+ radeon_fence_process(rdev, ring);
+ up_read(&rdev->exclusive_lock);
+
+ if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * radeon_fence_enable_signaling - enable signalling on fence
+ * @fence: fence
+ *
+ * This function is called with fence_queue lock held, and adds a callback
+ * to fence_queue that checks if this fence is signaled, and if so it
+ * signals the fence and removes itself.
+ */
+static bool radeon_fence_enable_signaling(struct fence *f)
+{
+ struct radeon_fence *fence = to_radeon_fence(f);
+ struct radeon_device *rdev = fence->rdev;
+
+ if (atomic64_read(&rdev->fence_drv[fence->ring].last_seq) >= fence->seq)
+ return false;
+
+ if (down_read_trylock(&rdev->exclusive_lock)) {
+ radeon_irq_kms_sw_irq_get(rdev, fence->ring);
+
+ if (radeon_fence_activity(rdev, fence->ring))
+ wake_up_all_locked(&rdev->fence_queue);
+
+ /* did fence get signaled after we enabled the sw irq? */
+ if (atomic64_read(&rdev->fence_drv[fence->ring].last_seq) >= fence->seq) {
+ radeon_irq_kms_sw_irq_put(rdev, fence->ring);
+ up_read(&rdev->exclusive_lock);
+ return false;
+ }
+
+ up_read(&rdev->exclusive_lock);
+ } else {
+ /* we're probably in a lockup, lets not fiddle too much */
+ if (radeon_irq_kms_sw_irq_get_delayed(rdev, fence->ring))
+ rdev->fence_drv[fence->ring].delayed_irq = true;
+ radeon_fence_schedule_check(rdev, fence->ring);
+ }
+
+ fence->fence_wake.flags = 0;
+ fence->fence_wake.private = NULL;
+ fence->fence_wake.func = radeon_fence_check_signaled;
+ __add_wait_queue(&rdev->fence_queue, &fence->fence_wake);
+ fence_get(f);
+
+ FENCE_TRACE(&fence->base, "armed on ring %i!\n", fence->ring);
+ return true;
+}
+
/**
* radeon_fence_signaled - check if a fence has signaled
*
@@ -247,14 +429,15 @@ static bool radeon_fence_seq_signaled(struct radeon_device *rdev,
*/
bool radeon_fence_signaled(struct radeon_fence *fence)
{
- if (!fence) {
+ if (!fence)
return true;
- }
- if (fence->seq == RADEON_FENCE_SIGNALED_SEQ) {
- return true;
- }
+
if (radeon_fence_seq_signaled(fence->rdev, fence->seq, fence->ring)) {
- fence->seq = RADEON_FENCE_SIGNALED_SEQ;
+ int ret;
+
+ ret = fence_signal(&fence->base);
+ if (!ret)
+ FENCE_TRACE(&fence->base, "signaled from radeon_fence_signaled\n");
return true;
}
return false;
@@ -283,110 +466,70 @@ static bool radeon_fence_any_seq_signaled(struct radeon_device *rdev, u64 *seq)
}
/**
- * radeon_fence_wait_seq - wait for a specific sequence numbers
+ * radeon_fence_wait_seq_timeout - wait for a specific sequence numbers
*
* @rdev: radeon device pointer
* @target_seq: sequence number(s) we want to wait for
* @intr: use interruptable sleep
+ * @timeout: maximum time to wait, or MAX_SCHEDULE_TIMEOUT for infinite wait
*
* Wait for the requested sequence number(s) to be written by any ring
* (all asics). Sequnce number array is indexed by ring id.
* @intr selects whether to use interruptable (true) or non-interruptable
* (false) sleep when waiting for the sequence number. Helper function
* for radeon_fence_wait_*().
- * Returns 0 if the sequence number has passed, error for all other cases.
+ * Returns remaining time if the sequence number has passed, 0 when
+ * the wait timeout, or an error for all other cases.
* -EDEADLK is returned when a GPU lockup has been detected.
*/
-static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 *target_seq,
- bool intr)
+static long radeon_fence_wait_seq_timeout(struct radeon_device *rdev,
+ u64 *target_seq, bool intr,
+ long timeout)
{
- uint64_t last_seq[RADEON_NUM_RINGS];
- bool signaled;
- int i, r;
-
- while (!radeon_fence_any_seq_signaled(rdev, target_seq)) {
+ long r;
+ int i;
- /* Save current sequence values, used to check for GPU lockups */
- for (i = 0; i < RADEON_NUM_RINGS; ++i) {
- if (!target_seq[i])
- continue;
+ if (radeon_fence_any_seq_signaled(rdev, target_seq))
+ return timeout;
- last_seq[i] = atomic64_read(&rdev->fence_drv[i].last_seq);
- trace_radeon_fence_wait_begin(rdev->ddev, i, target_seq[i]);
- radeon_irq_kms_sw_irq_get(rdev, i);
- }
+ /* enable IRQs and tracing */
+ for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+ if (!target_seq[i])
+ continue;
- if (intr) {
- r = wait_event_interruptible_timeout(rdev->fence_queue, (
- (signaled = radeon_fence_any_seq_signaled(rdev, target_seq))
- || rdev->needs_reset), RADEON_FENCE_JIFFIES_TIMEOUT);
- } else {
- r = wait_event_timeout(rdev->fence_queue, (
- (signaled = radeon_fence_any_seq_signaled(rdev, target_seq))
- || rdev->needs_reset), RADEON_FENCE_JIFFIES_TIMEOUT);
- }
+ trace_radeon_fence_wait_begin(rdev->ddev, i, target_seq[i]);
+ radeon_irq_kms_sw_irq_get(rdev, i);
+ }
- for (i = 0; i < RADEON_NUM_RINGS; ++i) {
- if (!target_seq[i])
- continue;
+ if (intr) {
+ r = wait_event_interruptible_timeout(rdev->fence_queue, (
+ radeon_fence_any_seq_signaled(rdev, target_seq)
+ || rdev->needs_reset), timeout);
+ } else {
+ r = wait_event_timeout(rdev->fence_queue, (
+ radeon_fence_any_seq_signaled(rdev, target_seq)
+ || rdev->needs_reset), timeout);
+ }
- radeon_irq_kms_sw_irq_put(rdev, i);
- trace_radeon_fence_wait_end(rdev->ddev, i, target_seq[i]);
- }
+ if (rdev->needs_reset)
+ r = -EDEADLK;
- if (unlikely(r < 0))
- return r;
+ for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+ if (!target_seq[i])
+ continue;
- if (unlikely(!signaled)) {
- if (rdev->needs_reset)
- return -EDEADLK;
-
- /* we were interrupted for some reason and fence
- * isn't signaled yet, resume waiting */
- if (r)
- continue;
-
- for (i = 0; i < RADEON_NUM_RINGS; ++i) {
- if (!target_seq[i])
- continue;
-
- if (last_seq[i] != atomic64_read(&rdev->fence_drv[i].last_seq))
- break;
- }
-
- if (i != RADEON_NUM_RINGS)
- continue;
-
- for (i = 0; i < RADEON_NUM_RINGS; ++i) {
- if (!target_seq[i])
- continue;
-
- if (radeon_ring_is_lockup(rdev, i, &rdev->ring[i]))
- break;
- }
-
- if (i < RADEON_NUM_RINGS) {
- /* good news we believe it's a lockup */
- dev_warn(rdev->dev, "GPU lockup (waiting for "
- "0x%016llx last fence id 0x%016llx on"
- " ring %d)\n",
- target_seq[i], last_seq[i], i);
-
- /* remember that we need an reset */
- rdev->needs_reset = true;
- wake_up_all(&rdev->fence_queue);
- return -EDEADLK;
- }
- }
+ radeon_irq_kms_sw_irq_put(rdev, i);
+ trace_radeon_fence_wait_end(rdev->ddev, i, target_seq[i]);
}
- return 0;
+
+ return r;
}
/**
* radeon_fence_wait - wait for a fence to signal
*
* @fence: radeon fence object
- * @intr: use interruptable sleep
+ * @intr: use interruptible sleep
*
* Wait for the requested fence to signal (all asics).
* @intr selects whether to use interruptable (true) or non-interruptable
@@ -396,22 +539,26 @@ static int radeon_fence_wait_seq(struct radeon_device *rdev, u64 *target_seq,
int radeon_fence_wait(struct radeon_fence *fence, bool intr)
{
uint64_t seq[RADEON_NUM_RINGS] = {};
- int r;
+ long r;
- if (fence == NULL) {
- WARN(1, "Querying an invalid fence : %p !\n", fence);
- return -EINVAL;
- }
+ /*
+ * This function should not be called on !radeon fences.
+ * If this is the case, it would mean this function can
+ * also be called on radeon fences belonging to another card.
+ * exclusive_lock is not held in that case.
+ */
+ if (WARN_ON_ONCE(!to_radeon_fence(&fence->base)))
+ return fence_wait(&fence->base, intr);
seq[fence->ring] = fence->seq;
- if (seq[fence->ring] == RADEON_FENCE_SIGNALED_SEQ)
- return 0;
-
- r = radeon_fence_wait_seq(fence->rdev, seq, intr);
- if (r)
+ r = radeon_fence_wait_seq_timeout(fence->rdev, seq, intr, MAX_SCHEDULE_TIMEOUT);
+ if (r < 0) {
return r;
+ }
- fence->seq = RADEON_FENCE_SIGNALED_SEQ;
+ r = fence_signal(&fence->base);
+ if (!r)
+ FENCE_TRACE(&fence->base, "signaled from fence_wait\n");
return 0;
}
@@ -434,7 +581,7 @@ int radeon_fence_wait_any(struct radeon_device *rdev,
{
uint64_t seq[RADEON_NUM_RINGS];
unsigned i, num_rings = 0;
- int r;
+ long r;
for (i = 0; i < RADEON_NUM_RINGS; ++i) {
seq[i] = 0;
@@ -445,18 +592,14 @@ int radeon_fence_wait_any(struct radeon_device *rdev,
seq[i] = fences[i]->seq;
++num_rings;
-
- /* test if something was allready signaled */
- if (seq[i] == RADEON_FENCE_SIGNALED_SEQ)
- return 0;
}
/* nothing to wait for ? */
if (num_rings == 0)
return -ENOENT;
- r = radeon_fence_wait_seq(rdev, seq, intr);
- if (r) {
+ r = radeon_fence_wait_seq_timeout(rdev, seq, intr, MAX_SCHEDULE_TIMEOUT);
+ if (r < 0) {
return r;
}
return 0;
@@ -475,6 +618,7 @@ int radeon_fence_wait_any(struct radeon_device *rdev,
int radeon_fence_wait_next(struct radeon_device *rdev, int ring)
{
uint64_t seq[RADEON_NUM_RINGS] = {};
+ long r;
seq[ring] = atomic64_read(&rdev->fence_drv[ring].last_seq) + 1ULL;
if (seq[ring] >= rdev->fence_drv[ring].sync_seq[ring]) {
@@ -482,7 +626,10 @@ int radeon_fence_wait_next(struct radeon_device *rdev, int ring)
already the last emited fence */
return -ENOENT;
}
- return radeon_fence_wait_seq(rdev, seq, false);
+ r = radeon_fence_wait_seq_timeout(rdev, seq, false, MAX_SCHEDULE_TIMEOUT);
+ if (r < 0)
+ return r;
+ return 0;
}
/**
@@ -498,18 +645,18 @@ int radeon_fence_wait_next(struct radeon_device *rdev, int ring)
int radeon_fence_wait_empty(struct radeon_device *rdev, int ring)
{
uint64_t seq[RADEON_NUM_RINGS] = {};
- int r;
+ long r;
seq[ring] = rdev->fence_drv[ring].sync_seq[ring];
if (!seq[ring])
return 0;
- r = radeon_fence_wait_seq(rdev, seq, false);
- if (r) {
+ r = radeon_fence_wait_seq_timeout(rdev, seq, false, MAX_SCHEDULE_TIMEOUT);
+ if (r < 0) {
if (r == -EDEADLK)
return -EDEADLK;
- dev_err(rdev->dev, "error waiting for ring[%d] to become idle (%d)\n",
+ dev_err(rdev->dev, "error waiting for ring[%d] to become idle (%ld)\n",
ring, r);
}
return 0;
@@ -525,7 +672,7 @@ int radeon_fence_wait_empty(struct radeon_device *rdev, int ring)
*/
struct radeon_fence *radeon_fence_ref(struct radeon_fence *fence)
{
- kref_get(&fence->kref);
+ fence_get(&fence->base);
return fence;
}
@@ -542,7 +689,7 @@ void radeon_fence_unref(struct radeon_fence **fence)
*fence = NULL;
if (tmp) {
- kref_put(&tmp->kref, radeon_fence_destroy);
+ fence_put(&tmp->base);
}
}
@@ -711,6 +858,9 @@ static void radeon_fence_driver_init_ring(struct radeon_device *rdev, int ring)
rdev->fence_drv[ring].sync_seq[i] = 0;
atomic64_set(&rdev->fence_drv[ring].last_seq, 0);
rdev->fence_drv[ring].initialized = false;
+ INIT_DELAYED_WORK(&rdev->fence_drv[ring].lockup_work,
+ radeon_fence_check_lockup);
+ rdev->fence_drv[ring].rdev = rdev;
}
/**
@@ -758,8 +908,9 @@ void radeon_fence_driver_fini(struct radeon_device *rdev)
r = radeon_fence_wait_empty(rdev, ring);
if (r) {
/* no need to trigger GPU reset as we are unloading */
- radeon_fence_driver_force_completion(rdev);
+ radeon_fence_driver_force_completion(rdev, ring);
}
+ cancel_delayed_work_sync(&rdev->fence_drv[ring].lockup_work);
wake_up_all(&rdev->fence_queue);
radeon_scratch_free(rdev, rdev->fence_drv[ring].scratch_reg);
rdev->fence_drv[ring].initialized = false;
@@ -771,18 +922,16 @@ void radeon_fence_driver_fini(struct radeon_device *rdev)
* radeon_fence_driver_force_completion - force all fence waiter to complete
*
* @rdev: radeon device pointer
+ * @ring: the ring to complete
*
* In case of GPU reset failure make sure no process keep waiting on fence
* that will never complete.
*/
-void radeon_fence_driver_force_completion(struct radeon_device *rdev)
+void radeon_fence_driver_force_completion(struct radeon_device *rdev, int ring)
{
- int ring;
-
- for (ring = 0; ring < RADEON_NUM_RINGS; ring++) {
- if (!rdev->fence_drv[ring].initialized)
- continue;
+ if (rdev->fence_drv[ring].initialized) {
radeon_fence_write(rdev, rdev->fence_drv[ring].sync_seq[ring], ring);
+ cancel_delayed_work_sync(&rdev->fence_drv[ring].lockup_work);
}
}
@@ -833,6 +982,7 @@ static int radeon_debugfs_gpu_reset(struct seq_file *m, void *data)
down_read(&rdev->exclusive_lock);
seq_printf(m, "%d\n", rdev->needs_reset);
rdev->needs_reset = true;
+ wake_up_all(&rdev->fence_queue);
up_read(&rdev->exclusive_lock);
return 0;
@@ -852,3 +1002,72 @@ int radeon_debugfs_fence_init(struct radeon_device *rdev)
return 0;
#endif
}
+
+static const char *radeon_fence_get_driver_name(struct fence *fence)
+{
+ return "radeon";
+}
+
+static const char *radeon_fence_get_timeline_name(struct fence *f)
+{
+ struct radeon_fence *fence = to_radeon_fence(f);
+ switch (fence->ring) {
+ case RADEON_RING_TYPE_GFX_INDEX: return "radeon.gfx";
+ case CAYMAN_RING_TYPE_CP1_INDEX: return "radeon.cp1";
+ case CAYMAN_RING_TYPE_CP2_INDEX: return "radeon.cp2";
+ case R600_RING_TYPE_DMA_INDEX: return "radeon.dma";
+ case CAYMAN_RING_TYPE_DMA1_INDEX: return "radeon.dma1";
+ case R600_RING_TYPE_UVD_INDEX: return "radeon.uvd";
+ case TN_RING_TYPE_VCE1_INDEX: return "radeon.vce1";
+ case TN_RING_TYPE_VCE2_INDEX: return "radeon.vce2";
+ default: WARN_ON_ONCE(1); return "radeon.unk";
+ }
+}
+
+static inline bool radeon_test_signaled(struct radeon_fence *fence)
+{
+ return test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags);
+}
+
+static signed long radeon_fence_default_wait(struct fence *f, bool intr,
+ signed long t)
+{
+ struct radeon_fence *fence = to_radeon_fence(f);
+ struct radeon_device *rdev = fence->rdev;
+ bool signaled;
+
+ fence_enable_sw_signaling(&fence->base);
+
+ /*
+ * This function has to return -EDEADLK, but cannot hold
+ * exclusive_lock during the wait because some callers
+ * may already hold it. This means checking needs_reset without
+ * lock, and not fiddling with any gpu internals.
+ *
+ * The callback installed with fence_enable_sw_signaling will
+ * run before our wait_event_*timeout call, so we will see
+ * both the signaled fence and the changes to needs_reset.
+ */
+
+ if (intr)
+ t = wait_event_interruptible_timeout(rdev->fence_queue,
+ ((signaled = radeon_test_signaled(fence)) ||
+ rdev->needs_reset), t);
+ else
+ t = wait_event_timeout(rdev->fence_queue,
+ ((signaled = radeon_test_signaled(fence)) ||
+ rdev->needs_reset), t);
+
+ if (t > 0 && !signaled)
+ return -EDEADLK;
+ return t;
+}
+
+const struct fence_ops radeon_fence_ops = {
+ .get_driver_name = radeon_fence_get_driver_name,
+ .get_timeline_name = radeon_fence_get_timeline_name,
+ .enable_signaling = radeon_fence_enable_signaling,
+ .signaled = radeon_fence_is_signaled,
+ .wait = radeon_fence_default_wait,
+ .release = NULL,
+};
diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c
index 2e723651069b..84146d5901aa 100644
--- a/drivers/gpu/drm/radeon/radeon_gart.c
+++ b/drivers/gpu/drm/radeon/radeon_gart.c
@@ -128,7 +128,7 @@ int radeon_gart_table_vram_alloc(struct radeon_device *rdev)
if (rdev->gart.robj == NULL) {
r = radeon_bo_create(rdev, rdev->gart.table_size,
PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM,
- NULL, &rdev->gart.robj);
+ 0, NULL, NULL, &rdev->gart.robj);
if (r) {
return r;
}
@@ -243,7 +243,8 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset,
page_base = rdev->gart.pages_addr[p];
for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) {
if (rdev->gart.ptr) {
- radeon_gart_set_page(rdev, t, page_base);
+ radeon_gart_set_page(rdev, t, page_base,
+ RADEON_GART_PAGE_DUMMY);
}
page_base += RADEON_GPU_PAGE_SIZE;
}
@@ -261,13 +262,15 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset,
* @pages: number of pages to bind
* @pagelist: pages to bind
* @dma_addr: DMA addresses of pages
+ * @flags: RADEON_GART_PAGE_* flags
*
* Binds the requested pages to the gart page table
* (all asics).
* Returns 0 for success, -EINVAL for failure.
*/
int radeon_gart_bind(struct radeon_device *rdev, unsigned offset,
- int pages, struct page **pagelist, dma_addr_t *dma_addr)
+ int pages, struct page **pagelist, dma_addr_t *dma_addr,
+ uint32_t flags)
{
unsigned t;
unsigned p;
@@ -287,7 +290,7 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset,
if (rdev->gart.ptr) {
page_base = rdev->gart.pages_addr[p];
for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) {
- radeon_gart_set_page(rdev, t, page_base);
+ radeon_gart_set_page(rdev, t, page_base, flags);
page_base += RADEON_GPU_PAGE_SIZE;
}
}
@@ -298,33 +301,6 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset,
}
/**
- * radeon_gart_restore - bind all pages in the gart page table
- *
- * @rdev: radeon_device pointer
- *
- * Binds all pages in the gart page table (all asics).
- * Used to rebuild the gart table on device startup or resume.
- */
-void radeon_gart_restore(struct radeon_device *rdev)
-{
- int i, j, t;
- u64 page_base;
-
- if (!rdev->gart.ptr) {
- return;
- }
- for (i = 0, t = 0; i < rdev->gart.num_cpu_pages; i++) {
- page_base = rdev->gart.pages_addr[i];
- for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) {
- radeon_gart_set_page(rdev, t, page_base);
- page_base += RADEON_GPU_PAGE_SIZE;
- }
- }
- mb();
- radeon_gart_tlb_flush(rdev);
-}
-
-/**
* radeon_gart_init - init the driver info for managing the gart
*
* @rdev: radeon_device pointer
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index d09650c1d720..c194497aa586 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -40,9 +40,9 @@ void radeon_gem_object_free(struct drm_gem_object *gobj)
}
}
-int radeon_gem_object_create(struct radeon_device *rdev, int size,
+int radeon_gem_object_create(struct radeon_device *rdev, unsigned long size,
int alignment, int initial_domain,
- bool discardable, bool kernel,
+ u32 flags, bool kernel,
struct drm_gem_object **obj)
{
struct radeon_bo *robj;
@@ -55,23 +55,26 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
alignment = PAGE_SIZE;
}
- /* maximun bo size is the minimun btw visible vram and gtt size */
- max_size = min(rdev->mc.visible_vram_size, rdev->mc.gtt_size);
+ /* Maximum bo size is the unpinned gtt size since we use the gtt to
+ * handle vram to system pool migrations.
+ */
+ max_size = rdev->mc.gtt_size - rdev->gart_pin_size;
if (size > max_size) {
- printk(KERN_WARNING "%s:%d alloc size %dMb bigger than %ldMb limit\n",
- __func__, __LINE__, size >> 20, max_size >> 20);
+ DRM_DEBUG("Allocation size %ldMb bigger than %ldMb limit\n",
+ size >> 20, max_size >> 20);
return -ENOMEM;
}
retry:
- r = radeon_bo_create(rdev, size, alignment, kernel, initial_domain, NULL, &robj);
+ r = radeon_bo_create(rdev, size, alignment, kernel, initial_domain,
+ flags, NULL, NULL, &robj);
if (r) {
if (r != -ERESTARTSYS) {
if (initial_domain == RADEON_GEM_DOMAIN_VRAM) {
initial_domain |= RADEON_GEM_DOMAIN_GTT;
goto retry;
}
- DRM_ERROR("Failed to allocate GEM object (%d, %d, %u, %d)\n",
+ DRM_ERROR("Failed to allocate GEM object (%ld, %d, %u, %d)\n",
size, initial_domain, alignment, r);
}
return r;
@@ -91,7 +94,7 @@ static int radeon_gem_set_domain(struct drm_gem_object *gobj,
{
struct radeon_bo *robj;
uint32_t domain;
- int r;
+ long r;
/* FIXME: reeimplement */
robj = gem_to_radeon_bo(gobj);
@@ -107,9 +110,12 @@ static int radeon_gem_set_domain(struct drm_gem_object *gobj,
}
if (domain == RADEON_GEM_DOMAIN_CPU) {
/* Asking for cpu access wait for object idle */
- r = radeon_bo_wait(robj, NULL, false);
- if (r) {
- printk(KERN_ERR "Failed to wait for object !\n");
+ r = reservation_object_wait_timeout_rcu(robj->tbo.resv, true, true, 30 * HZ);
+ if (!r)
+ r = -EBUSY;
+
+ if (r < 0 && r != -EINTR) {
+ printk(KERN_ERR "Failed to wait for object: %li\n", r);
return r;
}
}
@@ -208,18 +214,15 @@ int radeon_gem_info_ioctl(struct drm_device *dev, void *data,
struct radeon_device *rdev = dev->dev_private;
struct drm_radeon_gem_info *args = data;
struct ttm_mem_type_manager *man;
- unsigned i;
man = &rdev->mman.bdev.man[TTM_PL_VRAM];
args->vram_size = rdev->mc.real_vram_size;
args->vram_visible = (u64)man->size << PAGE_SHIFT;
- if (rdev->stollen_vga_memory)
- args->vram_visible -= radeon_bo_size(rdev->stollen_vga_memory);
- args->vram_visible -= radeon_fbdev_total_size(rdev);
- args->gart_size = rdev->mc.gtt_size - 4096 - RADEON_IB_POOL_SIZE*64*1024;
- for(i = 0; i < RADEON_NUM_RINGS; ++i)
- args->gart_size -= rdev->ring[i].ring_size;
+ args->vram_visible -= rdev->vram_pin_size;
+ args->gart_size = rdev->mc.gtt_size;
+ args->gart_size -= rdev->gart_pin_size;
+
return 0;
}
@@ -252,8 +255,8 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
/* create a gem object to contain this object in */
args->size = roundup(args->size, PAGE_SIZE);
r = radeon_gem_object_create(rdev, args->size, args->alignment,
- args->initial_domain, false,
- false, &gobj);
+ args->initial_domain, args->flags,
+ false, &gobj);
if (r) {
up_read(&rdev->exclusive_lock);
r = radeon_gem_handle_lockup(rdev, r);
@@ -272,6 +275,94 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
return 0;
}
+int radeon_gem_userptr_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ struct radeon_device *rdev = dev->dev_private;
+ struct drm_radeon_gem_userptr *args = data;
+ struct drm_gem_object *gobj;
+ struct radeon_bo *bo;
+ uint32_t handle;
+ int r;
+
+ if (offset_in_page(args->addr | args->size))
+ return -EINVAL;
+
+ /* reject unknown flag values */
+ if (args->flags & ~(RADEON_GEM_USERPTR_READONLY |
+ RADEON_GEM_USERPTR_ANONONLY | RADEON_GEM_USERPTR_VALIDATE |
+ RADEON_GEM_USERPTR_REGISTER))
+ return -EINVAL;
+
+ if (args->flags & RADEON_GEM_USERPTR_READONLY) {
+ /* readonly pages not tested on older hardware */
+ if (rdev->family < CHIP_R600)
+ return -EINVAL;
+
+ } else if (!(args->flags & RADEON_GEM_USERPTR_ANONONLY) ||
+ !(args->flags & RADEON_GEM_USERPTR_REGISTER)) {
+
+ /* if we want to write to it we must require anonymous
+ memory and install a MMU notifier */
+ return -EACCES;
+ }
+
+ down_read(&rdev->exclusive_lock);
+
+ /* create a gem object to contain this object in */
+ r = radeon_gem_object_create(rdev, args->size, 0,
+ RADEON_GEM_DOMAIN_CPU, 0,
+ false, &gobj);
+ if (r)
+ goto handle_lockup;
+
+ bo = gem_to_radeon_bo(gobj);
+ r = radeon_ttm_tt_set_userptr(bo->tbo.ttm, args->addr, args->flags);
+ if (r)
+ goto release_object;
+
+ if (args->flags & RADEON_GEM_USERPTR_REGISTER) {
+ r = radeon_mn_register(bo, args->addr);
+ if (r)
+ goto release_object;
+ }
+
+ if (args->flags & RADEON_GEM_USERPTR_VALIDATE) {
+ down_read(&current->mm->mmap_sem);
+ r = radeon_bo_reserve(bo, true);
+ if (r) {
+ up_read(&current->mm->mmap_sem);
+ goto release_object;
+ }
+
+ radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_GTT);
+ r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
+ radeon_bo_unreserve(bo);
+ up_read(&current->mm->mmap_sem);
+ if (r)
+ goto release_object;
+ }
+
+ r = drm_gem_handle_create(filp, gobj, &handle);
+ /* drop reference from allocate - handle holds it now */
+ drm_gem_object_unreference_unlocked(gobj);
+ if (r)
+ goto handle_lockup;
+
+ args->handle = handle;
+ up_read(&rdev->exclusive_lock);
+ return 0;
+
+release_object:
+ drm_gem_object_unreference_unlocked(gobj);
+
+handle_lockup:
+ up_read(&rdev->exclusive_lock);
+ r = radeon_gem_handle_lockup(rdev, r);
+
+ return r;
+}
+
int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp)
{
@@ -315,6 +406,10 @@ int radeon_mode_dumb_mmap(struct drm_file *filp,
return -ENOENT;
}
robj = gem_to_radeon_bo(gobj);
+ if (radeon_ttm_tt_has_userptr(robj->tbo.ttm)) {
+ drm_gem_object_unreference_unlocked(gobj);
+ return -EPERM;
+ }
*offset_p = radeon_bo_mmap_offset(robj);
drm_gem_object_unreference_unlocked(gobj);
return 0;
@@ -357,17 +452,26 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
struct drm_radeon_gem_wait_idle *args = data;
struct drm_gem_object *gobj;
struct radeon_bo *robj;
- int r;
+ int r = 0;
+ uint32_t cur_placement = 0;
+ long ret;
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL) {
return -ENOENT;
}
robj = gem_to_radeon_bo(gobj);
- r = radeon_bo_wait(robj, NULL, false);
- /* callback hw specific functions if any */
- if (rdev->asic->ioctl_wait_idle)
- robj->rdev->asic->ioctl_wait_idle(rdev, robj);
+
+ ret = reservation_object_wait_timeout_rcu(robj->tbo.resv, true, true, 30 * HZ);
+ if (ret == 0)
+ r = -EBUSY;
+ else if (ret < 0)
+ r = ret;
+
+ /* Flush HDP cache via MMIO if necessary */
+ if (rdev->asic->mmio_hdp_flush &&
+ radeon_mem_type_to_domain(cur_placement) == RADEON_GEM_DOMAIN_VRAM)
+ robj->rdev->asic->mmio_hdp_flush(rdev);
drm_gem_object_unreference_unlocked(gobj);
r = radeon_gem_handle_lockup(rdev, r);
return r;
@@ -461,11 +565,6 @@ int radeon_gem_va_ioctl(struct drm_device *dev, void *data,
args->operation = RADEON_VA_RESULT_ERROR;
return -EINVAL;
}
- if (!(args->flags & RADEON_VM_PAGE_SNOOPED)) {
- dev_err(&dev->pdev->dev, "only supported snooped mapping for now\n");
- args->operation = RADEON_VA_RESULT_ERROR;
- return -EINVAL;
- }
switch (args->operation) {
case RADEON_VA_MAP:
@@ -499,9 +598,9 @@ int radeon_gem_va_ioctl(struct drm_device *dev, void *data,
switch (args->operation) {
case RADEON_VA_MAP:
- if (bo_va->soffset) {
+ if (bo_va->it.start) {
args->operation = RADEON_VA_RESULT_VA_EXIST;
- args->offset = bo_va->soffset;
+ args->offset = bo_va->it.start * RADEON_GPU_PAGE_SIZE;
goto out;
}
r = radeon_vm_bo_set_addr(rdev, bo_va, args->offset, args->flags);
@@ -535,6 +634,11 @@ int radeon_gem_op_ioctl(struct drm_device *dev, void *data,
return -ENOENT;
}
robj = gem_to_radeon_bo(gobj);
+
+ r = -EPERM;
+ if (radeon_ttm_tt_has_userptr(robj->tbo.ttm))
+ goto out;
+
r = radeon_bo_reserve(robj, false);
if (unlikely(r))
goto out;
@@ -572,9 +676,8 @@ int radeon_mode_dumb_create(struct drm_file *file_priv,
args->size = ALIGN(args->size, PAGE_SIZE);
r = radeon_gem_object_create(rdev, args->size, 0,
- RADEON_GEM_DOMAIN_VRAM,
- false, ttm_bo_type_device,
- &gobj);
+ RADEON_GEM_DOMAIN_VRAM, 0,
+ false, &gobj);
if (r)
return -ENOMEM;
diff --git a/drivers/gpu/drm/radeon/radeon_ib.c b/drivers/gpu/drm/radeon/radeon_ib.c
new file mode 100644
index 000000000000..3f39fcca4d07
--- /dev/null
+++ b/drivers/gpu/drm/radeon/radeon_ib.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2008 Advanced Micro Devices, Inc.
+ * Copyright 2008 Red Hat Inc.
+ * Copyright 2009 Jerome Glisse.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: Dave Airlie
+ * Alex Deucher
+ * Jerome Glisse
+ * Christian König
+ */
+#include <drm/drmP.h>
+#include "radeon.h"
+
+/*
+ * IB
+ * IBs (Indirect Buffers) and areas of GPU accessible memory where
+ * commands are stored. You can put a pointer to the IB in the
+ * command ring and the hw will fetch the commands from the IB
+ * and execute them. Generally userspace acceleration drivers
+ * produce command buffers which are send to the kernel and
+ * put in IBs for execution by the requested ring.
+ */
+static int radeon_debugfs_sa_init(struct radeon_device *rdev);
+
+/**
+ * radeon_ib_get - request an IB (Indirect Buffer)
+ *
+ * @rdev: radeon_device pointer
+ * @ring: ring index the IB is associated with
+ * @ib: IB object returned
+ * @size: requested IB size
+ *
+ * Request an IB (all asics). IBs are allocated using the
+ * suballocator.
+ * Returns 0 on success, error on failure.
+ */
+int radeon_ib_get(struct radeon_device *rdev, int ring,
+ struct radeon_ib *ib, struct radeon_vm *vm,
+ unsigned size)
+{
+ int r;
+
+ r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, &ib->sa_bo, size, 256);
+ if (r) {
+ dev_err(rdev->dev, "failed to get a new IB (%d)\n", r);
+ return r;
+ }
+
+ r = radeon_semaphore_create(rdev, &ib->semaphore);
+ if (r) {
+ return r;
+ }
+
+ ib->ring = ring;
+ ib->fence = NULL;
+ ib->ptr = radeon_sa_bo_cpu_addr(ib->sa_bo);
+ ib->vm = vm;
+ if (vm) {
+ /* ib pool is bound at RADEON_VA_IB_OFFSET in virtual address
+ * space and soffset is the offset inside the pool bo
+ */
+ ib->gpu_addr = ib->sa_bo->soffset + RADEON_VA_IB_OFFSET;
+ } else {
+ ib->gpu_addr = radeon_sa_bo_gpu_addr(ib->sa_bo);
+ }
+ ib->is_const_ib = false;
+
+ return 0;
+}
+
+/**
+ * radeon_ib_free - free an IB (Indirect Buffer)
+ *
+ * @rdev: radeon_device pointer
+ * @ib: IB object to free
+ *
+ * Free an IB (all asics).
+ */
+void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib *ib)
+{
+ radeon_semaphore_free(rdev, &ib->semaphore, ib->fence);
+ radeon_sa_bo_free(rdev, &ib->sa_bo, ib->fence);
+ radeon_fence_unref(&ib->fence);
+}
+
+/**
+ * radeon_ib_schedule - schedule an IB (Indirect Buffer) on the ring
+ *
+ * @rdev: radeon_device pointer
+ * @ib: IB object to schedule
+ * @const_ib: Const IB to schedule (SI only)
+ * @hdp_flush: Whether or not to perform an HDP cache flush
+ *
+ * Schedule an IB on the associated ring (all asics).
+ * Returns 0 on success, error on failure.
+ *
+ * On SI, there are two parallel engines fed from the primary ring,
+ * the CE (Constant Engine) and the DE (Drawing Engine). Since
+ * resource descriptors have moved to memory, the CE allows you to
+ * prime the caches while the DE is updating register state so that
+ * the resource descriptors will be already in cache when the draw is
+ * processed. To accomplish this, the userspace driver submits two
+ * IBs, one for the CE and one for the DE. If there is a CE IB (called
+ * a CONST_IB), it will be put on the ring prior to the DE IB. Prior
+ * to SI there was just a DE IB.
+ */
+int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib,
+ struct radeon_ib *const_ib, bool hdp_flush)
+{
+ struct radeon_ring *ring = &rdev->ring[ib->ring];
+ int r = 0;
+
+ if (!ib->length_dw || !ring->ready) {
+ /* TODO: Nothings in the ib we should report. */
+ dev_err(rdev->dev, "couldn't schedule ib\n");
+ return -EINVAL;
+ }
+
+ /* 64 dwords should be enough for fence too */
+ r = radeon_ring_lock(rdev, ring, 64 + RADEON_NUM_SYNCS * 8);
+ if (r) {
+ dev_err(rdev->dev, "scheduling IB failed (%d).\n", r);
+ return r;
+ }
+
+ /* grab a vm id if necessary */
+ if (ib->vm) {
+ struct radeon_fence *vm_id_fence;
+ vm_id_fence = radeon_vm_grab_id(rdev, ib->vm, ib->ring);
+ radeon_semaphore_sync_fence(ib->semaphore, vm_id_fence);
+ }
+
+ /* sync with other rings */
+ r = radeon_semaphore_sync_rings(rdev, ib->semaphore, ib->ring);
+ if (r) {
+ dev_err(rdev->dev, "failed to sync rings (%d)\n", r);
+ radeon_ring_unlock_undo(rdev, ring);
+ return r;
+ }
+
+ if (ib->vm)
+ radeon_vm_flush(rdev, ib->vm, ib->ring);
+
+ if (const_ib) {
+ radeon_ring_ib_execute(rdev, const_ib->ring, const_ib);
+ radeon_semaphore_free(rdev, &const_ib->semaphore, NULL);
+ }
+ radeon_ring_ib_execute(rdev, ib->ring, ib);
+ r = radeon_fence_emit(rdev, &ib->fence, ib->ring);
+ if (r) {
+ dev_err(rdev->dev, "failed to emit fence for new IB (%d)\n", r);
+ radeon_ring_unlock_undo(rdev, ring);
+ return r;
+ }
+ if (const_ib) {
+ const_ib->fence = radeon_fence_ref(ib->fence);
+ }
+
+ if (ib->vm)
+ radeon_vm_fence(rdev, ib->vm, ib->fence);
+
+ radeon_ring_unlock_commit(rdev, ring, hdp_flush);
+ return 0;
+}
+
+/**
+ * radeon_ib_pool_init - Init the IB (Indirect Buffer) pool
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Initialize the suballocator to manage a pool of memory
+ * for use as IBs (all asics).
+ * Returns 0 on success, error on failure.
+ */
+int radeon_ib_pool_init(struct radeon_device *rdev)
+{
+ int r;
+
+ if (rdev->ib_pool_ready) {
+ return 0;
+ }
+
+ if (rdev->family >= CHIP_BONAIRE) {
+ r = radeon_sa_bo_manager_init(rdev, &rdev->ring_tmp_bo,
+ RADEON_IB_POOL_SIZE*64*1024,
+ RADEON_GPU_PAGE_SIZE,
+ RADEON_GEM_DOMAIN_GTT,
+ RADEON_GEM_GTT_WC);
+ } else {
+ /* Before CIK, it's better to stick to cacheable GTT due
+ * to the command stream checking
+ */
+ r = radeon_sa_bo_manager_init(rdev, &rdev->ring_tmp_bo,
+ RADEON_IB_POOL_SIZE*64*1024,
+ RADEON_GPU_PAGE_SIZE,
+ RADEON_GEM_DOMAIN_GTT, 0);
+ }
+ if (r) {
+ return r;
+ }
+
+ r = radeon_sa_bo_manager_start(rdev, &rdev->ring_tmp_bo);
+ if (r) {
+ return r;
+ }
+
+ rdev->ib_pool_ready = true;
+ if (radeon_debugfs_sa_init(rdev)) {
+ dev_err(rdev->dev, "failed to register debugfs file for SA\n");
+ }
+ return 0;
+}
+
+/**
+ * radeon_ib_pool_fini - Free the IB (Indirect Buffer) pool
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Tear down the suballocator managing the pool of memory
+ * for use as IBs (all asics).
+ */
+void radeon_ib_pool_fini(struct radeon_device *rdev)
+{
+ if (rdev->ib_pool_ready) {
+ radeon_sa_bo_manager_suspend(rdev, &rdev->ring_tmp_bo);
+ radeon_sa_bo_manager_fini(rdev, &rdev->ring_tmp_bo);
+ rdev->ib_pool_ready = false;
+ }
+}
+
+/**
+ * radeon_ib_ring_tests - test IBs on the rings
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Test an IB (Indirect Buffer) on each ring.
+ * If the test fails, disable the ring.
+ * Returns 0 on success, error if the primary GFX ring
+ * IB test fails.
+ */
+int radeon_ib_ring_tests(struct radeon_device *rdev)
+{
+ unsigned i;
+ int r;
+
+ for (i = 0; i < RADEON_NUM_RINGS; ++i) {
+ struct radeon_ring *ring = &rdev->ring[i];
+
+ if (!ring->ready)
+ continue;
+
+ r = radeon_ib_test(rdev, i, ring);
+ if (r) {
+ radeon_fence_driver_force_completion(rdev, i);
+ ring->ready = false;
+ rdev->needs_reset = false;
+
+ if (i == RADEON_RING_TYPE_GFX_INDEX) {
+ /* oh, oh, that's really bad */
+ DRM_ERROR("radeon: failed testing IB on GFX ring (%d).\n", r);
+ rdev->accel_working = false;
+ return r;
+
+ } else {
+ /* still not good, but we can live with it */
+ DRM_ERROR("radeon: failed testing IB on ring %d (%d).\n", i, r);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Debugfs info
+ */
+#if defined(CONFIG_DEBUG_FS)
+
+static int radeon_debugfs_sa_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct radeon_device *rdev = dev->dev_private;
+
+ radeon_sa_bo_dump_debug_info(&rdev->ring_tmp_bo, m);
+
+ return 0;
+
+}
+
+static struct drm_info_list radeon_debugfs_sa_list[] = {
+ {"radeon_sa_info", &radeon_debugfs_sa_info, 0, NULL},
+};
+
+#endif
+
+static int radeon_debugfs_sa_init(struct radeon_device *rdev)
+{
+#if defined(CONFIG_DEBUG_FS)
+ return radeon_debugfs_add_files(rdev, radeon_debugfs_sa_list, 1);
+#else
+ return 0;
+#endif
+}
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 16807afab362..7784911d78ef 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -88,23 +88,6 @@ static void radeon_hotplug_work_func(struct work_struct *work)
}
/**
- * radeon_irq_reset_work_func - execute gpu reset
- *
- * @work: work struct
- *
- * Execute scheduled gpu reset (cayman+).
- * This function is called when the irq handler
- * thinks we need a gpu reset.
- */
-static void radeon_irq_reset_work_func(struct work_struct *work)
-{
- struct radeon_device *rdev = container_of(work, struct radeon_device,
- reset_work);
-
- radeon_gpu_reset(rdev);
-}
-
-/**
* radeon_driver_irq_preinstall_kms - drm irq preinstall callback
*
* @dev: drm dev pointer
@@ -284,7 +267,6 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func);
INIT_WORK(&rdev->audio_work, r600_audio_update_hdmi);
- INIT_WORK(&rdev->reset_work, radeon_irq_reset_work_func);
rdev->irq.installed = true;
r = drm_irq_install(rdev->ddev, rdev->ddev->pdev->irq);
@@ -342,6 +324,21 @@ void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev, int ring)
}
/**
+ * radeon_irq_kms_sw_irq_get_delayed - enable software interrupt
+ *
+ * @rdev: radeon device pointer
+ * @ring: ring whose interrupt you want to enable
+ *
+ * Enables the software interrupt for a specific ring (all asics).
+ * The software interrupt is generally used to signal a fence on
+ * a particular ring.
+ */
+bool radeon_irq_kms_sw_irq_get_delayed(struct radeon_device *rdev, int ring)
+{
+ return atomic_inc_return(&rdev->irq.ring_int[ring]) == 1;
+}
+
+/**
* radeon_irq_kms_sw_irq_put - disable software interrupt
*
* @rdev: radeon device pointer
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index d25ae6acfd5a..8309b11e674d 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -254,7 +254,18 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file
}
break;
case RADEON_INFO_ACCEL_WORKING2:
- *value = rdev->accel_working;
+ if (rdev->family == CHIP_HAWAII) {
+ if (rdev->accel_working) {
+ if (rdev->new_fw)
+ *value = 3;
+ else
+ *value = 2;
+ } else {
+ *value = 0;
+ }
+ } else {
+ *value = rdev->accel_working;
+ }
break;
case RADEON_INFO_TILING_CONFIG:
if (rdev->family >= CHIP_BONAIRE)
@@ -874,5 +885,6 @@ const struct drm_ioctl_desc radeon_ioctls_kms[] = {
DRM_IOCTL_DEF_DRV(RADEON_GEM_BUSY, radeon_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(RADEON_GEM_VA, radeon_gem_va_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(RADEON_GEM_OP, radeon_gem_op_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(RADEON_GEM_USERPTR, radeon_gem_userptr_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
};
int radeon_max_kms_ioctl = ARRAY_SIZE(radeon_ioctls_kms);
diff --git a/drivers/gpu/drm/radeon/radeon_mn.c b/drivers/gpu/drm/radeon/radeon_mn.c
new file mode 100644
index 000000000000..a69bd441dd2d
--- /dev/null
+++ b/drivers/gpu/drm/radeon/radeon_mn.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2014 Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors:
+ * Christian König <christian.koenig@amd.com>
+ */
+
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/mmu_notifier.h>
+#include <drm/drmP.h>
+#include <drm/drm.h>
+
+#include "radeon.h"
+
+struct radeon_mn {
+ /* constant after initialisation */
+ struct radeon_device *rdev;
+ struct mm_struct *mm;
+ struct mmu_notifier mn;
+
+ /* only used on destruction */
+ struct work_struct work;
+
+ /* protected by rdev->mn_lock */
+ struct hlist_node node;
+
+ /* objects protected by lock */
+ struct mutex lock;
+ struct rb_root objects;
+};
+
+/**
+ * radeon_mn_destroy - destroy the rmn
+ *
+ * @work: previously sheduled work item
+ *
+ * Lazy destroys the notifier from a work item
+ */
+static void radeon_mn_destroy(struct work_struct *work)
+{
+ struct radeon_mn *rmn = container_of(work, struct radeon_mn, work);
+ struct radeon_device *rdev = rmn->rdev;
+ struct radeon_bo *bo, *next;
+
+ mutex_lock(&rdev->mn_lock);
+ mutex_lock(&rmn->lock);
+ hash_del(&rmn->node);
+ rbtree_postorder_for_each_entry_safe(bo, next, &rmn->objects, mn_it.rb) {
+ interval_tree_remove(&bo->mn_it, &rmn->objects);
+ bo->mn = NULL;
+ }
+ mutex_unlock(&rmn->lock);
+ mutex_unlock(&rdev->mn_lock);
+ mmu_notifier_unregister(&rmn->mn, rmn->mm);
+ kfree(rmn);
+}
+
+/**
+ * radeon_mn_release - callback to notify about mm destruction
+ *
+ * @mn: our notifier
+ * @mn: the mm this callback is about
+ *
+ * Shedule a work item to lazy destroy our notifier.
+ */
+static void radeon_mn_release(struct mmu_notifier *mn,
+ struct mm_struct *mm)
+{
+ struct radeon_mn *rmn = container_of(mn, struct radeon_mn, mn);
+ INIT_WORK(&rmn->work, radeon_mn_destroy);
+ schedule_work(&rmn->work);
+}
+
+/**
+ * radeon_mn_invalidate_range_start - callback to notify about mm change
+ *
+ * @mn: our notifier
+ * @mn: the mm this callback is about
+ * @start: start of updated range
+ * @end: end of updated range
+ *
+ * We block for all BOs between start and end to be idle and
+ * unmap them by move them into system domain again.
+ */
+static void radeon_mn_invalidate_range_start(struct mmu_notifier *mn,
+ struct mm_struct *mm,
+ unsigned long start,
+ unsigned long end)
+{
+ struct radeon_mn *rmn = container_of(mn, struct radeon_mn, mn);
+ struct interval_tree_node *it;
+
+ /* notification is exclusive, but interval is inclusive */
+ end -= 1;
+
+ mutex_lock(&rmn->lock);
+
+ it = interval_tree_iter_first(&rmn->objects, start, end);
+ while (it) {
+ struct radeon_bo *bo;
+ struct fence *fence;
+ int r;
+
+ bo = container_of(it, struct radeon_bo, mn_it);
+ it = interval_tree_iter_next(it, start, end);
+
+ r = radeon_bo_reserve(bo, true);
+ if (r) {
+ DRM_ERROR("(%d) failed to reserve user bo\n", r);
+ continue;
+ }
+
+ fence = reservation_object_get_excl(bo->tbo.resv);
+ if (fence) {
+ r = radeon_fence_wait((struct radeon_fence *)fence, false);
+ if (r)
+ DRM_ERROR("(%d) failed to wait for user bo\n", r);
+ }
+
+ radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_CPU);
+ r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
+ if (r)
+ DRM_ERROR("(%d) failed to validate user bo\n", r);
+
+ radeon_bo_unreserve(bo);
+ }
+
+ mutex_unlock(&rmn->lock);
+}
+
+static const struct mmu_notifier_ops radeon_mn_ops = {
+ .release = radeon_mn_release,
+ .invalidate_range_start = radeon_mn_invalidate_range_start,
+};
+
+/**
+ * radeon_mn_get - create notifier context
+ *
+ * @rdev: radeon device pointer
+ *
+ * Creates a notifier context for current->mm.
+ */
+static struct radeon_mn *radeon_mn_get(struct radeon_device *rdev)
+{
+ struct mm_struct *mm = current->mm;
+ struct radeon_mn *rmn;
+ int r;
+
+ down_write(&mm->mmap_sem);
+ mutex_lock(&rdev->mn_lock);
+
+ hash_for_each_possible(rdev->mn_hash, rmn, node, (unsigned long)mm)
+ if (rmn->mm == mm)
+ goto release_locks;
+
+ rmn = kzalloc(sizeof(*rmn), GFP_KERNEL);
+ if (!rmn) {
+ rmn = ERR_PTR(-ENOMEM);
+ goto release_locks;
+ }
+
+ rmn->rdev = rdev;
+ rmn->mm = mm;
+ rmn->mn.ops = &radeon_mn_ops;
+ mutex_init(&rmn->lock);
+ rmn->objects = RB_ROOT;
+
+ r = __mmu_notifier_register(&rmn->mn, mm);
+ if (r)
+ goto free_rmn;
+
+ hash_add(rdev->mn_hash, &rmn->node, (unsigned long)mm);
+
+release_locks:
+ mutex_unlock(&rdev->mn_lock);
+ up_write(&mm->mmap_sem);
+
+ return rmn;
+
+free_rmn:
+ mutex_unlock(&rdev->mn_lock);
+ up_write(&mm->mmap_sem);
+ kfree(rmn);
+
+ return ERR_PTR(r);
+}
+
+/**
+ * radeon_mn_register - register a BO for notifier updates
+ *
+ * @bo: radeon buffer object
+ * @addr: userptr addr we should monitor
+ *
+ * Registers an MMU notifier for the given BO at the specified address.
+ * Returns 0 on success, -ERRNO if anything goes wrong.
+ */
+int radeon_mn_register(struct radeon_bo *bo, unsigned long addr)
+{
+ unsigned long end = addr + radeon_bo_size(bo) - 1;
+ struct radeon_device *rdev = bo->rdev;
+ struct radeon_mn *rmn;
+ struct interval_tree_node *it;
+
+ rmn = radeon_mn_get(rdev);
+ if (IS_ERR(rmn))
+ return PTR_ERR(rmn);
+
+ mutex_lock(&rmn->lock);
+
+ it = interval_tree_iter_first(&rmn->objects, addr, end);
+ if (it) {
+ mutex_unlock(&rmn->lock);
+ return -EEXIST;
+ }
+
+ bo->mn = rmn;
+ bo->mn_it.start = addr;
+ bo->mn_it.last = end;
+ interval_tree_insert(&bo->mn_it, &rmn->objects);
+
+ mutex_unlock(&rmn->lock);
+
+ return 0;
+}
+
+/**
+ * radeon_mn_unregister - unregister a BO for notifier updates
+ *
+ * @bo: radeon buffer object
+ *
+ * Remove any registration of MMU notifier updates from the buffer object.
+ */
+void radeon_mn_unregister(struct radeon_bo *bo)
+{
+ struct radeon_device *rdev = bo->rdev;
+ struct radeon_mn *rmn;
+
+ mutex_lock(&rdev->mn_lock);
+ rmn = bo->mn;
+ if (rmn == NULL) {
+ mutex_unlock(&rdev->mn_lock);
+ return;
+ }
+
+ mutex_lock(&rmn->lock);
+ interval_tree_remove(&bo->mn_it, &rmn->objects);
+ bo->mn = NULL;
+ mutex_unlock(&rmn->lock);
+ mutex_unlock(&rdev->mn_lock);
+}
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 0592ddb0904b..04db2fdd8692 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -685,10 +685,11 @@ extern bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder,
extern u16 radeon_encoder_get_dp_bridge_encoder_id(struct drm_encoder *encoder);
extern u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *connector);
-extern bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector);
extern bool radeon_connector_is_dp12_capable(struct drm_connector *connector);
extern int radeon_get_monitor_bpc(struct drm_connector *connector);
+extern struct edid *radeon_connector_edid(struct drm_connector *connector);
+
extern void radeon_connector_hotplug(struct drm_connector *connector);
extern int radeon_dp_mode_valid_helper(struct drm_connector *connector,
struct drm_display_mode *mode);
@@ -738,7 +739,6 @@ extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c,
extern void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector);
extern void radeon_router_select_cd_port(struct radeon_connector *radeon_connector);
extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector, bool use_aux);
-extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector);
@@ -777,6 +777,7 @@ extern void atombios_digital_setup(struct drm_encoder *encoder, int action);
extern int atombios_get_encoder_mode(struct drm_encoder *encoder);
extern bool atombios_set_edp_panel_power(struct drm_connector *connector, int action);
extern void radeon_encoder_set_active_device(struct drm_encoder *encoder);
+extern bool radeon_encoder_is_digital(struct drm_encoder *encoder);
extern void radeon_crtc_load_lut(struct drm_crtc *crtc);
extern int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 6c717b257d6d..99a960a4f302 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -46,16 +46,6 @@ static void radeon_bo_clear_surface_reg(struct radeon_bo *bo);
* function are calling it.
*/
-static void radeon_bo_clear_va(struct radeon_bo *bo)
-{
- struct radeon_bo_va *bo_va, *tmp;
-
- list_for_each_entry_safe(bo_va, tmp, &bo->va, bo_list) {
- /* remove from all vm address space */
- radeon_vm_bo_rmv(bo->rdev, bo_va);
- }
-}
-
static void radeon_update_memory_usage(struct radeon_bo *bo,
unsigned mem_type, int sign)
{
@@ -85,12 +75,13 @@ static void radeon_ttm_bo_destroy(struct ttm_buffer_object *tbo)
bo = container_of(tbo, struct radeon_bo, tbo);
radeon_update_memory_usage(bo, bo->tbo.mem.mem_type, -1);
+ radeon_mn_unregister(bo);
mutex_lock(&bo->rdev->gem.mutex);
list_del_init(&bo->list);
mutex_unlock(&bo->rdev->gem.mutex);
radeon_bo_clear_surface_reg(bo);
- radeon_bo_clear_va(bo);
+ WARN_ON(!list_empty(&bo->va));
drm_gem_object_release(&bo->gem_base);
kfree(bo);
}
@@ -106,47 +97,80 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)
{
u32 c = 0, i;
- rbo->placement.fpfn = 0;
- rbo->placement.lpfn = 0;
rbo->placement.placement = rbo->placements;
rbo->placement.busy_placement = rbo->placements;
if (domain & RADEON_GEM_DOMAIN_VRAM)
- rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_VRAM;
+ rbo->placements[c++].flags = TTM_PL_FLAG_WC |
+ TTM_PL_FLAG_UNCACHED |
+ TTM_PL_FLAG_VRAM;
+
if (domain & RADEON_GEM_DOMAIN_GTT) {
- if (rbo->rdev->flags & RADEON_IS_AGP) {
- rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_TT;
+ if (rbo->flags & RADEON_GEM_GTT_UC) {
+ rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED |
+ TTM_PL_FLAG_TT;
+
+ } else if ((rbo->flags & RADEON_GEM_GTT_WC) ||
+ (rbo->rdev->flags & RADEON_IS_AGP)) {
+ rbo->placements[c++].flags = TTM_PL_FLAG_WC |
+ TTM_PL_FLAG_UNCACHED |
+ TTM_PL_FLAG_TT;
} else {
- rbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_TT;
+ rbo->placements[c++].flags = TTM_PL_FLAG_CACHED |
+ TTM_PL_FLAG_TT;
}
}
+
if (domain & RADEON_GEM_DOMAIN_CPU) {
- if (rbo->rdev->flags & RADEON_IS_AGP) {
- rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_SYSTEM;
+ if (rbo->flags & RADEON_GEM_GTT_UC) {
+ rbo->placements[c++].flags = TTM_PL_FLAG_UNCACHED |
+ TTM_PL_FLAG_SYSTEM;
+
+ } else if ((rbo->flags & RADEON_GEM_GTT_WC) ||
+ rbo->rdev->flags & RADEON_IS_AGP) {
+ rbo->placements[c++].flags = TTM_PL_FLAG_WC |
+ TTM_PL_FLAG_UNCACHED |
+ TTM_PL_FLAG_SYSTEM;
} else {
- rbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM;
+ rbo->placements[c++].flags = TTM_PL_FLAG_CACHED |
+ TTM_PL_FLAG_SYSTEM;
}
}
if (!c)
- rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+ rbo->placements[c++].flags = TTM_PL_MASK_CACHING |
+ TTM_PL_FLAG_SYSTEM;
+
rbo->placement.num_placement = c;
rbo->placement.num_busy_placement = c;
+ for (i = 0; i < c; ++i) {
+ rbo->placements[i].fpfn = 0;
+ if ((rbo->flags & RADEON_GEM_CPU_ACCESS) &&
+ (rbo->placements[i].flags & TTM_PL_FLAG_VRAM))
+ rbo->placements[i].lpfn =
+ rbo->rdev->mc.visible_vram_size >> PAGE_SHIFT;
+ else
+ rbo->placements[i].lpfn = 0;
+ }
+
/*
* Use two-ended allocation depending on the buffer size to
* improve fragmentation quality.
* 512kb was measured as the most optimal number.
*/
- if (rbo->tbo.mem.size > 512 * 1024) {
+ if (!((rbo->flags & RADEON_GEM_CPU_ACCESS) &&
+ (rbo->placements[i].flags & TTM_PL_FLAG_VRAM)) &&
+ rbo->tbo.mem.size > 512 * 1024) {
for (i = 0; i < c; i++) {
- rbo->placements[i] |= TTM_PL_FLAG_TOPDOWN;
+ rbo->placements[i].flags |= TTM_PL_FLAG_TOPDOWN;
}
}
}
int radeon_bo_create(struct radeon_device *rdev,
- unsigned long size, int byte_align, bool kernel, u32 domain,
- struct sg_table *sg, struct radeon_bo **bo_ptr)
+ unsigned long size, int byte_align, bool kernel,
+ u32 domain, u32 flags, struct sg_table *sg,
+ struct reservation_object *resv,
+ struct radeon_bo **bo_ptr)
{
struct radeon_bo *bo;
enum ttm_bo_type type;
@@ -183,12 +207,18 @@ int radeon_bo_create(struct radeon_device *rdev,
bo->initial_domain = domain & (RADEON_GEM_DOMAIN_VRAM |
RADEON_GEM_DOMAIN_GTT |
RADEON_GEM_DOMAIN_CPU);
+
+ bo->flags = flags;
+ /* PCI GART is always snooped */
+ if (!(rdev->flags & RADEON_IS_PCIE))
+ bo->flags &= ~(RADEON_GEM_GTT_WC | RADEON_GEM_GTT_UC);
+
radeon_ttm_placement_from_domain(bo, domain);
/* Kernel allocation are uninterruptible */
down_read(&rdev->pm.mclk_lock);
r = ttm_bo_init(&rdev->mman.bdev, &bo->tbo, size, type,
&bo->placement, page_align, !kernel, NULL,
- acc_size, sg, &radeon_ttm_bo_destroy);
+ acc_size, sg, resv, &radeon_ttm_bo_destroy);
up_read(&rdev->pm.mclk_lock);
if (unlikely(r != 0)) {
return r;
@@ -232,6 +262,15 @@ void radeon_bo_kunmap(struct radeon_bo *bo)
ttm_bo_kunmap(&bo->kmap);
}
+struct radeon_bo *radeon_bo_ref(struct radeon_bo *bo)
+{
+ if (bo == NULL)
+ return NULL;
+
+ ttm_bo_reference(&bo->tbo);
+ return bo;
+}
+
void radeon_bo_unref(struct radeon_bo **bo)
{
struct ttm_buffer_object *tbo;
@@ -241,9 +280,7 @@ void radeon_bo_unref(struct radeon_bo **bo)
return;
rdev = (*bo)->rdev;
tbo = &((*bo)->tbo);
- down_read(&rdev->pm.mclk_lock);
ttm_bo_unref(&tbo);
- up_read(&rdev->pm.mclk_lock);
if (tbo == NULL)
*bo = NULL;
}
@@ -253,6 +290,9 @@ int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset,
{
int r, i;
+ if (radeon_ttm_tt_has_userptr(bo->tbo.ttm))
+ return -EPERM;
+
if (bo->pin_count) {
bo->pin_count++;
if (gpu_addr)
@@ -272,29 +312,31 @@ int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset,
return 0;
}
radeon_ttm_placement_from_domain(bo, domain);
- if (domain == RADEON_GEM_DOMAIN_VRAM) {
+ for (i = 0; i < bo->placement.num_placement; i++) {
/* force to pin into visible video ram */
- bo->placement.lpfn = bo->rdev->mc.visible_vram_size >> PAGE_SHIFT;
- }
- if (max_offset) {
- u64 lpfn = max_offset >> PAGE_SHIFT;
-
- if (!bo->placement.lpfn)
- bo->placement.lpfn = bo->rdev->mc.gtt_size >> PAGE_SHIFT;
+ if ((bo->placements[i].flags & TTM_PL_FLAG_VRAM) &&
+ !(bo->flags & RADEON_GEM_NO_CPU_ACCESS) &&
+ (!max_offset || max_offset > bo->rdev->mc.visible_vram_size))
+ bo->placements[i].lpfn =
+ bo->rdev->mc.visible_vram_size >> PAGE_SHIFT;
+ else
+ bo->placements[i].lpfn = max_offset >> PAGE_SHIFT;
- if (lpfn < bo->placement.lpfn)
- bo->placement.lpfn = lpfn;
+ bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
}
- for (i = 0; i < bo->placement.num_placement; i++)
- bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+
r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
if (likely(r == 0)) {
bo->pin_count = 1;
if (gpu_addr != NULL)
*gpu_addr = radeon_bo_gpu_offset(bo);
- }
- if (unlikely(r != 0))
+ if (domain == RADEON_GEM_DOMAIN_VRAM)
+ bo->rdev->vram_pin_size += radeon_bo_size(bo);
+ else
+ bo->rdev->gart_pin_size += radeon_bo_size(bo);
+ } else {
dev_err(bo->rdev->dev, "%p pin failed\n", bo);
+ }
return r;
}
@@ -314,11 +356,19 @@ int radeon_bo_unpin(struct radeon_bo *bo)
bo->pin_count--;
if (bo->pin_count)
return 0;
- for (i = 0; i < bo->placement.num_placement; i++)
- bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
+ for (i = 0; i < bo->placement.num_placement; i++) {
+ bo->placements[i].lpfn = 0;
+ bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT;
+ }
r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
- if (unlikely(r != 0))
+ if (likely(r == 0)) {
+ if (bo->tbo.mem.mem_type == TTM_PL_VRAM)
+ bo->rdev->vram_pin_size -= radeon_bo_size(bo);
+ else
+ bo->rdev->gart_pin_size -= radeon_bo_size(bo);
+ } else {
dev_err(bo->rdev->dev, "%p validate failed for unpin\n", bo);
+ }
return r;
}
@@ -438,7 +488,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev,
u64 bytes_moved = 0, initial_bytes_moved;
u64 bytes_moved_threshold = radeon_bo_get_threshold_for_moves(rdev);
- r = ttm_eu_reserve_buffers(ticket, head);
+ r = ttm_eu_reserve_buffers(ticket, head, true);
if (unlikely(r != 0)) {
return r;
}
@@ -447,6 +497,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev,
bo = lobj->robj;
if (!bo->pin_count) {
u32 domain = lobj->prefered_domains;
+ u32 allowed = lobj->allowed_domains;
u32 current_domain =
radeon_mem_type_to_domain(bo->tbo.mem.mem_type);
@@ -458,7 +509,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev,
* into account. We don't want to disallow buffer moves
* completely.
*/
- if ((lobj->allowed_domains & current_domain) != 0 &&
+ if ((allowed & current_domain) != 0 &&
(domain & current_domain) == 0 && /* will be moved */
bytes_moved > bytes_moved_threshold) {
/* don't move it */
@@ -468,7 +519,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev,
retry:
radeon_ttm_placement_from_domain(bo, domain);
if (ring == R600_RING_TYPE_UVD_INDEX)
- radeon_uvd_force_into_uvd_segment(bo);
+ radeon_uvd_force_into_uvd_segment(bo, allowed);
initial_bytes_moved = atomic64_read(&rdev->num_bytes_moved);
r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
@@ -710,7 +761,7 @@ int radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
/* hurrah the memory is not visible ! */
radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM);
- rbo->placement.lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT;
+ rbo->placements[0].lpfn = rdev->mc.visible_vram_size >> PAGE_SHIFT;
r = ttm_bo_validate(bo, &rbo->placement, false, false);
if (unlikely(r == -ENOMEM)) {
radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_GTT);
@@ -734,12 +785,10 @@ int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, bool no_wait)
r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, NULL);
if (unlikely(r != 0))
return r;
- spin_lock(&bo->tbo.bdev->fence_lock);
if (mem_type)
*mem_type = bo->tbo.mem.mem_type;
- if (bo->tbo.sync_obj)
- r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
- spin_unlock(&bo->tbo.bdev->fence_lock);
+
+ r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
ttm_bo_unreserve(&bo->tbo);
return r;
}
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index 5a873f31a171..1b8ec7917154 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -124,11 +124,13 @@ extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
extern int radeon_bo_create(struct radeon_device *rdev,
unsigned long size, int byte_align,
- bool kernel, u32 domain,
+ bool kernel, u32 domain, u32 flags,
struct sg_table *sg,
+ struct reservation_object *resv,
struct radeon_bo **bo_ptr);
extern int radeon_bo_kmap(struct radeon_bo *bo, void **ptr);
extern void radeon_bo_kunmap(struct radeon_bo *bo);
+extern struct radeon_bo *radeon_bo_ref(struct radeon_bo *bo);
extern void radeon_bo_unref(struct radeon_bo **bo);
extern int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr);
extern int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain,
@@ -170,7 +172,8 @@ static inline void * radeon_sa_bo_cpu_addr(struct radeon_sa_bo *sa_bo)
extern int radeon_sa_bo_manager_init(struct radeon_device *rdev,
struct radeon_sa_manager *sa_manager,
- unsigned size, u32 align, u32 domain);
+ unsigned size, u32 align, u32 domain,
+ u32 flags);
extern void radeon_sa_bo_manager_fini(struct radeon_device *rdev,
struct radeon_sa_manager *sa_manager);
extern int radeon_sa_bo_manager_start(struct radeon_device *rdev,
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index e447e390d09a..32522cc940a1 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -460,10 +460,6 @@ static ssize_t radeon_get_dpm_state(struct device *dev,
struct radeon_device *rdev = ddev->dev_private;
enum radeon_pm_state_type pm = rdev->pm.dpm.user_state;
- if ((rdev->flags & RADEON_IS_PX) &&
- (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
- return snprintf(buf, PAGE_SIZE, "off\n");
-
return snprintf(buf, PAGE_SIZE, "%s\n",
(pm == POWER_STATE_TYPE_BATTERY) ? "battery" :
(pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance");
@@ -477,11 +473,6 @@ static ssize_t radeon_set_dpm_state(struct device *dev,
struct drm_device *ddev = dev_get_drvdata(dev);
struct radeon_device *rdev = ddev->dev_private;
- /* Can't set dpm state when the card is off */
- if ((rdev->flags & RADEON_IS_PX) &&
- (ddev->switch_power_state != DRM_SWITCH_POWER_ON))
- return -EINVAL;
-
mutex_lock(&rdev->pm.mutex);
if (strncmp("battery", buf, strlen("battery")) == 0)
rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY;
@@ -495,7 +486,12 @@ static ssize_t radeon_set_dpm_state(struct device *dev,
goto fail;
}
mutex_unlock(&rdev->pm.mutex);
- radeon_pm_compute_clocks(rdev);
+
+ /* Can't set dpm state when the card is off */
+ if (!(rdev->flags & RADEON_IS_PX) ||
+ (ddev->switch_power_state == DRM_SWITCH_POWER_ON))
+ radeon_pm_compute_clocks(rdev);
+
fail:
return count;
}
@@ -1303,10 +1299,6 @@ int radeon_pm_init(struct radeon_device *rdev)
case CHIP_RS780:
case CHIP_RS880:
case CHIP_RV770:
- case CHIP_BARTS:
- case CHIP_TURKS:
- case CHIP_CAICOS:
- case CHIP_CAYMAN:
/* DPM requires the RLC, RV770+ dGPU requires SMC */
if (!rdev->rlc_fw)
rdev->pm.pm_method = PM_METHOD_PROFILE;
@@ -1330,6 +1322,10 @@ int radeon_pm_init(struct radeon_device *rdev)
case CHIP_PALM:
case CHIP_SUMO:
case CHIP_SUMO2:
+ case CHIP_BARTS:
+ case CHIP_TURKS:
+ case CHIP_CAICOS:
+ case CHIP_CAYMAN:
case CHIP_ARUBA:
case CHIP_TAHITI:
case CHIP_PITCAIRN:
@@ -1400,9 +1396,7 @@ static void radeon_pm_fini_old(struct radeon_device *rdev)
}
radeon_hwmon_fini(rdev);
-
- if (rdev->pm.power_state)
- kfree(rdev->pm.power_state);
+ kfree(rdev->pm.power_state);
}
static void radeon_pm_fini_dpm(struct radeon_device *rdev)
@@ -1421,9 +1415,7 @@ static void radeon_pm_fini_dpm(struct radeon_device *rdev)
radeon_dpm_fini(rdev);
radeon_hwmon_fini(rdev);
-
- if (rdev->pm.power_state)
- kfree(rdev->pm.power_state);
+ kfree(rdev->pm.power_state);
}
void radeon_pm_fini(struct radeon_device *rdev)
@@ -1564,7 +1556,7 @@ static bool radeon_pm_in_vbl(struct radeon_device *rdev)
if (rdev->pm.active_crtcs & (1 << crtc)) {
vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, 0, &vpos, &hpos, NULL, NULL);
if ((vbl_status & DRM_SCANOUTPOS_VALID) &&
- !(vbl_status & DRM_SCANOUTPOS_INVBL))
+ !(vbl_status & DRM_SCANOUTPOS_IN_VBLANK))
in_vbl = false;
}
}
diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c
index 28d71070c389..f3609c97496b 100644
--- a/drivers/gpu/drm/radeon/radeon_prime.c
+++ b/drivers/gpu/drm/radeon/radeon_prime.c
@@ -27,6 +27,7 @@
#include "radeon.h"
#include <drm/radeon_drm.h>
+#include <linux/dma-buf.h>
struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj)
{
@@ -57,15 +58,18 @@ void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
}
struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev,
- size_t size,
+ struct dma_buf_attachment *attach,
struct sg_table *sg)
{
+ struct reservation_object *resv = attach->dmabuf->resv;
struct radeon_device *rdev = dev->dev_private;
struct radeon_bo *bo;
int ret;
- ret = radeon_bo_create(rdev, size, PAGE_SIZE, false,
- RADEON_GEM_DOMAIN_GTT, sg, &bo);
+ ww_mutex_lock(&resv->lock, NULL);
+ ret = radeon_bo_create(rdev, attach->dmabuf->size, PAGE_SIZE, false,
+ RADEON_GEM_DOMAIN_GTT, 0, sg, resv, &bo);
+ ww_mutex_unlock(&resv->lock);
if (ret)
return ERR_PTR(ret);
@@ -111,3 +115,13 @@ struct reservation_object *radeon_gem_prime_res_obj(struct drm_gem_object *obj)
return bo->tbo.resv;
}
+
+struct dma_buf *radeon_gem_prime_export(struct drm_device *dev,
+ struct drm_gem_object *gobj,
+ int flags)
+{
+ struct radeon_bo *bo = gem_to_radeon_bo(gobj);
+ if (radeon_ttm_tt_has_userptr(bo->tbo.ttm))
+ return ERR_PTR(-EPERM);
+ return drm_gem_prime_export(dev, gobj, flags);
+}
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index f8050f5429e2..2456f69efd23 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -26,258 +26,8 @@
* Jerome Glisse
* Christian König
*/
-#include <linux/seq_file.h>
-#include <linux/slab.h>
#include <drm/drmP.h>
-#include <drm/radeon_drm.h>
-#include "radeon_reg.h"
#include "radeon.h"
-#include "atom.h"
-
-/*
- * IB
- * IBs (Indirect Buffers) and areas of GPU accessible memory where
- * commands are stored. You can put a pointer to the IB in the
- * command ring and the hw will fetch the commands from the IB
- * and execute them. Generally userspace acceleration drivers
- * produce command buffers which are send to the kernel and
- * put in IBs for execution by the requested ring.
- */
-static int radeon_debugfs_sa_init(struct radeon_device *rdev);
-
-/**
- * radeon_ib_get - request an IB (Indirect Buffer)
- *
- * @rdev: radeon_device pointer
- * @ring: ring index the IB is associated with
- * @ib: IB object returned
- * @size: requested IB size
- *
- * Request an IB (all asics). IBs are allocated using the
- * suballocator.
- * Returns 0 on success, error on failure.
- */
-int radeon_ib_get(struct radeon_device *rdev, int ring,
- struct radeon_ib *ib, struct radeon_vm *vm,
- unsigned size)
-{
- int r;
-
- r = radeon_sa_bo_new(rdev, &rdev->ring_tmp_bo, &ib->sa_bo, size, 256);
- if (r) {
- dev_err(rdev->dev, "failed to get a new IB (%d)\n", r);
- return r;
- }
-
- r = radeon_semaphore_create(rdev, &ib->semaphore);
- if (r) {
- return r;
- }
-
- ib->ring = ring;
- ib->fence = NULL;
- ib->ptr = radeon_sa_bo_cpu_addr(ib->sa_bo);
- ib->vm = vm;
- if (vm) {
- /* ib pool is bound at RADEON_VA_IB_OFFSET in virtual address
- * space and soffset is the offset inside the pool bo
- */
- ib->gpu_addr = ib->sa_bo->soffset + RADEON_VA_IB_OFFSET;
- } else {
- ib->gpu_addr = radeon_sa_bo_gpu_addr(ib->sa_bo);
- }
- ib->is_const_ib = false;
-
- return 0;
-}
-
-/**
- * radeon_ib_free - free an IB (Indirect Buffer)
- *
- * @rdev: radeon_device pointer
- * @ib: IB object to free
- *
- * Free an IB (all asics).
- */
-void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib *ib)
-{
- radeon_semaphore_free(rdev, &ib->semaphore, ib->fence);
- radeon_sa_bo_free(rdev, &ib->sa_bo, ib->fence);
- radeon_fence_unref(&ib->fence);
-}
-
-/**
- * radeon_ib_schedule - schedule an IB (Indirect Buffer) on the ring
- *
- * @rdev: radeon_device pointer
- * @ib: IB object to schedule
- * @const_ib: Const IB to schedule (SI only)
- *
- * Schedule an IB on the associated ring (all asics).
- * Returns 0 on success, error on failure.
- *
- * On SI, there are two parallel engines fed from the primary ring,
- * the CE (Constant Engine) and the DE (Drawing Engine). Since
- * resource descriptors have moved to memory, the CE allows you to
- * prime the caches while the DE is updating register state so that
- * the resource descriptors will be already in cache when the draw is
- * processed. To accomplish this, the userspace driver submits two
- * IBs, one for the CE and one for the DE. If there is a CE IB (called
- * a CONST_IB), it will be put on the ring prior to the DE IB. Prior
- * to SI there was just a DE IB.
- */
-int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib,
- struct radeon_ib *const_ib)
-{
- struct radeon_ring *ring = &rdev->ring[ib->ring];
- int r = 0;
-
- if (!ib->length_dw || !ring->ready) {
- /* TODO: Nothings in the ib we should report. */
- dev_err(rdev->dev, "couldn't schedule ib\n");
- return -EINVAL;
- }
-
- /* 64 dwords should be enough for fence too */
- r = radeon_ring_lock(rdev, ring, 64 + RADEON_NUM_SYNCS * 8);
- if (r) {
- dev_err(rdev->dev, "scheduling IB failed (%d).\n", r);
- return r;
- }
-
- /* grab a vm id if necessary */
- if (ib->vm) {
- struct radeon_fence *vm_id_fence;
- vm_id_fence = radeon_vm_grab_id(rdev, ib->vm, ib->ring);
- radeon_semaphore_sync_to(ib->semaphore, vm_id_fence);
- }
-
- /* sync with other rings */
- r = radeon_semaphore_sync_rings(rdev, ib->semaphore, ib->ring);
- if (r) {
- dev_err(rdev->dev, "failed to sync rings (%d)\n", r);
- radeon_ring_unlock_undo(rdev, ring);
- return r;
- }
-
- if (ib->vm)
- radeon_vm_flush(rdev, ib->vm, ib->ring);
-
- if (const_ib) {
- radeon_ring_ib_execute(rdev, const_ib->ring, const_ib);
- radeon_semaphore_free(rdev, &const_ib->semaphore, NULL);
- }
- radeon_ring_ib_execute(rdev, ib->ring, ib);
- r = radeon_fence_emit(rdev, &ib->fence, ib->ring);
- if (r) {
- dev_err(rdev->dev, "failed to emit fence for new IB (%d)\n", r);
- radeon_ring_unlock_undo(rdev, ring);
- return r;
- }
- if (const_ib) {
- const_ib->fence = radeon_fence_ref(ib->fence);
- }
-
- if (ib->vm)
- radeon_vm_fence(rdev, ib->vm, ib->fence);
-
- radeon_ring_unlock_commit(rdev, ring);
- return 0;
-}
-
-/**
- * radeon_ib_pool_init - Init the IB (Indirect Buffer) pool
- *
- * @rdev: radeon_device pointer
- *
- * Initialize the suballocator to manage a pool of memory
- * for use as IBs (all asics).
- * Returns 0 on success, error on failure.
- */
-int radeon_ib_pool_init(struct radeon_device *rdev)
-{
- int r;
-
- if (rdev->ib_pool_ready) {
- return 0;
- }
- r = radeon_sa_bo_manager_init(rdev, &rdev->ring_tmp_bo,
- RADEON_IB_POOL_SIZE*64*1024,
- RADEON_GPU_PAGE_SIZE,
- RADEON_GEM_DOMAIN_GTT);
- if (r) {
- return r;
- }
-
- r = radeon_sa_bo_manager_start(rdev, &rdev->ring_tmp_bo);
- if (r) {
- return r;
- }
-
- rdev->ib_pool_ready = true;
- if (radeon_debugfs_sa_init(rdev)) {
- dev_err(rdev->dev, "failed to register debugfs file for SA\n");
- }
- return 0;
-}
-
-/**
- * radeon_ib_pool_fini - Free the IB (Indirect Buffer) pool
- *
- * @rdev: radeon_device pointer
- *
- * Tear down the suballocator managing the pool of memory
- * for use as IBs (all asics).
- */
-void radeon_ib_pool_fini(struct radeon_device *rdev)
-{
- if (rdev->ib_pool_ready) {
- radeon_sa_bo_manager_suspend(rdev, &rdev->ring_tmp_bo);
- radeon_sa_bo_manager_fini(rdev, &rdev->ring_tmp_bo);
- rdev->ib_pool_ready = false;
- }
-}
-
-/**
- * radeon_ib_ring_tests - test IBs on the rings
- *
- * @rdev: radeon_device pointer
- *
- * Test an IB (Indirect Buffer) on each ring.
- * If the test fails, disable the ring.
- * Returns 0 on success, error if the primary GFX ring
- * IB test fails.
- */
-int radeon_ib_ring_tests(struct radeon_device *rdev)
-{
- unsigned i;
- int r;
-
- for (i = 0; i < RADEON_NUM_RINGS; ++i) {
- struct radeon_ring *ring = &rdev->ring[i];
-
- if (!ring->ready)
- continue;
-
- r = radeon_ib_test(rdev, i, ring);
- if (r) {
- ring->ready = false;
- rdev->needs_reset = false;
-
- if (i == RADEON_RING_TYPE_GFX_INDEX) {
- /* oh, oh, that's really bad */
- DRM_ERROR("radeon: failed testing IB on GFX ring (%d).\n", r);
- rdev->accel_working = false;
- return r;
-
- } else {
- /* still not good, but we can live with it */
- DRM_ERROR("radeon: failed testing IB on ring %d (%d).\n", i, r);
- }
- }
- }
- return 0;
-}
/*
* Rings
@@ -295,27 +45,6 @@ int radeon_ib_ring_tests(struct radeon_device *rdev)
static int radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ring *ring);
/**
- * radeon_ring_write - write a value to the ring
- *
- * @ring: radeon_ring structure holding ring information
- * @v: dword (dw) value to write
- *
- * Write a value to the requested ring buffer (all asics).
- */
-void radeon_ring_write(struct radeon_ring *ring, uint32_t v)
-{
-#if DRM_DEBUG_CODE
- if (ring->count_dw <= 0) {
- DRM_ERROR("radeon: writing more dwords to the ring than expected!\n");
- }
-#endif
- ring->ring[ring->wptr++] = v;
- ring->wptr &= ring->ptr_mask;
- ring->count_dw--;
- ring->ring_free_dw--;
-}
-
-/**
* radeon_ring_supports_scratch_reg - check if the ring supports
* writing to scratch registers
*
@@ -427,17 +156,29 @@ int radeon_ring_lock(struct radeon_device *rdev, struct radeon_ring *ring, unsig
*
* @rdev: radeon_device pointer
* @ring: radeon_ring structure holding ring information
+ * @hdp_flush: Whether or not to perform an HDP cache flush
*
* Update the wptr (write pointer) to tell the GPU to
* execute new commands on the ring buffer (all asics).
*/
-void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring)
+void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring,
+ bool hdp_flush)
{
+ /* If we are emitting the HDP flush via the ring buffer, we need to
+ * do it before padding.
+ */
+ if (hdp_flush && rdev->asic->ring[ring->idx]->hdp_flush)
+ rdev->asic->ring[ring->idx]->hdp_flush(rdev, ring);
/* We pad to match fetch size */
while (ring->wptr & ring->align_mask) {
radeon_ring_write(ring, ring->nop);
}
mb();
+ /* If we are emitting the HDP flush via MMIO, we need to do it after
+ * all CPU writes to VRAM finished.
+ */
+ if (hdp_flush && rdev->asic->mmio_hdp_flush)
+ rdev->asic->mmio_hdp_flush(rdev);
radeon_ring_set_wptr(rdev, ring);
}
@@ -447,12 +188,14 @@ void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring)
*
* @rdev: radeon_device pointer
* @ring: radeon_ring structure holding ring information
+ * @hdp_flush: Whether or not to perform an HDP cache flush
*
* Call radeon_ring_commit() then unlock the ring (all asics).
*/
-void radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *ring)
+void radeon_ring_unlock_commit(struct radeon_device *rdev, struct radeon_ring *ring,
+ bool hdp_flush)
{
- radeon_ring_commit(rdev, ring);
+ radeon_ring_commit(rdev, ring, hdp_flush);
mutex_unlock(&rdev->ring_lock);
}
@@ -571,7 +314,7 @@ unsigned radeon_ring_backup(struct radeon_device *rdev, struct radeon_ring *ring
}
/* and then save the content of the ring */
- *data = kmalloc_array(size, sizeof(uint32_t), GFP_KERNEL);
+ *data = drm_malloc_ab(size, sizeof(uint32_t));
if (!*data) {
mutex_unlock(&rdev->ring_lock);
return 0;
@@ -612,8 +355,8 @@ int radeon_ring_restore(struct radeon_device *rdev, struct radeon_ring *ring,
radeon_ring_write(ring, data[i]);
}
- radeon_ring_unlock_commit(rdev, ring);
- kfree(data);
+ radeon_ring_unlock_commit(rdev, ring, false);
+ drm_free_large(data);
return 0;
}
@@ -640,7 +383,7 @@ int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsig
/* Allocate ring buffer */
if (ring->ring_obj == NULL) {
r = radeon_bo_create(rdev, ring->ring_size, PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_GTT,
+ RADEON_GEM_DOMAIN_GTT, 0, NULL,
NULL, &ring->ring_obj);
if (r) {
dev_err(rdev->dev, "(%d) ring create failed\n", r);
@@ -791,22 +534,6 @@ static struct drm_info_list radeon_debugfs_ring_info_list[] = {
{"radeon_ring_vce2", radeon_debugfs_ring_info, 0, &si_vce2_index},
};
-static int radeon_debugfs_sa_info(struct seq_file *m, void *data)
-{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
- struct drm_device *dev = node->minor->dev;
- struct radeon_device *rdev = dev->dev_private;
-
- radeon_sa_bo_dump_debug_info(&rdev->ring_tmp_bo, m);
-
- return 0;
-
-}
-
-static struct drm_info_list radeon_debugfs_sa_list[] = {
- {"radeon_sa_info", &radeon_debugfs_sa_info, 0, NULL},
-};
-
#endif
static int radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ring *ring)
@@ -828,12 +555,3 @@ static int radeon_debugfs_ring_init(struct radeon_device *rdev, struct radeon_ri
#endif
return 0;
}
-
-static int radeon_debugfs_sa_init(struct radeon_device *rdev)
-{
-#if defined(CONFIG_DEBUG_FS)
- return radeon_debugfs_add_files(rdev, radeon_debugfs_sa_list, 1);
-#else
- return 0;
-#endif
-}
diff --git a/drivers/gpu/drm/radeon/radeon_sa.c b/drivers/gpu/drm/radeon/radeon_sa.c
index adcf3e2f07da..c507896aca45 100644
--- a/drivers/gpu/drm/radeon/radeon_sa.c
+++ b/drivers/gpu/drm/radeon/radeon_sa.c
@@ -49,7 +49,7 @@ static void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager);
int radeon_sa_bo_manager_init(struct radeon_device *rdev,
struct radeon_sa_manager *sa_manager,
- unsigned size, u32 align, u32 domain)
+ unsigned size, u32 align, u32 domain, u32 flags)
{
int i, r;
@@ -65,7 +65,7 @@ int radeon_sa_bo_manager_init(struct radeon_device *rdev,
}
r = radeon_bo_create(rdev, size, align, true,
- domain, NULL, &sa_manager->bo);
+ domain, flags, NULL, NULL, &sa_manager->bo);
if (r) {
dev_err(rdev->dev, "(%d) failed to allocate bo for manager\n", r);
return r;
diff --git a/drivers/gpu/drm/radeon/radeon_semaphore.c b/drivers/gpu/drm/radeon/radeon_semaphore.c
index dbd6bcde92de..6deb08f045b7 100644
--- a/drivers/gpu/drm/radeon/radeon_semaphore.c
+++ b/drivers/gpu/drm/radeon/radeon_semaphore.c
@@ -34,7 +34,7 @@
int radeon_semaphore_create(struct radeon_device *rdev,
struct radeon_semaphore **semaphore)
{
- uint32_t *cpu_addr;
+ uint64_t *cpu_addr;
int i, r;
*semaphore = kmalloc(sizeof(struct radeon_semaphore), GFP_KERNEL);
@@ -96,15 +96,15 @@ bool radeon_semaphore_emit_wait(struct radeon_device *rdev, int ridx,
}
/**
- * radeon_semaphore_sync_to - use the semaphore to sync to a fence
+ * radeon_semaphore_sync_fence - use the semaphore to sync to a fence
*
* @semaphore: semaphore object to add fence to
* @fence: fence to sync to
*
* Sync to the fence using this semaphore object
*/
-void radeon_semaphore_sync_to(struct radeon_semaphore *semaphore,
- struct radeon_fence *fence)
+void radeon_semaphore_sync_fence(struct radeon_semaphore *semaphore,
+ struct radeon_fence *fence)
{
struct radeon_fence *other;
@@ -116,6 +116,53 @@ void radeon_semaphore_sync_to(struct radeon_semaphore *semaphore,
}
/**
+ * radeon_semaphore_sync_to - use the semaphore to sync to a reservation object
+ *
+ * @sema: semaphore object to add fence from reservation object to
+ * @resv: reservation object with embedded fence
+ * @shared: true if we should onyl sync to the exclusive fence
+ *
+ * Sync to the fence using this semaphore object
+ */
+int radeon_semaphore_sync_resv(struct radeon_device *rdev,
+ struct radeon_semaphore *sema,
+ struct reservation_object *resv,
+ bool shared)
+{
+ struct reservation_object_list *flist;
+ struct fence *f;
+ struct radeon_fence *fence;
+ unsigned i;
+ int r = 0;
+
+ /* always sync to the exclusive fence */
+ f = reservation_object_get_excl(resv);
+ fence = f ? to_radeon_fence(f) : NULL;
+ if (fence && fence->rdev == rdev)
+ radeon_semaphore_sync_fence(sema, fence);
+ else if (f)
+ r = fence_wait(f, true);
+
+ flist = reservation_object_get_list(resv);
+ if (shared || !flist || r)
+ return r;
+
+ for (i = 0; i < flist->shared_count; ++i) {
+ f = rcu_dereference_protected(flist->shared[i],
+ reservation_object_held(resv));
+ fence = to_radeon_fence(f);
+ if (fence && fence->rdev == rdev)
+ radeon_semaphore_sync_fence(sema, fence);
+ else
+ r = fence_wait(f, true);
+
+ if (r)
+ break;
+ }
+ return r;
+}
+
+/**
* radeon_semaphore_sync_rings - sync ring to all registered fences
*
* @rdev: radeon_device pointer
@@ -179,7 +226,7 @@ int radeon_semaphore_sync_rings(struct radeon_device *rdev,
continue;
}
- radeon_ring_commit(rdev, &rdev->ring[i]);
+ radeon_ring_commit(rdev, &rdev->ring[i], false);
radeon_fence_note_sync(fence, ring);
semaphore->gpu_addr += 8;
diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c
index 23bb64fd775f..535403e0c8a2 100644
--- a/drivers/gpu/drm/radeon/radeon_state.c
+++ b/drivers/gpu/drm/radeon/radeon_state.c
@@ -30,9 +30,9 @@
*/
#include <drm/drmP.h>
-#include <drm/drm_buffer.h>
#include <drm/radeon_drm.h>
#include "radeon_drv.h"
+#include "drm_buffer.h"
/* ================================================================
* Helper functions for client state checking and fixup
diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c
index 3a13e0d1055c..07b506b41008 100644
--- a/drivers/gpu/drm/radeon/radeon_test.c
+++ b/drivers/gpu/drm/radeon/radeon_test.c
@@ -56,13 +56,7 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
/* Number of tests =
* (Total GTT - IB pool - writeback page - ring buffers) / test size
*/
- n = rdev->mc.gtt_size - RADEON_IB_POOL_SIZE*64*1024;
- for (i = 0; i < RADEON_NUM_RINGS; ++i)
- n -= rdev->ring[i].ring_size;
- if (rdev->wb.wb_obj)
- n -= RADEON_GPU_PAGE_SIZE;
- if (rdev->ih.ring_obj)
- n -= rdev->ih.ring_size;
+ n = rdev->mc.gtt_size - rdev->gart_pin_size;
n /= size;
gtt_obj = kzalloc(n * sizeof(*gtt_obj), GFP_KERNEL);
@@ -73,7 +67,7 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
}
r = radeon_bo_create(rdev, size, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM,
- NULL, &vram_obj);
+ 0, NULL, NULL, &vram_obj);
if (r) {
DRM_ERROR("Failed to create VRAM object\n");
goto out_cleanup;
@@ -93,7 +87,8 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
struct radeon_fence *fence = NULL;
r = radeon_bo_create(rdev, size, PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_GTT, NULL, gtt_obj + i);
+ RADEON_GEM_DOMAIN_GTT, 0, NULL, NULL,
+ gtt_obj + i);
if (r) {
DRM_ERROR("Failed to create GTT object %d\n", i);
goto out_lclean;
@@ -122,11 +117,16 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
radeon_bo_kunmap(gtt_obj[i]);
if (ring == R600_RING_TYPE_DMA_INDEX)
- r = radeon_copy_dma(rdev, gtt_addr, vram_addr, size / RADEON_GPU_PAGE_SIZE, &fence);
+ fence = radeon_copy_dma(rdev, gtt_addr, vram_addr,
+ size / RADEON_GPU_PAGE_SIZE,
+ NULL);
else
- r = radeon_copy_blit(rdev, gtt_addr, vram_addr, size / RADEON_GPU_PAGE_SIZE, &fence);
- if (r) {
+ fence = radeon_copy_blit(rdev, gtt_addr, vram_addr,
+ size / RADEON_GPU_PAGE_SIZE,
+ NULL);
+ if (IS_ERR(fence)) {
DRM_ERROR("Failed GTT->VRAM copy %d\n", i);
+ r = PTR_ERR(fence);
goto out_lclean_unpin;
}
@@ -168,11 +168,16 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag)
radeon_bo_kunmap(vram_obj);
if (ring == R600_RING_TYPE_DMA_INDEX)
- r = radeon_copy_dma(rdev, vram_addr, gtt_addr, size / RADEON_GPU_PAGE_SIZE, &fence);
+ fence = radeon_copy_dma(rdev, vram_addr, gtt_addr,
+ size / RADEON_GPU_PAGE_SIZE,
+ NULL);
else
- r = radeon_copy_blit(rdev, vram_addr, gtt_addr, size / RADEON_GPU_PAGE_SIZE, &fence);
- if (r) {
+ fence = radeon_copy_blit(rdev, vram_addr, gtt_addr,
+ size / RADEON_GPU_PAGE_SIZE,
+ NULL);
+ if (IS_ERR(fence)) {
DRM_ERROR("Failed VRAM->GTT copy %d\n", i);
+ r = PTR_ERR(fence);
goto out_lclean_unpin;
}
@@ -228,7 +233,7 @@ out_lclean:
radeon_bo_unreserve(gtt_obj[i]);
radeon_bo_unref(&gtt_obj[i]);
}
- if (fence)
+ if (fence && !IS_ERR(fence))
radeon_fence_unref(&fence);
break;
}
@@ -294,7 +299,7 @@ static int radeon_test_create_and_emit_fence(struct radeon_device *rdev,
return r;
}
radeon_fence_emit(rdev, fence, ring->idx);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
}
return 0;
}
@@ -319,7 +324,7 @@ void radeon_test_ring_sync(struct radeon_device *rdev,
goto out_cleanup;
}
radeon_semaphore_emit_wait(rdev, ringA->idx, semaphore);
- radeon_ring_unlock_commit(rdev, ringA);
+ radeon_ring_unlock_commit(rdev, ringA, false);
r = radeon_test_create_and_emit_fence(rdev, ringA, &fence1);
if (r)
@@ -331,7 +336,7 @@ void radeon_test_ring_sync(struct radeon_device *rdev,
goto out_cleanup;
}
radeon_semaphore_emit_wait(rdev, ringA->idx, semaphore);
- radeon_ring_unlock_commit(rdev, ringA);
+ radeon_ring_unlock_commit(rdev, ringA, false);
r = radeon_test_create_and_emit_fence(rdev, ringA, &fence2);
if (r)
@@ -350,7 +355,7 @@ void radeon_test_ring_sync(struct radeon_device *rdev,
goto out_cleanup;
}
radeon_semaphore_emit_signal(rdev, ringB->idx, semaphore);
- radeon_ring_unlock_commit(rdev, ringB);
+ radeon_ring_unlock_commit(rdev, ringB, false);
r = radeon_fence_wait(fence1, false);
if (r) {
@@ -371,7 +376,7 @@ void radeon_test_ring_sync(struct radeon_device *rdev,
goto out_cleanup;
}
radeon_semaphore_emit_signal(rdev, ringB->idx, semaphore);
- radeon_ring_unlock_commit(rdev, ringB);
+ radeon_ring_unlock_commit(rdev, ringB, false);
r = radeon_fence_wait(fence2, false);
if (r) {
@@ -414,7 +419,7 @@ static void radeon_test_ring_sync2(struct radeon_device *rdev,
goto out_cleanup;
}
radeon_semaphore_emit_wait(rdev, ringA->idx, semaphore);
- radeon_ring_unlock_commit(rdev, ringA);
+ radeon_ring_unlock_commit(rdev, ringA, false);
r = radeon_test_create_and_emit_fence(rdev, ringA, &fenceA);
if (r)
@@ -426,7 +431,7 @@ static void radeon_test_ring_sync2(struct radeon_device *rdev,
goto out_cleanup;
}
radeon_semaphore_emit_wait(rdev, ringB->idx, semaphore);
- radeon_ring_unlock_commit(rdev, ringB);
+ radeon_ring_unlock_commit(rdev, ringB, false);
r = radeon_test_create_and_emit_fence(rdev, ringB, &fenceB);
if (r)
goto out_cleanup;
@@ -448,7 +453,7 @@ static void radeon_test_ring_sync2(struct radeon_device *rdev,
goto out_cleanup;
}
radeon_semaphore_emit_signal(rdev, ringC->idx, semaphore);
- radeon_ring_unlock_commit(rdev, ringC);
+ radeon_ring_unlock_commit(rdev, ringC, false);
for (i = 0; i < 30; ++i) {
mdelay(100);
@@ -474,7 +479,7 @@ static void radeon_test_ring_sync2(struct radeon_device *rdev,
goto out_cleanup;
}
radeon_semaphore_emit_signal(rdev, ringC->idx, semaphore);
- radeon_ring_unlock_commit(rdev, ringC);
+ radeon_ring_unlock_commit(rdev, ringC, false);
mdelay(1000);
diff --git a/drivers/gpu/drm/radeon/radeon_trace.h b/drivers/gpu/drm/radeon/radeon_trace.h
index f749f2c3bbdb..9db74a96ef61 100644
--- a/drivers/gpu/drm/radeon/radeon_trace.h
+++ b/drivers/gpu/drm/radeon/radeon_trace.h
@@ -72,8 +72,8 @@ TRACE_EVENT(radeon_vm_bo_update,
),
TP_fast_assign(
- __entry->soffset = bo_va->soffset;
- __entry->eoffset = bo_va->eoffset;
+ __entry->soffset = bo_va->it.start;
+ __entry->eoffset = bo_va->it.last + 1;
__entry->flags = bo_va->flags;
),
TP_printk("soffs=%010llx, eoffs=%010llx, flags=%08x",
@@ -104,6 +104,24 @@ TRACE_EVENT(radeon_vm_set_page,
__entry->flags, __entry->count)
);
+TRACE_EVENT(radeon_vm_flush,
+ TP_PROTO(uint64_t pd_addr, unsigned ring, unsigned id),
+ TP_ARGS(pd_addr, ring, id),
+ TP_STRUCT__entry(
+ __field(u64, pd_addr)
+ __field(u32, ring)
+ __field(u32, id)
+ ),
+
+ TP_fast_assign(
+ __entry->pd_addr = pd_addr;
+ __entry->ring = ring;
+ __entry->id = id;
+ ),
+ TP_printk("pd_addr=%010Lx, ring=%u, id=%u",
+ __entry->pd_addr, __entry->ring, __entry->id)
+);
+
DECLARE_EVENT_CLASS(radeon_fence_request,
TP_PROTO(struct drm_device *dev, int ring, u32 seqno),
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index c8a8a5144ec1..8624979afb65 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -39,6 +39,8 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/swiotlb.h>
+#include <linux/swap.h>
+#include <linux/pagemap.h>
#include <linux/debugfs.h>
#include "radeon_reg.h"
#include "radeon.h"
@@ -176,12 +178,15 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
static void radeon_evict_flags(struct ttm_buffer_object *bo,
struct ttm_placement *placement)
{
+ static struct ttm_place placements = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM
+ };
+
struct radeon_bo *rbo;
- static u32 placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
if (!radeon_ttm_bo_is_radeon_bo(bo)) {
- placement->fpfn = 0;
- placement->lpfn = 0;
placement->placement = &placements;
placement->busy_placement = &placements;
placement->num_placement = 1;
@@ -228,6 +233,7 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
struct radeon_device *rdev;
uint64_t old_start, new_start;
struct radeon_fence *fence;
+ unsigned num_pages;
int r, ridx;
rdev = radeon_get_rdev(bo->bdev);
@@ -264,13 +270,12 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
BUILD_BUG_ON((PAGE_SIZE % RADEON_GPU_PAGE_SIZE) != 0);
- /* sync other rings */
- fence = bo->sync_obj;
- r = radeon_copy(rdev, old_start, new_start,
- new_mem->num_pages * (PAGE_SIZE / RADEON_GPU_PAGE_SIZE), /* GPU pages */
- &fence);
- /* FIXME: handle copy error */
- r = ttm_bo_move_accel_cleanup(bo, (void *)fence,
+ num_pages = new_mem->num_pages * (PAGE_SIZE / RADEON_GPU_PAGE_SIZE);
+ fence = radeon_copy(rdev, old_start, new_start, num_pages, bo->resv);
+ if (IS_ERR(fence))
+ return PTR_ERR(fence);
+
+ r = ttm_bo_move_accel_cleanup(bo, &fence->base,
evict, no_wait_gpu, new_mem);
radeon_fence_unref(&fence);
return r;
@@ -284,20 +289,20 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo,
struct radeon_device *rdev;
struct ttm_mem_reg *old_mem = &bo->mem;
struct ttm_mem_reg tmp_mem;
- u32 placements;
+ struct ttm_place placements;
struct ttm_placement placement;
int r;
rdev = radeon_get_rdev(bo->bdev);
tmp_mem = *new_mem;
tmp_mem.mm_node = NULL;
- placement.fpfn = 0;
- placement.lpfn = 0;
placement.num_placement = 1;
placement.placement = &placements;
placement.num_busy_placement = 1;
placement.busy_placement = &placements;
- placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
+ placements.fpfn = 0;
+ placements.lpfn = 0;
+ placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
r = ttm_bo_mem_space(bo, &placement, &tmp_mem,
interruptible, no_wait_gpu);
if (unlikely(r)) {
@@ -332,19 +337,19 @@ static int radeon_move_ram_vram(struct ttm_buffer_object *bo,
struct ttm_mem_reg *old_mem = &bo->mem;
struct ttm_mem_reg tmp_mem;
struct ttm_placement placement;
- u32 placements;
+ struct ttm_place placements;
int r;
rdev = radeon_get_rdev(bo->bdev);
tmp_mem = *new_mem;
tmp_mem.mm_node = NULL;
- placement.fpfn = 0;
- placement.lpfn = 0;
placement.num_placement = 1;
placement.placement = &placements;
placement.num_busy_placement = 1;
placement.busy_placement = &placements;
- placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
+ placements.fpfn = 0;
+ placements.lpfn = 0;
+ placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
r = ttm_bo_mem_space(bo, &placement, &tmp_mem,
interruptible, no_wait_gpu);
if (unlikely(r)) {
@@ -483,53 +488,131 @@ static void radeon_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_re
{
}
-static int radeon_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible)
-{
- return radeon_fence_wait((struct radeon_fence *)sync_obj, interruptible);
-}
+/*
+ * TTM backend functions.
+ */
+struct radeon_ttm_tt {
+ struct ttm_dma_tt ttm;
+ struct radeon_device *rdev;
+ u64 offset;
-static int radeon_sync_obj_flush(void *sync_obj)
+ uint64_t userptr;
+ struct mm_struct *usermm;
+ uint32_t userflags;
+};
+
+/* prepare the sg table with the user pages */
+static int radeon_ttm_tt_pin_userptr(struct ttm_tt *ttm)
{
+ struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
+ struct radeon_ttm_tt *gtt = (void *)ttm;
+ unsigned pinned = 0, nents;
+ int r;
+
+ int write = !(gtt->userflags & RADEON_GEM_USERPTR_READONLY);
+ enum dma_data_direction direction = write ?
+ DMA_BIDIRECTIONAL : DMA_TO_DEVICE;
+
+ if (current->mm != gtt->usermm)
+ return -EPERM;
+
+ if (gtt->userflags & RADEON_GEM_USERPTR_ANONONLY) {
+ /* check that we only pin down anonymous memory
+ to prevent problems with writeback */
+ unsigned long end = gtt->userptr + ttm->num_pages * PAGE_SIZE;
+ struct vm_area_struct *vma;
+ vma = find_vma(gtt->usermm, gtt->userptr);
+ if (!vma || vma->vm_file || vma->vm_end < end)
+ return -EPERM;
+ }
+
+ do {
+ unsigned num_pages = ttm->num_pages - pinned;
+ uint64_t userptr = gtt->userptr + pinned * PAGE_SIZE;
+ struct page **pages = ttm->pages + pinned;
+
+ r = get_user_pages(current, current->mm, userptr, num_pages,
+ write, 0, pages, NULL);
+ if (r < 0)
+ goto release_pages;
+
+ pinned += r;
+
+ } while (pinned < ttm->num_pages);
+
+ r = sg_alloc_table_from_pages(ttm->sg, ttm->pages, ttm->num_pages, 0,
+ ttm->num_pages << PAGE_SHIFT,
+ GFP_KERNEL);
+ if (r)
+ goto release_sg;
+
+ r = -ENOMEM;
+ nents = dma_map_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction);
+ if (nents != ttm->sg->nents)
+ goto release_sg;
+
+ drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages,
+ gtt->ttm.dma_address, ttm->num_pages);
+
return 0;
-}
-static void radeon_sync_obj_unref(void **sync_obj)
-{
- radeon_fence_unref((struct radeon_fence **)sync_obj);
-}
+release_sg:
+ kfree(ttm->sg);
-static void *radeon_sync_obj_ref(void *sync_obj)
-{
- return radeon_fence_ref((struct radeon_fence *)sync_obj);
+release_pages:
+ release_pages(ttm->pages, pinned, 0);
+ return r;
}
-static bool radeon_sync_obj_signaled(void *sync_obj)
+static void radeon_ttm_tt_unpin_userptr(struct ttm_tt *ttm)
{
- return radeon_fence_signaled((struct radeon_fence *)sync_obj);
-}
+ struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
+ struct radeon_ttm_tt *gtt = (void *)ttm;
+ struct scatterlist *sg;
+ int i;
-/*
- * TTM backend functions.
- */
-struct radeon_ttm_tt {
- struct ttm_dma_tt ttm;
- struct radeon_device *rdev;
- u64 offset;
-};
+ int write = !(gtt->userflags & RADEON_GEM_USERPTR_READONLY);
+ enum dma_data_direction direction = write ?
+ DMA_BIDIRECTIONAL : DMA_TO_DEVICE;
+
+ /* free the sg table and pages again */
+ dma_unmap_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction);
+
+ for_each_sg(ttm->sg->sgl, sg, ttm->sg->nents, i) {
+ struct page *page = sg_page(sg);
+
+ if (!(gtt->userflags & RADEON_GEM_USERPTR_READONLY))
+ set_page_dirty(page);
+
+ mark_page_accessed(page);
+ page_cache_release(page);
+ }
+
+ sg_free_table(ttm->sg);
+}
static int radeon_ttm_backend_bind(struct ttm_tt *ttm,
struct ttm_mem_reg *bo_mem)
{
struct radeon_ttm_tt *gtt = (void*)ttm;
+ uint32_t flags = RADEON_GART_PAGE_VALID | RADEON_GART_PAGE_READ |
+ RADEON_GART_PAGE_WRITE;
int r;
+ if (gtt->userptr) {
+ radeon_ttm_tt_pin_userptr(ttm);
+ flags &= ~RADEON_GART_PAGE_WRITE;
+ }
+
gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT);
if (!ttm->num_pages) {
WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n",
ttm->num_pages, bo_mem, ttm);
}
- r = radeon_gart_bind(gtt->rdev, gtt->offset,
- ttm->num_pages, ttm->pages, gtt->ttm.dma_address);
+ if (ttm->caching_state == tt_cached)
+ flags |= RADEON_GART_PAGE_SNOOP;
+ r = radeon_gart_bind(gtt->rdev, gtt->offset, ttm->num_pages,
+ ttm->pages, gtt->ttm.dma_address, flags);
if (r) {
DRM_ERROR("failed to bind %lu pages at 0x%08X\n",
ttm->num_pages, (unsigned)gtt->offset);
@@ -543,6 +626,10 @@ static int radeon_ttm_backend_unbind(struct ttm_tt *ttm)
struct radeon_ttm_tt *gtt = (void *)ttm;
radeon_gart_unbind(gtt->rdev, gtt->offset, ttm->num_pages);
+
+ if (gtt->userptr)
+ radeon_ttm_tt_unpin_userptr(ttm);
+
return 0;
}
@@ -588,10 +675,17 @@ static struct ttm_tt *radeon_ttm_tt_create(struct ttm_bo_device *bdev,
return &gtt->ttm.ttm;
}
+static struct radeon_ttm_tt *radeon_ttm_tt_to_gtt(struct ttm_tt *ttm)
+{
+ if (!ttm || ttm->func != &radeon_backend_func)
+ return NULL;
+ return (struct radeon_ttm_tt *)ttm;
+}
+
static int radeon_ttm_tt_populate(struct ttm_tt *ttm)
{
+ struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm);
struct radeon_device *rdev;
- struct radeon_ttm_tt *gtt = (void *)ttm;
unsigned i;
int r;
bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG);
@@ -599,6 +693,16 @@ static int radeon_ttm_tt_populate(struct ttm_tt *ttm)
if (ttm->state != tt_unpopulated)
return 0;
+ if (gtt && gtt->userptr) {
+ ttm->sg = kcalloc(1, sizeof(struct sg_table), GFP_KERNEL);
+ if (!ttm->sg)
+ return -ENOMEM;
+
+ ttm->page_flags |= TTM_PAGE_FLAG_SG;
+ ttm->state = tt_unbound;
+ return 0;
+ }
+
if (slave && ttm->sg) {
drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages,
gtt->ttm.dma_address, ttm->num_pages);
@@ -644,10 +748,16 @@ static int radeon_ttm_tt_populate(struct ttm_tt *ttm)
static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm)
{
struct radeon_device *rdev;
- struct radeon_ttm_tt *gtt = (void *)ttm;
+ struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm);
unsigned i;
bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG);
+ if (gtt && gtt->userptr) {
+ kfree(ttm->sg);
+ ttm->page_flags &= ~TTM_PAGE_FLAG_SG;
+ return;
+ }
+
if (slave)
return;
@@ -676,6 +786,40 @@ static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm)
ttm_pool_unpopulate(ttm);
}
+int radeon_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
+ uint32_t flags)
+{
+ struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm);
+
+ if (gtt == NULL)
+ return -EINVAL;
+
+ gtt->userptr = addr;
+ gtt->usermm = current->mm;
+ gtt->userflags = flags;
+ return 0;
+}
+
+bool radeon_ttm_tt_has_userptr(struct ttm_tt *ttm)
+{
+ struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm);
+
+ if (gtt == NULL)
+ return false;
+
+ return !!gtt->userptr;
+}
+
+bool radeon_ttm_tt_is_readonly(struct ttm_tt *ttm)
+{
+ struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(ttm);
+
+ if (gtt == NULL)
+ return false;
+
+ return !!(gtt->userflags & RADEON_GEM_USERPTR_READONLY);
+}
+
static struct ttm_bo_driver radeon_bo_driver = {
.ttm_tt_create = &radeon_ttm_tt_create,
.ttm_tt_populate = &radeon_ttm_tt_populate,
@@ -685,11 +829,6 @@ static struct ttm_bo_driver radeon_bo_driver = {
.evict_flags = &radeon_evict_flags,
.move = &radeon_bo_move,
.verify_access = &radeon_verify_access,
- .sync_obj_signaled = &radeon_sync_obj_signaled,
- .sync_obj_wait = &radeon_sync_obj_wait,
- .sync_obj_flush = &radeon_sync_obj_flush,
- .sync_obj_unref = &radeon_sync_obj_unref,
- .sync_obj_ref = &radeon_sync_obj_ref,
.move_notify = &radeon_bo_move_notify,
.fault_reserve_notify = &radeon_bo_fault_reserve_notify,
.io_mem_reserve = &radeon_ttm_io_mem_reserve,
@@ -726,7 +865,7 @@ int radeon_ttm_init(struct radeon_device *rdev)
radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size);
r = radeon_bo_create(rdev, 256 * 1024, PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_VRAM,
+ RADEON_GEM_DOMAIN_VRAM, 0, NULL,
NULL, &rdev->stollen_vga_memory);
if (r) {
return r;
@@ -824,7 +963,7 @@ int radeon_mmap(struct file *filp, struct vm_area_struct *vma)
int r;
if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) {
- return drm_mmap(filp, vma);
+ return -EINVAL;
}
file_priv = filp->private_data;
diff --git a/drivers/gpu/drm/radeon/radeon_ucode.c b/drivers/gpu/drm/radeon/radeon_ucode.c
new file mode 100644
index 000000000000..6beec680390c
--- /dev/null
+++ b/drivers/gpu/drm/radeon/radeon_ucode.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2014 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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/firmware.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <drm/drmP.h>
+#include "radeon.h"
+#include "radeon_ucode.h"
+
+static void radeon_ucode_print_common_hdr(const struct common_firmware_header *hdr)
+{
+ DRM_DEBUG("size_bytes: %u\n", le32_to_cpu(hdr->size_bytes));
+ DRM_DEBUG("header_size_bytes: %u\n", le32_to_cpu(hdr->header_size_bytes));
+ DRM_DEBUG("header_version_major: %u\n", le16_to_cpu(hdr->header_version_major));
+ DRM_DEBUG("header_version_minor: %u\n", le16_to_cpu(hdr->header_version_minor));
+ DRM_DEBUG("ip_version_major: %u\n", le16_to_cpu(hdr->ip_version_major));
+ DRM_DEBUG("ip_version_minor: %u\n", le16_to_cpu(hdr->ip_version_minor));
+ DRM_DEBUG("ucode_version: 0x%08x\n", le32_to_cpu(hdr->ucode_version));
+ DRM_DEBUG("ucode_size_bytes: %u\n", le32_to_cpu(hdr->ucode_size_bytes));
+ DRM_DEBUG("ucode_array_offset_bytes: %u\n",
+ le32_to_cpu(hdr->ucode_array_offset_bytes));
+ DRM_DEBUG("crc32: 0x%08x\n", le32_to_cpu(hdr->crc32));
+}
+
+void radeon_ucode_print_mc_hdr(const struct common_firmware_header *hdr)
+{
+ uint16_t version_major = le16_to_cpu(hdr->header_version_major);
+ uint16_t version_minor = le16_to_cpu(hdr->header_version_minor);
+
+ DRM_DEBUG("MC\n");
+ radeon_ucode_print_common_hdr(hdr);
+
+ if (version_major == 1) {
+ const struct mc_firmware_header_v1_0 *mc_hdr =
+ container_of(hdr, struct mc_firmware_header_v1_0, header);
+
+ DRM_DEBUG("io_debug_size_bytes: %u\n",
+ le32_to_cpu(mc_hdr->io_debug_size_bytes));
+ DRM_DEBUG("io_debug_array_offset_bytes: %u\n",
+ le32_to_cpu(mc_hdr->io_debug_array_offset_bytes));
+ } else {
+ DRM_ERROR("Unknown MC ucode version: %u.%u\n", version_major, version_minor);
+ }
+}
+
+void radeon_ucode_print_smc_hdr(const struct common_firmware_header *hdr)
+{
+ uint16_t version_major = le16_to_cpu(hdr->header_version_major);
+ uint16_t version_minor = le16_to_cpu(hdr->header_version_minor);
+
+ DRM_DEBUG("SMC\n");
+ radeon_ucode_print_common_hdr(hdr);
+
+ if (version_major == 1) {
+ const struct smc_firmware_header_v1_0 *smc_hdr =
+ container_of(hdr, struct smc_firmware_header_v1_0, header);
+
+ DRM_DEBUG("ucode_start_addr: %u\n", le32_to_cpu(smc_hdr->ucode_start_addr));
+ } else {
+ DRM_ERROR("Unknown SMC ucode version: %u.%u\n", version_major, version_minor);
+ }
+}
+
+void radeon_ucode_print_gfx_hdr(const struct common_firmware_header *hdr)
+{
+ uint16_t version_major = le16_to_cpu(hdr->header_version_major);
+ uint16_t version_minor = le16_to_cpu(hdr->header_version_minor);
+
+ DRM_DEBUG("GFX\n");
+ radeon_ucode_print_common_hdr(hdr);
+
+ if (version_major == 1) {
+ const struct gfx_firmware_header_v1_0 *gfx_hdr =
+ container_of(hdr, struct gfx_firmware_header_v1_0, header);
+
+ DRM_DEBUG("ucode_feature_version: %u\n",
+ le32_to_cpu(gfx_hdr->ucode_feature_version));
+ DRM_DEBUG("jt_offset: %u\n", le32_to_cpu(gfx_hdr->jt_offset));
+ DRM_DEBUG("jt_size: %u\n", le32_to_cpu(gfx_hdr->jt_size));
+ } else {
+ DRM_ERROR("Unknown GFX ucode version: %u.%u\n", version_major, version_minor);
+ }
+}
+
+void radeon_ucode_print_rlc_hdr(const struct common_firmware_header *hdr)
+{
+ uint16_t version_major = le16_to_cpu(hdr->header_version_major);
+ uint16_t version_minor = le16_to_cpu(hdr->header_version_minor);
+
+ DRM_DEBUG("RLC\n");
+ radeon_ucode_print_common_hdr(hdr);
+
+ if (version_major == 1) {
+ const struct rlc_firmware_header_v1_0 *rlc_hdr =
+ container_of(hdr, struct rlc_firmware_header_v1_0, header);
+
+ DRM_DEBUG("ucode_feature_version: %u\n",
+ le32_to_cpu(rlc_hdr->ucode_feature_version));
+ DRM_DEBUG("save_and_restore_offset: %u\n",
+ le32_to_cpu(rlc_hdr->save_and_restore_offset));
+ DRM_DEBUG("clear_state_descriptor_offset: %u\n",
+ le32_to_cpu(rlc_hdr->clear_state_descriptor_offset));
+ DRM_DEBUG("avail_scratch_ram_locations: %u\n",
+ le32_to_cpu(rlc_hdr->avail_scratch_ram_locations));
+ DRM_DEBUG("master_pkt_description_offset: %u\n",
+ le32_to_cpu(rlc_hdr->master_pkt_description_offset));
+ } else {
+ DRM_ERROR("Unknown RLC ucode version: %u.%u\n", version_major, version_minor);
+ }
+}
+
+void radeon_ucode_print_sdma_hdr(const struct common_firmware_header *hdr)
+{
+ uint16_t version_major = le16_to_cpu(hdr->header_version_major);
+ uint16_t version_minor = le16_to_cpu(hdr->header_version_minor);
+
+ DRM_DEBUG("SDMA\n");
+ radeon_ucode_print_common_hdr(hdr);
+
+ if (version_major == 1) {
+ const struct sdma_firmware_header_v1_0 *sdma_hdr =
+ container_of(hdr, struct sdma_firmware_header_v1_0, header);
+
+ DRM_DEBUG("ucode_feature_version: %u\n",
+ le32_to_cpu(sdma_hdr->ucode_feature_version));
+ DRM_DEBUG("ucode_change_version: %u\n",
+ le32_to_cpu(sdma_hdr->ucode_change_version));
+ DRM_DEBUG("jt_offset: %u\n", le32_to_cpu(sdma_hdr->jt_offset));
+ DRM_DEBUG("jt_size: %u\n", le32_to_cpu(sdma_hdr->jt_size));
+ } else {
+ DRM_ERROR("Unknown SDMA ucode version: %u.%u\n",
+ version_major, version_minor);
+ }
+}
+
+int radeon_ucode_validate(const struct firmware *fw)
+{
+ const struct common_firmware_header *hdr =
+ (const struct common_firmware_header *)fw->data;
+
+ if (fw->size == le32_to_cpu(hdr->size_bytes))
+ return 0;
+
+ return -EINVAL;
+}
+
diff --git a/drivers/gpu/drm/radeon/radeon_ucode.h b/drivers/gpu/drm/radeon/radeon_ucode.h
index 4e7c3269b183..dc4576e4d8ad 100644
--- a/drivers/gpu/drm/radeon/radeon_ucode.h
+++ b/drivers/gpu/drm/radeon/radeon_ucode.h
@@ -153,4 +153,75 @@
#define HAWAII_SMC_UCODE_START 0x20000
#define HAWAII_SMC_UCODE_SIZE 0x1FDEC
+struct common_firmware_header {
+ uint32_t size_bytes; /* size of the entire header+image(s) in bytes */
+ uint32_t header_size_bytes; /* size of just the header in bytes */
+ uint16_t header_version_major; /* header version */
+ uint16_t header_version_minor; /* header version */
+ uint16_t ip_version_major; /* IP version */
+ uint16_t ip_version_minor; /* IP version */
+ uint32_t ucode_version;
+ uint32_t ucode_size_bytes; /* size of ucode in bytes */
+ uint32_t ucode_array_offset_bytes; /* payload offset from the start of the header */
+ uint32_t crc32; /* crc32 checksum of the payload */
+};
+
+/* version_major=1, version_minor=0 */
+struct mc_firmware_header_v1_0 {
+ struct common_firmware_header header;
+ uint32_t io_debug_size_bytes; /* size of debug array in dwords */
+ uint32_t io_debug_array_offset_bytes; /* payload offset from the start of the header */
+};
+
+/* version_major=1, version_minor=0 */
+struct smc_firmware_header_v1_0 {
+ struct common_firmware_header header;
+ uint32_t ucode_start_addr;
+};
+
+/* version_major=1, version_minor=0 */
+struct gfx_firmware_header_v1_0 {
+ struct common_firmware_header header;
+ uint32_t ucode_feature_version;
+ uint32_t jt_offset; /* jt location */
+ uint32_t jt_size; /* size of jt */
+};
+
+/* version_major=1, version_minor=0 */
+struct rlc_firmware_header_v1_0 {
+ struct common_firmware_header header;
+ uint32_t ucode_feature_version;
+ uint32_t save_and_restore_offset;
+ uint32_t clear_state_descriptor_offset;
+ uint32_t avail_scratch_ram_locations;
+ uint32_t master_pkt_description_offset;
+};
+
+/* version_major=1, version_minor=0 */
+struct sdma_firmware_header_v1_0 {
+ struct common_firmware_header header;
+ uint32_t ucode_feature_version;
+ uint32_t ucode_change_version;
+ uint32_t jt_offset; /* jt location */
+ uint32_t jt_size; /* size of jt */
+};
+
+/* header is fixed size */
+union radeon_firmware_header {
+ struct common_firmware_header common;
+ struct mc_firmware_header_v1_0 mc;
+ struct smc_firmware_header_v1_0 smc;
+ struct gfx_firmware_header_v1_0 gfx;
+ struct rlc_firmware_header_v1_0 rlc;
+ struct sdma_firmware_header_v1_0 sdma;
+ uint8_t raw[0x100];
+};
+
+void radeon_ucode_print_mc_hdr(const struct common_firmware_header *hdr);
+void radeon_ucode_print_smc_hdr(const struct common_firmware_header *hdr);
+void radeon_ucode_print_gfx_hdr(const struct common_firmware_header *hdr);
+void radeon_ucode_print_rlc_hdr(const struct common_firmware_header *hdr);
+void radeon_ucode_print_sdma_hdr(const struct common_firmware_header *hdr);
+int radeon_ucode_validate(const struct firmware *fw);
+
#endif
diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c
index a4ad270e8261..11b662469253 100644
--- a/drivers/gpu/drm/radeon/radeon_uvd.c
+++ b/drivers/gpu/drm/radeon/radeon_uvd.c
@@ -40,12 +40,18 @@
#define UVD_IDLE_TIMEOUT_MS 1000
/* Firmware Names */
+#define FIRMWARE_R600 "radeon/R600_uvd.bin"
+#define FIRMWARE_RS780 "radeon/RS780_uvd.bin"
+#define FIRMWARE_RV770 "radeon/RV770_uvd.bin"
#define FIRMWARE_RV710 "radeon/RV710_uvd.bin"
#define FIRMWARE_CYPRESS "radeon/CYPRESS_uvd.bin"
#define FIRMWARE_SUMO "radeon/SUMO_uvd.bin"
#define FIRMWARE_TAHITI "radeon/TAHITI_uvd.bin"
#define FIRMWARE_BONAIRE "radeon/BONAIRE_uvd.bin"
+MODULE_FIRMWARE(FIRMWARE_R600);
+MODULE_FIRMWARE(FIRMWARE_RS780);
+MODULE_FIRMWARE(FIRMWARE_RV770);
MODULE_FIRMWARE(FIRMWARE_RV710);
MODULE_FIRMWARE(FIRMWARE_CYPRESS);
MODULE_FIRMWARE(FIRMWARE_SUMO);
@@ -63,6 +69,23 @@ int radeon_uvd_init(struct radeon_device *rdev)
INIT_DELAYED_WORK(&rdev->uvd.idle_work, radeon_uvd_idle_work_handler);
switch (rdev->family) {
+ case CHIP_RV610:
+ case CHIP_RV630:
+ case CHIP_RV670:
+ case CHIP_RV620:
+ case CHIP_RV635:
+ fw_name = FIRMWARE_R600;
+ break;
+
+ case CHIP_RS780:
+ case CHIP_RS880:
+ fw_name = FIRMWARE_RS780;
+ break;
+
+ case CHIP_RV770:
+ fw_name = FIRMWARE_RV770;
+ break;
+
case CHIP_RV710:
case CHIP_RV730:
case CHIP_RV740:
@@ -115,9 +138,11 @@ int radeon_uvd_init(struct radeon_device *rdev)
}
bo_size = RADEON_GPU_PAGE_ALIGN(rdev->uvd_fw->size + 8) +
- RADEON_UVD_STACK_SIZE + RADEON_UVD_HEAP_SIZE;
+ RADEON_UVD_STACK_SIZE + RADEON_UVD_HEAP_SIZE +
+ RADEON_GPU_PAGE_SIZE;
r = radeon_bo_create(rdev, bo_size, PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->uvd.vcpu_bo);
+ RADEON_GEM_DOMAIN_VRAM, 0, NULL,
+ NULL, &rdev->uvd.vcpu_bo);
if (r) {
dev_err(rdev->dev, "(%d) failed to allocate UVD bo\n", r);
return r;
@@ -231,10 +256,30 @@ int radeon_uvd_resume(struct radeon_device *rdev)
return 0;
}
-void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo)
+void radeon_uvd_force_into_uvd_segment(struct radeon_bo *rbo,
+ uint32_t allowed_domains)
{
- rbo->placement.fpfn = 0 >> PAGE_SHIFT;
- rbo->placement.lpfn = (256 * 1024 * 1024) >> PAGE_SHIFT;
+ int i;
+
+ for (i = 0; i < rbo->placement.num_placement; ++i) {
+ rbo->placements[i].fpfn = 0 >> PAGE_SHIFT;
+ rbo->placements[i].lpfn = (256 * 1024 * 1024) >> PAGE_SHIFT;
+ }
+
+ /* If it must be in VRAM it must be in the first segment as well */
+ if (allowed_domains == RADEON_GEM_DOMAIN_VRAM)
+ return;
+
+ /* abort if we already have more than one placement */
+ if (rbo->placement.num_placement > 1)
+ return;
+
+ /* add another 256MB segment */
+ rbo->placements[1] = rbo->placements[0];
+ rbo->placements[1].fpfn += (256 * 1024 * 1024) >> PAGE_SHIFT;
+ rbo->placements[1].lpfn += (256 * 1024 * 1024) >> PAGE_SHIFT;
+ rbo->placement.num_placement++;
+ rbo->placement.num_busy_placement++;
}
void radeon_uvd_free_handles(struct radeon_device *rdev, struct drm_file *filp)
@@ -356,6 +401,7 @@ static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo,
{
int32_t *msg, msg_type, handle;
unsigned img_size = 0;
+ struct fence *f;
void *ptr;
int i, r;
@@ -365,8 +411,9 @@ static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo,
return -EINVAL;
}
- if (bo->tbo.sync_obj) {
- r = radeon_fence_wait(bo->tbo.sync_obj, false);
+ f = reservation_object_get_excl(bo->tbo.resv);
+ if (f) {
+ r = radeon_fence_wait((struct radeon_fence *)f, false);
if (r) {
DRM_ERROR("Failed waiting for UVD message (%d)!\n", r);
return r;
@@ -604,38 +651,16 @@ int radeon_uvd_cs_parse(struct radeon_cs_parser *p)
}
static int radeon_uvd_send_msg(struct radeon_device *rdev,
- int ring, struct radeon_bo *bo,
+ int ring, uint64_t addr,
struct radeon_fence **fence)
{
- struct ttm_validate_buffer tv;
- struct ww_acquire_ctx ticket;
- struct list_head head;
struct radeon_ib ib;
- uint64_t addr;
int i, r;
- memset(&tv, 0, sizeof(tv));
- tv.bo = &bo->tbo;
-
- INIT_LIST_HEAD(&head);
- list_add(&tv.head, &head);
-
- r = ttm_eu_reserve_buffers(&ticket, &head);
- if (r)
- return r;
-
- radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_VRAM);
- radeon_uvd_force_into_uvd_segment(bo);
-
- r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
- if (r)
- goto err;
-
r = radeon_ib_get(rdev, ring, &ib, NULL, 64);
if (r)
- goto err;
+ return r;
- addr = radeon_bo_gpu_offset(bo);
ib.ptr[0] = PACKET0(UVD_GPCOM_VCPU_DATA0, 0);
ib.ptr[1] = addr;
ib.ptr[2] = PACKET0(UVD_GPCOM_VCPU_DATA1, 0);
@@ -646,20 +671,12 @@ static int radeon_uvd_send_msg(struct radeon_device *rdev,
ib.ptr[i] = PACKET2(0);
ib.length_dw = 16;
- r = radeon_ib_schedule(rdev, &ib, NULL);
- if (r)
- goto err;
- ttm_eu_fence_buffer_objects(&ticket, &head, ib.fence);
+ r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (fence)
*fence = radeon_fence_ref(ib.fence);
radeon_ib_free(rdev, &ib);
- radeon_bo_unref(&bo);
- return 0;
-
-err:
- ttm_eu_backoff_reservation(&ticket, &head);
return r;
}
@@ -669,27 +686,18 @@ err:
int radeon_uvd_get_create_msg(struct radeon_device *rdev, int ring,
uint32_t handle, struct radeon_fence **fence)
{
- struct radeon_bo *bo;
- uint32_t *msg;
- int r, i;
+ /* we use the last page of the vcpu bo for the UVD message */
+ uint64_t offs = radeon_bo_size(rdev->uvd.vcpu_bo) -
+ RADEON_GPU_PAGE_SIZE;
- r = radeon_bo_create(rdev, 1024, PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_VRAM, NULL, &bo);
- if (r)
- return r;
+ uint32_t *msg = rdev->uvd.cpu_addr + offs;
+ uint64_t addr = rdev->uvd.gpu_addr + offs;
- r = radeon_bo_reserve(bo, false);
- if (r) {
- radeon_bo_unref(&bo);
- return r;
- }
+ int r, i;
- r = radeon_bo_kmap(bo, (void **)&msg);
- if (r) {
- radeon_bo_unreserve(bo);
- radeon_bo_unref(&bo);
+ r = radeon_bo_reserve(rdev->uvd.vcpu_bo, true);
+ if (r)
return r;
- }
/* stitch together an UVD create msg */
msg[0] = cpu_to_le32(0x00000de4);
@@ -706,36 +714,26 @@ int radeon_uvd_get_create_msg(struct radeon_device *rdev, int ring,
for (i = 11; i < 1024; ++i)
msg[i] = cpu_to_le32(0x0);
- radeon_bo_kunmap(bo);
- radeon_bo_unreserve(bo);
-
- return radeon_uvd_send_msg(rdev, ring, bo, fence);
+ r = radeon_uvd_send_msg(rdev, ring, addr, fence);
+ radeon_bo_unreserve(rdev->uvd.vcpu_bo);
+ return r;
}
int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring,
uint32_t handle, struct radeon_fence **fence)
{
- struct radeon_bo *bo;
- uint32_t *msg;
- int r, i;
+ /* we use the last page of the vcpu bo for the UVD message */
+ uint64_t offs = radeon_bo_size(rdev->uvd.vcpu_bo) -
+ RADEON_GPU_PAGE_SIZE;
- r = radeon_bo_create(rdev, 1024, PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_VRAM, NULL, &bo);
- if (r)
- return r;
+ uint32_t *msg = rdev->uvd.cpu_addr + offs;
+ uint64_t addr = rdev->uvd.gpu_addr + offs;
- r = radeon_bo_reserve(bo, false);
- if (r) {
- radeon_bo_unref(&bo);
- return r;
- }
+ int r, i;
- r = radeon_bo_kmap(bo, (void **)&msg);
- if (r) {
- radeon_bo_unreserve(bo);
- radeon_bo_unref(&bo);
+ r = radeon_bo_reserve(rdev->uvd.vcpu_bo, true);
+ if (r)
return r;
- }
/* stitch together an UVD destroy msg */
msg[0] = cpu_to_le32(0x00000de4);
@@ -745,10 +743,9 @@ int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring,
for (i = 4; i < 1024; ++i)
msg[i] = cpu_to_le32(0x0);
- radeon_bo_kunmap(bo);
- radeon_bo_unreserve(bo);
-
- return radeon_uvd_send_msg(rdev, ring, bo, fence);
+ r = radeon_uvd_send_msg(rdev, ring, addr, fence);
+ radeon_bo_unreserve(rdev->uvd.vcpu_bo);
+ return r;
}
/**
diff --git a/drivers/gpu/drm/radeon/radeon_vce.c b/drivers/gpu/drm/radeon/radeon_vce.c
index aa21c31a846c..9e85757d5599 100644
--- a/drivers/gpu/drm/radeon/radeon_vce.c
+++ b/drivers/gpu/drm/radeon/radeon_vce.c
@@ -126,7 +126,8 @@ int radeon_vce_init(struct radeon_device *rdev)
size = RADEON_GPU_PAGE_ALIGN(rdev->vce_fw->size) +
RADEON_VCE_STACK_SIZE + RADEON_VCE_HEAP_SIZE;
r = radeon_bo_create(rdev, size, PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->vce.vcpu_bo);
+ RADEON_GEM_DOMAIN_VRAM, 0, NULL, NULL,
+ &rdev->vce.vcpu_bo);
if (r) {
dev_err(rdev->dev, "(%d) failed to allocate VCE bo\n", r);
return r;
@@ -368,7 +369,7 @@ int radeon_vce_get_create_msg(struct radeon_device *rdev, int ring,
for (i = ib.length_dw; i < ib_size_dw; ++i)
ib.ptr[i] = 0x0;
- r = radeon_ib_schedule(rdev, &ib, NULL);
+ r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r) {
DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
}
@@ -425,7 +426,7 @@ int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring,
for (i = ib.length_dw; i < ib_size_dw; ++i)
ib.ptr[i] = 0x0;
- r = radeon_ib_schedule(rdev, &ib, NULL);
+ r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r) {
DRM_ERROR("radeon: failed to schedule ib (%d).\n", r);
}
@@ -715,7 +716,7 @@ int radeon_vce_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
return r;
}
radeon_ring_write(ring, VCE_CMD_END);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
for (i = 0; i < rdev->usec_timeout; i++) {
if (vce_v1_0_get_rptr(rdev, ring) != rptr)
diff --git a/drivers/gpu/drm/radeon/radeon_vm.c b/drivers/gpu/drm/radeon/radeon_vm.c
index 725d3669014f..dfde266529e2 100644
--- a/drivers/gpu/drm/radeon/radeon_vm.c
+++ b/drivers/gpu/drm/radeon/radeon_vm.c
@@ -132,8 +132,8 @@ struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev,
struct radeon_cs_reloc *list;
unsigned i, idx;
- list = kmalloc_array(vm->max_pde_used + 2,
- sizeof(struct radeon_cs_reloc), GFP_KERNEL);
+ list = drm_malloc_ab(vm->max_pde_used + 2,
+ sizeof(struct radeon_cs_reloc));
if (!list)
return NULL;
@@ -143,6 +143,7 @@ struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev,
list[0].prefered_domains = RADEON_GEM_DOMAIN_VRAM;
list[0].allowed_domains = RADEON_GEM_DOMAIN_VRAM;
list[0].tv.bo = &vm->page_directory->tbo;
+ list[0].tv.shared = false;
list[0].tiling_flags = 0;
list[0].handle = 0;
list_add(&list[0].tv.head, head);
@@ -156,6 +157,7 @@ struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev,
list[idx].prefered_domains = RADEON_GEM_DOMAIN_VRAM;
list[idx].allowed_domains = RADEON_GEM_DOMAIN_VRAM;
list[idx].tv.bo = &list[idx].robj->tbo;
+ list[idx].tv.shared = false;
list[idx].tiling_flags = 0;
list[idx].handle = 0;
list_add(&list[idx++].tv.head, head);
@@ -238,8 +240,8 @@ void radeon_vm_flush(struct radeon_device *rdev,
uint64_t pd_addr = radeon_bo_gpu_offset(vm->page_directory);
/* if we can't remember our last VM flush then flush now! */
- /* XXX figure out why we have to flush all the time */
- if (!vm->last_flush || true || pd_addr != vm->pd_gpu_addr) {
+ if (!vm->last_flush || pd_addr != vm->pd_gpu_addr) {
+ trace_radeon_vm_flush(pd_addr, ring, vm->id);
vm->pd_gpu_addr = pd_addr;
radeon_ring_vm_flush(rdev, ring, vm);
}
@@ -325,17 +327,15 @@ struct radeon_bo_va *radeon_vm_bo_add(struct radeon_device *rdev,
}
bo_va->vm = vm;
bo_va->bo = bo;
- bo_va->soffset = 0;
- bo_va->eoffset = 0;
+ bo_va->it.start = 0;
+ bo_va->it.last = 0;
bo_va->flags = 0;
- bo_va->valid = false;
+ bo_va->addr = 0;
bo_va->ref_count = 1;
INIT_LIST_HEAD(&bo_va->bo_list);
- INIT_LIST_HEAD(&bo_va->vm_list);
INIT_LIST_HEAD(&bo_va->vm_status);
mutex_lock(&vm->mutex);
- list_add(&bo_va->vm_list, &vm->va);
list_add_tail(&bo_va->bo_list, &bo->va);
mutex_unlock(&vm->mutex);
@@ -343,6 +343,42 @@ struct radeon_bo_va *radeon_vm_bo_add(struct radeon_device *rdev,
}
/**
+ * radeon_vm_set_pages - helper to call the right asic function
+ *
+ * @rdev: radeon_device pointer
+ * @ib: indirect buffer to fill with commands
+ * @pe: addr of the page entry
+ * @addr: dst addr to write into pe
+ * @count: number of page entries to update
+ * @incr: increase next addr by incr bytes
+ * @flags: hw access flags
+ *
+ * Traces the parameters and calls the right asic functions
+ * to setup the page table using the DMA.
+ */
+static void radeon_vm_set_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags)
+{
+ trace_radeon_vm_set_page(pe, addr, count, incr, flags);
+
+ if ((flags & R600_PTE_GART_MASK) == R600_PTE_GART_MASK) {
+ uint64_t src = rdev->gart.table_addr + (addr >> 12) * 8;
+ radeon_asic_vm_copy_pages(rdev, ib, pe, src, count);
+
+ } else if ((flags & R600_PTE_SYSTEM) || (count < 3)) {
+ radeon_asic_vm_write_pages(rdev, ib, pe, addr,
+ count, incr, flags);
+
+ } else {
+ radeon_asic_vm_set_pages(rdev, ib, pe, addr,
+ count, incr, flags);
+ }
+}
+
+/**
* radeon_vm_clear_bo - initially clear the page dir/table
*
* @rdev: radeon_device pointer
@@ -361,11 +397,12 @@ static int radeon_vm_clear_bo(struct radeon_device *rdev,
memset(&tv, 0, sizeof(tv));
tv.bo = &bo->tbo;
+ tv.shared = false;
INIT_LIST_HEAD(&head);
list_add(&tv.head, &head);
- r = ttm_eu_reserve_buffers(&ticket, &head);
+ r = ttm_eu_reserve_buffers(&ticket, &head, true);
if (r)
return r;
@@ -376,20 +413,21 @@ static int radeon_vm_clear_bo(struct radeon_device *rdev,
addr = radeon_bo_gpu_offset(bo);
entries = radeon_bo_size(bo) / 8;
- r = radeon_ib_get(rdev, R600_RING_TYPE_DMA_INDEX, &ib,
- NULL, entries * 2 + 64);
+ r = radeon_ib_get(rdev, R600_RING_TYPE_DMA_INDEX, &ib, NULL, 256);
if (r)
goto error;
ib.length_dw = 0;
- radeon_asic_vm_set_page(rdev, &ib, addr, 0, entries, 0, 0);
+ radeon_vm_set_pages(rdev, &ib, addr, 0, entries, 0, 0);
+ radeon_asic_vm_pad_ib(rdev, &ib);
+ WARN_ON(ib.length_dw > 64);
- r = radeon_ib_schedule(rdev, &ib, NULL);
+ r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r)
goto error;
- ttm_eu_fence_buffer_objects(&ticket, &head, ib.fence);
+ ttm_eu_fence_buffer_objects(&ticket, &head, &ib.fence->base);
radeon_ib_free(rdev, &ib);
return 0;
@@ -419,11 +457,9 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
uint32_t flags)
{
uint64_t size = radeon_bo_size(bo_va->bo);
- uint64_t eoffset, last_offset = 0;
struct radeon_vm *vm = bo_va->vm;
- struct radeon_bo_va *tmp;
- struct list_head *head;
unsigned last_pfn, pt_idx;
+ uint64_t eoffset;
int r;
if (soffset) {
@@ -445,51 +481,53 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
}
mutex_lock(&vm->mutex);
- head = &vm->va;
- last_offset = 0;
- list_for_each_entry(tmp, &vm->va, vm_list) {
- if (bo_va == tmp) {
- /* skip over currently modified bo */
- continue;
+ if (bo_va->it.start || bo_va->it.last) {
+ if (bo_va->addr) {
+ /* add a clone of the bo_va to clear the old address */
+ struct radeon_bo_va *tmp;
+ tmp = kzalloc(sizeof(struct radeon_bo_va), GFP_KERNEL);
+ if (!tmp) {
+ mutex_unlock(&vm->mutex);
+ return -ENOMEM;
+ }
+ tmp->it.start = bo_va->it.start;
+ tmp->it.last = bo_va->it.last;
+ tmp->vm = vm;
+ tmp->addr = bo_va->addr;
+ tmp->bo = radeon_bo_ref(bo_va->bo);
+ list_add(&tmp->vm_status, &vm->freed);
}
- if (soffset >= last_offset && eoffset <= tmp->soffset) {
- /* bo can be added before this one */
- break;
- }
- if (eoffset > tmp->soffset && soffset < tmp->eoffset) {
- /* bo and tmp overlap, invalid offset */
- dev_err(rdev->dev, "bo %p va 0x%08X conflict with (bo %p 0x%08X 0x%08X)\n",
- bo_va->bo, (unsigned)bo_va->soffset, tmp->bo,
- (unsigned)tmp->soffset, (unsigned)tmp->eoffset);
- mutex_unlock(&vm->mutex);
- return -EINVAL;
- }
- last_offset = tmp->eoffset;
- head = &tmp->vm_list;
+ interval_tree_remove(&bo_va->it, &vm->va);
+ bo_va->it.start = 0;
+ bo_va->it.last = 0;
}
- if (bo_va->soffset) {
- /* add a clone of the bo_va to clear the old address */
- tmp = kzalloc(sizeof(struct radeon_bo_va), GFP_KERNEL);
- if (!tmp) {
+ soffset /= RADEON_GPU_PAGE_SIZE;
+ eoffset /= RADEON_GPU_PAGE_SIZE;
+ if (soffset || eoffset) {
+ struct interval_tree_node *it;
+ it = interval_tree_iter_first(&vm->va, soffset, eoffset - 1);
+ if (it) {
+ struct radeon_bo_va *tmp;
+ tmp = container_of(it, struct radeon_bo_va, it);
+ /* bo and tmp overlap, invalid offset */
+ dev_err(rdev->dev, "bo %p va 0x%010Lx conflict with "
+ "(bo %p 0x%010lx 0x%010lx)\n", bo_va->bo,
+ soffset, tmp->bo, tmp->it.start, tmp->it.last);
mutex_unlock(&vm->mutex);
- return -ENOMEM;
+ return -EINVAL;
}
- tmp->soffset = bo_va->soffset;
- tmp->eoffset = bo_va->eoffset;
- tmp->vm = vm;
- list_add(&tmp->vm_status, &vm->freed);
+ bo_va->it.start = soffset;
+ bo_va->it.last = eoffset - 1;
+ interval_tree_insert(&bo_va->it, &vm->va);
}
- bo_va->soffset = soffset;
- bo_va->eoffset = eoffset;
bo_va->flags = flags;
- bo_va->valid = false;
- list_move(&bo_va->vm_list, head);
+ bo_va->addr = 0;
- soffset = (soffset / RADEON_GPU_PAGE_SIZE) >> radeon_vm_block_size;
- eoffset = (eoffset / RADEON_GPU_PAGE_SIZE) >> radeon_vm_block_size;
+ soffset >>= radeon_vm_block_size;
+ eoffset >>= radeon_vm_block_size;
BUG_ON(eoffset >= radeon_vm_num_pdes(rdev));
@@ -510,7 +548,8 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
r = radeon_bo_create(rdev, RADEON_VM_PTE_COUNT * 8,
RADEON_GPU_PAGE_SIZE, true,
- RADEON_GEM_DOMAIN_VRAM, NULL, &pt);
+ RADEON_GEM_DOMAIN_VRAM, 0,
+ NULL, NULL, &pt);
if (r)
return r;
@@ -611,7 +650,7 @@ int radeon_vm_update_page_directory(struct radeon_device *rdev,
ndw = 64;
/* assume the worst case */
- ndw += vm->max_pde_used * 16;
+ ndw += vm->max_pde_used * 6;
/* update too big for an IB */
if (ndw > 0xfffff)
@@ -640,9 +679,9 @@ int radeon_vm_update_page_directory(struct radeon_device *rdev,
((last_pt + incr * count) != pt)) {
if (count) {
- radeon_asic_vm_set_page(rdev, &ib, last_pde,
- last_pt, count, incr,
- R600_PTE_VALID);
+ radeon_vm_set_pages(rdev, &ib, last_pde,
+ last_pt, count, incr,
+ R600_PTE_VALID);
}
count = 1;
@@ -654,13 +693,16 @@ int radeon_vm_update_page_directory(struct radeon_device *rdev,
}
if (count)
- radeon_asic_vm_set_page(rdev, &ib, last_pde, last_pt, count,
- incr, R600_PTE_VALID);
+ radeon_vm_set_pages(rdev, &ib, last_pde, last_pt, count,
+ incr, R600_PTE_VALID);
if (ib.length_dw != 0) {
- radeon_semaphore_sync_to(ib.semaphore, pd->tbo.sync_obj);
- radeon_semaphore_sync_to(ib.semaphore, vm->last_id_use);
- r = radeon_ib_schedule(rdev, &ib, NULL);
+ radeon_asic_vm_pad_ib(rdev, &ib);
+
+ radeon_semaphore_sync_resv(rdev, ib.semaphore, pd->tbo.resv, false);
+ radeon_semaphore_sync_fence(ib.semaphore, vm->last_id_use);
+ WARN_ON(ib.length_dw > ndw);
+ r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r) {
radeon_ib_free(rdev, &ib);
return r;
@@ -725,30 +767,30 @@ static void radeon_vm_frag_ptes(struct radeon_device *rdev,
(frag_start >= frag_end)) {
count = (pe_end - pe_start) / 8;
- radeon_asic_vm_set_page(rdev, ib, pe_start, addr, count,
- RADEON_GPU_PAGE_SIZE, flags);
+ radeon_vm_set_pages(rdev, ib, pe_start, addr, count,
+ RADEON_GPU_PAGE_SIZE, flags);
return;
}
/* handle the 4K area at the beginning */
if (pe_start != frag_start) {
count = (frag_start - pe_start) / 8;
- radeon_asic_vm_set_page(rdev, ib, pe_start, addr, count,
- RADEON_GPU_PAGE_SIZE, flags);
+ radeon_vm_set_pages(rdev, ib, pe_start, addr, count,
+ RADEON_GPU_PAGE_SIZE, flags);
addr += RADEON_GPU_PAGE_SIZE * count;
}
/* handle the area in the middle */
count = (frag_end - frag_start) / 8;
- radeon_asic_vm_set_page(rdev, ib, frag_start, addr, count,
- RADEON_GPU_PAGE_SIZE, flags | frag_flags);
+ radeon_vm_set_pages(rdev, ib, frag_start, addr, count,
+ RADEON_GPU_PAGE_SIZE, flags | frag_flags);
/* handle the 4K area at the end */
if (frag_end != pe_end) {
addr += RADEON_GPU_PAGE_SIZE * count;
count = (pe_end - frag_end) / 8;
- radeon_asic_vm_set_page(rdev, ib, frag_end, addr, count,
- RADEON_GPU_PAGE_SIZE, flags);
+ radeon_vm_set_pages(rdev, ib, frag_end, addr, count,
+ RADEON_GPU_PAGE_SIZE, flags);
}
}
@@ -777,9 +819,6 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev,
unsigned count = 0;
uint64_t addr;
- start = start / RADEON_GPU_PAGE_SIZE;
- end = end / RADEON_GPU_PAGE_SIZE;
-
/* walk over the address space and update the page tables */
for (addr = start; addr < end; ) {
uint64_t pt_idx = addr >> radeon_vm_block_size;
@@ -787,7 +826,7 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev,
unsigned nptes;
uint64_t pte;
- radeon_semaphore_sync_to(ib->semaphore, pt->tbo.sync_obj);
+ radeon_semaphore_sync_resv(rdev, ib->semaphore, pt->tbo.resv, false);
if ((addr & ~mask) == (end & ~mask))
nptes = end - addr;
@@ -842,55 +881,76 @@ int radeon_vm_bo_update(struct radeon_device *rdev,
{
struct radeon_vm *vm = bo_va->vm;
struct radeon_ib ib;
- unsigned nptes, ndw;
+ unsigned nptes, ncmds, ndw;
uint64_t addr;
+ uint32_t flags;
int r;
-
- if (!bo_va->soffset) {
+ if (!bo_va->it.start) {
dev_err(rdev->dev, "bo %p don't has a mapping in vm %p\n",
bo_va->bo, vm);
return -EINVAL;
}
- if ((bo_va->valid && mem) || (!bo_va->valid && mem == NULL))
- return 0;
+ list_del_init(&bo_va->vm_status);
bo_va->flags &= ~RADEON_VM_PAGE_VALID;
bo_va->flags &= ~RADEON_VM_PAGE_SYSTEM;
+ bo_va->flags &= ~RADEON_VM_PAGE_SNOOPED;
+ if (bo_va->bo && radeon_ttm_tt_is_readonly(bo_va->bo->tbo.ttm))
+ bo_va->flags &= ~RADEON_VM_PAGE_WRITEABLE;
+
if (mem) {
addr = mem->start << PAGE_SHIFT;
if (mem->mem_type != TTM_PL_SYSTEM) {
bo_va->flags |= RADEON_VM_PAGE_VALID;
- bo_va->valid = true;
}
if (mem->mem_type == TTM_PL_TT) {
bo_va->flags |= RADEON_VM_PAGE_SYSTEM;
+ if (!(bo_va->bo->flags & (RADEON_GEM_GTT_WC | RADEON_GEM_GTT_UC)))
+ bo_va->flags |= RADEON_VM_PAGE_SNOOPED;
+
} else {
addr += rdev->vm_manager.vram_base_offset;
}
} else {
addr = 0;
- bo_va->valid = false;
}
+ if (addr == bo_va->addr)
+ return 0;
+ bo_va->addr = addr;
+
trace_radeon_vm_bo_update(bo_va);
- nptes = (bo_va->eoffset - bo_va->soffset) / RADEON_GPU_PAGE_SIZE;
+ nptes = bo_va->it.last - bo_va->it.start + 1;
+
+ /* reserve space for one command every (1 << BLOCK_SIZE) entries
+ or 2k dwords (whatever is smaller) */
+ ncmds = (nptes >> min(radeon_vm_block_size, 11)) + 1;
/* padding, etc. */
ndw = 64;
- if (radeon_vm_block_size > 11)
- /* reserve space for one header for every 2k dwords */
- ndw += (nptes >> 11) * 4;
- else
- /* reserve space for one header for
- every (1 << BLOCK_SIZE) entries */
- ndw += (nptes >> radeon_vm_block_size) * 4;
+ flags = radeon_vm_page_flags(bo_va->flags);
+ if ((flags & R600_PTE_GART_MASK) == R600_PTE_GART_MASK) {
+ /* only copy commands needed */
+ ndw += ncmds * 7;
+
+ } else if (flags & R600_PTE_SYSTEM) {
+ /* header for write data commands */
+ ndw += ncmds * 4;
+
+ /* body of write data command */
+ ndw += nptes * 2;
+
+ } else {
+ /* set page commands needed */
+ ndw += ncmds * 10;
- /* reserve space for pte addresses */
- ndw += nptes * 2;
+ /* two extra commands for begin/end of fragment */
+ ndw += 2 * 10;
+ }
/* update too big for an IB */
if (ndw > 0xfffff)
@@ -901,11 +961,15 @@ int radeon_vm_bo_update(struct radeon_device *rdev,
return r;
ib.length_dw = 0;
- radeon_vm_update_ptes(rdev, vm, &ib, bo_va->soffset, bo_va->eoffset,
- addr, radeon_vm_page_flags(bo_va->flags));
+ radeon_vm_update_ptes(rdev, vm, &ib, bo_va->it.start,
+ bo_va->it.last + 1, addr,
+ radeon_vm_page_flags(bo_va->flags));
- radeon_semaphore_sync_to(ib.semaphore, vm->fence);
- r = radeon_ib_schedule(rdev, &ib, NULL);
+ radeon_asic_vm_pad_ib(rdev, &ib);
+ WARN_ON(ib.length_dw > ndw);
+
+ radeon_semaphore_sync_fence(ib.semaphore, vm->fence);
+ r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r) {
radeon_ib_free(rdev, &ib);
return r;
@@ -936,8 +1000,8 @@ int radeon_vm_clear_freed(struct radeon_device *rdev,
int r;
list_for_each_entry_safe(bo_va, tmp, &vm->freed, vm_status) {
- list_del(&bo_va->vm_status);
r = radeon_vm_bo_update(rdev, bo_va, NULL);
+ radeon_bo_unref(&bo_va->bo);
kfree(bo_va);
if (r)
return r;
@@ -947,6 +1011,31 @@ int radeon_vm_clear_freed(struct radeon_device *rdev,
}
/**
+ * radeon_vm_clear_invalids - clear invalidated BOs in the PT
+ *
+ * @rdev: radeon_device pointer
+ * @vm: requested vm
+ *
+ * Make sure all invalidated BOs are cleared in the PT.
+ * Returns 0 for success.
+ *
+ * PTs have to be reserved and mutex must be locked!
+ */
+int radeon_vm_clear_invalids(struct radeon_device *rdev,
+ struct radeon_vm *vm)
+{
+ struct radeon_bo_va *bo_va, *tmp;
+ int r;
+
+ list_for_each_entry_safe(bo_va, tmp, &vm->invalidated, vm_status) {
+ r = radeon_vm_bo_update(rdev, bo_va, NULL);
+ if (r)
+ return r;
+ }
+ return 0;
+}
+
+/**
* radeon_vm_bo_rmv - remove a bo to a specific vm
*
* @rdev: radeon_device pointer
@@ -964,10 +1053,11 @@ void radeon_vm_bo_rmv(struct radeon_device *rdev,
list_del(&bo_va->bo_list);
mutex_lock(&vm->mutex);
- list_del(&bo_va->vm_list);
+ interval_tree_remove(&bo_va->it, &vm->va);
+ list_del(&bo_va->vm_status);
- if (bo_va->soffset) {
- bo_va->bo = NULL;
+ if (bo_va->addr) {
+ bo_va->bo = radeon_bo_ref(bo_va->bo);
list_add(&bo_va->vm_status, &vm->freed);
} else {
kfree(bo_va);
@@ -991,7 +1081,12 @@ void radeon_vm_bo_invalidate(struct radeon_device *rdev,
struct radeon_bo_va *bo_va;
list_for_each_entry(bo_va, &bo->va, bo_list) {
- bo_va->valid = false;
+ if (bo_va->addr) {
+ mutex_lock(&bo_va->vm->mutex);
+ list_del(&bo_va->vm_status);
+ list_add(&bo_va->vm_status, &bo_va->vm->invalidated);
+ mutex_unlock(&bo_va->vm->mutex);
+ }
}
}
@@ -1016,7 +1111,8 @@ int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm)
vm->last_flush = NULL;
vm->last_id_use = NULL;
mutex_init(&vm->mutex);
- INIT_LIST_HEAD(&vm->va);
+ vm->va = RB_ROOT;
+ INIT_LIST_HEAD(&vm->invalidated);
INIT_LIST_HEAD(&vm->freed);
pd_size = radeon_vm_directory_size(rdev);
@@ -1031,8 +1127,8 @@ int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm)
}
r = radeon_bo_create(rdev, pd_size, align, true,
- RADEON_GEM_DOMAIN_VRAM, NULL,
- &vm->page_directory);
+ RADEON_GEM_DOMAIN_VRAM, 0, NULL,
+ NULL, &vm->page_directory);
if (r)
return r;
@@ -1060,11 +1156,11 @@ void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm)
struct radeon_bo_va *bo_va, *tmp;
int i, r;
- if (!list_empty(&vm->va)) {
+ if (!RB_EMPTY_ROOT(&vm->va)) {
dev_err(rdev->dev, "still active bo inside vm\n");
}
- list_for_each_entry_safe(bo_va, tmp, &vm->va, vm_list) {
- list_del_init(&bo_va->vm_list);
+ rbtree_postorder_for_each_entry_safe(bo_va, tmp, &vm->va, it.rb) {
+ interval_tree_remove(&bo_va->it, &vm->va);
r = radeon_bo_reserve(bo_va->bo, false);
if (!r) {
list_del_init(&bo_va->bo_list);
@@ -1072,8 +1168,10 @@ void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm)
kfree(bo_va);
}
}
- list_for_each_entry_safe(bo_va, tmp, &vm->freed, vm_status)
+ list_for_each_entry_safe(bo_va, tmp, &vm->freed, vm_status) {
+ radeon_bo_unref(&bo_va->bo);
kfree(bo_va);
+ }
for (i = 0; i < radeon_vm_num_pdes(rdev); i++)
radeon_bo_unref(&vm->page_tables[i].bo);
diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c
index a0f96decece3..c5799f16aa4b 100644
--- a/drivers/gpu/drm/radeon/rs400.c
+++ b/drivers/gpu/drm/radeon/rs400.c
@@ -109,7 +109,6 @@ int rs400_gart_enable(struct radeon_device *rdev)
uint32_t size_reg;
uint32_t tmp;
- radeon_gart_restore(rdev);
tmp = RREG32_MC(RS690_AIC_CTRL_SCRATCH);
tmp |= RS690_DIS_OUT_OF_PCI_GART_ACCESS;
WREG32_MC(RS690_AIC_CTRL_SCRATCH, tmp);
@@ -209,17 +208,24 @@ void rs400_gart_fini(struct radeon_device *rdev)
radeon_gart_table_ram_free(rdev);
}
+#define RS400_PTE_UNSNOOPED (1 << 0)
#define RS400_PTE_WRITEABLE (1 << 2)
#define RS400_PTE_READABLE (1 << 3)
-void rs400_gart_set_page(struct radeon_device *rdev, unsigned i, uint64_t addr)
+void rs400_gart_set_page(struct radeon_device *rdev, unsigned i,
+ uint64_t addr, uint32_t flags)
{
uint32_t entry;
u32 *gtt = rdev->gart.ptr;
entry = (lower_32_bits(addr) & PAGE_MASK) |
- ((upper_32_bits(addr) & 0xff) << 4) |
- RS400_PTE_WRITEABLE | RS400_PTE_READABLE;
+ ((upper_32_bits(addr) & 0xff) << 4);
+ if (flags & RADEON_GART_PAGE_READ)
+ entry |= RS400_PTE_READABLE;
+ if (flags & RADEON_GART_PAGE_WRITE)
+ entry |= RS400_PTE_WRITEABLE;
+ if (!(flags & RADEON_GART_PAGE_SNOOP))
+ entry |= RS400_PTE_UNSNOOPED;
entry = cpu_to_le32(entry);
gtt[i] = entry;
}
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index d1a35cb1c91d..9acb1c3c005b 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -555,7 +555,6 @@ static int rs600_gart_enable(struct radeon_device *rdev)
r = radeon_gart_table_vram_pin(rdev);
if (r)
return r;
- radeon_gart_restore(rdev);
/* Enable bus master */
tmp = RREG32(RADEON_BUS_CNTL) & ~RS600_BUS_MASTER_DIS;
WREG32(RADEON_BUS_CNTL, tmp);
@@ -626,15 +625,21 @@ static void rs600_gart_fini(struct radeon_device *rdev)
radeon_gart_table_vram_free(rdev);
}
-void rs600_gart_set_page(struct radeon_device *rdev, unsigned i, uint64_t addr)
+void rs600_gart_set_page(struct radeon_device *rdev, unsigned i,
+ uint64_t addr, uint32_t flags)
{
void __iomem *ptr = (void *)rdev->gart.ptr;
addr = addr & 0xFFFFFFFFFFFFF000ULL;
- if (addr == rdev->dummy_page.addr)
- addr |= R600_PTE_SYSTEM | R600_PTE_SNOOPED;
- else
- addr |= R600_PTE_GART;
+ addr |= R600_PTE_SYSTEM;
+ if (flags & RADEON_GART_PAGE_VALID)
+ addr |= R600_PTE_VALID;
+ if (flags & RADEON_GART_PAGE_READ)
+ addr |= R600_PTE_READABLE;
+ if (flags & RADEON_GART_PAGE_WRITE)
+ addr |= R600_PTE_WRITEABLE;
+ if (flags & RADEON_GART_PAGE_SNOOP)
+ addr |= R600_PTE_SNOOPED;
writeq(addr, ptr + (i * 8));
}
@@ -874,6 +879,9 @@ void rs600_bandwidth_update(struct radeon_device *rdev)
u32 d1mode_priority_a_cnt, d2mode_priority_a_cnt;
/* FIXME: implement full support */
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled)
diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c
index 3462b64369bf..0a2d36e81108 100644
--- a/drivers/gpu/drm/radeon/rs690.c
+++ b/drivers/gpu/drm/radeon/rs690.c
@@ -579,6 +579,9 @@ void rs690_bandwidth_update(struct radeon_device *rdev)
u32 d1mode_priority_a_cnt, d1mode_priority_b_cnt;
u32 d2mode_priority_a_cnt, d2mode_priority_b_cnt;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled)
diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c
index 02f7710de470..9031f4b69824 100644
--- a/drivers/gpu/drm/radeon/rs780_dpm.c
+++ b/drivers/gpu/drm/radeon/rs780_dpm.c
@@ -24,6 +24,7 @@
#include "drmP.h"
#include "radeon.h"
+#include "radeon_asic.h"
#include "rs780d.h"
#include "r600_dpm.h"
#include "rs780_dpm.h"
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
index 3e21e869015f..c55d653aaf5f 100644
--- a/drivers/gpu/drm/radeon/rv515.c
+++ b/drivers/gpu/drm/radeon/rv515.c
@@ -124,7 +124,7 @@ void rv515_ring_start(struct radeon_device *rdev, struct radeon_ring *ring)
radeon_ring_write(ring, GEOMETRY_ROUND_NEAREST | COLOR_ROUND_NEAREST);
radeon_ring_write(ring, PACKET0(0x20C8, 0));
radeon_ring_write(ring, 0);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
}
int rv515_mc_wait_for_idle(struct radeon_device *rdev)
@@ -1277,6 +1277,9 @@ void rv515_bandwidth_update(struct radeon_device *rdev)
struct drm_display_mode *mode0 = NULL;
struct drm_display_mode *mode1 = NULL;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled)
diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c
index e7045b085715..6a5c233361e9 100644
--- a/drivers/gpu/drm/radeon/rv6xx_dpm.c
+++ b/drivers/gpu/drm/radeon/rv6xx_dpm.c
@@ -24,6 +24,7 @@
#include "drmP.h"
#include "radeon.h"
+#include "radeon_asic.h"
#include "rv6xxd.h"
#include "r600_dpm.h"
#include "rv6xx_dpm.h"
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index da8703d8d455..372016e266d0 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -26,7 +26,6 @@
* Jerome Glisse
*/
#include <linux/firmware.h>
-#include <linux/platform_device.h>
#include <linux/slab.h>
#include <drm/drmP.h>
#include "radeon.h"
@@ -900,7 +899,6 @@ static int rv770_pcie_gart_enable(struct radeon_device *rdev)
r = radeon_gart_table_vram_pin(rdev);
if (r)
return r;
- radeon_gart_restore(rdev);
/* Setup L2 cache */
WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING |
ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE |
@@ -1178,7 +1176,6 @@ static void rv770_gpu_init(struct radeon_device *rdev)
u32 hdp_host_path_cntl;
u32 sq_dyn_gpr_size_simd_ab_0;
u32 gb_tiling_config = 0;
- u32 cc_rb_backend_disable = 0;
u32 cc_gc_shader_pipe_config = 0;
u32 mc_arb_ramcfg;
u32 db_debug4, tmp;
@@ -1312,21 +1309,7 @@ static void rv770_gpu_init(struct radeon_device *rdev)
WREG32(SPI_CONFIG_CNTL, 0);
}
- cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000;
- tmp = R7XX_MAX_BACKENDS - r600_count_pipe_bits(cc_rb_backend_disable >> 16);
- if (tmp < rdev->config.rv770.max_backends) {
- rdev->config.rv770.max_backends = tmp;
- }
-
cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00;
- tmp = R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config >> 8) & R7XX_MAX_PIPES_MASK);
- if (tmp < rdev->config.rv770.max_pipes) {
- rdev->config.rv770.max_pipes = tmp;
- }
- tmp = R7XX_MAX_SIMDS - r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R7XX_MAX_SIMDS_MASK);
- if (tmp < rdev->config.rv770.max_simds) {
- rdev->config.rv770.max_simds = tmp;
- }
tmp = rdev->config.rv770.max_simds -
r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R7XX_MAX_SIMDS_MASK);
rdev->config.rv770.active_simds = tmp;
@@ -1349,6 +1332,14 @@ static void rv770_gpu_init(struct radeon_device *rdev)
rdev->config.rv770.tiling_npipes = rdev->config.rv770.max_tile_pipes;
disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R7XX_MAX_BACKENDS_MASK;
+ tmp = 0;
+ for (i = 0; i < rdev->config.rv770.max_backends; i++)
+ tmp |= (1 << i);
+ /* if all the backends are disabled, fix it up here */
+ if ((disabled_rb_mask & tmp) == tmp) {
+ for (i = 0; i < rdev->config.rv770.max_backends; i++)
+ disabled_rb_mask &= ~(1 << i);
+ }
tmp = (gb_tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT;
tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.rv770.max_backends,
R7XX_MAX_BACKENDS, disabled_rb_mask);
diff --git a/drivers/gpu/drm/radeon/rv770_dma.c b/drivers/gpu/drm/radeon/rv770_dma.c
index bbf2e076ee45..7f34bad2e724 100644
--- a/drivers/gpu/drm/radeon/rv770_dma.c
+++ b/drivers/gpu/drm/radeon/rv770_dma.c
@@ -33,18 +33,19 @@
* @src_offset: src GPU address
* @dst_offset: dst GPU address
* @num_gpu_pages: number of GPU pages to xfer
- * @fence: radeon fence object
+ * @resv: reservation object to sync to
*
* Copy GPU paging using the DMA engine (r7xx).
* Used by the radeon ttm implementation to move pages if
* registered as the asic copy callback.
*/
-int rv770_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence)
+struct radeon_fence *rv770_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv)
{
struct radeon_semaphore *sem = NULL;
+ struct radeon_fence *fence;
int ring_index = rdev->asic->copy.dma_ring_index;
struct radeon_ring *ring = &rdev->ring[ring_index];
u32 size_in_dw, cur_size_in_dw;
@@ -54,7 +55,7 @@ int rv770_copy_dma(struct radeon_device *rdev,
r = radeon_semaphore_create(rdev, &sem);
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
- return r;
+ return ERR_PTR(r);
}
size_in_dw = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT) / 4;
@@ -63,10 +64,10 @@ int rv770_copy_dma(struct radeon_device *rdev,
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_semaphore_sync_to(sem, *fence);
+ radeon_semaphore_sync_resv(rdev, sem, resv, false);
radeon_semaphore_sync_rings(rdev, sem, ring->idx);
for (i = 0; i < num_loops; i++) {
@@ -83,15 +84,15 @@ int rv770_copy_dma(struct radeon_device *rdev,
dst_offset += cur_size_in_dw * 4;
}
- r = radeon_fence_emit(rdev, fence, ring->idx);
+ r = radeon_fence_emit(rdev, &fence, ring->idx);
if (r) {
radeon_ring_unlock_undo(rdev, ring);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_ring_unlock_commit(rdev, ring);
- radeon_semaphore_free(rdev, &sem, *fence);
+ radeon_ring_unlock_commit(rdev, ring, false);
+ radeon_semaphore_free(rdev, &sem, fence);
- return r;
+ return fence;
}
diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c
index 3c76e1dcdf04..755a8f96fe46 100644
--- a/drivers/gpu/drm/radeon/rv770_dpm.c
+++ b/drivers/gpu/drm/radeon/rv770_dpm.c
@@ -24,6 +24,7 @@
#include "drmP.h"
#include "radeon.h"
+#include "radeon_asic.h"
#include "rv770d.h"
#include "r600_dpm.h"
#include "rv770_dpm.h"
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index 9e854fd016da..7d5083dc4acb 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -42,6 +42,14 @@ MODULE_FIRMWARE("radeon/TAHITI_mc.bin");
MODULE_FIRMWARE("radeon/TAHITI_mc2.bin");
MODULE_FIRMWARE("radeon/TAHITI_rlc.bin");
MODULE_FIRMWARE("radeon/TAHITI_smc.bin");
+
+MODULE_FIRMWARE("radeon/tahiti_pfp.bin");
+MODULE_FIRMWARE("radeon/tahiti_me.bin");
+MODULE_FIRMWARE("radeon/tahiti_ce.bin");
+MODULE_FIRMWARE("radeon/tahiti_mc.bin");
+MODULE_FIRMWARE("radeon/tahiti_rlc.bin");
+MODULE_FIRMWARE("radeon/tahiti_smc.bin");
+
MODULE_FIRMWARE("radeon/PITCAIRN_pfp.bin");
MODULE_FIRMWARE("radeon/PITCAIRN_me.bin");
MODULE_FIRMWARE("radeon/PITCAIRN_ce.bin");
@@ -49,6 +57,14 @@ MODULE_FIRMWARE("radeon/PITCAIRN_mc.bin");
MODULE_FIRMWARE("radeon/PITCAIRN_mc2.bin");
MODULE_FIRMWARE("radeon/PITCAIRN_rlc.bin");
MODULE_FIRMWARE("radeon/PITCAIRN_smc.bin");
+
+MODULE_FIRMWARE("radeon/pitcairn_pfp.bin");
+MODULE_FIRMWARE("radeon/pitcairn_me.bin");
+MODULE_FIRMWARE("radeon/pitcairn_ce.bin");
+MODULE_FIRMWARE("radeon/pitcairn_mc.bin");
+MODULE_FIRMWARE("radeon/pitcairn_rlc.bin");
+MODULE_FIRMWARE("radeon/pitcairn_smc.bin");
+
MODULE_FIRMWARE("radeon/VERDE_pfp.bin");
MODULE_FIRMWARE("radeon/VERDE_me.bin");
MODULE_FIRMWARE("radeon/VERDE_ce.bin");
@@ -56,6 +72,14 @@ MODULE_FIRMWARE("radeon/VERDE_mc.bin");
MODULE_FIRMWARE("radeon/VERDE_mc2.bin");
MODULE_FIRMWARE("radeon/VERDE_rlc.bin");
MODULE_FIRMWARE("radeon/VERDE_smc.bin");
+
+MODULE_FIRMWARE("radeon/verde_pfp.bin");
+MODULE_FIRMWARE("radeon/verde_me.bin");
+MODULE_FIRMWARE("radeon/verde_ce.bin");
+MODULE_FIRMWARE("radeon/verde_mc.bin");
+MODULE_FIRMWARE("radeon/verde_rlc.bin");
+MODULE_FIRMWARE("radeon/verde_smc.bin");
+
MODULE_FIRMWARE("radeon/OLAND_pfp.bin");
MODULE_FIRMWARE("radeon/OLAND_me.bin");
MODULE_FIRMWARE("radeon/OLAND_ce.bin");
@@ -63,6 +87,14 @@ MODULE_FIRMWARE("radeon/OLAND_mc.bin");
MODULE_FIRMWARE("radeon/OLAND_mc2.bin");
MODULE_FIRMWARE("radeon/OLAND_rlc.bin");
MODULE_FIRMWARE("radeon/OLAND_smc.bin");
+
+MODULE_FIRMWARE("radeon/oland_pfp.bin");
+MODULE_FIRMWARE("radeon/oland_me.bin");
+MODULE_FIRMWARE("radeon/oland_ce.bin");
+MODULE_FIRMWARE("radeon/oland_mc.bin");
+MODULE_FIRMWARE("radeon/oland_rlc.bin");
+MODULE_FIRMWARE("radeon/oland_smc.bin");
+
MODULE_FIRMWARE("radeon/HAINAN_pfp.bin");
MODULE_FIRMWARE("radeon/HAINAN_me.bin");
MODULE_FIRMWARE("radeon/HAINAN_ce.bin");
@@ -71,6 +103,13 @@ MODULE_FIRMWARE("radeon/HAINAN_mc2.bin");
MODULE_FIRMWARE("radeon/HAINAN_rlc.bin");
MODULE_FIRMWARE("radeon/HAINAN_smc.bin");
+MODULE_FIRMWARE("radeon/hainan_pfp.bin");
+MODULE_FIRMWARE("radeon/hainan_me.bin");
+MODULE_FIRMWARE("radeon/hainan_ce.bin");
+MODULE_FIRMWARE("radeon/hainan_mc.bin");
+MODULE_FIRMWARE("radeon/hainan_rlc.bin");
+MODULE_FIRMWARE("radeon/hainan_smc.bin");
+
static u32 si_get_cu_active_bitmap(struct radeon_device *rdev, u32 se, u32 sh);
static void si_pcie_gen3_enable(struct radeon_device *rdev);
static void si_program_aspm(struct radeon_device *rdev);
@@ -1470,38 +1509,54 @@ static const u32 hainan_io_mc_regs[TAHITI_IO_MC_REGS_SIZE][2] = {
/* ucode loading */
int si_mc_load_microcode(struct radeon_device *rdev)
{
- const __be32 *fw_data;
+ const __be32 *fw_data = NULL;
+ const __le32 *new_fw_data = NULL;
u32 running, blackout = 0;
- u32 *io_mc_regs;
+ u32 *io_mc_regs = NULL;
+ const __le32 *new_io_mc_regs = NULL;
int i, regs_size, ucode_size;
if (!rdev->mc_fw)
return -EINVAL;
- ucode_size = rdev->mc_fw->size / 4;
+ if (rdev->new_fw) {
+ const struct mc_firmware_header_v1_0 *hdr =
+ (const struct mc_firmware_header_v1_0 *)rdev->mc_fw->data;
+
+ radeon_ucode_print_mc_hdr(&hdr->header);
+ regs_size = le32_to_cpu(hdr->io_debug_size_bytes) / (4 * 2);
+ new_io_mc_regs = (const __le32 *)
+ (rdev->mc_fw->data + le32_to_cpu(hdr->io_debug_array_offset_bytes));
+ ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
+ new_fw_data = (const __le32 *)
+ (rdev->mc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ } else {
+ ucode_size = rdev->mc_fw->size / 4;
- switch (rdev->family) {
- case CHIP_TAHITI:
- io_mc_regs = (u32 *)&tahiti_io_mc_regs;
- regs_size = TAHITI_IO_MC_REGS_SIZE;
- break;
- case CHIP_PITCAIRN:
- io_mc_regs = (u32 *)&pitcairn_io_mc_regs;
- regs_size = TAHITI_IO_MC_REGS_SIZE;
- break;
- case CHIP_VERDE:
- default:
- io_mc_regs = (u32 *)&verde_io_mc_regs;
- regs_size = TAHITI_IO_MC_REGS_SIZE;
- break;
- case CHIP_OLAND:
- io_mc_regs = (u32 *)&oland_io_mc_regs;
- regs_size = TAHITI_IO_MC_REGS_SIZE;
- break;
- case CHIP_HAINAN:
- io_mc_regs = (u32 *)&hainan_io_mc_regs;
- regs_size = TAHITI_IO_MC_REGS_SIZE;
- break;
+ switch (rdev->family) {
+ case CHIP_TAHITI:
+ io_mc_regs = (u32 *)&tahiti_io_mc_regs;
+ regs_size = TAHITI_IO_MC_REGS_SIZE;
+ break;
+ case CHIP_PITCAIRN:
+ io_mc_regs = (u32 *)&pitcairn_io_mc_regs;
+ regs_size = TAHITI_IO_MC_REGS_SIZE;
+ break;
+ case CHIP_VERDE:
+ default:
+ io_mc_regs = (u32 *)&verde_io_mc_regs;
+ regs_size = TAHITI_IO_MC_REGS_SIZE;
+ break;
+ case CHIP_OLAND:
+ io_mc_regs = (u32 *)&oland_io_mc_regs;
+ regs_size = TAHITI_IO_MC_REGS_SIZE;
+ break;
+ case CHIP_HAINAN:
+ io_mc_regs = (u32 *)&hainan_io_mc_regs;
+ regs_size = TAHITI_IO_MC_REGS_SIZE;
+ break;
+ }
+ fw_data = (const __be32 *)rdev->mc_fw->data;
}
running = RREG32(MC_SEQ_SUP_CNTL) & RUN_MASK;
@@ -1518,13 +1573,21 @@ int si_mc_load_microcode(struct radeon_device *rdev)
/* load mc io regs */
for (i = 0; i < regs_size; i++) {
- WREG32(MC_SEQ_IO_DEBUG_INDEX, io_mc_regs[(i << 1)]);
- WREG32(MC_SEQ_IO_DEBUG_DATA, io_mc_regs[(i << 1) + 1]);
+ if (rdev->new_fw) {
+ WREG32(MC_SEQ_IO_DEBUG_INDEX, le32_to_cpup(new_io_mc_regs++));
+ WREG32(MC_SEQ_IO_DEBUG_DATA, le32_to_cpup(new_io_mc_regs++));
+ } else {
+ WREG32(MC_SEQ_IO_DEBUG_INDEX, io_mc_regs[(i << 1)]);
+ WREG32(MC_SEQ_IO_DEBUG_DATA, io_mc_regs[(i << 1) + 1]);
+ }
}
/* load the MC ucode */
- fw_data = (const __be32 *)rdev->mc_fw->data;
- for (i = 0; i < ucode_size; i++)
- WREG32(MC_SEQ_SUP_PGM, be32_to_cpup(fw_data++));
+ for (i = 0; i < ucode_size; i++) {
+ if (rdev->new_fw)
+ WREG32(MC_SEQ_SUP_PGM, le32_to_cpup(new_fw_data++));
+ else
+ WREG32(MC_SEQ_SUP_PGM, be32_to_cpup(fw_data++));
+ }
/* put the engine back into the active state */
WREG32(MC_SEQ_SUP_CNTL, 0x00000008);
@@ -1553,18 +1616,19 @@ int si_mc_load_microcode(struct radeon_device *rdev)
static int si_init_microcode(struct radeon_device *rdev)
{
const char *chip_name;
- const char *rlc_chip_name;
+ const char *new_chip_name;
size_t pfp_req_size, me_req_size, ce_req_size, rlc_req_size, mc_req_size;
size_t smc_req_size, mc2_req_size;
char fw_name[30];
int err;
+ int new_fw = 0;
DRM_DEBUG("\n");
switch (rdev->family) {
case CHIP_TAHITI:
chip_name = "TAHITI";
- rlc_chip_name = "TAHITI";
+ new_chip_name = "tahiti";
pfp_req_size = SI_PFP_UCODE_SIZE * 4;
me_req_size = SI_PM4_UCODE_SIZE * 4;
ce_req_size = SI_CE_UCODE_SIZE * 4;
@@ -1575,7 +1639,7 @@ static int si_init_microcode(struct radeon_device *rdev)
break;
case CHIP_PITCAIRN:
chip_name = "PITCAIRN";
- rlc_chip_name = "PITCAIRN";
+ new_chip_name = "pitcairn";
pfp_req_size = SI_PFP_UCODE_SIZE * 4;
me_req_size = SI_PM4_UCODE_SIZE * 4;
ce_req_size = SI_CE_UCODE_SIZE * 4;
@@ -1586,7 +1650,7 @@ static int si_init_microcode(struct radeon_device *rdev)
break;
case CHIP_VERDE:
chip_name = "VERDE";
- rlc_chip_name = "VERDE";
+ new_chip_name = "verde";
pfp_req_size = SI_PFP_UCODE_SIZE * 4;
me_req_size = SI_PM4_UCODE_SIZE * 4;
ce_req_size = SI_CE_UCODE_SIZE * 4;
@@ -1597,7 +1661,7 @@ static int si_init_microcode(struct radeon_device *rdev)
break;
case CHIP_OLAND:
chip_name = "OLAND";
- rlc_chip_name = "OLAND";
+ new_chip_name = "oland";
pfp_req_size = SI_PFP_UCODE_SIZE * 4;
me_req_size = SI_PM4_UCODE_SIZE * 4;
ce_req_size = SI_CE_UCODE_SIZE * 4;
@@ -1607,7 +1671,7 @@ static int si_init_microcode(struct radeon_device *rdev)
break;
case CHIP_HAINAN:
chip_name = "HAINAN";
- rlc_chip_name = "HAINAN";
+ new_chip_name = "hainan";
pfp_req_size = SI_PFP_UCODE_SIZE * 4;
me_req_size = SI_PM4_UCODE_SIZE * 4;
ce_req_size = SI_CE_UCODE_SIZE * 4;
@@ -1618,86 +1682,178 @@ static int si_init_microcode(struct radeon_device *rdev)
default: BUG();
}
- DRM_INFO("Loading %s Microcode\n", chip_name);
+ DRM_INFO("Loading %s Microcode\n", new_chip_name);
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", new_chip_name);
err = request_firmware(&rdev->pfp_fw, fw_name, rdev->dev);
- if (err)
- goto out;
- if (rdev->pfp_fw->size != pfp_req_size) {
- printk(KERN_ERR
- "si_cp: Bogus length %zu in firmware \"%s\"\n",
- rdev->pfp_fw->size, fw_name);
- err = -EINVAL;
- goto out;
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", chip_name);
+ err = request_firmware(&rdev->pfp_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ if (rdev->pfp_fw->size != pfp_req_size) {
+ printk(KERN_ERR
+ "si_cp: Bogus length %zu in firmware \"%s\"\n",
+ rdev->pfp_fw->size, fw_name);
+ err = -EINVAL;
+ goto out;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->pfp_fw);
+ if (err) {
+ printk(KERN_ERR
+ "si_cp: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_me.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_me.bin", new_chip_name);
err = request_firmware(&rdev->me_fw, fw_name, rdev->dev);
- if (err)
- goto out;
- if (rdev->me_fw->size != me_req_size) {
- printk(KERN_ERR
- "si_cp: Bogus length %zu in firmware \"%s\"\n",
- rdev->me_fw->size, fw_name);
- err = -EINVAL;
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_me.bin", chip_name);
+ err = request_firmware(&rdev->me_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ if (rdev->me_fw->size != me_req_size) {
+ printk(KERN_ERR
+ "si_cp: Bogus length %zu in firmware \"%s\"\n",
+ rdev->me_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->me_fw);
+ if (err) {
+ printk(KERN_ERR
+ "si_cp: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_ce.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_ce.bin", new_chip_name);
err = request_firmware(&rdev->ce_fw, fw_name, rdev->dev);
- if (err)
- goto out;
- if (rdev->ce_fw->size != ce_req_size) {
- printk(KERN_ERR
- "si_cp: Bogus length %zu in firmware \"%s\"\n",
- rdev->ce_fw->size, fw_name);
- err = -EINVAL;
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_ce.bin", chip_name);
+ err = request_firmware(&rdev->ce_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ if (rdev->ce_fw->size != ce_req_size) {
+ printk(KERN_ERR
+ "si_cp: Bogus length %zu in firmware \"%s\"\n",
+ rdev->ce_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->ce_fw);
+ if (err) {
+ printk(KERN_ERR
+ "si_cp: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", rlc_chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", new_chip_name);
err = request_firmware(&rdev->rlc_fw, fw_name, rdev->dev);
- if (err)
- goto out;
- if (rdev->rlc_fw->size != rlc_req_size) {
- printk(KERN_ERR
- "si_rlc: Bogus length %zu in firmware \"%s\"\n",
- rdev->rlc_fw->size, fw_name);
- err = -EINVAL;
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", chip_name);
+ err = request_firmware(&rdev->rlc_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ if (rdev->rlc_fw->size != rlc_req_size) {
+ printk(KERN_ERR
+ "si_rlc: Bogus length %zu in firmware \"%s\"\n",
+ rdev->rlc_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->rlc_fw);
+ if (err) {
+ printk(KERN_ERR
+ "si_cp: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc2.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", new_chip_name);
err = request_firmware(&rdev->mc_fw, fw_name, rdev->dev);
if (err) {
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc2.bin", chip_name);
err = request_firmware(&rdev->mc_fw, fw_name, rdev->dev);
- if (err)
+ if (err) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
+ err = request_firmware(&rdev->mc_fw, fw_name, rdev->dev);
+ if (err)
+ goto out;
+ }
+ if ((rdev->mc_fw->size != mc_req_size) &&
+ (rdev->mc_fw->size != mc2_req_size)) {
+ printk(KERN_ERR
+ "si_mc: Bogus length %zu in firmware \"%s\"\n",
+ rdev->mc_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ DRM_INFO("%s: %zu bytes\n", fw_name, rdev->mc_fw->size);
+ } else {
+ err = radeon_ucode_validate(rdev->mc_fw);
+ if (err) {
+ printk(KERN_ERR
+ "si_cp: validation failed for firmware \"%s\"\n",
+ fw_name);
goto out;
+ } else {
+ new_fw++;
+ }
}
- if ((rdev->mc_fw->size != mc_req_size) &&
- (rdev->mc_fw->size != mc2_req_size)) {
- printk(KERN_ERR
- "si_mc: Bogus length %zu in firmware \"%s\"\n",
- rdev->mc_fw->size, fw_name);
- err = -EINVAL;
- }
- DRM_INFO("%s: %zu bytes\n", fw_name, rdev->mc_fw->size);
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name);
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", new_chip_name);
err = request_firmware(&rdev->smc_fw, fw_name, rdev->dev);
if (err) {
- printk(KERN_ERR
- "smc: error loading firmware \"%s\"\n",
- fw_name);
- release_firmware(rdev->smc_fw);
- rdev->smc_fw = NULL;
- err = 0;
- } else if (rdev->smc_fw->size != smc_req_size) {
- printk(KERN_ERR
- "si_smc: Bogus length %zu in firmware \"%s\"\n",
- rdev->smc_fw->size, fw_name);
- err = -EINVAL;
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name);
+ err = request_firmware(&rdev->smc_fw, fw_name, rdev->dev);
+ if (err) {
+ printk(KERN_ERR
+ "smc: error loading firmware \"%s\"\n",
+ fw_name);
+ release_firmware(rdev->smc_fw);
+ rdev->smc_fw = NULL;
+ err = 0;
+ } else if (rdev->smc_fw->size != smc_req_size) {
+ printk(KERN_ERR
+ "si_smc: Bogus length %zu in firmware \"%s\"\n",
+ rdev->smc_fw->size, fw_name);
+ err = -EINVAL;
+ }
+ } else {
+ err = radeon_ucode_validate(rdev->smc_fw);
+ if (err) {
+ printk(KERN_ERR
+ "si_cp: validation failed for firmware \"%s\"\n",
+ fw_name);
+ goto out;
+ } else {
+ new_fw++;
+ }
}
+ if (new_fw == 0) {
+ rdev->new_fw = false;
+ } else if (new_fw < 6) {
+ printk(KERN_ERR "si_fw: mixing new and old firmware!\n");
+ err = -EINVAL;
+ } else {
+ rdev->new_fw = true;
+ }
out:
if (err) {
if (err != -EINVAL)
@@ -2228,6 +2384,9 @@ void dce6_bandwidth_update(struct radeon_device *rdev)
u32 num_heads = 0, lb_size;
int i;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
for (i = 0; i < rdev->num_crtc; i++) {
@@ -2901,7 +3060,7 @@ static void si_gpu_init(struct radeon_device *rdev)
u32 sx_debug_1;
u32 hdp_host_path_cntl;
u32 tmp;
- int i, j, k;
+ int i, j;
switch (rdev->family) {
case CHIP_TAHITI:
@@ -3099,12 +3258,11 @@ static void si_gpu_init(struct radeon_device *rdev)
rdev->config.si.max_sh_per_se,
rdev->config.si.max_cu_per_sh);
+ rdev->config.si.active_cus = 0;
for (i = 0; i < rdev->config.si.max_shader_engines; i++) {
for (j = 0; j < rdev->config.si.max_sh_per_se; j++) {
- for (k = 0; k < rdev->config.si.max_cu_per_sh; k++) {
- rdev->config.si.active_cus +=
- hweight32(si_get_cu_active_bitmap(rdev, i, j));
- }
+ rdev->config.si.active_cus +=
+ hweight32(si_get_cu_active_bitmap(rdev, i, j));
}
}
@@ -3282,34 +3440,77 @@ static void si_cp_enable(struct radeon_device *rdev, bool enable)
static int si_cp_load_microcode(struct radeon_device *rdev)
{
- const __be32 *fw_data;
int i;
- if (!rdev->me_fw || !rdev->pfp_fw)
+ if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw)
return -EINVAL;
si_cp_enable(rdev, false);
- /* PFP */
- fw_data = (const __be32 *)rdev->pfp_fw->data;
- WREG32(CP_PFP_UCODE_ADDR, 0);
- for (i = 0; i < SI_PFP_UCODE_SIZE; i++)
- WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++));
- WREG32(CP_PFP_UCODE_ADDR, 0);
-
- /* CE */
- fw_data = (const __be32 *)rdev->ce_fw->data;
- WREG32(CP_CE_UCODE_ADDR, 0);
- for (i = 0; i < SI_CE_UCODE_SIZE; i++)
- WREG32(CP_CE_UCODE_DATA, be32_to_cpup(fw_data++));
- WREG32(CP_CE_UCODE_ADDR, 0);
-
- /* ME */
- fw_data = (const __be32 *)rdev->me_fw->data;
- WREG32(CP_ME_RAM_WADDR, 0);
- for (i = 0; i < SI_PM4_UCODE_SIZE; i++)
- WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++));
- WREG32(CP_ME_RAM_WADDR, 0);
+ if (rdev->new_fw) {
+ const struct gfx_firmware_header_v1_0 *pfp_hdr =
+ (const struct gfx_firmware_header_v1_0 *)rdev->pfp_fw->data;
+ const struct gfx_firmware_header_v1_0 *ce_hdr =
+ (const struct gfx_firmware_header_v1_0 *)rdev->ce_fw->data;
+ const struct gfx_firmware_header_v1_0 *me_hdr =
+ (const struct gfx_firmware_header_v1_0 *)rdev->me_fw->data;
+ const __le32 *fw_data;
+ u32 fw_size;
+
+ radeon_ucode_print_gfx_hdr(&pfp_hdr->header);
+ radeon_ucode_print_gfx_hdr(&ce_hdr->header);
+ radeon_ucode_print_gfx_hdr(&me_hdr->header);
+
+ /* PFP */
+ fw_data = (const __le32 *)
+ (rdev->pfp_fw->data + le32_to_cpu(pfp_hdr->header.ucode_array_offset_bytes));
+ fw_size = le32_to_cpu(pfp_hdr->header.ucode_size_bytes) / 4;
+ WREG32(CP_PFP_UCODE_ADDR, 0);
+ for (i = 0; i < fw_size; i++)
+ WREG32(CP_PFP_UCODE_DATA, le32_to_cpup(fw_data++));
+ WREG32(CP_PFP_UCODE_ADDR, 0);
+
+ /* CE */
+ fw_data = (const __le32 *)
+ (rdev->ce_fw->data + le32_to_cpu(ce_hdr->header.ucode_array_offset_bytes));
+ fw_size = le32_to_cpu(ce_hdr->header.ucode_size_bytes) / 4;
+ WREG32(CP_CE_UCODE_ADDR, 0);
+ for (i = 0; i < fw_size; i++)
+ WREG32(CP_CE_UCODE_DATA, le32_to_cpup(fw_data++));
+ WREG32(CP_CE_UCODE_ADDR, 0);
+
+ /* ME */
+ fw_data = (const __be32 *)
+ (rdev->me_fw->data + le32_to_cpu(me_hdr->header.ucode_array_offset_bytes));
+ fw_size = le32_to_cpu(me_hdr->header.ucode_size_bytes) / 4;
+ WREG32(CP_ME_RAM_WADDR, 0);
+ for (i = 0; i < fw_size; i++)
+ WREG32(CP_ME_RAM_DATA, le32_to_cpup(fw_data++));
+ WREG32(CP_ME_RAM_WADDR, 0);
+ } else {
+ const __be32 *fw_data;
+
+ /* PFP */
+ fw_data = (const __be32 *)rdev->pfp_fw->data;
+ WREG32(CP_PFP_UCODE_ADDR, 0);
+ for (i = 0; i < SI_PFP_UCODE_SIZE; i++)
+ WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++));
+ WREG32(CP_PFP_UCODE_ADDR, 0);
+
+ /* CE */
+ fw_data = (const __be32 *)rdev->ce_fw->data;
+ WREG32(CP_CE_UCODE_ADDR, 0);
+ for (i = 0; i < SI_CE_UCODE_SIZE; i++)
+ WREG32(CP_CE_UCODE_DATA, be32_to_cpup(fw_data++));
+ WREG32(CP_CE_UCODE_ADDR, 0);
+
+ /* ME */
+ fw_data = (const __be32 *)rdev->me_fw->data;
+ WREG32(CP_ME_RAM_WADDR, 0);
+ for (i = 0; i < SI_PM4_UCODE_SIZE; i++)
+ WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++));
+ WREG32(CP_ME_RAM_WADDR, 0);
+ }
WREG32(CP_PFP_UCODE_ADDR, 0);
WREG32(CP_CE_UCODE_ADDR, 0);
@@ -3342,7 +3543,7 @@ static int si_cp_start(struct radeon_device *rdev)
radeon_ring_write(ring, PACKET3_BASE_INDEX(CE_PARTITION_BASE));
radeon_ring_write(ring, 0xc000);
radeon_ring_write(ring, 0xe000);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
si_cp_enable(rdev, true);
@@ -3371,7 +3572,7 @@ static int si_cp_start(struct radeon_device *rdev)
radeon_ring_write(ring, 0x0000000e); /* VGT_VERTEX_REUSE_BLOCK_CNTL */
radeon_ring_write(ring, 0x00000010); /* VGT_OUT_DEALLOC_CNTL */
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
for (i = RADEON_RING_TYPE_GFX_INDEX; i <= CAYMAN_RING_TYPE_CP2_INDEX; ++i) {
ring = &rdev->ring[i];
@@ -3381,7 +3582,7 @@ static int si_cp_start(struct radeon_device *rdev)
radeon_ring_write(ring, PACKET3_COMPUTE(PACKET3_CLEAR_STATE, 0));
radeon_ring_write(ring, 0);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
}
return 0;
@@ -4048,7 +4249,6 @@ static int si_pcie_gart_enable(struct radeon_device *rdev)
r = radeon_gart_table_vram_pin(rdev);
if (r)
return r;
- radeon_gart_restore(rdev);
/* Setup TLB control */
WREG32(MC_VM_MX_L1_TLB_CNTL,
(0xA << 7) |
@@ -4093,10 +4293,10 @@ static int si_pcie_gart_enable(struct radeon_device *rdev)
for (i = 1; i < 16; i++) {
if (i < 8)
WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2),
- rdev->gart.table_addr >> 12);
+ rdev->vm_manager.saved_table_addr[i]);
else
WREG32(VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2),
- rdev->gart.table_addr >> 12);
+ rdev->vm_manager.saved_table_addr[i]);
}
/* enable context1-15 */
@@ -4128,6 +4328,17 @@ static int si_pcie_gart_enable(struct radeon_device *rdev)
static void si_pcie_gart_disable(struct radeon_device *rdev)
{
+ unsigned i;
+
+ for (i = 1; i < 16; ++i) {
+ uint32_t reg;
+ if (i < 8)
+ reg = VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2);
+ else
+ reg = VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2);
+ rdev->vm_manager.saved_table_addr[i] = RREG32(reg);
+ }
+
/* Disable all tables */
WREG32(VM_CONTEXT0_CNTL, 0);
WREG32(VM_CONTEXT1_CNTL, 0);
@@ -4476,7 +4687,7 @@ static int si_vm_packet3_compute_check(struct radeon_device *rdev,
int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib)
{
int ret = 0;
- u32 idx = 0;
+ u32 idx = 0, i;
struct radeon_cs_packet pkt;
do {
@@ -4487,6 +4698,12 @@ int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib)
switch (pkt.type) {
case RADEON_PACKET_TYPE0:
dev_err(rdev->dev, "Packet0 not allowed!\n");
+ for (i = 0; i < ib->length_dw; i++) {
+ if (i == idx)
+ printk("\t0x%08x <---\n", ib->ptr[i]);
+ else
+ printk("\t0x%08x\n", ib->ptr[i]);
+ }
ret = -EINVAL;
break;
case RADEON_PACKET_TYPE2:
@@ -4815,7 +5032,7 @@ void si_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
/* write new base address */
radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
- radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+ radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(1) |
WRITE_DATA_DST_SEL(0)));
if (vm->id < 8) {
@@ -4830,7 +5047,7 @@ void si_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
/* flush hdp cache */
radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
- radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+ radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(1) |
WRITE_DATA_DST_SEL(0)));
radeon_ring_write(ring, HDP_MEM_COHERENCY_FLUSH_CNTL >> 2);
radeon_ring_write(ring, 0);
@@ -4838,7 +5055,7 @@ void si_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
/* bits 0-15 are the VM contexts0-15 */
radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
- radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
+ radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(1) |
WRITE_DATA_DST_SEL(0)));
radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2);
radeon_ring_write(ring, 0);
@@ -5592,7 +5809,6 @@ static void si_enable_lbpw(struct radeon_device *rdev, bool enable)
static int si_rlc_resume(struct radeon_device *rdev)
{
u32 i;
- const __be32 *fw_data;
if (!rdev->rlc_fw)
return -EINVAL;
@@ -5615,10 +5831,26 @@ static int si_rlc_resume(struct radeon_device *rdev)
WREG32(RLC_MC_CNTL, 0);
WREG32(RLC_UCODE_CNTL, 0);
- fw_data = (const __be32 *)rdev->rlc_fw->data;
- for (i = 0; i < SI_RLC_UCODE_SIZE; i++) {
- WREG32(RLC_UCODE_ADDR, i);
- WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
+ if (rdev->new_fw) {
+ const struct rlc_firmware_header_v1_0 *hdr =
+ (const struct rlc_firmware_header_v1_0 *)rdev->rlc_fw->data;
+ u32 fw_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
+ const __le32 *fw_data = (const __le32 *)
+ (rdev->rlc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+
+ radeon_ucode_print_rlc_hdr(&hdr->header);
+
+ for (i = 0; i < fw_size; i++) {
+ WREG32(RLC_UCODE_ADDR, i);
+ WREG32(RLC_UCODE_DATA, le32_to_cpup(fw_data++));
+ }
+ } else {
+ const __be32 *fw_data =
+ (const __be32 *)rdev->rlc_fw->data;
+ for (i = 0; i < SI_RLC_UCODE_SIZE; i++) {
+ WREG32(RLC_UCODE_ADDR, i);
+ WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
+ }
}
WREG32(RLC_UCODE_ADDR, 0);
@@ -6093,17 +6325,17 @@ static inline u32 si_get_ih_wptr(struct radeon_device *rdev)
wptr = RREG32(IH_RB_WPTR);
if (wptr & RB_OVERFLOW) {
+ wptr &= ~RB_OVERFLOW;
/* When a ring buffer overflow happen start parsing interrupt
* from the last not overwritten vector (wptr + 16). Hopefully
* this should allow us to catchup.
*/
- dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n",
- wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask);
+ dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
+ wptr, rdev->ih.rptr, (wptr + 16) & rdev->ih.ptr_mask);
rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask;
tmp = RREG32(IH_RB_CNTL);
tmp |= IH_WPTR_OVERFLOW_CLEAR;
WREG32(IH_RB_CNTL, tmp);
- wptr &= ~RB_OVERFLOW;
}
return (wptr & rdev->ih.ptr_mask);
}
@@ -6318,7 +6550,8 @@ restart_ih:
case 16: /* D5 page flip */
case 18: /* D6 page flip */
DRM_DEBUG("IH: D%d flip\n", ((src_id - 8) >> 1) + 1);
- radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1);
+ if (radeon_use_pflipirq > 0)
+ radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1);
break;
case 42: /* HPD hotplug */
switch (src_data) {
@@ -6440,13 +6673,13 @@ restart_ih:
/* wptr/rptr are in bytes! */
rptr += 16;
rptr &= rdev->ih.ptr_mask;
+ WREG32(IH_RB_RPTR, rptr);
}
if (queue_hotplug)
schedule_work(&rdev->hotplug_work);
if (queue_thermal && rdev->pm.dpm_enabled)
schedule_work(&rdev->pm.dpm.thermal.work);
rdev->ih.rptr = rptr;
- WREG32(IH_RB_RPTR, rdev->ih.rptr);
atomic_set(&rdev->ih.lock, 0);
/* make sure wptr hasn't changed while processing */
@@ -6964,6 +7197,9 @@ static void si_pcie_gen3_enable(struct radeon_device *rdev)
int ret, i;
u16 tmp16;
+ if (pci_is_root_bus(rdev->pdev->bus))
+ return;
+
if (radeon_pcie_gen2 == 0)
return;
@@ -7241,7 +7477,8 @@ static void si_program_aspm(struct radeon_device *rdev)
if (orig != data)
WREG32_PIF_PHY1(PB1_PIF_CNTL, data);
- if (!disable_clkreq) {
+ if (!disable_clkreq &&
+ !pci_is_root_bus(rdev->pdev->bus)) {
struct pci_dev *root = rdev->pdev->bus->self;
u32 lnkcap;
diff --git a/drivers/gpu/drm/radeon/si_dma.c b/drivers/gpu/drm/radeon/si_dma.c
index e24c94b6d14d..b58f12b762d7 100644
--- a/drivers/gpu/drm/radeon/si_dma.c
+++ b/drivers/gpu/drm/radeon/si_dma.c
@@ -56,7 +56,41 @@ bool si_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
}
/**
- * si_dma_vm_set_page - update the page tables using the DMA
+ * si_dma_vm_copy_pages - update PTEs by copying them from the GART
+ *
+ * @rdev: radeon_device pointer
+ * @ib: indirect buffer to fill with commands
+ * @pe: addr of the page entry
+ * @src: src addr where to copy from
+ * @count: number of page entries to update
+ *
+ * Update PTEs by copying them from the GART using the DMA (SI).
+ */
+void si_dma_vm_copy_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe, uint64_t src,
+ unsigned count)
+{
+ while (count) {
+ unsigned bytes = count * 8;
+ if (bytes > 0xFFFF8)
+ bytes = 0xFFFF8;
+
+ ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_COPY,
+ 1, 0, 0, bytes);
+ ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+ ib->ptr[ib->length_dw++] = lower_32_bits(src);
+ ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
+ ib->ptr[ib->length_dw++] = upper_32_bits(src) & 0xff;
+
+ pe += bytes;
+ src += bytes;
+ count -= bytes / 8;
+ }
+}
+
+/**
+ * si_dma_vm_write_pages - update PTEs by writing them manually
*
* @rdev: radeon_device pointer
* @ib: indirect buffer to fill with commands
@@ -66,83 +100,89 @@ bool si_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
* @incr: increase next addr by incr bytes
* @flags: access flags
*
- * Update the page tables using the DMA (SI).
+ * Update PTEs by writing them manually using the DMA (SI).
*/
-void si_dma_vm_set_page(struct radeon_device *rdev,
- struct radeon_ib *ib,
- uint64_t pe,
- uint64_t addr, unsigned count,
- uint32_t incr, uint32_t flags)
+void si_dma_vm_write_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags)
{
uint64_t value;
unsigned ndw;
- trace_radeon_vm_set_page(pe, addr, count, incr, flags);
-
- if (flags == R600_PTE_GART) {
- uint64_t src = rdev->gart.table_addr + (addr >> 12) * 8;
- while (count) {
- unsigned bytes = count * 8;
- if (bytes > 0xFFFF8)
- bytes = 0xFFFF8;
-
- ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_COPY,
- 1, 0, 0, bytes);
- ib->ptr[ib->length_dw++] = lower_32_bits(pe);
- ib->ptr[ib->length_dw++] = lower_32_bits(src);
- ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
- ib->ptr[ib->length_dw++] = upper_32_bits(src) & 0xff;
-
- pe += bytes;
- src += bytes;
- count -= bytes / 8;
- }
- } else if (flags & R600_PTE_SYSTEM) {
- while (count) {
- ndw = count * 2;
- if (ndw > 0xFFFFE)
- ndw = 0xFFFFE;
-
- /* for non-physically contiguous pages (system) */
- ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 0, ndw);
- ib->ptr[ib->length_dw++] = pe;
- ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
- for (; ndw > 0; ndw -= 2, --count, pe += 8) {
+ while (count) {
+ ndw = count * 2;
+ if (ndw > 0xFFFFE)
+ ndw = 0xFFFFE;
+
+ /* for non-physically contiguous pages (system) */
+ ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 0, ndw);
+ ib->ptr[ib->length_dw++] = pe;
+ ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
+ for (; ndw > 0; ndw -= 2, --count, pe += 8) {
+ if (flags & R600_PTE_SYSTEM) {
value = radeon_vm_map_gart(rdev, addr);
value &= 0xFFFFFFFFFFFFF000ULL;
- addr += incr;
- value |= flags;
- ib->ptr[ib->length_dw++] = value;
- ib->ptr[ib->length_dw++] = upper_32_bits(value);
- }
- }
- } else {
- while (count) {
- ndw = count * 2;
- if (ndw > 0xFFFFE)
- ndw = 0xFFFFE;
-
- if (flags & R600_PTE_VALID)
+ } else if (flags & R600_PTE_VALID) {
value = addr;
- else
+ } else {
value = 0;
- /* for physically contiguous pages (vram) */
- ib->ptr[ib->length_dw++] = DMA_PTE_PDE_PACKET(ndw);
- ib->ptr[ib->length_dw++] = pe; /* dst addr */
- ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
- ib->ptr[ib->length_dw++] = flags; /* mask */
- ib->ptr[ib->length_dw++] = 0;
- ib->ptr[ib->length_dw++] = value; /* value */
+ }
+ addr += incr;
+ value |= flags;
+ ib->ptr[ib->length_dw++] = value;
ib->ptr[ib->length_dw++] = upper_32_bits(value);
- ib->ptr[ib->length_dw++] = incr; /* increment size */
- ib->ptr[ib->length_dw++] = 0;
- pe += ndw * 4;
- addr += (ndw / 2) * incr;
- count -= ndw / 2;
}
}
- while (ib->length_dw & 0x7)
- ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0, 0);
+}
+
+/**
+ * si_dma_vm_set_pages - update the page tables using the DMA
+ *
+ * @rdev: radeon_device pointer
+ * @ib: indirect buffer to fill with commands
+ * @pe: addr of the page entry
+ * @addr: dst addr to write into pe
+ * @count: number of page entries to update
+ * @incr: increase next addr by incr bytes
+ * @flags: access flags
+ *
+ * Update the page tables using the DMA (SI).
+ */
+void si_dma_vm_set_pages(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe,
+ uint64_t addr, unsigned count,
+ uint32_t incr, uint32_t flags)
+{
+ uint64_t value;
+ unsigned ndw;
+
+ while (count) {
+ ndw = count * 2;
+ if (ndw > 0xFFFFE)
+ ndw = 0xFFFFE;
+
+ if (flags & R600_PTE_VALID)
+ value = addr;
+ else
+ value = 0;
+
+ /* for physically contiguous pages (vram) */
+ ib->ptr[ib->length_dw++] = DMA_PTE_PDE_PACKET(ndw);
+ ib->ptr[ib->length_dw++] = pe; /* dst addr */
+ ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
+ ib->ptr[ib->length_dw++] = flags; /* mask */
+ ib->ptr[ib->length_dw++] = 0;
+ ib->ptr[ib->length_dw++] = value; /* value */
+ ib->ptr[ib->length_dw++] = upper_32_bits(value);
+ ib->ptr[ib->length_dw++] = incr; /* increment size */
+ ib->ptr[ib->length_dw++] = 0;
+ pe += ndw * 4;
+ addr += (ndw / 2) * incr;
+ count -= ndw / 2;
+ }
}
void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
@@ -178,18 +218,19 @@ void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm)
* @src_offset: src GPU address
* @dst_offset: dst GPU address
* @num_gpu_pages: number of GPU pages to xfer
- * @fence: radeon fence object
+ * @resv: reservation object to sync to
*
* Copy GPU paging using the DMA engine (SI).
* Used by the radeon ttm implementation to move pages if
* registered as the asic copy callback.
*/
-int si_copy_dma(struct radeon_device *rdev,
- uint64_t src_offset, uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence **fence)
+struct radeon_fence *si_copy_dma(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct reservation_object *resv)
{
struct radeon_semaphore *sem = NULL;
+ struct radeon_fence *fence;
int ring_index = rdev->asic->copy.dma_ring_index;
struct radeon_ring *ring = &rdev->ring[ring_index];
u32 size_in_bytes, cur_size_in_bytes;
@@ -199,7 +240,7 @@ int si_copy_dma(struct radeon_device *rdev,
r = radeon_semaphore_create(rdev, &sem);
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
- return r;
+ return ERR_PTR(r);
}
size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT);
@@ -208,10 +249,10 @@ int si_copy_dma(struct radeon_device *rdev,
if (r) {
DRM_ERROR("radeon: moving bo (%d).\n", r);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_semaphore_sync_to(sem, *fence);
+ radeon_semaphore_sync_resv(rdev, sem, resv, false);
radeon_semaphore_sync_rings(rdev, sem, ring->idx);
for (i = 0; i < num_loops; i++) {
@@ -228,16 +269,16 @@ int si_copy_dma(struct radeon_device *rdev,
dst_offset += cur_size_in_bytes;
}
- r = radeon_fence_emit(rdev, fence, ring->idx);
+ r = radeon_fence_emit(rdev, &fence, ring->idx);
if (r) {
radeon_ring_unlock_undo(rdev, ring);
radeon_semaphore_free(rdev, &sem, NULL);
- return r;
+ return ERR_PTR(r);
}
- radeon_ring_unlock_commit(rdev, ring);
- radeon_semaphore_free(rdev, &sem, *fence);
+ radeon_ring_unlock_commit(rdev, ring, false);
+ radeon_semaphore_free(rdev, &sem, fence);
- return r;
+ return fence;
}
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index 58918868f894..676e6c2ba90a 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -23,6 +23,7 @@
#include "drmP.h"
#include "radeon.h"
+#include "radeon_asic.h"
#include "sid.h"
#include "r600_dpm.h"
#include "si_dpm.h"
@@ -3812,6 +3813,27 @@ void si_trim_voltage_table_to_fit_state_table(struct radeon_device *rdev,
voltage_table->count = max_voltage_steps;
}
+static int si_get_svi2_voltage_table(struct radeon_device *rdev,
+ struct radeon_clock_voltage_dependency_table *voltage_dependency_table,
+ struct atom_voltage_table *voltage_table)
+{
+ u32 i;
+
+ if (voltage_dependency_table == NULL)
+ return -EINVAL;
+
+ voltage_table->mask_low = 0;
+ voltage_table->phase_delay = 0;
+
+ voltage_table->count = voltage_dependency_table->count;
+ for (i = 0; i < voltage_table->count; i++) {
+ voltage_table->entries[i].value = voltage_dependency_table->entries[i].v;
+ voltage_table->entries[i].smio_low = 0;
+ }
+
+ return 0;
+}
+
static int si_construct_voltage_tables(struct radeon_device *rdev)
{
struct rv7xx_power_info *pi = rv770_get_pi(rdev);
@@ -3819,15 +3841,25 @@ static int si_construct_voltage_tables(struct radeon_device *rdev)
struct si_power_info *si_pi = si_get_pi(rdev);
int ret;
- ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDC,
- VOLTAGE_OBJ_GPIO_LUT, &eg_pi->vddc_voltage_table);
- if (ret)
- return ret;
+ if (pi->voltage_control) {
+ ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDC,
+ VOLTAGE_OBJ_GPIO_LUT, &eg_pi->vddc_voltage_table);
+ if (ret)
+ return ret;
- if (eg_pi->vddc_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
- si_trim_voltage_table_to_fit_state_table(rdev,
- SISLANDS_MAX_NO_VREG_STEPS,
- &eg_pi->vddc_voltage_table);
+ if (eg_pi->vddc_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+ si_trim_voltage_table_to_fit_state_table(rdev,
+ SISLANDS_MAX_NO_VREG_STEPS,
+ &eg_pi->vddc_voltage_table);
+ } else if (si_pi->voltage_control_svi2) {
+ ret = si_get_svi2_voltage_table(rdev,
+ &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+ &eg_pi->vddc_voltage_table);
+ if (ret)
+ return ret;
+ } else {
+ return -EINVAL;
+ }
if (eg_pi->vddci_control) {
ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_VDDCI,
@@ -3840,6 +3872,13 @@ static int si_construct_voltage_tables(struct radeon_device *rdev)
SISLANDS_MAX_NO_VREG_STEPS,
&eg_pi->vddci_voltage_table);
}
+ if (si_pi->vddci_control_svi2) {
+ ret = si_get_svi2_voltage_table(rdev,
+ &rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+ &eg_pi->vddci_voltage_table);
+ if (ret)
+ return ret;
+ }
if (pi->mvdd_control) {
ret = radeon_atom_get_voltage_table(rdev, VOLTAGE_TYPE_MVDDC,
@@ -3893,46 +3932,55 @@ static int si_populate_smc_voltage_tables(struct radeon_device *rdev,
struct si_power_info *si_pi = si_get_pi(rdev);
u8 i;
- if (eg_pi->vddc_voltage_table.count) {
- si_populate_smc_voltage_table(rdev, &eg_pi->vddc_voltage_table, table);
- table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
- cpu_to_be32(eg_pi->vddc_voltage_table.mask_low);
-
- for (i = 0; i < eg_pi->vddc_voltage_table.count; i++) {
- if (pi->max_vddc_in_table <= eg_pi->vddc_voltage_table.entries[i].value) {
- table->maxVDDCIndexInPPTable = i;
- break;
+ if (si_pi->voltage_control_svi2) {
+ si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svc,
+ si_pi->svc_gpio_id);
+ si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svd,
+ si_pi->svd_gpio_id);
+ si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_svi_rework_plat_type,
+ 2);
+ } else {
+ if (eg_pi->vddc_voltage_table.count) {
+ si_populate_smc_voltage_table(rdev, &eg_pi->vddc_voltage_table, table);
+ table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
+ cpu_to_be32(eg_pi->vddc_voltage_table.mask_low);
+
+ for (i = 0; i < eg_pi->vddc_voltage_table.count; i++) {
+ if (pi->max_vddc_in_table <= eg_pi->vddc_voltage_table.entries[i].value) {
+ table->maxVDDCIndexInPPTable = i;
+ break;
+ }
}
}
- }
- if (eg_pi->vddci_voltage_table.count) {
- si_populate_smc_voltage_table(rdev, &eg_pi->vddci_voltage_table, table);
+ if (eg_pi->vddci_voltage_table.count) {
+ si_populate_smc_voltage_table(rdev, &eg_pi->vddci_voltage_table, table);
- table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDCI] =
- cpu_to_be32(eg_pi->vddci_voltage_table.mask_low);
- }
+ table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDCI] =
+ cpu_to_be32(eg_pi->vddci_voltage_table.mask_low);
+ }
- if (si_pi->mvdd_voltage_table.count) {
- si_populate_smc_voltage_table(rdev, &si_pi->mvdd_voltage_table, table);
+ if (si_pi->mvdd_voltage_table.count) {
+ si_populate_smc_voltage_table(rdev, &si_pi->mvdd_voltage_table, table);
- table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_MVDD] =
- cpu_to_be32(si_pi->mvdd_voltage_table.mask_low);
- }
+ table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_MVDD] =
+ cpu_to_be32(si_pi->mvdd_voltage_table.mask_low);
+ }
- if (si_pi->vddc_phase_shed_control) {
- if (si_validate_phase_shedding_tables(rdev, &si_pi->vddc_phase_shed_table,
- &rdev->pm.dpm.dyn_state.phase_shedding_limits_table)) {
- si_populate_smc_voltage_table(rdev, &si_pi->vddc_phase_shed_table, table);
+ if (si_pi->vddc_phase_shed_control) {
+ if (si_validate_phase_shedding_tables(rdev, &si_pi->vddc_phase_shed_table,
+ &rdev->pm.dpm.dyn_state.phase_shedding_limits_table)) {
+ si_populate_smc_voltage_table(rdev, &si_pi->vddc_phase_shed_table, table);
- table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
- cpu_to_be32(si_pi->vddc_phase_shed_table.mask_low);
+ table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
+ cpu_to_be32(si_pi->vddc_phase_shed_table.mask_low);
- si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_phase_shedding_delay,
- (u32)si_pi->vddc_phase_shed_table.phase_delay);
- } else {
- si_pi->vddc_phase_shed_control = false;
+ si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_phase_shedding_delay,
+ (u32)si_pi->vddc_phase_shed_table.phase_delay);
+ } else {
+ si_pi->vddc_phase_shed_control = false;
+ }
}
}
@@ -5798,16 +5846,17 @@ int si_dpm_enable(struct radeon_device *rdev)
{
struct rv7xx_power_info *pi = rv770_get_pi(rdev);
struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+ struct si_power_info *si_pi = si_get_pi(rdev);
struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
int ret;
if (si_is_smc_running(rdev))
return -EINVAL;
- if (pi->voltage_control)
+ if (pi->voltage_control || si_pi->voltage_control_svi2)
si_enable_voltage_control(rdev, true);
if (pi->mvdd_control)
si_get_mvdd_configuration(rdev);
- if (pi->voltage_control) {
+ if (pi->voltage_control || si_pi->voltage_control_svi2) {
ret = si_construct_voltage_tables(rdev);
if (ret) {
DRM_ERROR("si_construct_voltage_tables failed\n");
@@ -6207,7 +6256,7 @@ static void si_parse_pplib_clock_info(struct radeon_device *rdev,
if ((rps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) &&
index == 0) {
/* XXX disable for A0 tahiti */
- si_pi->ulv.supported = true;
+ si_pi->ulv.supported = false;
si_pi->ulv.pl = *pl;
si_pi->ulv.one_pcie_lane_in_ulv = false;
si_pi->ulv.volt_change_delay = SISLANDS_ULVVOLTAGECHANGEDELAY_DFLT;
@@ -6406,16 +6455,32 @@ int si_dpm_init(struct radeon_device *rdev)
ni_pi->mclk_rtt_mode_threshold = eg_pi->mclk_edc_wr_enable_threshold;
pi->voltage_control =
- radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, VOLTAGE_OBJ_GPIO_LUT);
+ radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+ VOLTAGE_OBJ_GPIO_LUT);
+ if (!pi->voltage_control) {
+ si_pi->voltage_control_svi2 =
+ radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+ VOLTAGE_OBJ_SVID2);
+ if (si_pi->voltage_control_svi2)
+ radeon_atom_get_svi2_info(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+ &si_pi->svd_gpio_id, &si_pi->svc_gpio_id);
+ }
pi->mvdd_control =
- radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC, VOLTAGE_OBJ_GPIO_LUT);
+ radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC,
+ VOLTAGE_OBJ_GPIO_LUT);
eg_pi->vddci_control =
- radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI, VOLTAGE_OBJ_GPIO_LUT);
+ radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI,
+ VOLTAGE_OBJ_GPIO_LUT);
+ if (!eg_pi->vddci_control)
+ si_pi->vddci_control_svi2 =
+ radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI,
+ VOLTAGE_OBJ_SVID2);
si_pi->vddc_phase_shed_control =
- radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, VOLTAGE_OBJ_PHASE_LUT);
+ radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+ VOLTAGE_OBJ_PHASE_LUT);
rv770_get_engine_memory_ss(rdev);
diff --git a/drivers/gpu/drm/radeon/si_dpm.h b/drivers/gpu/drm/radeon/si_dpm.h
index 4ce5032cdf49..8b5c06a0832d 100644
--- a/drivers/gpu/drm/radeon/si_dpm.h
+++ b/drivers/gpu/drm/radeon/si_dpm.h
@@ -170,6 +170,8 @@ struct si_power_info {
bool vddc_phase_shed_control;
bool pspp_notify_required;
bool sclk_deep_sleep_above_low;
+ bool voltage_control_svi2;
+ bool vddci_control_svi2;
/* smc offsets */
u32 sram_end;
u32 state_table_start;
@@ -192,6 +194,9 @@ struct si_power_info {
SMC_SIslands_MCRegisters smc_mc_reg_table;
SISLANDS_SMC_STATETABLE smc_statetable;
PP_SIslands_PAPMParameters papm_parm;
+ /* SVI2 */
+ u8 svd_gpio_id;
+ u8 svc_gpio_id;
};
#define SISLANDS_INITIAL_STATE_ARB_INDEX 0
diff --git a/drivers/gpu/drm/radeon/si_smc.c b/drivers/gpu/drm/radeon/si_smc.c
index e80efcf0c230..73dbc79c959d 100644
--- a/drivers/gpu/drm/radeon/si_smc.c
+++ b/drivers/gpu/drm/radeon/si_smc.c
@@ -219,36 +219,48 @@ int si_load_smc_ucode(struct radeon_device *rdev, u32 limit)
if (!rdev->smc_fw)
return -EINVAL;
- switch (rdev->family) {
- case CHIP_TAHITI:
- ucode_start_address = TAHITI_SMC_UCODE_START;
- ucode_size = TAHITI_SMC_UCODE_SIZE;
- break;
- case CHIP_PITCAIRN:
- ucode_start_address = PITCAIRN_SMC_UCODE_START;
- ucode_size = PITCAIRN_SMC_UCODE_SIZE;
- break;
- case CHIP_VERDE:
- ucode_start_address = VERDE_SMC_UCODE_START;
- ucode_size = VERDE_SMC_UCODE_SIZE;
- break;
- case CHIP_OLAND:
- ucode_start_address = OLAND_SMC_UCODE_START;
- ucode_size = OLAND_SMC_UCODE_SIZE;
- break;
- case CHIP_HAINAN:
- ucode_start_address = HAINAN_SMC_UCODE_START;
- ucode_size = HAINAN_SMC_UCODE_SIZE;
- break;
- default:
- DRM_ERROR("unknown asic in smc ucode loader\n");
- BUG();
+ if (rdev->new_fw) {
+ const struct smc_firmware_header_v1_0 *hdr =
+ (const struct smc_firmware_header_v1_0 *)rdev->smc_fw->data;
+
+ radeon_ucode_print_smc_hdr(&hdr->header);
+
+ ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
+ ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
+ src = (const u8 *)
+ (rdev->smc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+ } else {
+ switch (rdev->family) {
+ case CHIP_TAHITI:
+ ucode_start_address = TAHITI_SMC_UCODE_START;
+ ucode_size = TAHITI_SMC_UCODE_SIZE;
+ break;
+ case CHIP_PITCAIRN:
+ ucode_start_address = PITCAIRN_SMC_UCODE_START;
+ ucode_size = PITCAIRN_SMC_UCODE_SIZE;
+ break;
+ case CHIP_VERDE:
+ ucode_start_address = VERDE_SMC_UCODE_START;
+ ucode_size = VERDE_SMC_UCODE_SIZE;
+ break;
+ case CHIP_OLAND:
+ ucode_start_address = OLAND_SMC_UCODE_START;
+ ucode_size = OLAND_SMC_UCODE_SIZE;
+ break;
+ case CHIP_HAINAN:
+ ucode_start_address = HAINAN_SMC_UCODE_START;
+ ucode_size = HAINAN_SMC_UCODE_SIZE;
+ break;
+ default:
+ DRM_ERROR("unknown asic in smc ucode loader\n");
+ BUG();
+ }
+ src = (const u8 *)rdev->smc_fw->data;
}
if (ucode_size & 3)
return -EINVAL;
- src = (const u8 *)rdev->smc_fw->data;
spin_lock_irqsave(&rdev->smc_idx_lock, flags);
WREG32(SMC_IND_INDEX_0, ucode_start_address);
WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h
index fd414d34d885..6635da9ec986 100644
--- a/drivers/gpu/drm/radeon/sid.h
+++ b/drivers/gpu/drm/radeon/sid.h
@@ -736,7 +736,7 @@
# define DESCRIPTION16(x) (((x) & 0xff) << 0)
# define DESCRIPTION17(x) (((x) & 0xff) << 8)
-#define AZ_F0_CODEC_PIN_CONTROL_HOTPLUG_CONTROL 0x54
+#define AZ_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL 0x54
# define AUDIO_ENABLED (1 << 31)
#define AZ_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT 0x56
diff --git a/drivers/gpu/drm/radeon/sislands_smc.h b/drivers/gpu/drm/radeon/sislands_smc.h
index 10e945a49479..623a0b1e2d9d 100644
--- a/drivers/gpu/drm/radeon/sislands_smc.h
+++ b/drivers/gpu/drm/radeon/sislands_smc.h
@@ -241,6 +241,9 @@ typedef struct SISLANDS_SMC_STATETABLE SISLANDS_SMC_STATETABLE;
#define SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width 0xF4
#define SI_SMC_SOFT_REGISTER_tdr_is_about_to_happen 0xFC
#define SI_SMC_SOFT_REGISTER_vr_hot_gpio 0x100
+#define SI_SMC_SOFT_REGISTER_svi_rework_plat_type 0x118
+#define SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svd 0x11c
+#define SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svc 0x120
#define SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES 16
#define SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES 32
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c
index 3f0e8d7b8dbe..1f8a8833e1be 100644
--- a/drivers/gpu/drm/radeon/sumo_dpm.c
+++ b/drivers/gpu/drm/radeon/sumo_dpm.c
@@ -23,6 +23,7 @@
#include "drmP.h"
#include "radeon.h"
+#include "radeon_asic.h"
#include "sumod.h"
#include "r600_dpm.h"
#include "cypress_dpm.h"
diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c
index 32e50be9c4ac..b4ec5c4e7969 100644
--- a/drivers/gpu/drm/radeon/trinity_dpm.c
+++ b/drivers/gpu/drm/radeon/trinity_dpm.c
@@ -23,6 +23,7 @@
#include "drmP.h"
#include "radeon.h"
+#include "radeon_asic.h"
#include "trinityd.h"
#include "r600_dpm.h"
#include "trinity_dpm.h"
@@ -1874,16 +1875,22 @@ int trinity_dpm_init(struct radeon_device *rdev)
for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++)
pi->at[i] = TRINITY_AT_DFLT;
- /* There are stability issues reported on with
- * bapm enabled when switching between AC and battery
- * power. At the same time, some MSI boards hang
- * if it's not enabled and dpm is enabled. Just enable
- * it for MSI boards right now.
- */
- if (rdev->pdev->subsystem_vendor == 0x1462)
- pi->enable_bapm = true;
- else
+ if (radeon_bapm == -1) {
+ /* There are stability issues reported on with
+ * bapm enabled when switching between AC and battery
+ * power. At the same time, some MSI boards hang
+ * if it's not enabled and dpm is enabled. Just enable
+ * it for MSI boards right now.
+ */
+ if (rdev->pdev->subsystem_vendor == 0x1462)
+ pi->enable_bapm = true;
+ else
+ pi->enable_bapm = false;
+ } else if (radeon_bapm == 0) {
pi->enable_bapm = false;
+ } else {
+ pi->enable_bapm = true;
+ }
pi->enable_nbps_policy = true;
pi->enable_sclk_ds = true;
pi->enable_gfx_power_gating = true;
diff --git a/drivers/gpu/drm/radeon/uvd_v1_0.c b/drivers/gpu/drm/radeon/uvd_v1_0.c
index be42c8125203..e72b3cb59358 100644
--- a/drivers/gpu/drm/radeon/uvd_v1_0.c
+++ b/drivers/gpu/drm/radeon/uvd_v1_0.c
@@ -22,6 +22,7 @@
* Authors: Christian König <christian.koenig@amd.com>
*/
+#include <linux/firmware.h>
#include <drm/drmP.h>
#include "radeon.h"
#include "radeon_asic.h"
@@ -70,6 +71,82 @@ void uvd_v1_0_set_wptr(struct radeon_device *rdev,
}
/**
+ * uvd_v1_0_fence_emit - emit an fence & trap command
+ *
+ * @rdev: radeon_device pointer
+ * @fence: fence to emit
+ *
+ * Write a fence and a trap command to the ring.
+ */
+void uvd_v1_0_fence_emit(struct radeon_device *rdev,
+ struct radeon_fence *fence)
+{
+ struct radeon_ring *ring = &rdev->ring[fence->ring];
+ uint64_t addr = rdev->fence_drv[fence->ring].gpu_addr;
+
+ radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA0, 0));
+ radeon_ring_write(ring, addr & 0xffffffff);
+ radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA1, 0));
+ radeon_ring_write(ring, fence->seq);
+ radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_CMD, 0));
+ radeon_ring_write(ring, 0);
+
+ radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA0, 0));
+ radeon_ring_write(ring, 0);
+ radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA1, 0));
+ radeon_ring_write(ring, 0);
+ radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_CMD, 0));
+ radeon_ring_write(ring, 2);
+ return;
+}
+
+/**
+ * uvd_v1_0_resume - memory controller programming
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Let the UVD memory controller know it's offsets
+ */
+int uvd_v1_0_resume(struct radeon_device *rdev)
+{
+ uint64_t addr;
+ uint32_t size;
+ int r;
+
+ r = radeon_uvd_resume(rdev);
+ if (r)
+ return r;
+
+ /* programm the VCPU memory controller bits 0-27 */
+ addr = (rdev->uvd.gpu_addr >> 3) + 16;
+ size = RADEON_GPU_PAGE_ALIGN(rdev->uvd_fw->size) >> 3;
+ WREG32(UVD_VCPU_CACHE_OFFSET0, addr);
+ WREG32(UVD_VCPU_CACHE_SIZE0, size);
+
+ addr += size;
+ size = RADEON_UVD_STACK_SIZE >> 3;
+ WREG32(UVD_VCPU_CACHE_OFFSET1, addr);
+ WREG32(UVD_VCPU_CACHE_SIZE1, size);
+
+ addr += size;
+ size = RADEON_UVD_HEAP_SIZE >> 3;
+ WREG32(UVD_VCPU_CACHE_OFFSET2, addr);
+ WREG32(UVD_VCPU_CACHE_SIZE2, size);
+
+ /* bits 28-31 */
+ addr = (rdev->uvd.gpu_addr >> 28) & 0xF;
+ WREG32(UVD_LMI_ADDR_EXT, (addr << 12) | (addr << 0));
+
+ /* bits 32-39 */
+ addr = (rdev->uvd.gpu_addr >> 32) & 0xFF;
+ WREG32(UVD_LMI_EXT40_ADDR, addr | (0x9 << 16) | (0x1 << 31));
+
+ WREG32(UVD_FW_START, *((uint32_t*)rdev->uvd.cpu_addr));
+
+ return 0;
+}
+
+/**
* uvd_v1_0_init - start and test UVD block
*
* @rdev: radeon_device pointer
@@ -124,14 +201,38 @@ int uvd_v1_0_init(struct radeon_device *rdev)
radeon_ring_write(ring, PACKET0(UVD_SEMA_CNTL, 0));
radeon_ring_write(ring, 3);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
done:
/* lower clocks again */
radeon_set_uvd_clocks(rdev, 0, 0);
- if (!r)
+ if (!r) {
+ switch (rdev->family) {
+ case CHIP_RV610:
+ case CHIP_RV630:
+ case CHIP_RV620:
+ /* 64byte granularity workaround */
+ WREG32(MC_CONFIG, 0);
+ WREG32(MC_CONFIG, 1 << 4);
+ WREG32(RS_DQ_RD_RET_CONF, 0x3f);
+ WREG32(MC_CONFIG, 0x1f);
+
+ /* fall through */
+ case CHIP_RV670:
+ case CHIP_RV635:
+
+ /* write clean workaround */
+ WREG32_P(UVD_VCPU_CNTL, 0x10, ~0x10);
+ break;
+
+ default:
+ /* TODO: Do we need more? */
+ break;
+ }
+
DRM_INFO("UVD initialized successfully.\n");
+ }
return r;
}
@@ -218,12 +319,12 @@ int uvd_v1_0_start(struct radeon_device *rdev)
/* enable UMC */
WREG32_P(UVD_LMI_CTRL2, 0, ~(1 << 8));
+ WREG32_P(UVD_RB_ARB_CTRL, 0, ~(1 << 3));
+
/* boot up the VCPU */
WREG32(UVD_SOFT_RESET, 0);
mdelay(10);
- WREG32_P(UVD_RB_ARB_CTRL, 0, ~(1 << 3));
-
for (i = 0; i < 10; ++i) {
uint32_t status;
for (j = 0; j < 100; ++j) {
@@ -331,7 +432,7 @@ int uvd_v1_0_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
}
radeon_ring_write(ring, PACKET0(UVD_CONTEXT_ID, 0));
radeon_ring_write(ring, 0xDEADBEEF);
- radeon_ring_unlock_commit(rdev, ring);
+ radeon_ring_unlock_commit(rdev, ring, false);
for (i = 0; i < rdev->usec_timeout; i++) {
tmp = RREG32(UVD_CONTEXT_ID);
if (tmp == 0xDEADBEEF)
diff --git a/drivers/gpu/drm/radeon/uvd_v2_2.c b/drivers/gpu/drm/radeon/uvd_v2_2.c
index 8bfdadd56598..89193519f8a1 100644
--- a/drivers/gpu/drm/radeon/uvd_v2_2.c
+++ b/drivers/gpu/drm/radeon/uvd_v2_2.c
@@ -72,6 +72,10 @@ int uvd_v2_2_resume(struct radeon_device *rdev)
uint32_t chip_id, size;
int r;
+ /* RV770 uses V1.0 MC */
+ if (rdev->family == CHIP_RV770)
+ return uvd_v1_0_resume(rdev);
+
r = radeon_uvd_resume(rdev);
if (r)
return r;
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index 2e3d7b5b0ad7..c96f6089f8bf 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -6,6 +6,7 @@ config DRM_RCAR_DU
select DRM_KMS_CMA_HELPER
select DRM_GEM_CMA_HELPER
select DRM_KMS_FB_HELPER
+ select VIDEOMODE_HELPERS
help
Choose this option if you have an R-Car chipset.
If M is selected the module will be called rcar-du-drm.
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 299267db2898..148b50589181 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -1,7 +1,7 @@
/*
* rcar_du_crtc.c -- R-Car Display Unit CRTCs
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index 43e7575c700c..e97ae502dec5 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -1,7 +1,7 @@
/*
* rcar_du_crtc.h -- R-Car Display Unit CRTCs
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 792fd1d20e86..d212efa6a495 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -1,7 +1,7 @@
/*
* rcar_du_drv.c -- R-Car Display Unit DRM driver
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -15,6 +15,7 @@
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/slab.h>
@@ -30,6 +31,97 @@
#include "rcar_du_regs.h"
/* -----------------------------------------------------------------------------
+ * Device Information
+ */
+
+static const struct rcar_du_device_info rcar_du_r8a7779_info = {
+ .features = 0,
+ .num_crtcs = 2,
+ .routes = {
+ /* R8A7779 has two RGB outputs and one (currently unsupported)
+ * TCON output.
+ */
+ [RCAR_DU_OUTPUT_DPAD0] = {
+ .possible_crtcs = BIT(0),
+ .encoder_type = DRM_MODE_ENCODER_NONE,
+ .port = 0,
+ },
+ [RCAR_DU_OUTPUT_DPAD1] = {
+ .possible_crtcs = BIT(1) | BIT(0),
+ .encoder_type = DRM_MODE_ENCODER_NONE,
+ .port = 1,
+ },
+ },
+ .num_lvds = 0,
+};
+
+static const struct rcar_du_device_info rcar_du_r8a7790_info = {
+ .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8,
+ .quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES,
+ .num_crtcs = 3,
+ .routes = {
+ /* R8A7790 has one RGB output, two LVDS outputs and one
+ * (currently unsupported) TCON output.
+ */
+ [RCAR_DU_OUTPUT_DPAD0] = {
+ .possible_crtcs = BIT(2) | BIT(1) | BIT(0),
+ .encoder_type = DRM_MODE_ENCODER_NONE,
+ .port = 0,
+ },
+ [RCAR_DU_OUTPUT_LVDS0] = {
+ .possible_crtcs = BIT(0),
+ .encoder_type = DRM_MODE_ENCODER_LVDS,
+ .port = 1,
+ },
+ [RCAR_DU_OUTPUT_LVDS1] = {
+ .possible_crtcs = BIT(2) | BIT(1),
+ .encoder_type = DRM_MODE_ENCODER_LVDS,
+ .port = 2,
+ },
+ },
+ .num_lvds = 2,
+};
+
+static const struct rcar_du_device_info rcar_du_r8a7791_info = {
+ .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8,
+ .num_crtcs = 2,
+ .routes = {
+ /* R8A7791 has one RGB output, one LVDS output and one
+ * (currently unsupported) TCON output.
+ */
+ [RCAR_DU_OUTPUT_DPAD0] = {
+ .possible_crtcs = BIT(1),
+ .encoder_type = DRM_MODE_ENCODER_NONE,
+ .port = 0,
+ },
+ [RCAR_DU_OUTPUT_LVDS0] = {
+ .possible_crtcs = BIT(0),
+ .encoder_type = DRM_MODE_ENCODER_LVDS,
+ .port = 1,
+ },
+ },
+ .num_lvds = 1,
+};
+
+static const struct platform_device_id rcar_du_id_table[] = {
+ { "rcar-du-r8a7779", (kernel_ulong_t)&rcar_du_r8a7779_info },
+ { "rcar-du-r8a7790", (kernel_ulong_t)&rcar_du_r8a7790_info },
+ { "rcar-du-r8a7791", (kernel_ulong_t)&rcar_du_r8a7791_info },
+ { }
+};
+
+MODULE_DEVICE_TABLE(platform, rcar_du_id_table);
+
+static const struct of_device_id rcar_du_of_table[] = {
+ { .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
+ { .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
+ { .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, rcar_du_of_table);
+
+/* -----------------------------------------------------------------------------
* DRM operations
*/
@@ -53,12 +145,13 @@ static int rcar_du_unload(struct drm_device *dev)
static int rcar_du_load(struct drm_device *dev, unsigned long flags)
{
struct platform_device *pdev = dev->platformdev;
+ struct device_node *np = pdev->dev.of_node;
struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
struct rcar_du_device *rcdu;
struct resource *mem;
int ret;
- if (pdata == NULL) {
+ if (pdata == NULL && np == NULL) {
dev_err(dev->dev, "no platform data\n");
return -ENODEV;
}
@@ -71,7 +164,8 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
rcdu->dev = &pdev->dev;
rcdu->pdata = pdata;
- rcdu->info = (struct rcar_du_device_info *)pdev->id_entry->driver_data;
+ rcdu->info = np ? of_match_device(rcar_du_of_table, rcdu->dev)->data
+ : (void *)platform_get_device_id(pdev)->driver_data;
rcdu->ddev = dev;
dev->dev_private = rcdu;
@@ -158,6 +252,7 @@ static struct drm_driver rcar_du_driver = {
.unload = rcar_du_unload,
.preclose = rcar_du_preclose,
.lastclose = rcar_du_lastclose,
+ .set_busid = drm_platform_set_busid,
.get_vblank_counter = drm_vblank_count,
.enable_vblank = rcar_du_enable_vblank,
.disable_vblank = rcar_du_disable_vblank,
@@ -187,7 +282,7 @@ static struct drm_driver rcar_du_driver = {
* Power management
*/
-#if CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_SLEEP
static int rcar_du_pm_suspend(struct device *dev)
{
struct rcar_du_device *rcdu = dev_get_drvdata(dev);
@@ -231,77 +326,6 @@ static int rcar_du_remove(struct platform_device *pdev)
return 0;
}
-static const struct rcar_du_device_info rcar_du_r8a7779_info = {
- .features = 0,
- .num_crtcs = 2,
- .routes = {
- /* R8A7779 has two RGB outputs and one (currently unsupported)
- * TCON output.
- */
- [RCAR_DU_OUTPUT_DPAD0] = {
- .possible_crtcs = BIT(0),
- .encoder_type = DRM_MODE_ENCODER_NONE,
- },
- [RCAR_DU_OUTPUT_DPAD1] = {
- .possible_crtcs = BIT(1) | BIT(0),
- .encoder_type = DRM_MODE_ENCODER_NONE,
- },
- },
- .num_lvds = 0,
-};
-
-static const struct rcar_du_device_info rcar_du_r8a7790_info = {
- .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8,
- .quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES,
- .num_crtcs = 3,
- .routes = {
- /* R8A7790 has one RGB output, two LVDS outputs and one
- * (currently unsupported) TCON output.
- */
- [RCAR_DU_OUTPUT_DPAD0] = {
- .possible_crtcs = BIT(2) | BIT(1) | BIT(0),
- .encoder_type = DRM_MODE_ENCODER_NONE,
- },
- [RCAR_DU_OUTPUT_LVDS0] = {
- .possible_crtcs = BIT(0),
- .encoder_type = DRM_MODE_ENCODER_LVDS,
- },
- [RCAR_DU_OUTPUT_LVDS1] = {
- .possible_crtcs = BIT(2) | BIT(1),
- .encoder_type = DRM_MODE_ENCODER_LVDS,
- },
- },
- .num_lvds = 2,
-};
-
-static const struct rcar_du_device_info rcar_du_r8a7791_info = {
- .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8,
- .num_crtcs = 2,
- .routes = {
- /* R8A7791 has one RGB output, one LVDS output and one
- * (currently unsupported) TCON output.
- */
- [RCAR_DU_OUTPUT_DPAD0] = {
- .possible_crtcs = BIT(1),
- .encoder_type = DRM_MODE_ENCODER_NONE,
- },
- [RCAR_DU_OUTPUT_LVDS0] = {
- .possible_crtcs = BIT(0),
- .encoder_type = DRM_MODE_ENCODER_LVDS,
- },
- },
- .num_lvds = 1,
-};
-
-static const struct platform_device_id rcar_du_id_table[] = {
- { "rcar-du-r8a7779", (kernel_ulong_t)&rcar_du_r8a7779_info },
- { "rcar-du-r8a7790", (kernel_ulong_t)&rcar_du_r8a7790_info },
- { "rcar-du-r8a7791", (kernel_ulong_t)&rcar_du_r8a7791_info },
- { }
-};
-
-MODULE_DEVICE_TABLE(platform, rcar_du_id_table);
-
static struct platform_driver rcar_du_platform_driver = {
.probe = rcar_du_probe,
.remove = rcar_du_remove,
@@ -309,6 +333,7 @@ static struct platform_driver rcar_du_platform_driver = {
.owner = THIS_MODULE,
.name = "rcar-du",
.pm = &rcar_du_pm_ops,
+ .of_match_table = rcar_du_of_table,
},
.id_table = rcar_du_id_table,
};
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index e31b735d3f25..8e494633c3b3 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -1,7 +1,7 @@
/*
* rcar_du_drv.h -- R-Car Display Unit DRM driver
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -37,6 +37,7 @@ struct rcar_du_lvdsenc;
* struct rcar_du_output_routing - Output routing specification
* @possible_crtcs: bitmask of possible CRTCs for the output
* @encoder_type: DRM type of the internal encoder associated with the output
+ * @port: device tree port number corresponding to this output route
*
* The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data
* specify the valid SoC outputs, which CRTCs can drive the output, and the type
@@ -45,6 +46,7 @@ struct rcar_du_lvdsenc;
struct rcar_du_output_routing {
unsigned int possible_crtcs;
unsigned int encoder_type;
+ unsigned int port;
};
/*
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index 3daa7a168dc6..7c0ec95915ef 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -1,7 +1,7 @@
/*
* rcar_du_encoder.c -- R-Car Display Unit Encoder
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -142,7 +142,8 @@ static const struct drm_encoder_funcs encoder_funcs = {
int rcar_du_encoder_init(struct rcar_du_device *rcdu,
enum rcar_du_encoder_type type,
enum rcar_du_output output,
- const struct rcar_du_encoder_data *data)
+ const struct rcar_du_encoder_data *data,
+ struct device_node *np)
{
struct rcar_du_encoder *renc;
unsigned int encoder_type;
@@ -189,9 +190,11 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
switch (encoder_type) {
- case DRM_MODE_ENCODER_LVDS:
- return rcar_du_lvds_connector_init(rcdu, renc,
- &data->connector.lvds.panel);
+ case DRM_MODE_ENCODER_LVDS: {
+ const struct rcar_du_panel_data *pdata =
+ data ? &data->connector.lvds.panel : NULL;
+ return rcar_du_lvds_connector_init(rcdu, renc, pdata, np);
+ }
case DRM_MODE_ENCODER_DAC:
return rcar_du_vga_connector_init(rcdu, renc);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
index 0e5a65e45d0e..bd624135ef1f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -1,7 +1,7 @@
/*
* rcar_du_encoder.h -- R-Car Display Unit Encoder
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -44,6 +44,7 @@ rcar_du_connector_best_encoder(struct drm_connector *connector);
int rcar_du_encoder_init(struct rcar_du_device *rcdu,
enum rcar_du_encoder_type type,
enum rcar_du_output output,
- const struct rcar_du_encoder_data *data);
+ const struct rcar_du_encoder_data *data,
+ struct device_node *np);
#endif /* __RCAR_DU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
index eb53cd97e8c6..4e7614b145db 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
@@ -1,7 +1,7 @@
/*
* rcar_du_group.c -- R-Car Display Unit Channels Pair
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.h b/drivers/gpu/drm/rcar-du/rcar_du_group.h
index 5025930972ec..0c38cdcda4ca 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_group.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_group.h
@@ -1,7 +1,7 @@
/*
* rcar_du_group.c -- R-Car Display Unit Planes and CRTCs Group
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index a87edfac111f..6c24ad7d03ef 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -1,7 +1,7 @@
/*
* rcar_du_kms.c -- R-Car Display Unit Mode Setting
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -17,6 +17,8 @@
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
+#include <linux/of_graph.h>
+
#include "rcar_du_crtc.h"
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
@@ -135,7 +137,9 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
{
struct rcar_du_device *rcdu = dev->dev_private;
const struct rcar_du_format_info *format;
+ unsigned int max_pitch;
unsigned int align;
+ unsigned int bpp;
format = rcar_du_format_info(mode_cmd->pixel_format);
if (format == NULL) {
@@ -144,13 +148,20 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv,
return ERR_PTR(-EINVAL);
}
+ /*
+ * The pitch and alignment constraints are expressed in pixels on the
+ * hardware side and in bytes in the DRM API.
+ */
+ bpp = format->planes == 2 ? 1 : format->bpp / 8;
+ max_pitch = 4096 * bpp;
+
if (rcar_du_needs(rcdu, RCAR_DU_QUIRK_ALIGN_128B))
align = 128;
else
- align = 16 * format->bpp / 8;
+ align = 16 * bpp;
if (mode_cmd->pitches[0] & (align - 1) ||
- mode_cmd->pitches[0] >= 8192) {
+ mode_cmd->pitches[0] >= max_pitch) {
dev_dbg(dev->dev, "invalid pitch value %u\n",
mode_cmd->pitches[0]);
return ERR_PTR(-EINVAL);
@@ -179,6 +190,205 @@ static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
.output_poll_changed = rcar_du_output_poll_changed,
};
+static int rcar_du_encoders_init_pdata(struct rcar_du_device *rcdu)
+{
+ unsigned int num_encoders = 0;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
+ const struct rcar_du_encoder_data *pdata =
+ &rcdu->pdata->encoders[i];
+ const struct rcar_du_output_routing *route =
+ &rcdu->info->routes[pdata->output];
+
+ if (pdata->type == RCAR_DU_ENCODER_UNUSED)
+ continue;
+
+ if (pdata->output >= RCAR_DU_OUTPUT_MAX ||
+ route->possible_crtcs == 0) {
+ dev_warn(rcdu->dev,
+ "encoder %u references unexisting output %u, skipping\n",
+ i, pdata->output);
+ continue;
+ }
+
+ ret = rcar_du_encoder_init(rcdu, pdata->type, pdata->output,
+ pdata, NULL);
+ if (ret < 0)
+ return ret;
+
+ num_encoders++;
+ }
+
+ return num_encoders;
+}
+
+static int rcar_du_encoders_init_dt_one(struct rcar_du_device *rcdu,
+ enum rcar_du_output output,
+ struct of_endpoint *ep)
+{
+ static const struct {
+ const char *compatible;
+ enum rcar_du_encoder_type type;
+ } encoders[] = {
+ { "adi,adv7123", RCAR_DU_ENCODER_VGA },
+ { "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS },
+ };
+
+ enum rcar_du_encoder_type enc_type = RCAR_DU_ENCODER_NONE;
+ struct device_node *connector = NULL;
+ struct device_node *encoder = NULL;
+ struct device_node *prev = NULL;
+ struct device_node *entity_ep_node;
+ struct device_node *entity;
+ int ret;
+
+ /*
+ * Locate the connected entity and infer its type from the number of
+ * endpoints.
+ */
+ entity = of_graph_get_remote_port_parent(ep->local_node);
+ if (!entity) {
+ dev_dbg(rcdu->dev, "unconnected endpoint %s, skipping\n",
+ ep->local_node->full_name);
+ return 0;
+ }
+
+ entity_ep_node = of_parse_phandle(ep->local_node, "remote-endpoint", 0);
+
+ while (1) {
+ struct device_node *ep_node;
+
+ ep_node = of_graph_get_next_endpoint(entity, prev);
+ of_node_put(prev);
+ prev = ep_node;
+
+ if (!ep_node)
+ break;
+
+ if (ep_node == entity_ep_node)
+ continue;
+
+ /*
+ * We've found one endpoint other than the input, this must
+ * be an encoder. Locate the connector.
+ */
+ encoder = entity;
+ connector = of_graph_get_remote_port_parent(ep_node);
+ of_node_put(ep_node);
+
+ if (!connector) {
+ dev_warn(rcdu->dev,
+ "no connector for encoder %s, skipping\n",
+ encoder->full_name);
+ of_node_put(entity_ep_node);
+ of_node_put(encoder);
+ return 0;
+ }
+
+ break;
+ }
+
+ of_node_put(entity_ep_node);
+
+ if (encoder) {
+ /*
+ * If an encoder has been found, get its type based on its
+ * compatible string.
+ */
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(encoders); ++i) {
+ if (of_device_is_compatible(encoder,
+ encoders[i].compatible)) {
+ enc_type = encoders[i].type;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(encoders)) {
+ dev_warn(rcdu->dev,
+ "unknown encoder type for %s, skipping\n",
+ encoder->full_name);
+ of_node_put(encoder);
+ of_node_put(connector);
+ return 0;
+ }
+ } else {
+ /*
+ * If no encoder has been found the entity must be the
+ * connector.
+ */
+ connector = entity;
+ }
+
+ ret = rcar_du_encoder_init(rcdu, enc_type, output, NULL, connector);
+ of_node_put(encoder);
+ of_node_put(connector);
+
+ return ret < 0 ? ret : 1;
+}
+
+static int rcar_du_encoders_init_dt(struct rcar_du_device *rcdu)
+{
+ struct device_node *np = rcdu->dev->of_node;
+ struct device_node *prev = NULL;
+ unsigned int num_encoders = 0;
+
+ /*
+ * Iterate over the endpoints and create one encoder for each output
+ * pipeline.
+ */
+ while (1) {
+ struct device_node *ep_node;
+ enum rcar_du_output output;
+ struct of_endpoint ep;
+ unsigned int i;
+ int ret;
+
+ ep_node = of_graph_get_next_endpoint(np, prev);
+ of_node_put(prev);
+ prev = ep_node;
+
+ if (ep_node == NULL)
+ break;
+
+ ret = of_graph_parse_endpoint(ep_node, &ep);
+ if (ret < 0) {
+ of_node_put(ep_node);
+ return ret;
+ }
+
+ /* Find the output route corresponding to the port number. */
+ for (i = 0; i < RCAR_DU_OUTPUT_MAX; ++i) {
+ if (rcdu->info->routes[i].possible_crtcs &&
+ rcdu->info->routes[i].port == ep.port) {
+ output = i;
+ break;
+ }
+ }
+
+ if (i == RCAR_DU_OUTPUT_MAX) {
+ dev_warn(rcdu->dev,
+ "port %u references unexisting output, skipping\n",
+ ep.port);
+ continue;
+ }
+
+ /* Process the output pipeline. */
+ ret = rcar_du_encoders_init_dt_one(rcdu, output, &ep);
+ if (ret < 0) {
+ of_node_put(ep_node);
+ return ret;
+ }
+
+ num_encoders += ret;
+ }
+
+ return num_encoders;
+}
+
int rcar_du_modeset_init(struct rcar_du_device *rcdu)
{
static const unsigned int mmio_offsets[] = {
@@ -188,6 +398,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
struct drm_device *dev = rcdu->ddev;
struct drm_encoder *encoder;
struct drm_fbdev_cma *fbdev;
+ unsigned int num_encoders;
unsigned int num_groups;
unsigned int i;
int ret;
@@ -231,28 +442,15 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
if (ret < 0)
return ret;
- for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
- const struct rcar_du_encoder_data *pdata =
- &rcdu->pdata->encoders[i];
- const struct rcar_du_output_routing *route =
- &rcdu->info->routes[pdata->output];
-
- if (pdata->type == RCAR_DU_ENCODER_UNUSED)
- continue;
+ if (rcdu->pdata)
+ ret = rcar_du_encoders_init_pdata(rcdu);
+ else
+ ret = rcar_du_encoders_init_dt(rcdu);
- if (pdata->output >= RCAR_DU_OUTPUT_MAX ||
- route->possible_crtcs == 0) {
- dev_warn(rcdu->dev,
- "encoder %u references unexisting output %u, skipping\n",
- i, pdata->output);
- continue;
- }
+ if (ret < 0)
+ return ret;
- ret = rcar_du_encoder_init(rcdu, pdata->type, pdata->output,
- pdata);
- if (ret < 0)
- return ret;
- }
+ num_encoders = ret;
/* Set the possible CRTCs and possible clones. There's always at least
* one way for all encoders to clone each other, set all bits in the
@@ -264,7 +462,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
&rcdu->info->routes[renc->output];
encoder->possible_crtcs = route->possible_crtcs;
- encoder->possible_clones = (1 << rcdu->pdata->num_encoders) - 1;
+ encoder->possible_clones = (1 << num_encoders) - 1;
}
/* Now that the CRTCs have been initialized register the planes. */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
index 5750e6af5655..07951d5fe38b 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h
@@ -1,7 +1,7 @@
/*
* rcar_du_kms.h -- R-Car Display Unit Mode Setting
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
index 289048d1c7b2..115eed20db12 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
@@ -1,7 +1,7 @@
/*
* rcar_du_lvdscon.c -- R-Car Display Unit LVDS Connector
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -15,6 +15,10 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <video/display_timing.h>
+#include <video/of_display_timing.h>
+#include <video/videomode.h>
+
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
#include "rcar_du_kms.h"
@@ -23,7 +27,7 @@
struct rcar_du_lvds_connector {
struct rcar_du_connector connector;
- const struct rcar_du_panel_data *panel;
+ struct rcar_du_panel_data panel;
};
#define to_rcar_lvds_connector(c) \
@@ -40,18 +44,9 @@ static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
return 0;
mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
- mode->clock = lvdscon->panel->mode.clock;
- mode->hdisplay = lvdscon->panel->mode.hdisplay;
- mode->hsync_start = lvdscon->panel->mode.hsync_start;
- mode->hsync_end = lvdscon->panel->mode.hsync_end;
- mode->htotal = lvdscon->panel->mode.htotal;
- mode->vdisplay = lvdscon->panel->mode.vdisplay;
- mode->vsync_start = lvdscon->panel->mode.vsync_start;
- mode->vsync_end = lvdscon->panel->mode.vsync_end;
- mode->vtotal = lvdscon->panel->mode.vtotal;
- mode->flags = lvdscon->panel->mode.flags;
-
- drm_mode_set_name(mode);
+
+ drm_display_mode_from_videomode(&lvdscon->panel.mode, mode);
+
drm_mode_probed_add(connector, mode);
return 1;
@@ -64,7 +59,7 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = {
static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
{
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
@@ -83,7 +78,8 @@ static const struct drm_connector_funcs connector_funcs = {
int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
struct rcar_du_encoder *renc,
- const struct rcar_du_panel_data *panel)
+ const struct rcar_du_panel_data *panel,
+ /* TODO const */ struct device_node *np)
{
struct rcar_du_lvds_connector *lvdscon;
struct drm_connector *connector;
@@ -93,11 +89,24 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
if (lvdscon == NULL)
return -ENOMEM;
- lvdscon->panel = panel;
+ if (panel) {
+ lvdscon->panel = *panel;
+ } else {
+ struct display_timing timing;
+
+ ret = of_get_display_timing(np, "panel-timing", &timing);
+ if (ret < 0)
+ return ret;
+
+ videomode_from_timing(&timing, &lvdscon->panel.mode);
+
+ of_property_read_u32(np, "width-mm", &lvdscon->panel.width_mm);
+ of_property_read_u32(np, "height-mm", &lvdscon->panel.height_mm);
+ }
connector = &lvdscon->connector.connector;
- connector->display_info.width_mm = panel->width_mm;
- connector->display_info.height_mm = panel->height_mm;
+ connector->display_info.width_mm = lvdscon->panel.width_mm;
+ connector->display_info.height_mm = lvdscon->panel.height_mm;
ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
@@ -105,7 +114,7 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
return ret;
drm_connector_helper_add(connector, &connector_helper_funcs);
- ret = drm_sysfs_connector_add(connector);
+ ret = drm_connector_register(connector);
if (ret < 0)
return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
index bff8683699ca..d11424d537f9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
@@ -1,7 +1,7 @@
/*
* rcar_du_lvdscon.h -- R-Car Display Unit LVDS Connector
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -20,6 +20,7 @@ struct rcar_du_panel_data;
int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
struct rcar_du_encoder *renc,
- const struct rcar_du_panel_data *panel);
+ const struct rcar_du_panel_data *panel,
+ struct device_node *np);
#endif /* __RCAR_DU_LVDSCON_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
index df30a075d793..7cfb48ce1791 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
@@ -1,7 +1,7 @@
/*
* rcar_du_lvdsenc.c -- R-Car Display Unit LVDS Encoder
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
index 7051c6de19ae..3303a55cec79 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
@@ -1,7 +1,7 @@
/*
* rcar_du_lvdsenc.h -- R-Car Display Unit LVDS Encoder
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
index 3fb69d9ae61b..72a7cb47bd9f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
@@ -1,7 +1,7 @@
/*
* rcar_du_plane.c -- R-Car Display Unit Planes
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
index f94f9ce84998..3021288b1a89 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h
@@ -1,7 +1,7 @@
/*
* rcar_du_plane.h -- R-Car Display Unit Planes
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
index ccfe64c7188f..564a723ede03 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
@@ -1,7 +1,7 @@
/*
* rcar_du_vgacon.c -- R-Car Display Unit VGA Connector
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -32,7 +32,7 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = {
static void rcar_du_vga_connector_destroy(struct drm_connector *connector)
{
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
@@ -70,7 +70,7 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
return ret;
drm_connector_helper_add(connector, &connector_helper_funcs);
- ret = drm_sysfs_connector_add(connector);
+ ret = drm_connector_register(connector);
if (ret < 0)
return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
index b12b0cf7f117..112f50316e01 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
@@ -1,7 +1,7 @@
/*
* rcar_du_vgacon.h -- R-Car Display Unit VGA Connector
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/savage/savage_bci.c b/drivers/gpu/drm/savage/savage_bci.c
index c97cdc9ab239..d47dff95fe52 100644
--- a/drivers/gpu/drm/savage/savage_bci.c
+++ b/drivers/gpu/drm/savage/savage_bci.c
@@ -556,7 +556,7 @@ int savage_driver_load(struct drm_device *dev, unsigned long chipset)
/*
* Initialize mappings. On Savage4 and SavageIX the alignment
* and size of the aperture is not suitable for automatic MTRR setup
- * in drm_addmap. Therefore we add them manually before the maps are
+ * in drm_legacy_addmap. Therefore we add them manually before the maps are
* initialized, and tear them down on last close.
*/
int savage_driver_firstopen(struct drm_device *dev)
@@ -624,19 +624,20 @@ int savage_driver_firstopen(struct drm_device *dev)
/* Automatic MTRR setup will do the right thing. */
}
- ret = drm_addmap(dev, mmio_base, SAVAGE_MMIO_SIZE, _DRM_REGISTERS,
- _DRM_READ_ONLY, &dev_priv->mmio);
+ ret = drm_legacy_addmap(dev, mmio_base, SAVAGE_MMIO_SIZE,
+ _DRM_REGISTERS, _DRM_READ_ONLY,
+ &dev_priv->mmio);
if (ret)
return ret;
- ret = drm_addmap(dev, fb_base, fb_size, _DRM_FRAME_BUFFER,
- _DRM_WRITE_COMBINING, &dev_priv->fb);
+ ret = drm_legacy_addmap(dev, fb_base, fb_size, _DRM_FRAME_BUFFER,
+ _DRM_WRITE_COMBINING, &dev_priv->fb);
if (ret)
return ret;
- ret = drm_addmap(dev, aperture_base, SAVAGE_APERTURE_SIZE,
- _DRM_FRAME_BUFFER, _DRM_WRITE_COMBINING,
- &dev_priv->aperture);
+ ret = drm_legacy_addmap(dev, aperture_base, SAVAGE_APERTURE_SIZE,
+ _DRM_FRAME_BUFFER, _DRM_WRITE_COMBINING,
+ &dev_priv->aperture);
return ret;
}
@@ -698,14 +699,14 @@ static int savage_do_init_bci(struct drm_device * dev, drm_savage_init_t * init)
dev_priv->texture_offset = init->texture_offset;
dev_priv->texture_size = init->texture_size;
- dev_priv->sarea = drm_getsarea(dev);
+ dev_priv->sarea = drm_legacy_getsarea(dev);
if (!dev_priv->sarea) {
DRM_ERROR("could not find sarea!\n");
savage_do_cleanup_bci(dev);
return -EINVAL;
}
if (init->status_offset != 0) {
- dev_priv->status = drm_core_findmap(dev, init->status_offset);
+ dev_priv->status = drm_legacy_findmap(dev, init->status_offset);
if (!dev_priv->status) {
DRM_ERROR("could not find shadow status region!\n");
savage_do_cleanup_bci(dev);
@@ -716,14 +717,14 @@ static int savage_do_init_bci(struct drm_device * dev, drm_savage_init_t * init)
}
if (dev_priv->dma_type == SAVAGE_DMA_AGP && init->buffers_offset) {
dev->agp_buffer_token = init->buffers_offset;
- dev->agp_buffer_map = drm_core_findmap(dev,
+ dev->agp_buffer_map = drm_legacy_findmap(dev,
init->buffers_offset);
if (!dev->agp_buffer_map) {
DRM_ERROR("could not find DMA buffer region!\n");
savage_do_cleanup_bci(dev);
return -EINVAL;
}
- drm_core_ioremap(dev->agp_buffer_map, dev);
+ drm_legacy_ioremap(dev->agp_buffer_map, dev);
if (!dev->agp_buffer_map->handle) {
DRM_ERROR("failed to ioremap DMA buffer region!\n");
savage_do_cleanup_bci(dev);
@@ -732,7 +733,7 @@ static int savage_do_init_bci(struct drm_device * dev, drm_savage_init_t * init)
}
if (init->agp_textures_offset) {
dev_priv->agp_textures =
- drm_core_findmap(dev, init->agp_textures_offset);
+ drm_legacy_findmap(dev, init->agp_textures_offset);
if (!dev_priv->agp_textures) {
DRM_ERROR("could not find agp texture region!\n");
savage_do_cleanup_bci(dev);
@@ -755,7 +756,7 @@ static int savage_do_init_bci(struct drm_device * dev, drm_savage_init_t * init)
savage_do_cleanup_bci(dev);
return -EINVAL;
}
- dev_priv->cmd_dma = drm_core_findmap(dev, init->cmd_dma_offset);
+ dev_priv->cmd_dma = drm_legacy_findmap(dev, init->cmd_dma_offset);
if (!dev_priv->cmd_dma) {
DRM_ERROR("could not find command DMA region!\n");
savage_do_cleanup_bci(dev);
@@ -768,7 +769,7 @@ static int savage_do_init_bci(struct drm_device * dev, drm_savage_init_t * init)
savage_do_cleanup_bci(dev);
return -EINVAL;
}
- drm_core_ioremap(dev_priv->cmd_dma, dev);
+ drm_legacy_ioremap(dev_priv->cmd_dma, dev);
if (!dev_priv->cmd_dma->handle) {
DRM_ERROR("failed to ioremap command "
"DMA region!\n");
@@ -894,11 +895,11 @@ static int savage_do_cleanup_bci(struct drm_device * dev)
} else if (dev_priv->cmd_dma && dev_priv->cmd_dma->handle &&
dev_priv->cmd_dma->type == _DRM_AGP &&
dev_priv->dma_type == SAVAGE_DMA_AGP)
- drm_core_ioremapfree(dev_priv->cmd_dma, dev);
+ drm_legacy_ioremapfree(dev_priv->cmd_dma, dev);
if (dev_priv->dma_type == SAVAGE_DMA_AGP &&
dev->agp_buffer_map && dev->agp_buffer_map->handle) {
- drm_core_ioremapfree(dev->agp_buffer_map, dev);
+ drm_legacy_ioremapfree(dev->agp_buffer_map, dev);
/* make sure the next instance (which may be running
* in PCI mode) doesn't try to use an old
* agp_buffer_map. */
@@ -1050,7 +1051,7 @@ void savage_reclaim_buffers(struct drm_device *dev, struct drm_file *file_priv)
return;
if (file_priv->master && file_priv->master->lock.hw_lock) {
- drm_idlelock_take(&file_priv->master->lock);
+ drm_legacy_idlelock_take(&file_priv->master->lock);
release_idlelock = 1;
}
@@ -1069,7 +1070,7 @@ void savage_reclaim_buffers(struct drm_device *dev, struct drm_file *file_priv)
}
if (release_idlelock)
- drm_idlelock_release(&file_priv->master->lock);
+ drm_legacy_idlelock_release(&file_priv->master->lock);
}
const struct drm_ioctl_desc savage_ioctls[] = {
diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c
index 3c030216e888..21aed1febeb4 100644
--- a/drivers/gpu/drm/savage/savage_drv.c
+++ b/drivers/gpu/drm/savage/savage_drv.c
@@ -40,7 +40,7 @@ static const struct file_operations savage_driver_fops = {
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
- .mmap = drm_mmap,
+ .mmap = drm_legacy_mmap,
.poll = drm_poll,
#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
@@ -57,6 +57,7 @@ static struct drm_driver driver = {
.preclose = savage_reclaim_buffers,
.lastclose = savage_driver_lastclose,
.unload = savage_driver_unload,
+ .set_busid = drm_pci_set_busid,
.ioctls = savage_ioctls,
.dma_ioctl = savage_bci_buffers,
.fops = &savage_driver_fops,
diff --git a/drivers/gpu/drm/savage/savage_drv.h b/drivers/gpu/drm/savage/savage_drv.h
index 335f8fcf1041..37b699571ad0 100644
--- a/drivers/gpu/drm/savage/savage_drv.h
+++ b/drivers/gpu/drm/savage/savage_drv.h
@@ -26,6 +26,8 @@
#ifndef __SAVAGE_DRV_H__
#define __SAVAGE_DRV_H__
+#include <drm/drm_legacy.h>
+
#define DRIVER_AUTHOR "Felix Kuehling"
#define DRIVER_NAME "savage"
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_backlight.c b/drivers/gpu/drm/shmobile/shmob_drm_backlight.c
index 463aee18f774..33dd41afea0e 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_backlight.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_backlight.c
@@ -1,7 +1,7 @@
/*
* shmob_drm_backlight.c -- SH Mobile DRM Backlight
*
- * Copyright (C) 2012 Renesas Corporation
+ * Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_backlight.h b/drivers/gpu/drm/shmobile/shmob_drm_backlight.h
index 9477595d2ff3..bac719ecc301 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_backlight.h
+++ b/drivers/gpu/drm/shmobile/shmob_drm_backlight.h
@@ -1,7 +1,7 @@
/*
* shmob_drm_backlight.h -- SH Mobile DRM Backlight
*
- * Copyright (C) 2012 Renesas Corporation
+ * Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
index faf176b2daf9..0ddce4d046d9 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
@@ -1,7 +1,7 @@
/*
* shmob_drm_crtc.c -- SH Mobile DRM CRTCs
*
- * Copyright (C) 2012 Renesas Corporation
+ * Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -692,7 +692,7 @@ static void shmob_drm_connector_destroy(struct drm_connector *connector)
struct shmob_drm_connector *scon = to_shmob_connector(connector);
shmob_drm_backlight_exit(scon);
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
@@ -726,7 +726,7 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev,
return ret;
drm_connector_helper_add(connector, &connector_helper_funcs);
- ret = drm_sysfs_connector_add(connector);
+ ret = drm_connector_register(connector);
if (ret < 0)
goto err_cleanup;
@@ -749,7 +749,7 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev,
err_backlight:
shmob_drm_backlight_exit(&sdev->connector);
err_sysfs:
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
err_cleanup:
drm_connector_cleanup(connector);
return ret;
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.h b/drivers/gpu/drm/shmobile/shmob_drm_crtc.h
index e5bd109c4c38..eddad6dcc88a 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.h
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.h
@@ -1,7 +1,7 @@
/*
* shmob_drm_crtc.h -- SH Mobile DRM CRTCs
*
- * Copyright (C) 2012 Renesas Corporation
+ * Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
index 82c84c7fd4f6..e62cbde81e50 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
@@ -1,7 +1,7 @@
/*
* shmob_drm_drv.c -- SH Mobile DRM driver
*
- * Copyright (C) 2012 Renesas Corporation
+ * Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -267,6 +267,7 @@ static struct drm_driver shmob_drm_driver = {
.load = shmob_drm_load,
.unload = shmob_drm_unload,
.preclose = shmob_drm_preclose,
+ .set_busid = drm_platform_set_busid,
.irq_handler = shmob_drm_irq,
.get_vblank_counter = drm_vblank_count,
.enable_vblank = shmob_drm_enable_vblank,
@@ -297,7 +298,7 @@ static struct drm_driver shmob_drm_driver = {
* Power management
*/
-#if CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_SLEEP
static int shmob_drm_pm_suspend(struct device *dev)
{
struct shmob_drm_device *sdev = dev_get_drvdata(dev);
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.h b/drivers/gpu/drm/shmobile/shmob_drm_drv.h
index 4d46b811b5a7..02ea315ba69a 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_drv.h
+++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.h
@@ -1,7 +1,7 @@
/*
* shmob_drm.h -- SH Mobile DRM driver
*
- * Copyright (C) 2012 Renesas Corporation
+ * Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_kms.c b/drivers/gpu/drm/shmobile/shmob_drm_kms.c
index fc0ef0ca7d04..aaf98ace4a90 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_kms.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_kms.c
@@ -1,7 +1,7 @@
/*
* shmob_drm_kms.c -- SH Mobile DRM Mode Setting
*
- * Copyright (C) 2012 Renesas Corporation
+ * Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_kms.h b/drivers/gpu/drm/shmobile/shmob_drm_kms.h
index 9495c9111308..06d5b7caa026 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_kms.h
+++ b/drivers/gpu/drm/shmobile/shmob_drm_kms.h
@@ -1,7 +1,7 @@
/*
* shmob_drm_kms.h -- SH Mobile DRM Mode Setting
*
- * Copyright (C) 2012 Renesas Corporation
+ * Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
index 060ae03e5f9b..1805bb23b113 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
@@ -1,7 +1,7 @@
/*
* shmob_drm_plane.c -- SH Mobile DRM Planes
*
- * Copyright (C) 2012 Renesas Corporation
+ * Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.h b/drivers/gpu/drm/shmobile/shmob_drm_plane.h
index 99623d05e3b0..a58cc1fc3240 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_plane.h
+++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.h
@@ -1,7 +1,7 @@
/*
* shmob_drm_plane.h -- SH Mobile DRM Planes
*
- * Copyright (C) 2012 Renesas Corporation
+ * Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_regs.h b/drivers/gpu/drm/shmobile/shmob_drm_regs.h
index 7923cdd6368e..ea17d4415b9e 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_regs.h
+++ b/drivers/gpu/drm/shmobile/shmob_drm_regs.h
@@ -1,7 +1,7 @@
/*
* shmob_drm_regs.h -- SH Mobile DRM registers
*
- * Copyright (C) 2012 Renesas Corporation
+ * Copyright (C) 2012 Renesas Electronics Corporation
*
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c
index 756f787b7143..79bce76cb8f7 100644
--- a/drivers/gpu/drm/sis/sis_drv.c
+++ b/drivers/gpu/drm/sis/sis_drv.c
@@ -70,7 +70,7 @@ static const struct file_operations sis_driver_fops = {
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
- .mmap = drm_mmap,
+ .mmap = drm_legacy_mmap,
.poll = drm_poll,
#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
@@ -108,6 +108,7 @@ static struct drm_driver driver = {
.open = sis_driver_open,
.preclose = sis_reclaim_buffers_locked,
.postclose = sis_driver_postclose,
+ .set_busid = drm_pci_set_busid,
.dma_quiescent = sis_idle,
.lastclose = sis_lastclose,
.ioctls = sis_ioctls,
diff --git a/drivers/gpu/drm/sis/sis_drv.h b/drivers/gpu/drm/sis/sis_drv.h
index c31c0253054d..16f972b2a76a 100644
--- a/drivers/gpu/drm/sis/sis_drv.h
+++ b/drivers/gpu/drm/sis/sis_drv.h
@@ -28,6 +28,8 @@
#ifndef _SIS_DRV_H_
#define _SIS_DRV_H_
+#include <drm/drm_legacy.h>
+
/* General customization:
*/
diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c
index 77f288e4a0a6..93ad8a5704d1 100644
--- a/drivers/gpu/drm/sis/sis_mm.c
+++ b/drivers/gpu/drm/sis/sis_mm.c
@@ -319,12 +319,12 @@ void sis_reclaim_buffers_locked(struct drm_device *dev,
if (!(file->minor->master && file->master->lock.hw_lock))
return;
- drm_idlelock_take(&file->master->lock);
+ drm_legacy_idlelock_take(&file->master->lock);
mutex_lock(&dev->struct_mutex);
if (list_empty(&file_priv->obj_list)) {
mutex_unlock(&dev->struct_mutex);
- drm_idlelock_release(&file->master->lock);
+ drm_legacy_idlelock_release(&file->master->lock);
return;
}
@@ -345,7 +345,7 @@ void sis_reclaim_buffers_locked(struct drm_device *dev,
}
mutex_unlock(&dev->struct_mutex);
- drm_idlelock_release(&file->master->lock);
+ drm_legacy_idlelock_release(&file->master->lock);
return;
}
diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig
new file mode 100644
index 000000000000..ae8850f3e63b
--- /dev/null
+++ b/drivers/gpu/drm/sti/Kconfig
@@ -0,0 +1,15 @@
+config DRM_STI
+ tristate "DRM Support for STMicroelectronics SoC stiH41x Series"
+ depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM)
+ select RESET_CONTROLLER
+ select DRM_KMS_HELPER
+ select DRM_GEM_CMA_HELPER
+ select DRM_KMS_CMA_HELPER
+ help
+ Choose this option to enable DRM on STM stiH41x chipset
+
+config DRM_STI_FBDEV
+ bool "DRM frame buffer device for STMicroelectronics SoC stiH41x Serie"
+ depends on DRM_STI
+ help
+ Choose this option to enable FBDEV on top of DRM for STM stiH41x chipset
diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
new file mode 100644
index 000000000000..04ac2ceef27f
--- /dev/null
+++ b/drivers/gpu/drm/sti/Makefile
@@ -0,0 +1,21 @@
+sticompositor-y := \
+ sti_layer.o \
+ sti_mixer.o \
+ sti_gdp.o \
+ sti_vid.o \
+ sti_compositor.o \
+ sti_drm_crtc.o \
+ sti_drm_plane.o
+
+stihdmi-y := sti_hdmi.o \
+ sti_hdmi_tx3g0c55phy.o \
+ sti_hdmi_tx3g4c28phy.o \
+
+obj-$(CONFIG_DRM_STI) = \
+ sti_vtg.o \
+ sti_vtac.o \
+ stihdmi.o \
+ sti_hda.o \
+ sti_tvout.o \
+ sticompositor.o \
+ sti_drm_drv.o \ No newline at end of file
diff --git a/drivers/gpu/drm/sti/NOTES b/drivers/gpu/drm/sti/NOTES
new file mode 100644
index 000000000000..57e257969198
--- /dev/null
+++ b/drivers/gpu/drm/sti/NOTES
@@ -0,0 +1,58 @@
+1. stiH display hardware IP
+---------------------------
+The STMicroelectronics stiH SoCs use a common chain of HW display IP blocks:
+- The High Quality Video Display Processor (HQVDP) gets video frames from a
+ video decoder and does high quality video processing, including scaling.
+
+- The Compositor is a multiplane, dual-mixer (Main & Aux) digital processor. It
+ has several inputs:
+ - The graphics planes are internally processed by the Generic Display
+ Pipeline (GDP).
+ - The video plug (VID) connects to the HQVDP output.
+ - The cursor handles ... a cursor.
+- The TV OUT pre-formats (convert, clip, round) the compositor output data
+- The HDMI / DVO / HD Analog / SD analog IP builds the video signals
+ - DVO (Digital Video Output) handles a 24bits parallel signal
+ - The HD analog signal is typically driven by a YCbCr cable, supporting up to
+ 1080i mode.
+ - The SD analog signal is typically used for legacy TV
+- The VTG (Video Timing Generators) build Vsync signals used by the other HW IP
+Note that some stiH drivers support only a subset of thee HW IP.
+
+ .-------------. .-----------. .-----------.
+GPU >-------------+GDP Main | | +---+ HDMI +--> HDMI
+GPU >-------------+GDP mixer+---+ | :===========:
+GPU >-------------+Cursor | | +---+ DVO +--> 24b//
+ ------- | COMPOSITOR | | TV OUT | :===========:
+ | | | | | +---+ HD analog +--> YCbCr
+Vid >--+ HQVDP +--+VID Aux +---+ | :===========:
+dec | | | mixer| | +---+ SD analog +--> CVBS
+ '-------' '-------------' '-----------' '-----------'
+ .-----------.
+ | main+--> Vsync
+ | VTG |
+ | aux+--> Vsync
+ '-----------'
+
+2. DRM / HW mapping
+-------------------
+These IP are mapped to the DRM objects as following:
+- The CRTCs are mapped to the Compositor Main and Aux Mixers
+- The Framebuffers and planes are mapped to the Compositor GDP (non video
+ buffers) and to HQVDP+VID (video buffers)
+- The Cursor is mapped to the Compositor Cursor
+- The Encoders are mapped to the TVOut
+- The Bridges/Connectors are mapped to the HDMI / DVO / HD Analog / SD analog
+
+FB & planes Cursor CRTC Encoders Bridges/Connectors
+ | | | | |
+ | | | | |
+ | .-------------. | .-----------. .-----------. |
+ +------------> |GDP | Main | | | +-> | | HDMI | <-+
+ +------------> |GDP v mixer|<+ | | | :===========: |
+ | |Cursor | | | +-> | | DVO | <-+
+ | ------- | COMPOSITOR | | |TV OUT | | :===========: |
+ | | | | | | | +-> | | HD analog | <-+
+ +-> | HQVDP | |VID Aux |<+ | | | :===========: |
+ | | | mixer| | +-> | | SD analog | <-+
+ '-------' '-------------' '-----------' '-----------'
diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c
new file mode 100644
index 000000000000..390d93e9a06c
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_compositor.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include <drm/drmP.h>
+
+#include "sti_compositor.h"
+#include "sti_drm_crtc.h"
+#include "sti_drm_drv.h"
+#include "sti_drm_plane.h"
+#include "sti_gdp.h"
+#include "sti_vtg.h"
+
+/*
+ * stiH407 compositor properties
+ */
+struct sti_compositor_data stih407_compositor_data = {
+ .nb_subdev = 6,
+ .subdev_desc = {
+ {STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100},
+ {STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200},
+ {STI_GPD_SUBDEV, (int)STI_GDP_2, 0x300},
+ {STI_GPD_SUBDEV, (int)STI_GDP_3, 0x400},
+ {STI_VID_SUBDEV, (int)STI_VID_0, 0x700},
+ {STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00}
+ },
+};
+
+/*
+ * stiH416 compositor properties
+ * Note:
+ * on stih416 MIXER_AUX has a different base address from MIXER_MAIN
+ * Moreover, GDPx is different for Main and Aux Mixer. So this subdev map does
+ * not fit for stiH416 if we want to enable the MIXER_AUX.
+ */
+struct sti_compositor_data stih416_compositor_data = {
+ .nb_subdev = 3,
+ .subdev_desc = {
+ {STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100},
+ {STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200},
+ {STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00}
+ },
+};
+
+static int sti_compositor_init_subdev(struct sti_compositor *compo,
+ struct sti_compositor_subdev_descriptor *desc,
+ unsigned int array_size)
+{
+ unsigned int i, mixer_id = 0, layer_id = 0;
+
+ for (i = 0; i < array_size; i++) {
+ switch (desc[i].type) {
+ case STI_MIXER_MAIN_SUBDEV:
+ case STI_MIXER_AUX_SUBDEV:
+ compo->mixer[mixer_id++] =
+ sti_mixer_create(compo->dev, desc[i].id,
+ compo->regs + desc[i].offset);
+ break;
+ case STI_GPD_SUBDEV:
+ case STI_VID_SUBDEV:
+ compo->layer[layer_id++] =
+ sti_layer_create(compo->dev, desc[i].id,
+ compo->regs + desc[i].offset);
+ break;
+ /* case STI_CURSOR_SUBDEV : TODO */
+ default:
+ DRM_ERROR("Unknow subdev compoment type\n");
+ return 1;
+ }
+
+ }
+ compo->nb_mixers = mixer_id;
+ compo->nb_layers = layer_id;
+
+ return 0;
+}
+
+static int sti_compositor_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct sti_compositor *compo = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
+ unsigned int i, crtc = 0, plane = 0;
+ struct sti_drm_private *dev_priv = drm_dev->dev_private;
+ struct drm_plane *cursor = NULL;
+ struct drm_plane *primary = NULL;
+
+ dev_priv->compo = compo;
+
+ for (i = 0; i < compo->nb_layers; i++) {
+ if (compo->layer[i]) {
+ enum sti_layer_desc desc = compo->layer[i]->desc;
+ enum sti_layer_type type = desc & STI_LAYER_TYPE_MASK;
+ enum drm_plane_type plane_type = DRM_PLANE_TYPE_OVERLAY;
+
+ if (compo->mixer[crtc])
+ plane_type = DRM_PLANE_TYPE_PRIMARY;
+
+ switch (type) {
+ case STI_CUR:
+ cursor = sti_drm_plane_init(drm_dev,
+ compo->layer[i],
+ (1 << crtc) - 1,
+ DRM_PLANE_TYPE_CURSOR);
+ break;
+ case STI_GDP:
+ case STI_VID:
+ primary = sti_drm_plane_init(drm_dev,
+ compo->layer[i],
+ (1 << crtc) - 1, plane_type);
+ plane++;
+ break;
+ case STI_BCK:
+ break;
+ }
+
+ /* The first planes are reserved for primary planes*/
+ if (compo->mixer[crtc]) {
+ sti_drm_crtc_init(drm_dev, compo->mixer[crtc],
+ primary, cursor);
+ crtc++;
+ cursor = NULL;
+ }
+ }
+ }
+
+ drm_vblank_init(drm_dev, crtc);
+ /* Allow usage of vblank without having to call drm_irq_install */
+ drm_dev->irq_enabled = 1;
+
+ DRM_DEBUG_DRIVER("Initialized %d DRM CRTC(s) and %d DRM plane(s)\n",
+ crtc, plane);
+ DRM_DEBUG_DRIVER("DRM plane(s) for VID/VDP not created yet\n");
+
+ return 0;
+}
+
+static void sti_compositor_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ /* do nothing */
+}
+
+static const struct component_ops sti_compositor_ops = {
+ .bind = sti_compositor_bind,
+ .unbind = sti_compositor_unbind,
+};
+
+static const struct of_device_id compositor_of_match[] = {
+ {
+ .compatible = "st,stih416-compositor",
+ .data = &stih416_compositor_data,
+ }, {
+ .compatible = "st,stih407-compositor",
+ .data = &stih407_compositor_data,
+ }, {
+ /* end node */
+ }
+};
+MODULE_DEVICE_TABLE(of, compositor_of_match);
+
+static int sti_compositor_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *vtg_np;
+ struct sti_compositor *compo;
+ struct resource *res;
+ int err;
+
+ compo = devm_kzalloc(dev, sizeof(*compo), GFP_KERNEL);
+ if (!compo) {
+ DRM_ERROR("Failed to allocate compositor context\n");
+ return -ENOMEM;
+ }
+ compo->dev = dev;
+ compo->vtg_vblank_nb.notifier_call = sti_drm_crtc_vblank_cb;
+
+ /* populate data structure depending on compatibility */
+ BUG_ON(!of_match_node(compositor_of_match, np)->data);
+
+ memcpy(&compo->data, of_match_node(compositor_of_match, np)->data,
+ sizeof(struct sti_compositor_data));
+
+ /* Get Memory ressources */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ DRM_ERROR("Get memory resource failed\n");
+ return -ENXIO;
+ }
+ compo->regs = devm_ioremap(dev, res->start, resource_size(res));
+ if (compo->regs == NULL) {
+ DRM_ERROR("Register mapping failed\n");
+ return -ENXIO;
+ }
+
+ /* Get clock resources */
+ compo->clk_compo_main = devm_clk_get(dev, "compo_main");
+ if (IS_ERR(compo->clk_compo_main)) {
+ DRM_ERROR("Cannot get compo_main clock\n");
+ return PTR_ERR(compo->clk_compo_main);
+ }
+
+ compo->clk_compo_aux = devm_clk_get(dev, "compo_aux");
+ if (IS_ERR(compo->clk_compo_aux)) {
+ DRM_ERROR("Cannot get compo_aux clock\n");
+ return PTR_ERR(compo->clk_compo_aux);
+ }
+
+ compo->clk_pix_main = devm_clk_get(dev, "pix_main");
+ if (IS_ERR(compo->clk_pix_main)) {
+ DRM_ERROR("Cannot get pix_main clock\n");
+ return PTR_ERR(compo->clk_pix_main);
+ }
+
+ compo->clk_pix_aux = devm_clk_get(dev, "pix_aux");
+ if (IS_ERR(compo->clk_pix_aux)) {
+ DRM_ERROR("Cannot get pix_aux clock\n");
+ return PTR_ERR(compo->clk_pix_aux);
+ }
+
+ /* Get reset resources */
+ compo->rst_main = devm_reset_control_get(dev, "compo-main");
+ /* Take compo main out of reset */
+ if (!IS_ERR(compo->rst_main))
+ reset_control_deassert(compo->rst_main);
+
+ compo->rst_aux = devm_reset_control_get(dev, "compo-aux");
+ /* Take compo aux out of reset */
+ if (!IS_ERR(compo->rst_aux))
+ reset_control_deassert(compo->rst_aux);
+
+ vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 0);
+ if (vtg_np)
+ compo->vtg_main = of_vtg_find(vtg_np);
+
+ vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 1);
+ if (vtg_np)
+ compo->vtg_aux = of_vtg_find(vtg_np);
+
+ /* Initialize compositor subdevices */
+ err = sti_compositor_init_subdev(compo, compo->data.subdev_desc,
+ compo->data.nb_subdev);
+ if (err)
+ return err;
+
+ platform_set_drvdata(pdev, compo);
+
+ return component_add(&pdev->dev, &sti_compositor_ops);
+}
+
+static int sti_compositor_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &sti_compositor_ops);
+ return 0;
+}
+
+static struct platform_driver sti_compositor_driver = {
+ .driver = {
+ .name = "sti-compositor",
+ .owner = THIS_MODULE,
+ .of_match_table = compositor_of_match,
+ },
+ .probe = sti_compositor_probe,
+ .remove = sti_compositor_remove,
+};
+
+module_platform_driver(sti_compositor_driver);
+
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sti/sti_compositor.h b/drivers/gpu/drm/sti/sti_compositor.h
new file mode 100644
index 000000000000..3ea19db72e0f
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_compositor.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_COMPOSITOR_H_
+#define _STI_COMPOSITOR_H_
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+
+#include "sti_layer.h"
+#include "sti_mixer.h"
+
+#define WAIT_NEXT_VSYNC_MS 50 /*ms*/
+
+#define STI_MAX_LAYER 8
+#define STI_MAX_MIXER 2
+
+enum sti_compositor_subdev_type {
+ STI_MIXER_MAIN_SUBDEV,
+ STI_MIXER_AUX_SUBDEV,
+ STI_GPD_SUBDEV,
+ STI_VID_SUBDEV,
+ STI_CURSOR_SUBDEV,
+};
+
+struct sti_compositor_subdev_descriptor {
+ enum sti_compositor_subdev_type type;
+ int id;
+ unsigned int offset;
+};
+
+/**
+ * STI Compositor data structure
+ *
+ * @nb_subdev: number of subdevices supported by the compositor
+ * @subdev_desc: subdev list description
+ */
+#define MAX_SUBDEV 9
+struct sti_compositor_data {
+ unsigned int nb_subdev;
+ struct sti_compositor_subdev_descriptor subdev_desc[MAX_SUBDEV];
+};
+
+/**
+ * STI Compositor structure
+ *
+ * @dev: driver device
+ * @regs: registers (main)
+ * @data: device data
+ * @clk_compo_main: clock for main compo
+ * @clk_compo_aux: clock for aux compo
+ * @clk_pix_main: pixel clock for main path
+ * @clk_pix_aux: pixel clock for aux path
+ * @rst_main: reset control of the main path
+ * @rst_aux: reset control of the aux path
+ * @mixer: array of mixers
+ * @vtg_main: vtg for main data path
+ * @vtg_aux: vtg for auxillary data path
+ * @layer: array of layers
+ * @nb_mixers: number of mixers for this compositor
+ * @nb_layers: number of layers (GDP,VID,...) for this compositor
+ * @enable: true if compositor is enable else false
+ * @vtg_vblank_nb: callback for VTG VSYNC notification
+ */
+struct sti_compositor {
+ struct device *dev;
+ void __iomem *regs;
+ struct sti_compositor_data data;
+ struct clk *clk_compo_main;
+ struct clk *clk_compo_aux;
+ struct clk *clk_pix_main;
+ struct clk *clk_pix_aux;
+ struct reset_control *rst_main;
+ struct reset_control *rst_aux;
+ struct sti_mixer *mixer[STI_MAX_MIXER];
+ struct sti_vtg *vtg_main;
+ struct sti_vtg *vtg_aux;
+ struct sti_layer *layer[STI_MAX_LAYER];
+ int nb_mixers;
+ int nb_layers;
+ bool enable;
+ struct notifier_block vtg_vblank_nb;
+};
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.c b/drivers/gpu/drm/sti/sti_drm_crtc.c
new file mode 100644
index 000000000000..d2ae0c0e13be
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_crtc.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "sti_compositor.h"
+#include "sti_drm_drv.h"
+#include "sti_drm_crtc.h"
+#include "sti_vtg.h"
+
+static void sti_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ DRM_DEBUG_KMS("\n");
+}
+
+static void sti_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+ struct sti_mixer *mixer = to_sti_mixer(crtc);
+ struct device *dev = mixer->dev;
+ struct sti_compositor *compo = dev_get_drvdata(dev);
+
+ compo->enable = true;
+
+ /* Prepare and enable the compo IP clock */
+ if (mixer->id == STI_MIXER_MAIN) {
+ if (clk_prepare_enable(compo->clk_compo_main))
+ DRM_INFO("Failed to prepare/enable compo_main clk\n");
+ } else {
+ if (clk_prepare_enable(compo->clk_compo_aux))
+ DRM_INFO("Failed to prepare/enable compo_aux clk\n");
+ }
+}
+
+static void sti_drm_crtc_commit(struct drm_crtc *crtc)
+{
+ struct sti_mixer *mixer = to_sti_mixer(crtc);
+ struct device *dev = mixer->dev;
+ struct sti_compositor *compo = dev_get_drvdata(dev);
+ struct sti_layer *layer;
+
+ if ((!mixer || !compo)) {
+ DRM_ERROR("Can not find mixer or compositor)\n");
+ return;
+ }
+
+ /* get GDP which is reserved to the CRTC FB */
+ layer = to_sti_layer(crtc->primary);
+ if (layer)
+ sti_layer_commit(layer);
+ else
+ DRM_ERROR("Can not find CRTC dedicated plane (GDP0)\n");
+
+ /* Enable layer on mixer */
+ if (sti_mixer_set_layer_status(mixer, layer, true))
+ DRM_ERROR("Can not enable layer at mixer\n");
+}
+
+static bool sti_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* accept the provided drm_display_mode, do not fix it up */
+ return true;
+}
+
+static int
+sti_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct sti_mixer *mixer = to_sti_mixer(crtc);
+ struct device *dev = mixer->dev;
+ struct sti_compositor *compo = dev_get_drvdata(dev);
+ struct sti_layer *layer;
+ struct clk *clk;
+ int rate = mode->clock * 1000;
+ int res;
+ unsigned int w, h;
+
+ DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d mode:%d (%s)\n",
+ crtc->base.id, sti_mixer_to_str(mixer),
+ crtc->primary->fb->base.id, mode->base.id, mode->name);
+
+ DRM_DEBUG_KMS("%d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
+ mode->vrefresh, mode->clock,
+ mode->hdisplay,
+ mode->hsync_start, mode->hsync_end,
+ mode->htotal,
+ mode->vdisplay,
+ mode->vsync_start, mode->vsync_end,
+ mode->vtotal, mode->type, mode->flags);
+
+ /* Set rate and prepare/enable pixel clock */
+ if (mixer->id == STI_MIXER_MAIN)
+ clk = compo->clk_pix_main;
+ else
+ clk = compo->clk_pix_aux;
+
+ res = clk_set_rate(clk, rate);
+ if (res < 0) {
+ DRM_ERROR("Cannot set rate (%dHz) for pix clk\n", rate);
+ return -EINVAL;
+ }
+ if (clk_prepare_enable(clk)) {
+ DRM_ERROR("Failed to prepare/enable pix clk\n");
+ return -EINVAL;
+ }
+
+ sti_vtg_set_config(mixer->id == STI_MIXER_MAIN ?
+ compo->vtg_main : compo->vtg_aux, &crtc->mode);
+
+ /* a GDP is reserved to the CRTC FB */
+ layer = to_sti_layer(crtc->primary);
+ if (!layer) {
+ DRM_ERROR("Can not find GDP0)\n");
+ return -EINVAL;
+ }
+
+ /* copy the mode data adjusted by mode_fixup() into crtc->mode
+ * so that hardware can be set to proper mode
+ */
+ memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
+
+ res = sti_mixer_set_layer_depth(mixer, layer);
+ if (res) {
+ DRM_ERROR("Can not set layer depth\n");
+ return -EINVAL;
+ }
+ res = sti_mixer_active_video_area(mixer, &crtc->mode);
+ if (res) {
+ DRM_ERROR("Can not set active video area\n");
+ return -EINVAL;
+ }
+
+ w = crtc->primary->fb->width - x;
+ h = crtc->primary->fb->height - y;
+
+ return sti_layer_prepare(layer, crtc->primary->fb, &crtc->mode,
+ mixer->id, 0, 0, w, h, x, y, w, h);
+}
+
+static int sti_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct sti_mixer *mixer = to_sti_mixer(crtc);
+ struct sti_layer *layer;
+ unsigned int w, h;
+ int ret;
+
+ DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d (%d,%d)\n",
+ crtc->base.id, sti_mixer_to_str(mixer),
+ crtc->primary->fb->base.id, x, y);
+
+ /* GDP is reserved to the CRTC FB */
+ layer = to_sti_layer(crtc->primary);
+ if (!layer) {
+ DRM_ERROR("Can not find GDP0)\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ w = crtc->primary->fb->width - crtc->x;
+ h = crtc->primary->fb->height - crtc->y;
+
+ ret = sti_layer_prepare(layer, crtc->primary->fb, &crtc->mode,
+ mixer->id, 0, 0, w, h,
+ crtc->x, crtc->y, w, h);
+ if (ret) {
+ DRM_ERROR("Can not prepare layer\n");
+ goto out;
+ }
+
+ sti_drm_crtc_commit(crtc);
+out:
+ return ret;
+}
+
+static void sti_drm_crtc_load_lut(struct drm_crtc *crtc)
+{
+ /* do nothing */
+}
+
+static void sti_drm_crtc_disable(struct drm_crtc *crtc)
+{
+ struct sti_mixer *mixer = to_sti_mixer(crtc);
+ struct device *dev = mixer->dev;
+ struct sti_compositor *compo = dev_get_drvdata(dev);
+ struct sti_layer *layer;
+
+ if (!compo->enable)
+ return;
+
+ DRM_DEBUG_KMS("CRTC:%d (%s)\n", crtc->base.id, sti_mixer_to_str(mixer));
+
+ /* Disable Background */
+ sti_mixer_set_background_status(mixer, false);
+
+ /* Disable GDP */
+ layer = to_sti_layer(crtc->primary);
+ if (!layer) {
+ DRM_ERROR("Cannot find GDP0\n");
+ return;
+ }
+
+ /* Disable layer at mixer level */
+ if (sti_mixer_set_layer_status(mixer, layer, false))
+ DRM_ERROR("Can not disable %s layer at mixer\n",
+ sti_layer_to_str(layer));
+
+ /* Wait a while to be sure that a Vsync event is received */
+ msleep(WAIT_NEXT_VSYNC_MS);
+
+ /* Then disable layer itself */
+ sti_layer_disable(layer);
+
+ drm_vblank_off(crtc->dev, mixer->id);
+
+ /* Disable pixel clock and compo IP clocks */
+ if (mixer->id == STI_MIXER_MAIN) {
+ clk_disable_unprepare(compo->clk_pix_main);
+ clk_disable_unprepare(compo->clk_compo_main);
+ } else {
+ clk_disable_unprepare(compo->clk_pix_aux);
+ clk_disable_unprepare(compo->clk_compo_aux);
+ }
+
+ compo->enable = false;
+}
+
+static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = {
+ .dpms = sti_drm_crtc_dpms,
+ .prepare = sti_drm_crtc_prepare,
+ .commit = sti_drm_crtc_commit,
+ .mode_fixup = sti_drm_crtc_mode_fixup,
+ .mode_set = sti_drm_crtc_mode_set,
+ .mode_set_base = sti_drm_crtc_mode_set_base,
+ .load_lut = sti_drm_crtc_load_lut,
+ .disable = sti_drm_crtc_disable,
+};
+
+static int sti_drm_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event,
+ uint32_t page_flip_flags)
+{
+ struct drm_device *drm_dev = crtc->dev;
+ struct drm_framebuffer *old_fb;
+ struct sti_mixer *mixer = to_sti_mixer(crtc);
+ unsigned long flags;
+ int ret;
+
+ DRM_DEBUG_KMS("fb %d --> fb %d\n",
+ crtc->primary->fb->base.id, fb->base.id);
+
+ mutex_lock(&drm_dev->struct_mutex);
+
+ old_fb = crtc->primary->fb;
+ crtc->primary->fb = fb;
+ ret = sti_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb);
+ if (ret) {
+ DRM_ERROR("failed\n");
+ crtc->primary->fb = old_fb;
+ goto out;
+ }
+
+ if (event) {
+ event->pipe = mixer->id;
+
+ ret = drm_vblank_get(drm_dev, event->pipe);
+ if (ret) {
+ DRM_ERROR("Cannot get vblank\n");
+ goto out;
+ }
+
+ spin_lock_irqsave(&drm_dev->event_lock, flags);
+ if (mixer->pending_event) {
+ drm_vblank_put(drm_dev, event->pipe);
+ ret = -EBUSY;
+ } else {
+ mixer->pending_event = event;
+ }
+ spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+ }
+out:
+ mutex_unlock(&drm_dev->struct_mutex);
+ return ret;
+}
+
+static void sti_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+ DRM_DEBUG_KMS("\n");
+ drm_crtc_cleanup(crtc);
+}
+
+static int sti_drm_crtc_set_property(struct drm_crtc *crtc,
+ struct drm_property *property,
+ uint64_t val)
+{
+ DRM_DEBUG_KMS("\n");
+ return 0;
+}
+
+int sti_drm_crtc_vblank_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct drm_device *drm_dev;
+ struct sti_compositor *compo =
+ container_of(nb, struct sti_compositor, vtg_vblank_nb);
+ int *crtc = data;
+ unsigned long flags;
+ struct sti_drm_private *priv;
+
+ drm_dev = compo->mixer[*crtc]->drm_crtc.dev;
+ priv = drm_dev->dev_private;
+
+ if ((event != VTG_TOP_FIELD_EVENT) &&
+ (event != VTG_BOTTOM_FIELD_EVENT)) {
+ DRM_ERROR("unknown event: %lu\n", event);
+ return -EINVAL;
+ }
+
+ drm_handle_vblank(drm_dev, *crtc);
+
+ spin_lock_irqsave(&drm_dev->event_lock, flags);
+ if (compo->mixer[*crtc]->pending_event) {
+ drm_send_vblank_event(drm_dev, -1,
+ compo->mixer[*crtc]->pending_event);
+ drm_vblank_put(drm_dev, *crtc);
+ compo->mixer[*crtc]->pending_event = NULL;
+ }
+ spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+
+ return 0;
+}
+
+int sti_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
+{
+ struct sti_drm_private *dev_priv = dev->dev_private;
+ struct sti_compositor *compo = dev_priv->compo;
+ struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb;
+
+ if (sti_vtg_register_client(crtc == STI_MIXER_MAIN ?
+ compo->vtg_main : compo->vtg_aux,
+ vtg_vblank_nb, crtc)) {
+ DRM_ERROR("Cannot register VTG notifier\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(sti_drm_crtc_enable_vblank);
+
+void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
+{
+ struct sti_drm_private *priv = dev->dev_private;
+ struct sti_compositor *compo = priv->compo;
+ struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb;
+ unsigned long flags;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ if (sti_vtg_unregister_client(crtc == STI_MIXER_MAIN ?
+ compo->vtg_main : compo->vtg_aux, vtg_vblank_nb))
+ DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");
+
+ /* free the resources of the pending requests */
+ spin_lock_irqsave(&dev->event_lock, flags);
+ if (compo->mixer[crtc]->pending_event) {
+ drm_vblank_put(dev, crtc);
+ compo->mixer[crtc]->pending_event = NULL;
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+}
+EXPORT_SYMBOL(sti_drm_crtc_disable_vblank);
+
+static struct drm_crtc_funcs sti_crtc_funcs = {
+ .set_config = drm_crtc_helper_set_config,
+ .page_flip = sti_drm_crtc_page_flip,
+ .destroy = sti_drm_crtc_destroy,
+ .set_property = sti_drm_crtc_set_property,
+};
+
+bool sti_drm_crtc_is_main(struct drm_crtc *crtc)
+{
+ struct sti_mixer *mixer = to_sti_mixer(crtc);
+
+ if (mixer->id == STI_MIXER_MAIN)
+ return true;
+
+ return false;
+}
+
+int sti_drm_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer,
+ struct drm_plane *primary, struct drm_plane *cursor)
+{
+ struct drm_crtc *crtc = &mixer->drm_crtc;
+ int res;
+
+ res = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
+ &sti_crtc_funcs);
+ if (res) {
+ DRM_ERROR("Can not initialze CRTC\n");
+ return -EINVAL;
+ }
+
+ drm_crtc_helper_add(crtc, &sti_crtc_helper_funcs);
+
+ DRM_DEBUG_DRIVER("drm CRTC:%d mapped to %s\n",
+ crtc->base.id, sti_mixer_to_str(mixer));
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.h b/drivers/gpu/drm/sti/sti_drm_crtc.h
new file mode 100644
index 000000000000..caca8b14f017
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_crtc.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_DRM_CRTC_H_
+#define _STI_DRM_CRTC_H_
+
+#include <drm/drmP.h>
+
+struct sti_mixer;
+
+int sti_drm_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer,
+ struct drm_plane *primary, struct drm_plane *cursor);
+int sti_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
+void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
+int sti_drm_crtc_vblank_cb(struct notifier_block *nb,
+ unsigned long event, void *data);
+bool sti_drm_crtc_is_main(struct drm_crtc *drm_crtc);
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_drm_drv.c b/drivers/gpu/drm/sti/sti_drm_drv.c
new file mode 100644
index 000000000000..223d93c3a05d
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_drv.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <drm/drmP.h>
+
+#include <linux/component.h>
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "sti_drm_drv.h"
+#include "sti_drm_crtc.h"
+
+#define DRIVER_NAME "sti"
+#define DRIVER_DESC "STMicroelectronics SoC DRM"
+#define DRIVER_DATE "20140601"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+#define STI_MAX_FB_HEIGHT 4096
+#define STI_MAX_FB_WIDTH 4096
+
+static struct drm_mode_config_funcs sti_drm_mode_config_funcs = {
+ .fb_create = drm_fb_cma_create,
+};
+
+static void sti_drm_mode_config_init(struct drm_device *dev)
+{
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ /*
+ * set max width and height as default value.
+ * this value would be used to check framebuffer size limitation
+ * at drm_mode_addfb().
+ */
+ dev->mode_config.max_width = STI_MAX_FB_HEIGHT;
+ dev->mode_config.max_height = STI_MAX_FB_WIDTH;
+
+ dev->mode_config.funcs = &sti_drm_mode_config_funcs;
+}
+
+static int sti_drm_load(struct drm_device *dev, unsigned long flags)
+{
+ struct sti_drm_private *private;
+ int ret;
+
+ private = kzalloc(sizeof(struct sti_drm_private), GFP_KERNEL);
+ if (!private) {
+ DRM_ERROR("Failed to allocate private\n");
+ return -ENOMEM;
+ }
+ dev->dev_private = (void *)private;
+ private->drm_dev = dev;
+
+ drm_mode_config_init(dev);
+ drm_kms_helper_poll_init(dev);
+
+ sti_drm_mode_config_init(dev);
+
+ ret = component_bind_all(dev->dev, dev);
+ if (ret)
+ return ret;
+
+ drm_helper_disable_unused_functions(dev);
+
+#ifdef CONFIG_DRM_STI_FBDEV
+ drm_fbdev_cma_init(dev, 32,
+ dev->mode_config.num_crtc,
+ dev->mode_config.num_connector);
+#endif
+ return 0;
+}
+
+static const struct file_operations sti_drm_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = drm_gem_cma_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .release = drm_release,
+};
+
+static struct dma_buf *sti_drm_gem_prime_export(struct drm_device *dev,
+ struct drm_gem_object *obj,
+ int flags)
+{
+ /* we want to be able to write in mmapped buffer */
+ flags |= O_RDWR;
+ return drm_gem_prime_export(dev, obj, flags);
+}
+
+static struct drm_driver sti_drm_driver = {
+ .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
+ DRIVER_GEM | DRIVER_PRIME,
+ .load = sti_drm_load,
+ .gem_free_object = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .dumb_create = drm_gem_cma_dumb_create,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .fops = &sti_drm_driver_fops,
+
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = sti_drm_crtc_enable_vblank,
+ .disable_vblank = sti_drm_crtc_disable_vblank,
+
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_export = sti_drm_gem_prime_export,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_vmap = drm_gem_cma_prime_vmap,
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static int sti_drm_bind(struct device *dev)
+{
+ return drm_platform_init(&sti_drm_driver, to_platform_device(dev));
+}
+
+static void sti_drm_unbind(struct device *dev)
+{
+ drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops sti_drm_ops = {
+ .bind = sti_drm_bind,
+ .unbind = sti_drm_unbind,
+};
+
+static int sti_drm_master_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->parent->of_node;
+ struct device_node *child_np;
+ struct component_match *match = NULL;
+
+ dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+
+ child_np = of_get_next_available_child(node, NULL);
+
+ while (child_np) {
+ component_match_add(dev, &match, compare_of, child_np);
+ of_node_put(child_np);
+ child_np = of_get_next_available_child(node, child_np);
+ }
+
+ return component_master_add_with_match(dev, &sti_drm_ops, match);
+}
+
+static int sti_drm_master_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &sti_drm_ops);
+ return 0;
+}
+
+static struct platform_driver sti_drm_master_driver = {
+ .probe = sti_drm_master_probe,
+ .remove = sti_drm_master_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME "__master",
+ },
+};
+
+static int sti_drm_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct platform_device *master;
+
+ of_platform_populate(node, NULL, NULL, dev);
+
+ platform_driver_register(&sti_drm_master_driver);
+ master = platform_device_register_resndata(dev,
+ DRIVER_NAME "__master", -1,
+ NULL, 0, NULL, 0);
+ if (IS_ERR(master))
+ return PTR_ERR(master);
+
+ platform_set_drvdata(pdev, master);
+ return 0;
+}
+
+static int sti_drm_platform_remove(struct platform_device *pdev)
+{
+ struct platform_device *master = platform_get_drvdata(pdev);
+
+ of_platform_depopulate(&pdev->dev);
+ platform_device_unregister(master);
+ platform_driver_unregister(&sti_drm_master_driver);
+ return 0;
+}
+
+static const struct of_device_id sti_drm_dt_ids[] = {
+ { .compatible = "st,sti-display-subsystem", },
+ { /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, sti_drm_dt_ids);
+
+static struct platform_driver sti_drm_platform_driver = {
+ .probe = sti_drm_platform_probe,
+ .remove = sti_drm_platform_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ .of_match_table = sti_drm_dt_ids,
+ },
+};
+
+module_platform_driver(sti_drm_platform_driver);
+
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sti/sti_drm_drv.h b/drivers/gpu/drm/sti/sti_drm_drv.h
new file mode 100644
index 000000000000..ec5e2eb8dff9
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_drv.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_DRM_DRV_H_
+#define _STI_DRM_DRV_H_
+
+#include <drm/drmP.h>
+
+struct sti_compositor;
+struct sti_tvout;
+
+/**
+ * STI drm private structure
+ * This structure is stored as private in the drm_device
+ *
+ * @compo: compositor
+ * @plane_zorder_property: z-order property for CRTC planes
+ * @drm_dev: drm device
+ */
+struct sti_drm_private {
+ struct sti_compositor *compo;
+ struct drm_property *plane_zorder_property;
+ struct drm_device *drm_dev;
+};
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_drm_plane.c b/drivers/gpu/drm/sti/sti_drm_plane.c
new file mode 100644
index 000000000000..f4118d4cac22
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_plane.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include "sti_compositor.h"
+#include "sti_drm_drv.h"
+#include "sti_drm_plane.h"
+#include "sti_vtg.h"
+
+enum sti_layer_desc sti_layer_default_zorder[] = {
+ STI_GDP_0,
+ STI_VID_0,
+ STI_GDP_1,
+ STI_VID_1,
+ STI_GDP_2,
+ STI_GDP_3,
+};
+
+/* (Background) < GDP0 < VID0 < GDP1 < VID1 < GDP2 < GDP3 < (ForeGround) */
+
+static int
+sti_drm_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ uint32_t src_x, uint32_t src_y,
+ uint32_t src_w, uint32_t src_h)
+{
+ struct sti_layer *layer = to_sti_layer(plane);
+ struct sti_mixer *mixer = to_sti_mixer(crtc);
+ int res;
+
+ DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s) drm fb:%d\n",
+ crtc->base.id, sti_mixer_to_str(mixer),
+ plane->base.id, sti_layer_to_str(layer), fb->base.id);
+ DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", crtc_w, crtc_h, crtc_x, crtc_y);
+
+ res = sti_mixer_set_layer_depth(mixer, layer);
+ if (res) {
+ DRM_ERROR("Can not set layer depth\n");
+ return res;
+ }
+
+ /* src_x are in 16.16 format. */
+ res = sti_layer_prepare(layer, fb, &crtc->mode, mixer->id,
+ crtc_x, crtc_y, crtc_w, crtc_h,
+ src_x >> 16, src_y >> 16,
+ src_w >> 16, src_h >> 16);
+ if (res) {
+ DRM_ERROR("Layer prepare failed\n");
+ return res;
+ }
+
+ res = sti_layer_commit(layer);
+ if (res) {
+ DRM_ERROR("Layer commit failed\n");
+ return res;
+ }
+
+ res = sti_mixer_set_layer_status(mixer, layer, true);
+ if (res) {
+ DRM_ERROR("Can not enable layer at mixer\n");
+ return res;
+ }
+
+ return 0;
+}
+
+static int sti_drm_disable_plane(struct drm_plane *plane)
+{
+ struct sti_layer *layer;
+ struct sti_mixer *mixer;
+ int lay_res, mix_res;
+
+ if (!plane->crtc) {
+ DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", plane->base.id);
+ return 0;
+ }
+ layer = to_sti_layer(plane);
+ mixer = to_sti_mixer(plane->crtc);
+
+ DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
+ plane->crtc->base.id, sti_mixer_to_str(mixer),
+ plane->base.id, sti_layer_to_str(layer));
+
+ /* Disable layer at mixer level */
+ mix_res = sti_mixer_set_layer_status(mixer, layer, false);
+ if (mix_res)
+ DRM_ERROR("Can not disable layer at mixer\n");
+
+ /* Wait a while to be sure that a Vsync event is received */
+ msleep(WAIT_NEXT_VSYNC_MS);
+
+ /* Then disable layer itself */
+ lay_res = sti_layer_disable(layer);
+ if (lay_res)
+ DRM_ERROR("Layer disable failed\n");
+
+ if (lay_res || mix_res)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void sti_drm_plane_destroy(struct drm_plane *plane)
+{
+ DRM_DEBUG_DRIVER("\n");
+
+ sti_drm_disable_plane(plane);
+ drm_plane_cleanup(plane);
+}
+
+static int sti_drm_plane_set_property(struct drm_plane *plane,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = plane->dev;
+ struct sti_drm_private *private = dev->dev_private;
+ struct sti_layer *layer = to_sti_layer(plane);
+
+ DRM_DEBUG_DRIVER("\n");
+
+ if (property == private->plane_zorder_property) {
+ layer->zorder = val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static struct drm_plane_funcs sti_drm_plane_funcs = {
+ .update_plane = sti_drm_update_plane,
+ .disable_plane = sti_drm_disable_plane,
+ .destroy = sti_drm_plane_destroy,
+ .set_property = sti_drm_plane_set_property,
+};
+
+static void sti_drm_plane_attach_zorder_property(struct drm_plane *plane,
+ uint64_t default_val)
+{
+ struct drm_device *dev = plane->dev;
+ struct sti_drm_private *private = dev->dev_private;
+ struct drm_property *prop;
+ struct sti_layer *layer = to_sti_layer(plane);
+
+ prop = private->plane_zorder_property;
+ if (!prop) {
+ prop = drm_property_create_range(dev, 0, "zpos", 0,
+ GAM_MIXER_NB_DEPTH_LEVEL - 1);
+ if (!prop)
+ return;
+
+ private->plane_zorder_property = prop;
+ }
+
+ drm_object_attach_property(&plane->base, prop, default_val);
+ layer->zorder = default_val;
+}
+
+struct drm_plane *sti_drm_plane_init(struct drm_device *dev,
+ struct sti_layer *layer,
+ unsigned int possible_crtcs,
+ enum drm_plane_type type)
+{
+ int err, i;
+ uint64_t default_zorder = 0;
+
+ err = drm_universal_plane_init(dev, &layer->plane, possible_crtcs,
+ &sti_drm_plane_funcs,
+ sti_layer_get_formats(layer),
+ sti_layer_get_nb_formats(layer), type);
+ if (err) {
+ DRM_ERROR("Failed to initialize plane\n");
+ return NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sti_layer_default_zorder); i++)
+ if (sti_layer_default_zorder[i] == layer->desc)
+ break;
+
+ default_zorder = i;
+
+ if (type == DRM_PLANE_TYPE_OVERLAY)
+ sti_drm_plane_attach_zorder_property(&layer->plane,
+ default_zorder);
+
+ DRM_DEBUG_DRIVER("drm plane:%d mapped to %s with zorder:%llu\n",
+ layer->plane.base.id,
+ sti_layer_to_str(layer), default_zorder);
+
+ return &layer->plane;
+}
diff --git a/drivers/gpu/drm/sti/sti_drm_plane.h b/drivers/gpu/drm/sti/sti_drm_plane.h
new file mode 100644
index 000000000000..4f191839f2a7
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_plane.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_DRM_PLANE_H_
+#define _STI_DRM_PLANE_H_
+
+#include <drm/drmP.h>
+
+struct sti_layer;
+
+struct drm_plane *sti_drm_plane_init(struct drm_device *dev,
+ struct sti_layer *layer,
+ unsigned int possible_crtcs,
+ enum drm_plane_type type);
+#endif
diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c
new file mode 100644
index 000000000000..4e30b74559f5
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_gdp.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+
+#include "sti_compositor.h"
+#include "sti_gdp.h"
+#include "sti_layer.h"
+#include "sti_vtg.h"
+
+#define ENA_COLOR_FILL BIT(8)
+#define WAIT_NEXT_VSYNC BIT(31)
+
+/* GDP color formats */
+#define GDP_RGB565 0x00
+#define GDP_RGB888 0x01
+#define GDP_RGB888_32 0x02
+#define GDP_ARGB8565 0x04
+#define GDP_ARGB8888 0x05
+#define GDP_ARGB1555 0x06
+#define GDP_ARGB4444 0x07
+#define GDP_CLUT8 0x0B
+#define GDP_YCBR888 0x10
+#define GDP_YCBR422R 0x12
+#define GDP_AYCBR8888 0x15
+
+#define GAM_GDP_CTL_OFFSET 0x00
+#define GAM_GDP_AGC_OFFSET 0x04
+#define GAM_GDP_VPO_OFFSET 0x0C
+#define GAM_GDP_VPS_OFFSET 0x10
+#define GAM_GDP_PML_OFFSET 0x14
+#define GAM_GDP_PMP_OFFSET 0x18
+#define GAM_GDP_SIZE_OFFSET 0x1C
+#define GAM_GDP_NVN_OFFSET 0x24
+#define GAM_GDP_KEY1_OFFSET 0x28
+#define GAM_GDP_KEY2_OFFSET 0x2C
+#define GAM_GDP_PPT_OFFSET 0x34
+#define GAM_GDP_CML_OFFSET 0x3C
+#define GAM_GDP_MST_OFFSET 0x68
+
+#define GAM_GDP_ALPHARANGE_255 BIT(5)
+#define GAM_GDP_AGC_FULL_RANGE 0x00808080
+#define GAM_GDP_PPT_IGNORE (BIT(1) | BIT(0))
+#define GAM_GDP_SIZE_MAX 0x7FF
+
+#define GDP_NODE_NB_BANK 2
+#define GDP_NODE_PER_FIELD 2
+
+struct sti_gdp_node {
+ u32 gam_gdp_ctl;
+ u32 gam_gdp_agc;
+ u32 reserved1;
+ u32 gam_gdp_vpo;
+ u32 gam_gdp_vps;
+ u32 gam_gdp_pml;
+ u32 gam_gdp_pmp;
+ u32 gam_gdp_size;
+ u32 reserved2;
+ u32 gam_gdp_nvn;
+ u32 gam_gdp_key1;
+ u32 gam_gdp_key2;
+ u32 reserved3;
+ u32 gam_gdp_ppt;
+ u32 reserved4;
+ u32 gam_gdp_cml;
+};
+
+struct sti_gdp_node_list {
+ struct sti_gdp_node *top_field;
+ struct sti_gdp_node *btm_field;
+};
+
+/**
+ * STI GDP structure
+ *
+ * @layer: layer structure
+ * @clk_pix: pixel clock for the current gdp
+ * @vtg_field_nb: callback for VTG FIELD (top or bottom) notification
+ * @is_curr_top: true if the current node processed is the top field
+ * @node_list: array of node list
+ */
+struct sti_gdp {
+ struct sti_layer layer;
+ struct clk *clk_pix;
+ struct notifier_block vtg_field_nb;
+ bool is_curr_top;
+ struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK];
+};
+
+#define to_sti_gdp(x) container_of(x, struct sti_gdp, layer)
+
+static const uint32_t gdp_supported_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ARGB4444,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_AYUV,
+ DRM_FORMAT_YUV444,
+ DRM_FORMAT_VYUY,
+ DRM_FORMAT_C8,
+};
+
+static const uint32_t *sti_gdp_get_formats(struct sti_layer *layer)
+{
+ return gdp_supported_formats;
+}
+
+static unsigned int sti_gdp_get_nb_formats(struct sti_layer *layer)
+{
+ return ARRAY_SIZE(gdp_supported_formats);
+}
+
+static int sti_gdp_fourcc2format(int fourcc)
+{
+ switch (fourcc) {
+ case DRM_FORMAT_XRGB8888:
+ return GDP_RGB888_32;
+ case DRM_FORMAT_ARGB8888:
+ return GDP_ARGB8888;
+ case DRM_FORMAT_ARGB4444:
+ return GDP_ARGB4444;
+ case DRM_FORMAT_ARGB1555:
+ return GDP_ARGB1555;
+ case DRM_FORMAT_RGB565:
+ return GDP_RGB565;
+ case DRM_FORMAT_RGB888:
+ return GDP_RGB888;
+ case DRM_FORMAT_AYUV:
+ return GDP_AYCBR8888;
+ case DRM_FORMAT_YUV444:
+ return GDP_YCBR888;
+ case DRM_FORMAT_VYUY:
+ return GDP_YCBR422R;
+ case DRM_FORMAT_C8:
+ return GDP_CLUT8;
+ }
+ return -1;
+}
+
+static int sti_gdp_get_alpharange(int format)
+{
+ switch (format) {
+ case GDP_ARGB8565:
+ case GDP_ARGB8888:
+ case GDP_AYCBR8888:
+ return GAM_GDP_ALPHARANGE_255;
+ }
+ return 0;
+}
+
+/**
+ * sti_gdp_get_free_nodes
+ * @layer: gdp layer
+ *
+ * Look for a GDP node list that is not currently read by the HW.
+ *
+ * RETURNS:
+ * Pointer to the free GDP node list
+ */
+static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_layer *layer)
+{
+ int hw_nvn;
+ void *virt_nvn;
+ struct sti_gdp *gdp = to_sti_gdp(layer);
+ unsigned int i;
+
+ hw_nvn = readl(layer->regs + GAM_GDP_NVN_OFFSET);
+ if (!hw_nvn)
+ goto end;
+
+ virt_nvn = dma_to_virt(layer->dev, (dma_addr_t) hw_nvn);
+
+ for (i = 0; i < GDP_NODE_NB_BANK; i++)
+ if ((virt_nvn != gdp->node_list[i].btm_field) &&
+ (virt_nvn != gdp->node_list[i].top_field))
+ return &gdp->node_list[i];
+
+ /* in hazardious cases restart with the first node */
+ DRM_ERROR("inconsistent NVN for %s: 0x%08X\n",
+ sti_layer_to_str(layer), hw_nvn);
+
+end:
+ return &gdp->node_list[0];
+}
+
+/**
+ * sti_gdp_get_current_nodes
+ * @layer: GDP layer
+ *
+ * Look for GDP nodes that are currently read by the HW.
+ *
+ * RETURNS:
+ * Pointer to the current GDP node list
+ */
+static
+struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_layer *layer)
+{
+ int hw_nvn;
+ void *virt_nvn;
+ struct sti_gdp *gdp = to_sti_gdp(layer);
+ unsigned int i;
+
+ hw_nvn = readl(layer->regs + GAM_GDP_NVN_OFFSET);
+ if (!hw_nvn)
+ goto end;
+
+ virt_nvn = dma_to_virt(layer->dev, (dma_addr_t) hw_nvn);
+
+ for (i = 0; i < GDP_NODE_NB_BANK; i++)
+ if ((virt_nvn == gdp->node_list[i].btm_field) ||
+ (virt_nvn == gdp->node_list[i].top_field))
+ return &gdp->node_list[i];
+
+end:
+ DRM_DEBUG_DRIVER("Warning, NVN 0x%08X for %s does not match any node\n",
+ hw_nvn, sti_layer_to_str(layer));
+
+ return NULL;
+}
+
+/**
+ * sti_gdp_prepare_layer
+ * @lay: gdp layer
+ * @first_prepare: true if it is the first time this function is called
+ *
+ * Update the free GDP node list according to the layer properties.
+ *
+ * RETURNS:
+ * 0 on success.
+ */
+static int sti_gdp_prepare_layer(struct sti_layer *layer, bool first_prepare)
+{
+ struct sti_gdp_node_list *list;
+ struct sti_gdp_node *top_field, *btm_field;
+ struct drm_display_mode *mode = layer->mode;
+ struct device *dev = layer->dev;
+ struct sti_gdp *gdp = to_sti_gdp(layer);
+ struct sti_compositor *compo = dev_get_drvdata(dev);
+ int format;
+ unsigned int depth, bpp;
+ int rate = mode->clock * 1000;
+ int res;
+ u32 ydo, xdo, yds, xds;
+
+ list = sti_gdp_get_free_nodes(layer);
+ top_field = list->top_field;
+ btm_field = list->btm_field;
+
+ dev_dbg(dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__,
+ sti_layer_to_str(layer), top_field, btm_field);
+
+ /* Build the top field from layer params */
+ top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE;
+ top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC;
+ format = sti_gdp_fourcc2format(layer->format);
+ if (format == -1) {
+ DRM_ERROR("Format not supported by GDP %.4s\n",
+ (char *)&layer->format);
+ return 1;
+ }
+ top_field->gam_gdp_ctl |= format;
+ top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format);
+ top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE;
+
+ /* pixel memory location */
+ drm_fb_get_bpp_depth(layer->format, &depth, &bpp);
+ top_field->gam_gdp_pml = (u32) layer->paddr + layer->offsets[0];
+ top_field->gam_gdp_pml += layer->src_x * (bpp >> 3);
+ top_field->gam_gdp_pml += layer->src_y * layer->pitches[0];
+
+ /* input parameters */
+ top_field->gam_gdp_pmp = layer->pitches[0];
+ top_field->gam_gdp_size =
+ clamp_val(layer->src_h, 0, GAM_GDP_SIZE_MAX) << 16 |
+ clamp_val(layer->src_w, 0, GAM_GDP_SIZE_MAX);
+
+ /* output parameters */
+ ydo = sti_vtg_get_line_number(*mode, layer->dst_y);
+ yds = sti_vtg_get_line_number(*mode, layer->dst_y + layer->dst_h - 1);
+ xdo = sti_vtg_get_pixel_number(*mode, layer->dst_x);
+ xds = sti_vtg_get_pixel_number(*mode, layer->dst_x + layer->dst_w - 1);
+ top_field->gam_gdp_vpo = (ydo << 16) | xdo;
+ top_field->gam_gdp_vps = (yds << 16) | xds;
+
+ /* Same content and chained together */
+ memcpy(btm_field, top_field, sizeof(*btm_field));
+ top_field->gam_gdp_nvn = virt_to_dma(dev, btm_field);
+ btm_field->gam_gdp_nvn = virt_to_dma(dev, top_field);
+
+ /* Interlaced mode */
+ if (layer->mode->flags & DRM_MODE_FLAG_INTERLACE)
+ btm_field->gam_gdp_pml = top_field->gam_gdp_pml +
+ layer->pitches[0];
+
+ if (first_prepare) {
+ /* Register gdp callback */
+ if (sti_vtg_register_client(layer->mixer_id == STI_MIXER_MAIN ?
+ compo->vtg_main : compo->vtg_aux,
+ &gdp->vtg_field_nb, layer->mixer_id)) {
+ DRM_ERROR("Cannot register VTG notifier\n");
+ return 1;
+ }
+
+ /* Set and enable gdp clock */
+ if (gdp->clk_pix) {
+ res = clk_set_rate(gdp->clk_pix, rate);
+ if (res < 0) {
+ DRM_ERROR("Cannot set rate (%dHz) for gdp\n",
+ rate);
+ return 1;
+ }
+
+ if (clk_prepare_enable(gdp->clk_pix)) {
+ DRM_ERROR("Failed to prepare/enable gdp\n");
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * sti_gdp_commit_layer
+ * @lay: gdp layer
+ *
+ * Update the NVN field of the 'right' field of the current GDP node (being
+ * used by the HW) with the address of the updated ('free') top field GDP node.
+ * - In interlaced mode the 'right' field is the bottom field as we update
+ * frames starting from their top field
+ * - In progressive mode, we update both bottom and top fields which are
+ * equal nodes.
+ * At the next VSYNC, the updated node list will be used by the HW.
+ *
+ * RETURNS:
+ * 0 on success.
+ */
+static int sti_gdp_commit_layer(struct sti_layer *layer)
+{
+ struct sti_gdp_node_list *updated_list = sti_gdp_get_free_nodes(layer);
+ struct sti_gdp_node *updated_top_node = updated_list->top_field;
+ struct sti_gdp_node *updated_btm_node = updated_list->btm_field;
+ struct sti_gdp *gdp = to_sti_gdp(layer);
+ u32 dma_updated_top = virt_to_dma(layer->dev, updated_top_node);
+ u32 dma_updated_btm = virt_to_dma(layer->dev, updated_btm_node);
+ struct sti_gdp_node_list *curr_list = sti_gdp_get_current_nodes(layer);
+
+ dev_dbg(layer->dev, "%s %s top/btm_node:0x%p/0x%p\n", __func__,
+ sti_layer_to_str(layer),
+ updated_top_node, updated_btm_node);
+ dev_dbg(layer->dev, "Current NVN:0x%X\n",
+ readl(layer->regs + GAM_GDP_NVN_OFFSET));
+ dev_dbg(layer->dev, "Posted buff: %lx current buff: %x\n",
+ (unsigned long)layer->paddr,
+ readl(layer->regs + GAM_GDP_PML_OFFSET));
+
+ if (curr_list == NULL) {
+ /* First update or invalid node should directly write in the
+ * hw register */
+ DRM_DEBUG_DRIVER("%s first update (or invalid node)",
+ sti_layer_to_str(layer));
+
+ writel(gdp->is_curr_top == true ?
+ dma_updated_btm : dma_updated_top,
+ layer->regs + GAM_GDP_NVN_OFFSET);
+ return 0;
+ }
+
+ if (layer->mode->flags & DRM_MODE_FLAG_INTERLACE) {
+ if (gdp->is_curr_top == true) {
+ /* Do not update in the middle of the frame, but
+ * postpone the update after the bottom field has
+ * been displayed */
+ curr_list->btm_field->gam_gdp_nvn = dma_updated_top;
+ } else {
+ /* Direct update to avoid one frame delay */
+ writel(dma_updated_top,
+ layer->regs + GAM_GDP_NVN_OFFSET);
+ }
+ } else {
+ /* Direct update for progressive to avoid one frame delay */
+ writel(dma_updated_top, layer->regs + GAM_GDP_NVN_OFFSET);
+ }
+
+ return 0;
+}
+
+/**
+ * sti_gdp_disable_layer
+ * @lay: gdp layer
+ *
+ * Disable a GDP.
+ *
+ * RETURNS:
+ * 0 on success.
+ */
+static int sti_gdp_disable_layer(struct sti_layer *layer)
+{
+ unsigned int i;
+ struct sti_gdp *gdp = to_sti_gdp(layer);
+ struct sti_compositor *compo = dev_get_drvdata(layer->dev);
+
+ DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer));
+
+ /* Set the nodes as 'to be ignored on mixer' */
+ for (i = 0; i < GDP_NODE_NB_BANK; i++) {
+ gdp->node_list[i].top_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE;
+ gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE;
+ }
+
+ if (sti_vtg_unregister_client(layer->mixer_id == STI_MIXER_MAIN ?
+ compo->vtg_main : compo->vtg_aux, &gdp->vtg_field_nb))
+ DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");
+
+ if (gdp->clk_pix)
+ clk_disable_unprepare(gdp->clk_pix);
+
+ return 0;
+}
+
+/**
+ * sti_gdp_field_cb
+ * @nb: notifier block
+ * @event: event message
+ * @data: private data
+ *
+ * Handle VTG top field and bottom field event.
+ *
+ * RETURNS:
+ * 0 on success.
+ */
+int sti_gdp_field_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct sti_gdp *gdp = container_of(nb, struct sti_gdp, vtg_field_nb);
+
+ switch (event) {
+ case VTG_TOP_FIELD_EVENT:
+ gdp->is_curr_top = true;
+ break;
+ case VTG_BOTTOM_FIELD_EVENT:
+ gdp->is_curr_top = false;
+ break;
+ default:
+ DRM_ERROR("unsupported event: %lu\n", event);
+ break;
+ }
+
+ return 0;
+}
+
+static void sti_gdp_init(struct sti_layer *layer)
+{
+ struct sti_gdp *gdp = to_sti_gdp(layer);
+ struct device_node *np = layer->dev->of_node;
+ dma_addr_t dma;
+ void *base;
+ unsigned int i, size;
+
+ /* Allocate all the nodes within a single memory page */
+ size = sizeof(struct sti_gdp_node) *
+ GDP_NODE_PER_FIELD * GDP_NODE_NB_BANK;
+
+ base = dma_alloc_writecombine(layer->dev,
+ size, &dma, GFP_KERNEL | GFP_DMA);
+ if (!base) {
+ DRM_ERROR("Failed to allocate memory for GDP node\n");
+ return;
+ }
+ memset(base, 0, size);
+
+ for (i = 0; i < GDP_NODE_NB_BANK; i++) {
+ if (virt_to_dma(layer->dev, base) & 0xF) {
+ DRM_ERROR("Mem alignment failed\n");
+ return;
+ }
+ gdp->node_list[i].top_field = base;
+ DRM_DEBUG_DRIVER("node[%d].top_field=%p\n", i, base);
+ base += sizeof(struct sti_gdp_node);
+
+ if (virt_to_dma(layer->dev, base) & 0xF) {
+ DRM_ERROR("Mem alignment failed\n");
+ return;
+ }
+ gdp->node_list[i].btm_field = base;
+ DRM_DEBUG_DRIVER("node[%d].btm_field=%p\n", i, base);
+ base += sizeof(struct sti_gdp_node);
+ }
+
+ if (of_device_is_compatible(np, "st,stih407-compositor")) {
+ /* GDP of STiH407 chip have its own pixel clock */
+ char *clk_name;
+
+ switch (layer->desc) {
+ case STI_GDP_0:
+ clk_name = "pix_gdp1";
+ break;
+ case STI_GDP_1:
+ clk_name = "pix_gdp2";
+ break;
+ case STI_GDP_2:
+ clk_name = "pix_gdp3";
+ break;
+ case STI_GDP_3:
+ clk_name = "pix_gdp4";
+ break;
+ default:
+ DRM_ERROR("GDP id not recognized\n");
+ return;
+ }
+
+ gdp->clk_pix = devm_clk_get(layer->dev, clk_name);
+ if (IS_ERR(gdp->clk_pix))
+ DRM_ERROR("Cannot get %s clock\n", clk_name);
+ }
+}
+
+static const struct sti_layer_funcs gdp_ops = {
+ .get_formats = sti_gdp_get_formats,
+ .get_nb_formats = sti_gdp_get_nb_formats,
+ .init = sti_gdp_init,
+ .prepare = sti_gdp_prepare_layer,
+ .commit = sti_gdp_commit_layer,
+ .disable = sti_gdp_disable_layer,
+};
+
+struct sti_layer *sti_gdp_create(struct device *dev, int id)
+{
+ struct sti_gdp *gdp;
+
+ gdp = devm_kzalloc(dev, sizeof(*gdp), GFP_KERNEL);
+ if (!gdp) {
+ DRM_ERROR("Failed to allocate memory for GDP\n");
+ return NULL;
+ }
+
+ gdp->layer.ops = &gdp_ops;
+ gdp->vtg_field_nb.notifier_call = sti_gdp_field_cb;
+
+ return (struct sti_layer *)gdp;
+}
diff --git a/drivers/gpu/drm/sti/sti_gdp.h b/drivers/gpu/drm/sti/sti_gdp.h
new file mode 100644
index 000000000000..1dab68274ad3
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_gdp.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_GDP_H_
+#define _STI_GDP_H_
+
+#include <linux/types.h>
+
+struct sti_layer *sti_gdp_create(struct device *dev, int id);
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
new file mode 100644
index 000000000000..2ae9a9b73666
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hda.c
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+/* HDformatter registers */
+#define HDA_ANA_CFG 0x0000
+#define HDA_ANA_SCALE_CTRL_Y 0x0004
+#define HDA_ANA_SCALE_CTRL_CB 0x0008
+#define HDA_ANA_SCALE_CTRL_CR 0x000C
+#define HDA_ANA_ANC_CTRL 0x0010
+#define HDA_ANA_SRC_Y_CFG 0x0014
+#define HDA_COEFF_Y_PH1_TAP123 0x0018
+#define HDA_COEFF_Y_PH1_TAP456 0x001C
+#define HDA_COEFF_Y_PH2_TAP123 0x0020
+#define HDA_COEFF_Y_PH2_TAP456 0x0024
+#define HDA_COEFF_Y_PH3_TAP123 0x0028
+#define HDA_COEFF_Y_PH3_TAP456 0x002C
+#define HDA_COEFF_Y_PH4_TAP123 0x0030
+#define HDA_COEFF_Y_PH4_TAP456 0x0034
+#define HDA_ANA_SRC_C_CFG 0x0040
+#define HDA_COEFF_C_PH1_TAP123 0x0044
+#define HDA_COEFF_C_PH1_TAP456 0x0048
+#define HDA_COEFF_C_PH2_TAP123 0x004C
+#define HDA_COEFF_C_PH2_TAP456 0x0050
+#define HDA_COEFF_C_PH3_TAP123 0x0054
+#define HDA_COEFF_C_PH3_TAP456 0x0058
+#define HDA_COEFF_C_PH4_TAP123 0x005C
+#define HDA_COEFF_C_PH4_TAP456 0x0060
+#define HDA_SYNC_AWGI 0x0300
+
+/* HDA_ANA_CFG */
+#define CFG_AWG_ASYNC_EN BIT(0)
+#define CFG_AWG_ASYNC_HSYNC_MTD BIT(1)
+#define CFG_AWG_ASYNC_VSYNC_MTD BIT(2)
+#define CFG_AWG_SYNC_DEL BIT(3)
+#define CFG_AWG_FLTR_MODE_SHIFT 4
+#define CFG_AWG_FLTR_MODE_MASK (0xF << CFG_AWG_FLTR_MODE_SHIFT)
+#define CFG_AWG_FLTR_MODE_SD (0 << CFG_AWG_FLTR_MODE_SHIFT)
+#define CFG_AWG_FLTR_MODE_ED (1 << CFG_AWG_FLTR_MODE_SHIFT)
+#define CFG_AWG_FLTR_MODE_HD (2 << CFG_AWG_FLTR_MODE_SHIFT)
+#define CFG_SYNC_ON_PBPR_MASK BIT(8)
+#define CFG_PREFILTER_EN_MASK BIT(9)
+#define CFG_PBPR_SYNC_OFF_SHIFT 16
+#define CFG_PBPR_SYNC_OFF_MASK (0x7FF << CFG_PBPR_SYNC_OFF_SHIFT)
+#define CFG_PBPR_SYNC_OFF_VAL 0x117 /* Voltage dependent. stiH416 */
+
+/* Default scaling values */
+#define SCALE_CTRL_Y_DFLT 0x00C50256
+#define SCALE_CTRL_CB_DFLT 0x00DB0249
+#define SCALE_CTRL_CR_DFLT 0x00DB0249
+
+/* Video DACs control */
+#define VIDEO_DACS_CONTROL_MASK 0x0FFF
+#define VIDEO_DACS_CONTROL_SYSCFG2535 0x085C /* for stih416 */
+#define DAC_CFG_HD_OFF_SHIFT 5
+#define DAC_CFG_HD_OFF_MASK (0x7 << DAC_CFG_HD_OFF_SHIFT)
+#define VIDEO_DACS_CONTROL_SYSCFG5072 0x0120 /* for stih407 */
+#define DAC_CFG_HD_HZUVW_OFF_MASK BIT(1)
+
+
+/* Upsampler values for the alternative 2X Filter */
+#define SAMPLER_COEF_NB 8
+#define HDA_ANA_SRC_Y_CFG_ALT_2X 0x01130000
+static u32 coef_y_alt_2x[] = {
+ 0x00FE83FB, 0x1F900401, 0x00000000, 0x00000000,
+ 0x00F408F9, 0x055F7C25, 0x00000000, 0x00000000
+};
+
+#define HDA_ANA_SRC_C_CFG_ALT_2X 0x01750004
+static u32 coef_c_alt_2x[] = {
+ 0x001305F7, 0x05274BD0, 0x00000000, 0x00000000,
+ 0x0004907C, 0x09C80B9D, 0x00000000, 0x00000000
+};
+
+/* Upsampler values for the 4X Filter */
+#define HDA_ANA_SRC_Y_CFG_4X 0x01ED0005
+#define HDA_ANA_SRC_C_CFG_4X 0x01ED0004
+static u32 coef_yc_4x[] = {
+ 0x00FC827F, 0x008FE20B, 0x00F684FC, 0x050F7C24,
+ 0x00F4857C, 0x0A1F402E, 0x00FA027F, 0x0E076E1D
+};
+
+/* AWG instructions for some video modes */
+#define AWG_MAX_INST 64
+
+/* 720p@50 */
+static u32 AWGi_720p_50[] = {
+ 0x00000971, 0x00000C26, 0x0000013B, 0x00000CDA,
+ 0x00000104, 0x00000E7E, 0x00000E7F, 0x0000013B,
+ 0x00000D8E, 0x00000104, 0x00001804, 0x00000971,
+ 0x00000C26, 0x0000003B, 0x00000FB4, 0x00000FB5,
+ 0x00000104, 0x00001AE8
+};
+
+#define NN_720p_50 ARRAY_SIZE(AWGi_720p_50)
+
+/* 720p@60 */
+static u32 AWGi_720p_60[] = {
+ 0x00000971, 0x00000C26, 0x0000013B, 0x00000CDA,
+ 0x00000104, 0x00000E7E, 0x00000E7F, 0x0000013B,
+ 0x00000C44, 0x00000104, 0x00001804, 0x00000971,
+ 0x00000C26, 0x0000003B, 0x00000F0F, 0x00000F10,
+ 0x00000104, 0x00001AE8
+};
+
+#define NN_720p_60 ARRAY_SIZE(AWGi_720p_60)
+
+/* 1080p@30 */
+static u32 AWGi_1080p_30[] = {
+ 0x00000971, 0x00000C2A, 0x0000013B, 0x00000C56,
+ 0x00000104, 0x00000FDC, 0x00000FDD, 0x0000013B,
+ 0x00000C2A, 0x00000104, 0x00001804, 0x00000971,
+ 0x00000C2A, 0x0000003B, 0x00000EBE, 0x00000EBF,
+ 0x00000EBF, 0x00000104, 0x00001A2F, 0x00001C4B,
+ 0x00001C52
+};
+
+#define NN_1080p_30 ARRAY_SIZE(AWGi_1080p_30)
+
+/* 1080p@25 */
+static u32 AWGi_1080p_25[] = {
+ 0x00000971, 0x00000C2A, 0x0000013B, 0x00000C56,
+ 0x00000104, 0x00000FDC, 0x00000FDD, 0x0000013B,
+ 0x00000DE2, 0x00000104, 0x00001804, 0x00000971,
+ 0x00000C2A, 0x0000003B, 0x00000F51, 0x00000F51,
+ 0x00000F52, 0x00000104, 0x00001A2F, 0x00001C4B,
+ 0x00001C52
+};
+
+#define NN_1080p_25 ARRAY_SIZE(AWGi_1080p_25)
+
+/* 1080p@24 */
+static u32 AWGi_1080p_24[] = {
+ 0x00000971, 0x00000C2A, 0x0000013B, 0x00000C56,
+ 0x00000104, 0x00000FDC, 0x00000FDD, 0x0000013B,
+ 0x00000E50, 0x00000104, 0x00001804, 0x00000971,
+ 0x00000C2A, 0x0000003B, 0x00000F76, 0x00000F76,
+ 0x00000F76, 0x00000104, 0x00001A2F, 0x00001C4B,
+ 0x00001C52
+};
+
+#define NN_1080p_24 ARRAY_SIZE(AWGi_1080p_24)
+
+/* 720x480p@60 */
+static u32 AWGi_720x480p_60[] = {
+ 0x00000904, 0x00000F18, 0x0000013B, 0x00001805,
+ 0x00000904, 0x00000C3D, 0x0000003B, 0x00001A06
+};
+
+#define NN_720x480p_60 ARRAY_SIZE(AWGi_720x480p_60)
+
+/* Video mode category */
+enum sti_hda_vid_cat {
+ VID_SD,
+ VID_ED,
+ VID_HD_74M,
+ VID_HD_148M
+};
+
+struct sti_hda_video_config {
+ struct drm_display_mode mode;
+ u32 *awg_instr;
+ int nb_instr;
+ enum sti_hda_vid_cat vid_cat;
+};
+
+/* HD analog supported modes
+ * Interlaced modes may be added when supported by the whole display chain
+ */
+static const struct sti_hda_video_config hda_supported_modes[] = {
+ /* 1080p30 74.250Mhz */
+ {{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+ AWGi_1080p_30, NN_1080p_30, VID_HD_74M},
+ /* 1080p30 74.176Mhz */
+ {{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74176, 1920, 2008,
+ 2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+ AWGi_1080p_30, NN_1080p_30, VID_HD_74M},
+ /* 1080p24 74.250Mhz */
+ {{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
+ 2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+ AWGi_1080p_24, NN_1080p_24, VID_HD_74M},
+ /* 1080p24 74.176Mhz */
+ {{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74176, 1920, 2558,
+ 2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+ AWGi_1080p_24, NN_1080p_24, VID_HD_74M},
+ /* 1080p25 74.250Mhz */
+ {{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
+ 2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+ AWGi_1080p_25, NN_1080p_25, VID_HD_74M},
+ /* 720p60 74.250Mhz */
+ {{DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+ AWGi_720p_60, NN_720p_60, VID_HD_74M},
+ /* 720p60 74.176Mhz */
+ {{DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74176, 1280, 1390,
+ 1430, 1650, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+ AWGi_720p_60, NN_720p_60, VID_HD_74M},
+ /* 720p50 74.250Mhz */
+ {{DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
+ 1760, 1980, 0, 720, 725, 730, 750, 0,
+ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+ AWGi_720p_50, NN_720p_50, VID_HD_74M},
+ /* 720x480p60 27.027Mhz */
+ {{DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27027, 720, 736,
+ 798, 858, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)},
+ AWGi_720x480p_60, NN_720x480p_60, VID_ED},
+ /* 720x480p60 27.000Mhz */
+ {{DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
+ 798, 858, 0, 480, 489, 495, 525, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)},
+ AWGi_720x480p_60, NN_720x480p_60, VID_ED}
+};
+
+/**
+ * STI hd analog structure
+ *
+ * @dev: driver device
+ * @drm_dev: pointer to drm device
+ * @mode: current display mode selected
+ * @regs: HD analog register
+ * @video_dacs_ctrl: video DACS control register
+ * @enabled: true if HD analog is enabled else false
+ */
+struct sti_hda {
+ struct device dev;
+ struct drm_device *drm_dev;
+ struct drm_display_mode mode;
+ void __iomem *regs;
+ void __iomem *video_dacs_ctrl;
+ struct clk *clk_pix;
+ struct clk *clk_hddac;
+ bool enabled;
+};
+
+struct sti_hda_connector {
+ struct drm_connector drm_connector;
+ struct drm_encoder *encoder;
+ struct sti_hda *hda;
+};
+
+#define to_sti_hda_connector(x) \
+ container_of(x, struct sti_hda_connector, drm_connector)
+
+static u32 hda_read(struct sti_hda *hda, int offset)
+{
+ return readl(hda->regs + offset);
+}
+
+static void hda_write(struct sti_hda *hda, u32 val, int offset)
+{
+ writel(val, hda->regs + offset);
+}
+
+/**
+ * Search for a video mode in the supported modes table
+ *
+ * @mode: mode being searched
+ * @idx: index of the found mode
+ *
+ * Return true if mode is found
+ */
+static bool hda_get_mode_idx(struct drm_display_mode mode, int *idx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(hda_supported_modes); i++)
+ if (drm_mode_equal(&hda_supported_modes[i].mode, &mode)) {
+ *idx = i;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Enable the HD DACS
+ *
+ * @hda: pointer to HD analog structure
+ * @enable: true if HD DACS need to be enabled, else false
+ */
+static void hda_enable_hd_dacs(struct sti_hda *hda, bool enable)
+{
+ u32 mask;
+
+ if (hda->video_dacs_ctrl) {
+ u32 val;
+
+ switch ((u32)hda->video_dacs_ctrl & VIDEO_DACS_CONTROL_MASK) {
+ case VIDEO_DACS_CONTROL_SYSCFG2535:
+ mask = DAC_CFG_HD_OFF_MASK;
+ break;
+ case VIDEO_DACS_CONTROL_SYSCFG5072:
+ mask = DAC_CFG_HD_HZUVW_OFF_MASK;
+ break;
+ default:
+ DRM_INFO("Video DACS control register not supported!");
+ return;
+ }
+
+ val = readl(hda->video_dacs_ctrl);
+ if (enable)
+ val &= ~mask;
+ else
+ val |= mask;
+
+ writel(val, hda->video_dacs_ctrl);
+ }
+}
+
+/**
+ * Configure AWG, writing instructions
+ *
+ * @hda: pointer to HD analog structure
+ * @awg_instr: pointer to AWG instructions table
+ * @nb: nb of AWG instructions
+ */
+static void sti_hda_configure_awg(struct sti_hda *hda, u32 *awg_instr, int nb)
+{
+ unsigned int i;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ for (i = 0; i < nb; i++)
+ hda_write(hda, awg_instr[i], HDA_SYNC_AWGI + i * 4);
+ for (i = nb; i < AWG_MAX_INST; i++)
+ hda_write(hda, 0, HDA_SYNC_AWGI + i * 4);
+}
+
+static void sti_hda_disable(struct drm_bridge *bridge)
+{
+ struct sti_hda *hda = bridge->driver_private;
+ u32 val;
+
+ if (!hda->enabled)
+ return;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ /* Disable HD DAC and AWG */
+ val = hda_read(hda, HDA_ANA_CFG);
+ val &= ~CFG_AWG_ASYNC_EN;
+ hda_write(hda, val, HDA_ANA_CFG);
+ hda_write(hda, 0, HDA_ANA_ANC_CTRL);
+
+ hda_enable_hd_dacs(hda, false);
+
+ /* Disable/unprepare hda clock */
+ clk_disable_unprepare(hda->clk_hddac);
+ clk_disable_unprepare(hda->clk_pix);
+
+ hda->enabled = false;
+}
+
+static void sti_hda_pre_enable(struct drm_bridge *bridge)
+{
+ struct sti_hda *hda = bridge->driver_private;
+ u32 val, i, mode_idx;
+ u32 src_filter_y, src_filter_c;
+ u32 *coef_y, *coef_c;
+ u32 filter_mode;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ if (hda->enabled)
+ return;
+
+ /* Prepare/enable clocks */
+ if (clk_prepare_enable(hda->clk_pix))
+ DRM_ERROR("Failed to prepare/enable hda_pix clk\n");
+ if (clk_prepare_enable(hda->clk_hddac))
+ DRM_ERROR("Failed to prepare/enable hda_hddac clk\n");
+
+ if (!hda_get_mode_idx(hda->mode, &mode_idx)) {
+ DRM_ERROR("Undefined mode\n");
+ return;
+ }
+
+ switch (hda_supported_modes[mode_idx].vid_cat) {
+ case VID_HD_148M:
+ DRM_ERROR("Beyond HD analog capabilities\n");
+ return;
+ case VID_HD_74M:
+ /* HD use alternate 2x filter */
+ filter_mode = CFG_AWG_FLTR_MODE_HD;
+ src_filter_y = HDA_ANA_SRC_Y_CFG_ALT_2X;
+ src_filter_c = HDA_ANA_SRC_C_CFG_ALT_2X;
+ coef_y = coef_y_alt_2x;
+ coef_c = coef_c_alt_2x;
+ break;
+ case VID_ED:
+ /* ED uses 4x filter */
+ filter_mode = CFG_AWG_FLTR_MODE_ED;
+ src_filter_y = HDA_ANA_SRC_Y_CFG_4X;
+ src_filter_c = HDA_ANA_SRC_C_CFG_4X;
+ coef_y = coef_yc_4x;
+ coef_c = coef_yc_4x;
+ break;
+ case VID_SD:
+ DRM_ERROR("Not supported\n");
+ return;
+ default:
+ DRM_ERROR("Undefined resolution\n");
+ return;
+ }
+ DRM_DEBUG_DRIVER("Using HDA mode #%d\n", mode_idx);
+
+ /* Enable HD Video DACs */
+ hda_enable_hd_dacs(hda, true);
+
+ /* Configure scaler */
+ hda_write(hda, SCALE_CTRL_Y_DFLT, HDA_ANA_SCALE_CTRL_Y);
+ hda_write(hda, SCALE_CTRL_CB_DFLT, HDA_ANA_SCALE_CTRL_CB);
+ hda_write(hda, SCALE_CTRL_CR_DFLT, HDA_ANA_SCALE_CTRL_CR);
+
+ /* Configure sampler */
+ hda_write(hda , src_filter_y, HDA_ANA_SRC_Y_CFG);
+ hda_write(hda, src_filter_c, HDA_ANA_SRC_C_CFG);
+ for (i = 0; i < SAMPLER_COEF_NB; i++) {
+ hda_write(hda, coef_y[i], HDA_COEFF_Y_PH1_TAP123 + i * 4);
+ hda_write(hda, coef_c[i], HDA_COEFF_C_PH1_TAP123 + i * 4);
+ }
+
+ /* Configure main HDFormatter */
+ val = 0;
+ val |= (hda->mode.flags & DRM_MODE_FLAG_INTERLACE) ?
+ 0 : CFG_AWG_ASYNC_VSYNC_MTD;
+ val |= (CFG_PBPR_SYNC_OFF_VAL << CFG_PBPR_SYNC_OFF_SHIFT);
+ val |= filter_mode;
+ hda_write(hda, val, HDA_ANA_CFG);
+
+ /* Configure AWG */
+ sti_hda_configure_awg(hda, hda_supported_modes[mode_idx].awg_instr,
+ hda_supported_modes[mode_idx].nb_instr);
+
+ /* Enable AWG */
+ val = hda_read(hda, HDA_ANA_CFG);
+ val |= CFG_AWG_ASYNC_EN;
+ hda_write(hda, val, HDA_ANA_CFG);
+
+ hda->enabled = true;
+}
+
+static void sti_hda_set_mode(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct sti_hda *hda = bridge->driver_private;
+ u32 mode_idx;
+ int hddac_rate;
+ int ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ memcpy(&hda->mode, mode, sizeof(struct drm_display_mode));
+
+ if (!hda_get_mode_idx(hda->mode, &mode_idx)) {
+ DRM_ERROR("Undefined mode\n");
+ return;
+ }
+
+ switch (hda_supported_modes[mode_idx].vid_cat) {
+ case VID_HD_74M:
+ /* HD use alternate 2x filter */
+ hddac_rate = mode->clock * 1000 * 2;
+ break;
+ case VID_ED:
+ /* ED uses 4x filter */
+ hddac_rate = mode->clock * 1000 * 4;
+ break;
+ default:
+ DRM_ERROR("Undefined mode\n");
+ return;
+ }
+
+ /* HD DAC = 148.5Mhz or 108 Mhz */
+ ret = clk_set_rate(hda->clk_hddac, hddac_rate);
+ if (ret < 0)
+ DRM_ERROR("Cannot set rate (%dHz) for hda_hddac clk\n",
+ hddac_rate);
+
+ /* HDformatter clock = compositor clock */
+ ret = clk_set_rate(hda->clk_pix, mode->clock * 1000);
+ if (ret < 0)
+ DRM_ERROR("Cannot set rate (%dHz) for hda_pix clk\n",
+ mode->clock * 1000);
+}
+
+static void sti_hda_bridge_nope(struct drm_bridge *bridge)
+{
+ /* do nothing */
+}
+
+static void sti_hda_brigde_destroy(struct drm_bridge *bridge)
+{
+ drm_bridge_cleanup(bridge);
+ kfree(bridge);
+}
+
+static const struct drm_bridge_funcs sti_hda_bridge_funcs = {
+ .pre_enable = sti_hda_pre_enable,
+ .enable = sti_hda_bridge_nope,
+ .disable = sti_hda_disable,
+ .post_disable = sti_hda_bridge_nope,
+ .mode_set = sti_hda_set_mode,
+ .destroy = sti_hda_brigde_destroy,
+};
+
+static int sti_hda_connector_get_modes(struct drm_connector *connector)
+{
+ unsigned int i;
+ int count = 0;
+ struct sti_hda_connector *hda_connector
+ = to_sti_hda_connector(connector);
+ struct sti_hda *hda = hda_connector->hda;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ for (i = 0; i < ARRAY_SIZE(hda_supported_modes); i++) {
+ struct drm_display_mode *mode =
+ drm_mode_duplicate(hda->drm_dev,
+ &hda_supported_modes[i].mode);
+ if (!mode)
+ continue;
+ mode->vrefresh = drm_mode_vrefresh(mode);
+
+ /* the first mode is the preferred mode */
+ if (i == 0)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_probed_add(connector, mode);
+ count++;
+ }
+
+ drm_mode_sort(&connector->modes);
+
+ return count;
+}
+
+#define CLK_TOLERANCE_HZ 50
+
+static int sti_hda_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int target = mode->clock * 1000;
+ int target_min = target - CLK_TOLERANCE_HZ;
+ int target_max = target + CLK_TOLERANCE_HZ;
+ int result;
+ int idx;
+ struct sti_hda_connector *hda_connector
+ = to_sti_hda_connector(connector);
+ struct sti_hda *hda = hda_connector->hda;
+
+ if (!hda_get_mode_idx(*mode, &idx)) {
+ return MODE_BAD;
+ } else {
+ result = clk_round_rate(hda->clk_pix, target);
+
+ DRM_DEBUG_DRIVER("target rate = %d => available rate = %d\n",
+ target, result);
+
+ if ((result < target_min) || (result > target_max)) {
+ DRM_DEBUG_DRIVER("hda pixclk=%d not supported\n",
+ target);
+ return MODE_BAD;
+ }
+ }
+
+ return MODE_OK;
+}
+
+struct drm_encoder *sti_hda_best_encoder(struct drm_connector *connector)
+{
+ struct sti_hda_connector *hda_connector
+ = to_sti_hda_connector(connector);
+
+ /* Best encoder is the one associated during connector creation */
+ return hda_connector->encoder;
+}
+
+static struct drm_connector_helper_funcs sti_hda_connector_helper_funcs = {
+ .get_modes = sti_hda_connector_get_modes,
+ .mode_valid = sti_hda_connector_mode_valid,
+ .best_encoder = sti_hda_best_encoder,
+};
+
+static enum drm_connector_status
+sti_hda_connector_detect(struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static void sti_hda_connector_destroy(struct drm_connector *connector)
+{
+ struct sti_hda_connector *hda_connector
+ = to_sti_hda_connector(connector);
+
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+ kfree(hda_connector);
+}
+
+static struct drm_connector_funcs sti_hda_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = sti_hda_connector_detect,
+ .destroy = sti_hda_connector_destroy,
+};
+
+static struct drm_encoder *sti_hda_find_encoder(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->encoder_type == DRM_MODE_ENCODER_DAC)
+ return encoder;
+ }
+
+ return NULL;
+}
+
+static int sti_hda_bind(struct device *dev, struct device *master, void *data)
+{
+ struct sti_hda *hda = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
+ struct drm_encoder *encoder;
+ struct sti_hda_connector *connector;
+ struct drm_connector *drm_connector;
+ struct drm_bridge *bridge;
+ int err;
+
+ /* Set the drm device handle */
+ hda->drm_dev = drm_dev;
+
+ encoder = sti_hda_find_encoder(drm_dev);
+ if (!encoder)
+ return -ENOMEM;
+
+ connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
+ if (!connector)
+ return -ENOMEM;
+
+ connector->hda = hda;
+
+ bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->driver_private = hda;
+ drm_bridge_init(drm_dev, bridge, &sti_hda_bridge_funcs);
+
+ encoder->bridge = bridge;
+ connector->encoder = encoder;
+
+ drm_connector = (struct drm_connector *)connector;
+
+ drm_connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ drm_connector_init(drm_dev, drm_connector,
+ &sti_hda_connector_funcs, DRM_MODE_CONNECTOR_Component);
+ drm_connector_helper_add(drm_connector,
+ &sti_hda_connector_helper_funcs);
+
+ err = drm_connector_register(drm_connector);
+ if (err)
+ goto err_connector;
+
+ err = drm_mode_connector_attach_encoder(drm_connector, encoder);
+ if (err) {
+ DRM_ERROR("Failed to attach a connector to a encoder\n");
+ goto err_sysfs;
+ }
+
+ return 0;
+
+err_sysfs:
+ drm_connector_unregister(drm_connector);
+err_connector:
+ drm_bridge_cleanup(bridge);
+ drm_connector_cleanup(drm_connector);
+ return -EINVAL;
+}
+
+static void sti_hda_unbind(struct device *dev,
+ struct device *master, void *data)
+{
+ /* do nothing */
+}
+
+static const struct component_ops sti_hda_ops = {
+ .bind = sti_hda_bind,
+ .unbind = sti_hda_unbind,
+};
+
+static int sti_hda_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sti_hda *hda;
+ struct resource *res;
+
+ DRM_INFO("%s\n", __func__);
+
+ hda = devm_kzalloc(dev, sizeof(*hda), GFP_KERNEL);
+ if (!hda)
+ return -ENOMEM;
+
+ hda->dev = pdev->dev;
+
+ /* Get resources */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hda-reg");
+ if (!res) {
+ DRM_ERROR("Invalid hda resource\n");
+ return -ENOMEM;
+ }
+ hda->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
+ if (!hda->regs)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "video-dacs-ctrl");
+ if (res) {
+ hda->video_dacs_ctrl = devm_ioremap_nocache(dev, res->start,
+ resource_size(res));
+ if (!hda->video_dacs_ctrl)
+ return -ENOMEM;
+ } else {
+ /* If no existing video-dacs-ctrl resource continue the probe */
+ DRM_DEBUG_DRIVER("No video-dacs-ctrl resource\n");
+ hda->video_dacs_ctrl = NULL;
+ }
+
+ /* Get clock resources */
+ hda->clk_pix = devm_clk_get(dev, "pix");
+ if (IS_ERR(hda->clk_pix)) {
+ DRM_ERROR("Cannot get hda_pix clock\n");
+ return PTR_ERR(hda->clk_pix);
+ }
+
+ hda->clk_hddac = devm_clk_get(dev, "hddac");
+ if (IS_ERR(hda->clk_hddac)) {
+ DRM_ERROR("Cannot get hda_hddac clock\n");
+ return PTR_ERR(hda->clk_hddac);
+ }
+
+ platform_set_drvdata(pdev, hda);
+
+ return component_add(&pdev->dev, &sti_hda_ops);
+}
+
+static int sti_hda_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &sti_hda_ops);
+ return 0;
+}
+
+static const struct of_device_id hda_of_match[] = {
+ { .compatible = "st,stih416-hda", },
+ { .compatible = "st,stih407-hda", },
+ { /* end node */ }
+};
+MODULE_DEVICE_TABLE(of, hda_of_match);
+
+struct platform_driver sti_hda_driver = {
+ .driver = {
+ .name = "sti-hda",
+ .owner = THIS_MODULE,
+ .of_match_table = hda_of_match,
+ },
+ .probe = sti_hda_probe,
+ .remove = sti_hda_remove,
+};
+
+module_platform_driver(sti_hda_driver);
+
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
new file mode 100644
index 000000000000..b22968c08d1f
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -0,0 +1,809 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/hdmi.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+
+#include "sti_hdmi.h"
+#include "sti_hdmi_tx3g4c28phy.h"
+#include "sti_hdmi_tx3g0c55phy.h"
+#include "sti_vtg.h"
+
+#define HDMI_CFG 0x0000
+#define HDMI_INT_EN 0x0004
+#define HDMI_INT_STA 0x0008
+#define HDMI_INT_CLR 0x000C
+#define HDMI_STA 0x0010
+#define HDMI_ACTIVE_VID_XMIN 0x0100
+#define HDMI_ACTIVE_VID_XMAX 0x0104
+#define HDMI_ACTIVE_VID_YMIN 0x0108
+#define HDMI_ACTIVE_VID_YMAX 0x010C
+#define HDMI_DFLT_CHL0_DAT 0x0110
+#define HDMI_DFLT_CHL1_DAT 0x0114
+#define HDMI_DFLT_CHL2_DAT 0x0118
+#define HDMI_SW_DI_1_HEAD_WORD 0x0210
+#define HDMI_SW_DI_1_PKT_WORD0 0x0214
+#define HDMI_SW_DI_1_PKT_WORD1 0x0218
+#define HDMI_SW_DI_1_PKT_WORD2 0x021C
+#define HDMI_SW_DI_1_PKT_WORD3 0x0220
+#define HDMI_SW_DI_1_PKT_WORD4 0x0224
+#define HDMI_SW_DI_1_PKT_WORD5 0x0228
+#define HDMI_SW_DI_1_PKT_WORD6 0x022C
+#define HDMI_SW_DI_CFG 0x0230
+
+#define HDMI_IFRAME_SLOT_AVI 1
+
+#define XCAT(prefix, x, suffix) prefix ## x ## suffix
+#define HDMI_SW_DI_N_HEAD_WORD(x) XCAT(HDMI_SW_DI_, x, _HEAD_WORD)
+#define HDMI_SW_DI_N_PKT_WORD0(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD0)
+#define HDMI_SW_DI_N_PKT_WORD1(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD1)
+#define HDMI_SW_DI_N_PKT_WORD2(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD2)
+#define HDMI_SW_DI_N_PKT_WORD3(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD3)
+#define HDMI_SW_DI_N_PKT_WORD4(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD4)
+#define HDMI_SW_DI_N_PKT_WORD5(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD5)
+#define HDMI_SW_DI_N_PKT_WORD6(x) XCAT(HDMI_SW_DI_, x, _PKT_WORD6)
+
+#define HDMI_IFRAME_DISABLED 0x0
+#define HDMI_IFRAME_SINGLE_SHOT 0x1
+#define HDMI_IFRAME_FIELD 0x2
+#define HDMI_IFRAME_FRAME 0x3
+#define HDMI_IFRAME_MASK 0x3
+#define HDMI_IFRAME_CFG_DI_N(x, n) ((x) << ((n-1)*4)) /* n from 1 to 6 */
+
+#define HDMI_CFG_DEVICE_EN BIT(0)
+#define HDMI_CFG_HDMI_NOT_DVI BIT(1)
+#define HDMI_CFG_HDCP_EN BIT(2)
+#define HDMI_CFG_ESS_NOT_OESS BIT(3)
+#define HDMI_CFG_H_SYNC_POL_NEG BIT(4)
+#define HDMI_CFG_SINK_TERM_DET_EN BIT(5)
+#define HDMI_CFG_V_SYNC_POL_NEG BIT(6)
+#define HDMI_CFG_422_EN BIT(8)
+#define HDMI_CFG_FIFO_OVERRUN_CLR BIT(12)
+#define HDMI_CFG_FIFO_UNDERRUN_CLR BIT(13)
+#define HDMI_CFG_SW_RST_EN BIT(31)
+
+#define HDMI_INT_GLOBAL BIT(0)
+#define HDMI_INT_SW_RST BIT(1)
+#define HDMI_INT_PIX_CAP BIT(3)
+#define HDMI_INT_HOT_PLUG BIT(4)
+#define HDMI_INT_DLL_LCK BIT(5)
+#define HDMI_INT_NEW_FRAME BIT(6)
+#define HDMI_INT_GENCTRL_PKT BIT(7)
+#define HDMI_INT_SINK_TERM_PRESENT BIT(11)
+
+#define HDMI_DEFAULT_INT (HDMI_INT_SINK_TERM_PRESENT \
+ | HDMI_INT_DLL_LCK \
+ | HDMI_INT_HOT_PLUG \
+ | HDMI_INT_GLOBAL)
+
+#define HDMI_WORKING_INT (HDMI_INT_SINK_TERM_PRESENT \
+ | HDMI_INT_GENCTRL_PKT \
+ | HDMI_INT_NEW_FRAME \
+ | HDMI_INT_DLL_LCK \
+ | HDMI_INT_HOT_PLUG \
+ | HDMI_INT_PIX_CAP \
+ | HDMI_INT_SW_RST \
+ | HDMI_INT_GLOBAL)
+
+#define HDMI_STA_SW_RST BIT(1)
+
+struct sti_hdmi_connector {
+ struct drm_connector drm_connector;
+ struct drm_encoder *encoder;
+ struct sti_hdmi *hdmi;
+};
+
+#define to_sti_hdmi_connector(x) \
+ container_of(x, struct sti_hdmi_connector, drm_connector)
+
+u32 hdmi_read(struct sti_hdmi *hdmi, int offset)
+{
+ return readl(hdmi->regs + offset);
+}
+
+void hdmi_write(struct sti_hdmi *hdmi, u32 val, int offset)
+{
+ writel(val, hdmi->regs + offset);
+}
+
+/**
+ * HDMI interrupt handler threaded
+ *
+ * @irq: irq number
+ * @arg: connector structure
+ */
+static irqreturn_t hdmi_irq_thread(int irq, void *arg)
+{
+ struct sti_hdmi *hdmi = arg;
+
+ /* Hot plug/unplug IRQ */
+ if (hdmi->irq_status & HDMI_INT_HOT_PLUG) {
+ /* read gpio to get the status */
+ hdmi->hpd = gpio_get_value(hdmi->hpd_gpio);
+ if (hdmi->drm_dev)
+ drm_helper_hpd_irq_event(hdmi->drm_dev);
+ }
+
+ /* Sw reset and PLL lock are exclusive so we can use the same
+ * event to signal them
+ */
+ if (hdmi->irq_status & (HDMI_INT_SW_RST | HDMI_INT_DLL_LCK)) {
+ hdmi->event_received = true;
+ wake_up_interruptible(&hdmi->wait_event);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * HDMI interrupt handler
+ *
+ * @irq: irq number
+ * @arg: connector structure
+ */
+static irqreturn_t hdmi_irq(int irq, void *arg)
+{
+ struct sti_hdmi *hdmi = arg;
+
+ /* read interrupt status */
+ hdmi->irq_status = hdmi_read(hdmi, HDMI_INT_STA);
+
+ /* clear interrupt status */
+ hdmi_write(hdmi, hdmi->irq_status, HDMI_INT_CLR);
+
+ /* force sync bus write */
+ hdmi_read(hdmi, HDMI_INT_STA);
+
+ return IRQ_WAKE_THREAD;
+}
+
+/**
+ * Set hdmi active area depending on the drm display mode selected
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ */
+static void hdmi_active_area(struct sti_hdmi *hdmi)
+{
+ u32 xmin, xmax;
+ u32 ymin, ymax;
+
+ xmin = sti_vtg_get_pixel_number(hdmi->mode, 0);
+ xmax = sti_vtg_get_pixel_number(hdmi->mode, hdmi->mode.hdisplay - 1);
+ ymin = sti_vtg_get_line_number(hdmi->mode, 0);
+ ymax = sti_vtg_get_line_number(hdmi->mode, hdmi->mode.vdisplay - 1);
+
+ hdmi_write(hdmi, xmin, HDMI_ACTIVE_VID_XMIN);
+ hdmi_write(hdmi, xmax, HDMI_ACTIVE_VID_XMAX);
+ hdmi_write(hdmi, ymin, HDMI_ACTIVE_VID_YMIN);
+ hdmi_write(hdmi, ymax, HDMI_ACTIVE_VID_YMAX);
+}
+
+/**
+ * Overall hdmi configuration
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ */
+static void hdmi_config(struct sti_hdmi *hdmi)
+{
+ u32 conf;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ /* Clear overrun and underrun fifo */
+ conf = HDMI_CFG_FIFO_OVERRUN_CLR | HDMI_CFG_FIFO_UNDERRUN_CLR;
+
+ /* Enable HDMI mode not DVI */
+ conf |= HDMI_CFG_HDMI_NOT_DVI | HDMI_CFG_ESS_NOT_OESS;
+
+ /* Enable sink term detection */
+ conf |= HDMI_CFG_SINK_TERM_DET_EN;
+
+ /* Set Hsync polarity */
+ if (hdmi->mode.flags & DRM_MODE_FLAG_NHSYNC) {
+ DRM_DEBUG_DRIVER("H Sync Negative\n");
+ conf |= HDMI_CFG_H_SYNC_POL_NEG;
+ }
+
+ /* Set Vsync polarity */
+ if (hdmi->mode.flags & DRM_MODE_FLAG_NVSYNC) {
+ DRM_DEBUG_DRIVER("V Sync Negative\n");
+ conf |= HDMI_CFG_V_SYNC_POL_NEG;
+ }
+
+ /* Enable HDMI */
+ conf |= HDMI_CFG_DEVICE_EN;
+
+ hdmi_write(hdmi, conf, HDMI_CFG);
+}
+
+/**
+ * Prepare and configure the AVI infoframe
+ *
+ * AVI infoframe are transmitted at least once per two video field and
+ * contains information about HDMI transmission mode such as color space,
+ * colorimetry, ...
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ * Return negative value if error occurs
+ */
+static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)
+{
+ struct drm_display_mode *mode = &hdmi->mode;
+ struct hdmi_avi_infoframe infoframe;
+ u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];
+ u8 *frame = buffer + HDMI_INFOFRAME_HEADER_SIZE;
+ u32 val;
+ int ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ ret = drm_hdmi_avi_infoframe_from_display_mode(&infoframe, mode);
+ if (ret < 0) {
+ DRM_ERROR("failed to setup AVI infoframe: %d\n", ret);
+ return ret;
+ }
+
+ /* fixed infoframe configuration not linked to the mode */
+ infoframe.colorspace = HDMI_COLORSPACE_RGB;
+ infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
+ infoframe.colorimetry = HDMI_COLORIMETRY_NONE;
+
+ ret = hdmi_avi_infoframe_pack(&infoframe, buffer, sizeof(buffer));
+ if (ret < 0) {
+ DRM_ERROR("failed to pack AVI infoframe: %d\n", ret);
+ return ret;
+ }
+
+ /* Disable transmission slot for AVI infoframe */
+ val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
+ val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, HDMI_IFRAME_SLOT_AVI);
+ hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
+
+ /* Infoframe header */
+ val = buffer[0x0];
+ val |= buffer[0x1] << 8;
+ val |= buffer[0x2] << 16;
+ hdmi_write(hdmi, val, HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI));
+
+ /* Infoframe packet bytes */
+ val = frame[0x0];
+ val |= frame[0x1] << 8;
+ val |= frame[0x2] << 16;
+ val |= frame[0x3] << 24;
+ hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI));
+
+ val = frame[0x4];
+ val |= frame[0x5] << 8;
+ val |= frame[0x6] << 16;
+ val |= frame[0x7] << 24;
+ hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD1(HDMI_IFRAME_SLOT_AVI));
+
+ val = frame[0x8];
+ val |= frame[0x9] << 8;
+ val |= frame[0xA] << 16;
+ val |= frame[0xB] << 24;
+ hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD2(HDMI_IFRAME_SLOT_AVI));
+
+ val = frame[0xC];
+ hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD3(HDMI_IFRAME_SLOT_AVI));
+
+ /* Enable transmission slot for AVI infoframe
+ * According to the hdmi specification, AVI infoframe should be
+ * transmitted at least once per two video fields
+ */
+ val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
+ val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, HDMI_IFRAME_SLOT_AVI);
+ hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
+
+ return 0;
+}
+
+/**
+ * Software reset of the hdmi subsystem
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ */
+#define HDMI_TIMEOUT_SWRESET 100 /*milliseconds */
+static void hdmi_swreset(struct sti_hdmi *hdmi)
+{
+ u32 val;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ /* Enable hdmi_audio clock only during hdmi reset */
+ if (clk_prepare_enable(hdmi->clk_audio))
+ DRM_INFO("Failed to prepare/enable hdmi_audio clk\n");
+
+ /* Sw reset */
+ hdmi->event_received = false;
+
+ val = hdmi_read(hdmi, HDMI_CFG);
+ val |= HDMI_CFG_SW_RST_EN;
+ hdmi_write(hdmi, val, HDMI_CFG);
+
+ /* Wait reset completed */
+ wait_event_interruptible_timeout(hdmi->wait_event,
+ hdmi->event_received == true,
+ msecs_to_jiffies
+ (HDMI_TIMEOUT_SWRESET));
+
+ /*
+ * HDMI_STA_SW_RST bit is set to '1' when SW_RST bit in HDMI_CFG is
+ * set to '1' and clk_audio is running.
+ */
+ if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_SW_RST) == 0)
+ DRM_DEBUG_DRIVER("Warning: HDMI sw reset timeout occurs\n");
+
+ val = hdmi_read(hdmi, HDMI_CFG);
+ val &= ~HDMI_CFG_SW_RST_EN;
+ hdmi_write(hdmi, val, HDMI_CFG);
+
+ /* Disable hdmi_audio clock. Not used anymore for drm purpose */
+ clk_disable_unprepare(hdmi->clk_audio);
+}
+
+static void sti_hdmi_disable(struct drm_bridge *bridge)
+{
+ struct sti_hdmi *hdmi = bridge->driver_private;
+
+ u32 val = hdmi_read(hdmi, HDMI_CFG);
+
+ if (!hdmi->enabled)
+ return;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ /* Disable HDMI */
+ val &= ~HDMI_CFG_DEVICE_EN;
+ hdmi_write(hdmi, val, HDMI_CFG);
+
+ hdmi_write(hdmi, 0xffffffff, HDMI_INT_CLR);
+
+ /* Stop the phy */
+ hdmi->phy_ops->stop(hdmi);
+
+ /* Set the default channel data to be a dark red */
+ hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL0_DAT);
+ hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL1_DAT);
+ hdmi_write(hdmi, 0x0060, HDMI_DFLT_CHL2_DAT);
+
+ /* Disable/unprepare hdmi clock */
+ clk_disable_unprepare(hdmi->clk_phy);
+ clk_disable_unprepare(hdmi->clk_tmds);
+ clk_disable_unprepare(hdmi->clk_pix);
+
+ hdmi->enabled = false;
+}
+
+static void sti_hdmi_pre_enable(struct drm_bridge *bridge)
+{
+ struct sti_hdmi *hdmi = bridge->driver_private;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ if (hdmi->enabled)
+ return;
+
+ /* Prepare/enable clocks */
+ if (clk_prepare_enable(hdmi->clk_pix))
+ DRM_ERROR("Failed to prepare/enable hdmi_pix clk\n");
+ if (clk_prepare_enable(hdmi->clk_tmds))
+ DRM_ERROR("Failed to prepare/enable hdmi_tmds clk\n");
+ if (clk_prepare_enable(hdmi->clk_phy))
+ DRM_ERROR("Failed to prepare/enable hdmi_rejec_pll clk\n");
+
+ hdmi->enabled = true;
+
+ /* Program hdmi serializer and start phy */
+ if (!hdmi->phy_ops->start(hdmi)) {
+ DRM_ERROR("Unable to start hdmi phy\n");
+ return;
+ }
+
+ /* Program hdmi active area */
+ hdmi_active_area(hdmi);
+
+ /* Enable working interrupts */
+ hdmi_write(hdmi, HDMI_WORKING_INT, HDMI_INT_EN);
+
+ /* Program hdmi config */
+ hdmi_config(hdmi);
+
+ /* Program AVI infoframe */
+ if (hdmi_avi_infoframe_config(hdmi))
+ DRM_ERROR("Unable to configure AVI infoframe\n");
+
+ /* Sw reset */
+ hdmi_swreset(hdmi);
+}
+
+static void sti_hdmi_set_mode(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct sti_hdmi *hdmi = bridge->driver_private;
+ int ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ /* Copy the drm display mode in the connector local structure */
+ memcpy(&hdmi->mode, mode, sizeof(struct drm_display_mode));
+
+ /* Update clock framerate according to the selected mode */
+ ret = clk_set_rate(hdmi->clk_pix, mode->clock * 1000);
+ if (ret < 0) {
+ DRM_ERROR("Cannot set rate (%dHz) for hdmi_pix clk\n",
+ mode->clock * 1000);
+ return;
+ }
+ ret = clk_set_rate(hdmi->clk_phy, mode->clock * 1000);
+ if (ret < 0) {
+ DRM_ERROR("Cannot set rate (%dHz) for hdmi_rejection_pll clk\n",
+ mode->clock * 1000);
+ return;
+ }
+}
+
+static void sti_hdmi_bridge_nope(struct drm_bridge *bridge)
+{
+ /* do nothing */
+}
+
+static void sti_hdmi_brigde_destroy(struct drm_bridge *bridge)
+{
+ drm_bridge_cleanup(bridge);
+ kfree(bridge);
+}
+
+static const struct drm_bridge_funcs sti_hdmi_bridge_funcs = {
+ .pre_enable = sti_hdmi_pre_enable,
+ .enable = sti_hdmi_bridge_nope,
+ .disable = sti_hdmi_disable,
+ .post_disable = sti_hdmi_bridge_nope,
+ .mode_set = sti_hdmi_set_mode,
+ .destroy = sti_hdmi_brigde_destroy,
+};
+
+static int sti_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+ struct i2c_adapter *i2c_adap;
+ struct edid *edid;
+ int count;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ i2c_adap = i2c_get_adapter(1);
+ if (!i2c_adap)
+ goto fail;
+
+ edid = drm_get_edid(connector, i2c_adap);
+ if (!edid)
+ goto fail;
+
+ count = drm_add_edid_modes(connector, edid);
+ drm_mode_connector_update_edid_property(connector, edid);
+
+ kfree(edid);
+ return count;
+
+fail:
+ DRM_ERROR("Can not read HDMI EDID\n");
+ return 0;
+}
+
+#define CLK_TOLERANCE_HZ 50
+
+static int sti_hdmi_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ int target = mode->clock * 1000;
+ int target_min = target - CLK_TOLERANCE_HZ;
+ int target_max = target + CLK_TOLERANCE_HZ;
+ int result;
+ struct sti_hdmi_connector *hdmi_connector
+ = to_sti_hdmi_connector(connector);
+ struct sti_hdmi *hdmi = hdmi_connector->hdmi;
+
+
+ result = clk_round_rate(hdmi->clk_pix, target);
+
+ DRM_DEBUG_DRIVER("target rate = %d => available rate = %d\n",
+ target, result);
+
+ if ((result < target_min) || (result > target_max)) {
+ DRM_DEBUG_DRIVER("hdmi pixclk=%d not supported\n", target);
+ return MODE_BAD;
+ }
+
+ return MODE_OK;
+}
+
+struct drm_encoder *sti_hdmi_best_encoder(struct drm_connector *connector)
+{
+ struct sti_hdmi_connector *hdmi_connector
+ = to_sti_hdmi_connector(connector);
+
+ /* Best encoder is the one associated during connector creation */
+ return hdmi_connector->encoder;
+}
+
+static struct drm_connector_helper_funcs sti_hdmi_connector_helper_funcs = {
+ .get_modes = sti_hdmi_connector_get_modes,
+ .mode_valid = sti_hdmi_connector_mode_valid,
+ .best_encoder = sti_hdmi_best_encoder,
+};
+
+/* get detection status of display device */
+static enum drm_connector_status
+sti_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct sti_hdmi_connector *hdmi_connector
+ = to_sti_hdmi_connector(connector);
+ struct sti_hdmi *hdmi = hdmi_connector->hdmi;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ if (hdmi->hpd) {
+ DRM_DEBUG_DRIVER("hdmi cable connected\n");
+ return connector_status_connected;
+ }
+
+ DRM_DEBUG_DRIVER("hdmi cable disconnected\n");
+ return connector_status_disconnected;
+}
+
+static void sti_hdmi_connector_destroy(struct drm_connector *connector)
+{
+ struct sti_hdmi_connector *hdmi_connector
+ = to_sti_hdmi_connector(connector);
+
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+ kfree(hdmi_connector);
+}
+
+static struct drm_connector_funcs sti_hdmi_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = sti_hdmi_connector_detect,
+ .destroy = sti_hdmi_connector_destroy,
+};
+
+static struct drm_encoder *sti_hdmi_find_encoder(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
+ return encoder;
+ }
+
+ return NULL;
+}
+
+static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+ struct sti_hdmi *hdmi = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
+ struct drm_encoder *encoder;
+ struct sti_hdmi_connector *connector;
+ struct drm_connector *drm_connector;
+ struct drm_bridge *bridge;
+ struct i2c_adapter *i2c_adap;
+ int err;
+
+ i2c_adap = i2c_get_adapter(1);
+ if (!i2c_adap)
+ return -EPROBE_DEFER;
+
+ /* Set the drm device handle */
+ hdmi->drm_dev = drm_dev;
+
+ encoder = sti_hdmi_find_encoder(drm_dev);
+ if (!encoder)
+ return -ENOMEM;
+
+ connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
+ if (!connector)
+ return -ENOMEM;
+
+ connector->hdmi = hdmi;
+
+ bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->driver_private = hdmi;
+ drm_bridge_init(drm_dev, bridge, &sti_hdmi_bridge_funcs);
+
+ encoder->bridge = bridge;
+ connector->encoder = encoder;
+
+ drm_connector = (struct drm_connector *)connector;
+
+ drm_connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ drm_connector_init(drm_dev, drm_connector,
+ &sti_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
+ drm_connector_helper_add(drm_connector,
+ &sti_hdmi_connector_helper_funcs);
+
+ err = drm_connector_register(drm_connector);
+ if (err)
+ goto err_connector;
+
+ err = drm_mode_connector_attach_encoder(drm_connector, encoder);
+ if (err) {
+ DRM_ERROR("Failed to attach a connector to a encoder\n");
+ goto err_sysfs;
+ }
+
+ /* Enable default interrupts */
+ hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN);
+
+ return 0;
+
+err_sysfs:
+ drm_connector_unregister(drm_connector);
+err_connector:
+ drm_bridge_cleanup(bridge);
+ drm_connector_cleanup(drm_connector);
+ return -EINVAL;
+}
+
+static void sti_hdmi_unbind(struct device *dev,
+ struct device *master, void *data)
+{
+ /* do nothing */
+}
+
+static const struct component_ops sti_hdmi_ops = {
+ .bind = sti_hdmi_bind,
+ .unbind = sti_hdmi_unbind,
+};
+
+static const struct of_device_id hdmi_of_match[] = {
+ {
+ .compatible = "st,stih416-hdmi",
+ .data = &tx3g0c55phy_ops,
+ }, {
+ .compatible = "st,stih407-hdmi",
+ .data = &tx3g4c28phy_ops,
+ }, {
+ /* end node */
+ }
+};
+MODULE_DEVICE_TABLE(of, hdmi_of_match);
+
+static int sti_hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sti_hdmi *hdmi;
+ struct device_node *np = dev->of_node;
+ struct resource *res;
+ int ret;
+
+ DRM_INFO("%s\n", __func__);
+
+ hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi)
+ return -ENOMEM;
+
+ hdmi->dev = pdev->dev;
+
+ /* Get resources */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi-reg");
+ if (!res) {
+ DRM_ERROR("Invalid hdmi resource\n");
+ return -ENOMEM;
+ }
+ hdmi->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
+ if (!hdmi->regs)
+ return -ENOMEM;
+
+ if (of_device_is_compatible(np, "st,stih416-hdmi")) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "syscfg");
+ if (!res) {
+ DRM_ERROR("Invalid syscfg resource\n");
+ return -ENOMEM;
+ }
+ hdmi->syscfg = devm_ioremap_nocache(dev, res->start,
+ resource_size(res));
+ if (!hdmi->syscfg)
+ return -ENOMEM;
+
+ }
+
+ hdmi->phy_ops = (struct hdmi_phy_ops *)
+ of_match_node(hdmi_of_match, np)->data;
+
+ /* Get clock resources */
+ hdmi->clk_pix = devm_clk_get(dev, "pix");
+ if (IS_ERR(hdmi->clk_pix)) {
+ DRM_ERROR("Cannot get hdmi_pix clock\n");
+ return PTR_ERR(hdmi->clk_pix);
+ }
+
+ hdmi->clk_tmds = devm_clk_get(dev, "tmds");
+ if (IS_ERR(hdmi->clk_tmds)) {
+ DRM_ERROR("Cannot get hdmi_tmds clock\n");
+ return PTR_ERR(hdmi->clk_tmds);
+ }
+
+ hdmi->clk_phy = devm_clk_get(dev, "phy");
+ if (IS_ERR(hdmi->clk_phy)) {
+ DRM_ERROR("Cannot get hdmi_phy clock\n");
+ return PTR_ERR(hdmi->clk_phy);
+ }
+
+ hdmi->clk_audio = devm_clk_get(dev, "audio");
+ if (IS_ERR(hdmi->clk_audio)) {
+ DRM_ERROR("Cannot get hdmi_audio clock\n");
+ return PTR_ERR(hdmi->clk_audio);
+ }
+
+ hdmi->hpd_gpio = of_get_named_gpio(np, "hdmi,hpd-gpio", 0);
+ if (hdmi->hpd_gpio < 0) {
+ DRM_ERROR("Failed to get hdmi hpd-gpio\n");
+ return -EIO;
+ }
+
+ hdmi->hpd = gpio_get_value(hdmi->hpd_gpio);
+
+ init_waitqueue_head(&hdmi->wait_event);
+
+ hdmi->irq = platform_get_irq_byname(pdev, "irq");
+
+ ret = devm_request_threaded_irq(dev, hdmi->irq, hdmi_irq,
+ hdmi_irq_thread, IRQF_ONESHOT, dev_name(dev), hdmi);
+ if (ret) {
+ DRM_ERROR("Failed to register HDMI interrupt\n");
+ return ret;
+ }
+
+ hdmi->reset = devm_reset_control_get(dev, "hdmi");
+ /* Take hdmi out of reset */
+ if (!IS_ERR(hdmi->reset))
+ reset_control_deassert(hdmi->reset);
+
+ platform_set_drvdata(pdev, hdmi);
+
+ return component_add(&pdev->dev, &sti_hdmi_ops);
+}
+
+static int sti_hdmi_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &sti_hdmi_ops);
+ return 0;
+}
+
+struct platform_driver sti_hdmi_driver = {
+ .driver = {
+ .name = "sti-hdmi",
+ .owner = THIS_MODULE,
+ .of_match_table = hdmi_of_match,
+ },
+ .probe = sti_hdmi_probe,
+ .remove = sti_hdmi_remove,
+};
+
+module_platform_driver(sti_hdmi_driver);
+
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sti/sti_hdmi.h b/drivers/gpu/drm/sti/sti_hdmi.h
new file mode 100644
index 000000000000..61bec6557ceb
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_HDMI_H_
+#define _STI_HDMI_H_
+
+#include <linux/platform_device.h>
+
+#include <drm/drmP.h>
+
+#define HDMI_STA 0x0010
+#define HDMI_STA_DLL_LCK BIT(5)
+
+struct sti_hdmi;
+
+struct hdmi_phy_ops {
+ bool (*start)(struct sti_hdmi *hdmi);
+ void (*stop)(struct sti_hdmi *hdmi);
+};
+
+/**
+ * STI hdmi structure
+ *
+ * @dev: driver device
+ * @drm_dev: pointer to drm device
+ * @mode: current display mode selected
+ * @regs: hdmi register
+ * @syscfg: syscfg register for pll rejection configuration
+ * @clk_pix: hdmi pixel clock
+ * @clk_tmds: hdmi tmds clock
+ * @clk_phy: hdmi phy clock
+ * @clk_audio: hdmi audio clock
+ * @irq: hdmi interrupt number
+ * @irq_status: interrupt status register
+ * @phy_ops: phy start/stop operations
+ * @enabled: true if hdmi is enabled else false
+ * @hpd_gpio: hdmi hot plug detect gpio number
+ * @hpd: hot plug detect status
+ * @wait_event: wait event
+ * @event_received: wait event status
+ * @reset: reset control of the hdmi phy
+ */
+struct sti_hdmi {
+ struct device dev;
+ struct drm_device *drm_dev;
+ struct drm_display_mode mode;
+ void __iomem *regs;
+ void __iomem *syscfg;
+ struct clk *clk_pix;
+ struct clk *clk_tmds;
+ struct clk *clk_phy;
+ struct clk *clk_audio;
+ int irq;
+ u32 irq_status;
+ struct hdmi_phy_ops *phy_ops;
+ bool enabled;
+ int hpd_gpio;
+ bool hpd;
+ wait_queue_head_t wait_event;
+ bool event_received;
+ struct reset_control *reset;
+};
+
+u32 hdmi_read(struct sti_hdmi *hdmi, int offset);
+void hdmi_write(struct sti_hdmi *hdmi, u32 val, int offset);
+
+/**
+ * hdmi phy config structure
+ *
+ * A pointer to an array of these structures is passed to a TMDS (HDMI) output
+ * via the control interface to provide board and SoC specific
+ * configurations of the HDMI PHY. Each entry in the array specifies a hardware
+ * specific configuration for a given TMDS clock frequency range.
+ *
+ * @min_tmds_freq: Lower bound of TMDS clock frequency this entry applies to
+ * @max_tmds_freq: Upper bound of TMDS clock frequency this entry applies to
+ * @config: SoC specific register configuration
+ */
+struct hdmi_phy_config {
+ u32 min_tmds_freq;
+ u32 max_tmds_freq;
+ u32 config[4];
+};
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c
new file mode 100644
index 000000000000..49ae8e44b285
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include "sti_hdmi_tx3g0c55phy.h"
+
+#define HDMI_SRZ_PLL_CFG 0x0504
+#define HDMI_SRZ_TAP_1 0x0508
+#define HDMI_SRZ_TAP_2 0x050C
+#define HDMI_SRZ_TAP_3 0x0510
+#define HDMI_SRZ_CTRL 0x0514
+
+#define HDMI_SRZ_PLL_CFG_POWER_DOWN BIT(0)
+#define HDMI_SRZ_PLL_CFG_VCOR_SHIFT 1
+#define HDMI_SRZ_PLL_CFG_VCOR_425MHZ 0
+#define HDMI_SRZ_PLL_CFG_VCOR_850MHZ 1
+#define HDMI_SRZ_PLL_CFG_VCOR_1700MHZ 2
+#define HDMI_SRZ_PLL_CFG_VCOR_3000MHZ 3
+#define HDMI_SRZ_PLL_CFG_VCOR_MASK 3
+#define HDMI_SRZ_PLL_CFG_VCOR(x) (x << HDMI_SRZ_PLL_CFG_VCOR_SHIFT)
+#define HDMI_SRZ_PLL_CFG_NDIV_SHIFT 8
+#define HDMI_SRZ_PLL_CFG_NDIV_MASK (0x1F << HDMI_SRZ_PLL_CFG_NDIV_SHIFT)
+#define HDMI_SRZ_PLL_CFG_MODE_SHIFT 16
+#define HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ 0x1
+#define HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ 0x4
+#define HDMI_SRZ_PLL_CFG_MODE_27_MHZ 0x5
+#define HDMI_SRZ_PLL_CFG_MODE_33_75_MHZ 0x6
+#define HDMI_SRZ_PLL_CFG_MODE_40_5_MHZ 0x7
+#define HDMI_SRZ_PLL_CFG_MODE_54_MHZ 0x8
+#define HDMI_SRZ_PLL_CFG_MODE_67_5_MHZ 0x9
+#define HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ 0xA
+#define HDMI_SRZ_PLL_CFG_MODE_81_MHZ 0xB
+#define HDMI_SRZ_PLL_CFG_MODE_82_5_MHZ 0xC
+#define HDMI_SRZ_PLL_CFG_MODE_108_MHZ 0xD
+#define HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ 0xE
+#define HDMI_SRZ_PLL_CFG_MODE_165_MHZ 0xF
+#define HDMI_SRZ_PLL_CFG_MODE_MASK 0xF
+#define HDMI_SRZ_PLL_CFG_MODE(x) (x << HDMI_SRZ_PLL_CFG_MODE_SHIFT)
+
+#define HDMI_SRZ_CTRL_POWER_DOWN (1 << 0)
+#define HDMI_SRZ_CTRL_EXTERNAL_DATA_EN (1 << 1)
+
+/* sysconf registers */
+#define HDMI_REJECTION_PLL_CONFIGURATION 0x0858 /* SYSTEM_CONFIG2534 */
+#define HDMI_REJECTION_PLL_STATUS 0x0948 /* SYSTEM_CONFIG2594 */
+
+#define REJECTION_PLL_HDMI_ENABLE_SHIFT 0
+#define REJECTION_PLL_HDMI_ENABLE_MASK (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT)
+#define REJECTION_PLL_HDMI_PDIV_SHIFT 24
+#define REJECTION_PLL_HDMI_PDIV_MASK (0x7 << REJECTION_PLL_HDMI_PDIV_SHIFT)
+#define REJECTION_PLL_HDMI_NDIV_SHIFT 16
+#define REJECTION_PLL_HDMI_NDIV_MASK (0xFF << REJECTION_PLL_HDMI_NDIV_SHIFT)
+#define REJECTION_PLL_HDMI_MDIV_SHIFT 8
+#define REJECTION_PLL_HDMI_MDIV_MASK (0xFF << REJECTION_PLL_HDMI_MDIV_SHIFT)
+
+#define REJECTION_PLL_HDMI_REJ_PLL_LOCK BIT(0)
+
+#define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */
+
+/**
+ * pll mode structure
+ *
+ * A pointer to an array of these structures is passed to a TMDS (HDMI) output
+ * via the control interface to provide board and SoC specific
+ * configurations of the HDMI PHY. Each entry in the array specifies a hardware
+ * specific configuration for a given TMDS clock frequency range. The array
+ * should be terminated with an entry that has all fields set to zero.
+ *
+ * @min: Lower bound of TMDS clock frequency this entry applies to
+ * @max: Upper bound of TMDS clock frequency this entry applies to
+ * @mode: SoC specific register configuration
+ */
+struct pllmode {
+ u32 min;
+ u32 max;
+ u32 mode;
+};
+
+#define NB_PLL_MODE 7
+static struct pllmode pllmodes[NB_PLL_MODE] = {
+ {13500000, 13513500, HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ},
+ {25174800, 25200000, HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ},
+ {27000000, 27027000, HDMI_SRZ_PLL_CFG_MODE_27_MHZ},
+ {54000000, 54054000, HDMI_SRZ_PLL_CFG_MODE_54_MHZ},
+ {72000000, 74250000, HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ},
+ {108000000, 108108000, HDMI_SRZ_PLL_CFG_MODE_108_MHZ},
+ {148351648, 297000000, HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ}
+};
+
+#define NB_HDMI_PHY_CONFIG 5
+static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
+ {0, 40000000, {0x00101010, 0x00101010, 0x00101010, 0x02} },
+ {40000000, 140000000, {0x00111111, 0x00111111, 0x00111111, 0x02} },
+ {140000000, 160000000, {0x00131313, 0x00101010, 0x00101010, 0x02} },
+ {160000000, 250000000, {0x00131313, 0x00111111, 0x00111111, 0x03FE} },
+ {250000000, 300000000, {0x00151515, 0x00101010, 0x00101010, 0x03FE} },
+};
+
+#define PLL_CHANGE_DELAY 1 /* ms */
+
+/**
+ * Disable the pll rejection
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ * return true if the pll has been disabled
+ */
+static bool disable_pll_rejection(struct sti_hdmi *hdmi)
+{
+ u32 val;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
+ val &= ~REJECTION_PLL_HDMI_ENABLE_MASK;
+ writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
+
+ msleep(PLL_CHANGE_DELAY);
+ val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
+
+ return !(val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
+}
+
+/**
+ * Enable the old BCH/rejection PLL is now reused to provide the CLKPXPLL
+ * clock input to the new PHY PLL that generates the serializer clock
+ * (TMDS*10) and the TMDS clock which is now fed back into the HDMI
+ * formatter instead of the TMDS clock line from ClockGenB.
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ * return true if pll has been correctly set
+ */
+static bool enable_pll_rejection(struct sti_hdmi *hdmi)
+{
+ unsigned int inputclock;
+ u32 mdiv, ndiv, pdiv, val;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ if (!disable_pll_rejection(hdmi))
+ return false;
+
+ inputclock = hdmi->mode.clock * 1000;
+
+ DRM_DEBUG_DRIVER("hdmi rejection pll input clock = %dHz\n", inputclock);
+
+
+ /* Power up the HDMI rejection PLL
+ * Note: On this SoC (stiH416) we are forced to have the input clock
+ * be equal to the HDMI pixel clock.
+ *
+ * The values here have been suggested by validation however they are
+ * still provisional and subject to change.
+ *
+ * PLLout = (Fin*Mdiv) / ((2 * Ndiv) / 2^Pdiv)
+ */
+ if (inputclock < 50000000) {
+ /*
+ * For slower clocks we need to multiply more to keep the
+ * internal VCO frequency within the physical specification
+ * of the PLL.
+ */
+ pdiv = 4;
+ ndiv = 240;
+ mdiv = 30;
+ } else {
+ pdiv = 2;
+ ndiv = 60;
+ mdiv = 30;
+ }
+
+ val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
+
+ val &= ~(REJECTION_PLL_HDMI_PDIV_MASK |
+ REJECTION_PLL_HDMI_NDIV_MASK |
+ REJECTION_PLL_HDMI_MDIV_MASK |
+ REJECTION_PLL_HDMI_ENABLE_MASK);
+
+ val |= (pdiv << REJECTION_PLL_HDMI_PDIV_SHIFT) |
+ (ndiv << REJECTION_PLL_HDMI_NDIV_SHIFT) |
+ (mdiv << REJECTION_PLL_HDMI_MDIV_SHIFT) |
+ (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT);
+
+ writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
+
+ msleep(PLL_CHANGE_DELAY);
+ val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
+
+ return (val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
+}
+
+/**
+ * Start hdmi phy macro cell tx3g0c55
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ * Return false if an error occur
+ */
+static bool sti_hdmi_tx3g0c55phy_start(struct sti_hdmi *hdmi)
+{
+ u32 ckpxpll = hdmi->mode.clock * 1000;
+ u32 val, tmdsck, freqvco, pllctrl = 0;
+ unsigned int i;
+
+ if (!enable_pll_rejection(hdmi))
+ return false;
+
+ DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
+
+ /* Assuming no pixel repetition and 24bits color */
+ tmdsck = ckpxpll;
+ pllctrl = 2 << HDMI_SRZ_PLL_CFG_NDIV_SHIFT;
+
+ /*
+ * Setup the PLL mode parameter based on the ckpxpll. If we haven't got
+ * a clock frequency supported by one of the specific PLL modes then we
+ * will end up using the generic mode (0) which only supports a 10x
+ * multiplier, hence only 24bit color.
+ */
+ for (i = 0; i < NB_PLL_MODE; i++) {
+ if (ckpxpll >= pllmodes[i].min && ckpxpll <= pllmodes[i].max)
+ pllctrl |= HDMI_SRZ_PLL_CFG_MODE(pllmodes[i].mode);
+ }
+
+ freqvco = tmdsck * 10;
+ if (freqvco <= 425000000UL)
+ pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_425MHZ);
+ else if (freqvco <= 850000000UL)
+ pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_850MHZ);
+ else if (freqvco <= 1700000000UL)
+ pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_1700MHZ);
+ else if (freqvco <= 2970000000UL)
+ pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_3000MHZ);
+ else {
+ DRM_ERROR("PHY serializer clock out of range\n");
+ goto err;
+ }
+
+ /*
+ * Configure and power up the PHY PLL
+ */
+ hdmi->event_received = false;
+ DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
+ hdmi_write(hdmi, pllctrl, HDMI_SRZ_PLL_CFG);
+
+ /* wait PLL interrupt */
+ wait_event_interruptible_timeout(hdmi->wait_event,
+ hdmi->event_received == true,
+ msecs_to_jiffies
+ (HDMI_TIMEOUT_PLL_LOCK));
+
+ if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
+ DRM_ERROR("hdmi phy pll not locked\n");
+ goto err;
+ }
+
+ DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
+
+ /*
+ * To configure the source termination and pre-emphasis appropriately
+ * for different high speed TMDS clock frequencies a phy configuration
+ * table must be provided, tailored to the SoC and board combination.
+ */
+ for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) {
+ if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
+ (hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
+ val = hdmiphy_config[i].config[0];
+ hdmi_write(hdmi, val, HDMI_SRZ_TAP_1);
+ val = hdmiphy_config[i].config[1];
+ hdmi_write(hdmi, val, HDMI_SRZ_TAP_2);
+ val = hdmiphy_config[i].config[2];
+ hdmi_write(hdmi, val, HDMI_SRZ_TAP_3);
+ val = hdmiphy_config[i].config[3];
+ val |= HDMI_SRZ_CTRL_EXTERNAL_DATA_EN;
+ val &= ~HDMI_SRZ_CTRL_POWER_DOWN;
+ hdmi_write(hdmi, val, HDMI_SRZ_CTRL);
+
+ DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x 0x%x\n",
+ hdmiphy_config[i].config[0],
+ hdmiphy_config[i].config[1],
+ hdmiphy_config[i].config[2],
+ hdmiphy_config[i].config[3]);
+ return true;
+ }
+ }
+
+ /*
+ * Default, power up the serializer with no pre-emphasis or source
+ * termination.
+ */
+ hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_1);
+ hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_2);
+ hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_3);
+ hdmi_write(hdmi, HDMI_SRZ_CTRL_EXTERNAL_DATA_EN, HDMI_SRZ_CTRL);
+
+ return true;
+
+err:
+ disable_pll_rejection(hdmi);
+
+ return false;
+}
+
+/**
+ * Stop hdmi phy macro cell tx3g0c55
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ */
+static void sti_hdmi_tx3g0c55phy_stop(struct sti_hdmi *hdmi)
+{
+ DRM_DEBUG_DRIVER("\n");
+
+ hdmi->event_received = false;
+
+ hdmi_write(hdmi, HDMI_SRZ_CTRL_POWER_DOWN, HDMI_SRZ_CTRL);
+ hdmi_write(hdmi, HDMI_SRZ_PLL_CFG_POWER_DOWN, HDMI_SRZ_PLL_CFG);
+
+ /* wait PLL interrupt */
+ wait_event_interruptible_timeout(hdmi->wait_event,
+ hdmi->event_received == true,
+ msecs_to_jiffies
+ (HDMI_TIMEOUT_PLL_LOCK));
+
+ if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
+ DRM_ERROR("hdmi phy pll not well disabled\n");
+
+ disable_pll_rejection(hdmi);
+}
+
+struct hdmi_phy_ops tx3g0c55phy_ops = {
+ .start = sti_hdmi_tx3g0c55phy_start,
+ .stop = sti_hdmi_tx3g0c55phy_stop,
+};
diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h
new file mode 100644
index 000000000000..068237b3a303
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_HDMI_TX3G0C55PHY_H_
+#define _STI_HDMI_TX3G0C55PHY_H_
+
+#include "sti_hdmi.h"
+
+extern struct hdmi_phy_ops tx3g0c55phy_ops;
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c
new file mode 100644
index 000000000000..8e0ceb0ced33
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include "sti_hdmi_tx3g4c28phy.h"
+
+#define HDMI_SRZ_CFG 0x504
+#define HDMI_SRZ_PLL_CFG 0x510
+#define HDMI_SRZ_ICNTL 0x518
+#define HDMI_SRZ_CALCODE_EXT 0x520
+
+#define HDMI_SRZ_CFG_EN BIT(0)
+#define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1)
+#define HDMI_SRZ_CFG_EXTERNAL_DATA BIT(16)
+#define HDMI_SRZ_CFG_RBIAS_EXT BIT(17)
+#define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION BIT(18)
+#define HDMI_SRZ_CFG_EN_BIASRES_DETECTION BIT(19)
+#define HDMI_SRZ_CFG_EN_SRC_TERMINATION BIT(24)
+
+#define HDMI_SRZ_CFG_INTERNAL_MASK (HDMI_SRZ_CFG_EN | \
+ HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \
+ HDMI_SRZ_CFG_EXTERNAL_DATA | \
+ HDMI_SRZ_CFG_RBIAS_EXT | \
+ HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION | \
+ HDMI_SRZ_CFG_EN_BIASRES_DETECTION | \
+ HDMI_SRZ_CFG_EN_SRC_TERMINATION)
+
+#define PLL_CFG_EN BIT(0)
+#define PLL_CFG_NDIV_SHIFT (8)
+#define PLL_CFG_IDF_SHIFT (16)
+#define PLL_CFG_ODF_SHIFT (24)
+
+#define ODF_DIV_1 (0)
+#define ODF_DIV_2 (1)
+#define ODF_DIV_4 (2)
+#define ODF_DIV_8 (3)
+
+#define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */
+
+struct plldividers_s {
+ uint32_t min;
+ uint32_t max;
+ uint32_t idf;
+ uint32_t odf;
+};
+
+/*
+ * Functional specification recommended values
+ */
+#define NB_PLL_MODE 5
+static struct plldividers_s plldividers[NB_PLL_MODE] = {
+ {0, 20000000, 1, ODF_DIV_8},
+ {20000000, 42500000, 2, ODF_DIV_8},
+ {42500000, 85000000, 4, ODF_DIV_4},
+ {85000000, 170000000, 8, ODF_DIV_2},
+ {170000000, 340000000, 16, ODF_DIV_1}
+};
+
+#define NB_HDMI_PHY_CONFIG 2
+static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
+ {0, 250000000, {0x0, 0x0, 0x0, 0x0} },
+ {250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} },
+};
+
+/**
+ * Start hdmi phy macro cell tx3g4c28
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ * Return false if an error occur
+ */
+static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi)
+{
+ u32 ckpxpll = hdmi->mode.clock * 1000;
+ u32 val, tmdsck, idf, odf, pllctrl = 0;
+ bool foundplldivides = false;
+ int i;
+
+ DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
+
+ for (i = 0; i < NB_PLL_MODE; i++) {
+ if (ckpxpll >= plldividers[i].min &&
+ ckpxpll < plldividers[i].max) {
+ idf = plldividers[i].idf;
+ odf = plldividers[i].odf;
+ foundplldivides = true;
+ break;
+ }
+ }
+
+ if (!foundplldivides) {
+ DRM_ERROR("input TMDS clock speed (%d) not supported\n",
+ ckpxpll);
+ goto err;
+ }
+
+ /* Assuming no pixel repetition and 24bits color */
+ tmdsck = ckpxpll;
+ pllctrl |= 40 << PLL_CFG_NDIV_SHIFT;
+
+ if (tmdsck > 340000000) {
+ DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck);
+ goto err;
+ }
+
+ pllctrl |= idf << PLL_CFG_IDF_SHIFT;
+ pllctrl |= odf << PLL_CFG_ODF_SHIFT;
+
+ /*
+ * Configure and power up the PHY PLL
+ */
+ hdmi->event_received = false;
+ DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
+ hdmi_write(hdmi, (pllctrl | PLL_CFG_EN), HDMI_SRZ_PLL_CFG);
+
+ /* wait PLL interrupt */
+ wait_event_interruptible_timeout(hdmi->wait_event,
+ hdmi->event_received == true,
+ msecs_to_jiffies
+ (HDMI_TIMEOUT_PLL_LOCK));
+
+ if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
+ DRM_ERROR("hdmi phy pll not locked\n");
+ goto err;
+ }
+
+ DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
+
+ val = (HDMI_SRZ_CFG_EN |
+ HDMI_SRZ_CFG_EXTERNAL_DATA |
+ HDMI_SRZ_CFG_EN_BIASRES_DETECTION |
+ HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION);
+
+ if (tmdsck > 165000000)
+ val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION;
+
+ /*
+ * To configure the source termination and pre-emphasis appropriately
+ * for different high speed TMDS clock frequencies a phy configuration
+ * table must be provided, tailored to the SoC and board combination.
+ */
+ for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) {
+ if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
+ (hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
+ val |= (hdmiphy_config[i].config[0]
+ & ~HDMI_SRZ_CFG_INTERNAL_MASK);
+ hdmi_write(hdmi, val, HDMI_SRZ_CFG);
+
+ val = hdmiphy_config[i].config[1];
+ hdmi_write(hdmi, val, HDMI_SRZ_ICNTL);
+
+ val = hdmiphy_config[i].config[2];
+ hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT);
+
+ DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n",
+ hdmiphy_config[i].config[0],
+ hdmiphy_config[i].config[1],
+ hdmiphy_config[i].config[2]);
+ return true;
+ }
+ }
+
+ /*
+ * Default, power up the serializer with no pre-emphasis or
+ * output swing correction
+ */
+ hdmi_write(hdmi, val, HDMI_SRZ_CFG);
+ hdmi_write(hdmi, 0x0, HDMI_SRZ_ICNTL);
+ hdmi_write(hdmi, 0x0, HDMI_SRZ_CALCODE_EXT);
+
+ return true;
+
+err:
+ return false;
+}
+
+/**
+ * Stop hdmi phy macro cell tx3g4c28
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ */
+static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi)
+{
+ int val = 0;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ hdmi->event_received = false;
+
+ val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION;
+ val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION;
+
+ hdmi_write(hdmi, val, HDMI_SRZ_CFG);
+ hdmi_write(hdmi, 0, HDMI_SRZ_PLL_CFG);
+
+ /* wait PLL interrupt */
+ wait_event_interruptible_timeout(hdmi->wait_event,
+ hdmi->event_received == true,
+ msecs_to_jiffies
+ (HDMI_TIMEOUT_PLL_LOCK));
+
+ if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
+ DRM_ERROR("hdmi phy pll not well disabled\n");
+}
+
+struct hdmi_phy_ops tx3g4c28phy_ops = {
+ .start = sti_hdmi_tx3g4c28phy_start,
+ .stop = sti_hdmi_tx3g4c28phy_stop,
+};
diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h
new file mode 100644
index 000000000000..f99a7ff281ef
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_HDMI_TX3G4C28PHY_H_
+#define _STI_HDMI_TX3G4C28PHY_H_
+
+#include "sti_hdmi.h"
+
+extern struct hdmi_phy_ops tx3g4c28phy_ops;
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_layer.c b/drivers/gpu/drm/sti/sti_layer.c
new file mode 100644
index 000000000000..06a587c4f1bb
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_layer.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "sti_compositor.h"
+#include "sti_gdp.h"
+#include "sti_layer.h"
+#include "sti_vid.h"
+
+const char *sti_layer_to_str(struct sti_layer *layer)
+{
+ switch (layer->desc) {
+ case STI_GDP_0:
+ return "GDP0";
+ case STI_GDP_1:
+ return "GDP1";
+ case STI_GDP_2:
+ return "GDP2";
+ case STI_GDP_3:
+ return "GDP3";
+ case STI_VID_0:
+ return "VID0";
+ case STI_VID_1:
+ return "VID1";
+ case STI_CURSOR:
+ return "CURSOR";
+ default:
+ return "<UNKNOWN LAYER>";
+ }
+}
+
+struct sti_layer *sti_layer_create(struct device *dev, int desc,
+ void __iomem *baseaddr)
+{
+
+ struct sti_layer *layer = NULL;
+
+ switch (desc & STI_LAYER_TYPE_MASK) {
+ case STI_GDP:
+ layer = sti_gdp_create(dev, desc);
+ break;
+ case STI_VID:
+ layer = sti_vid_create(dev);
+ break;
+ }
+
+ if (!layer) {
+ DRM_ERROR("Failed to create layer\n");
+ return NULL;
+ }
+
+ layer->desc = desc;
+ layer->dev = dev;
+ layer->regs = baseaddr;
+
+ layer->ops->init(layer);
+
+ DRM_DEBUG_DRIVER("%s created\n", sti_layer_to_str(layer));
+
+ return layer;
+}
+
+int sti_layer_prepare(struct sti_layer *layer, struct drm_framebuffer *fb,
+ struct drm_display_mode *mode, int mixer_id,
+ int dest_x, int dest_y, int dest_w, int dest_h,
+ int src_x, int src_y, int src_w, int src_h)
+{
+ int ret;
+ unsigned int i;
+ struct drm_gem_cma_object *cma_obj;
+
+ if (!layer || !fb || !mode) {
+ DRM_ERROR("Null fb, layer or mode\n");
+ return 1;
+ }
+
+ cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+ if (!cma_obj) {
+ DRM_ERROR("Can't get CMA GEM object for fb\n");
+ return 1;
+ }
+
+ layer->fb = fb;
+ layer->mode = mode;
+ layer->mixer_id = mixer_id;
+ layer->dst_x = dest_x;
+ layer->dst_y = dest_y;
+ layer->dst_w = clamp_val(dest_w, 0, mode->crtc_hdisplay - dest_x);
+ layer->dst_h = clamp_val(dest_h, 0, mode->crtc_vdisplay - dest_y);
+ layer->src_x = src_x;
+ layer->src_y = src_y;
+ layer->src_w = src_w;
+ layer->src_h = src_h;
+ layer->format = fb->pixel_format;
+ layer->paddr = cma_obj->paddr;
+ for (i = 0; i < 4; i++) {
+ layer->pitches[i] = fb->pitches[i];
+ layer->offsets[i] = fb->offsets[i];
+ }
+
+ DRM_DEBUG_DRIVER("%s is associated with mixer_id %d\n",
+ sti_layer_to_str(layer),
+ layer->mixer_id);
+ DRM_DEBUG_DRIVER("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n",
+ sti_layer_to_str(layer),
+ layer->dst_w, layer->dst_h, layer->dst_x, layer->dst_y,
+ layer->src_w, layer->src_h, layer->src_x,
+ layer->src_y);
+
+ DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id,
+ (char *)&layer->format, (unsigned long)layer->paddr);
+
+ if (!layer->ops->prepare)
+ goto err_no_prepare;
+
+ ret = layer->ops->prepare(layer, !layer->enabled);
+ if (!ret)
+ layer->enabled = true;
+
+ return ret;
+
+err_no_prepare:
+ DRM_ERROR("Cannot prepare\n");
+ return 1;
+}
+
+int sti_layer_commit(struct sti_layer *layer)
+{
+ if (!layer)
+ return 1;
+
+ if (!layer->ops->commit)
+ goto err_no_commit;
+
+ return layer->ops->commit(layer);
+
+err_no_commit:
+ DRM_ERROR("Cannot commit\n");
+ return 1;
+}
+
+int sti_layer_disable(struct sti_layer *layer)
+{
+ int ret;
+
+ DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer));
+ if (!layer)
+ return 1;
+
+ if (!layer->enabled)
+ return 0;
+
+ if (!layer->ops->disable)
+ goto err_no_disable;
+
+ ret = layer->ops->disable(layer);
+ if (!ret)
+ layer->enabled = false;
+ else
+ DRM_ERROR("Disable failed\n");
+
+ return ret;
+
+err_no_disable:
+ DRM_ERROR("Cannot disable\n");
+ return 1;
+}
+
+const uint32_t *sti_layer_get_formats(struct sti_layer *layer)
+{
+ if (!layer)
+ return NULL;
+
+ if (!layer->ops->get_formats)
+ return NULL;
+
+ return layer->ops->get_formats(layer);
+}
+
+unsigned int sti_layer_get_nb_formats(struct sti_layer *layer)
+{
+ if (!layer)
+ return 0;
+
+ if (!layer->ops->get_nb_formats)
+ return 0;
+
+ return layer->ops->get_nb_formats(layer);
+}
diff --git a/drivers/gpu/drm/sti/sti_layer.h b/drivers/gpu/drm/sti/sti_layer.h
new file mode 100644
index 000000000000..198c3774cc12
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_layer.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_LAYER_H_
+#define _STI_LAYER_H_
+
+#include <drm/drmP.h>
+
+#define to_sti_layer(x) container_of(x, struct sti_layer, plane)
+
+#define STI_LAYER_TYPE_SHIFT 8
+#define STI_LAYER_TYPE_MASK (~((1<<STI_LAYER_TYPE_SHIFT)-1))
+
+struct sti_layer;
+
+enum sti_layer_type {
+ STI_GDP = 1 << STI_LAYER_TYPE_SHIFT,
+ STI_VID = 2 << STI_LAYER_TYPE_SHIFT,
+ STI_CUR = 3 << STI_LAYER_TYPE_SHIFT,
+ STI_BCK = 4 << STI_LAYER_TYPE_SHIFT
+};
+
+enum sti_layer_id_of_type {
+ STI_ID_0 = 0,
+ STI_ID_1 = 1,
+ STI_ID_2 = 2,
+ STI_ID_3 = 3
+};
+
+enum sti_layer_desc {
+ STI_GDP_0 = STI_GDP | STI_ID_0,
+ STI_GDP_1 = STI_GDP | STI_ID_1,
+ STI_GDP_2 = STI_GDP | STI_ID_2,
+ STI_GDP_3 = STI_GDP | STI_ID_3,
+ STI_VID_0 = STI_VID | STI_ID_0,
+ STI_VID_1 = STI_VID | STI_ID_1,
+ STI_CURSOR = STI_CUR,
+ STI_BACK = STI_BCK
+};
+
+/**
+ * STI layer functions structure
+ *
+ * @get_formats: get layer supported formats
+ * @get_nb_formats: get number of format supported
+ * @init: initialize the layer
+ * @prepare: prepare layer before rendering
+ * @commit: set layer for rendering
+ * @disable: disable layer
+ */
+struct sti_layer_funcs {
+ const uint32_t* (*get_formats)(struct sti_layer *layer);
+ unsigned int (*get_nb_formats)(struct sti_layer *layer);
+ void (*init)(struct sti_layer *layer);
+ int (*prepare)(struct sti_layer *layer, bool first_prepare);
+ int (*commit)(struct sti_layer *layer);
+ int (*disable)(struct sti_layer *layer);
+};
+
+/**
+ * STI layer structure
+ *
+ * @plane: drm plane it is bound to (if any)
+ * @fb: drm fb it is bound to
+ * @mode: display mode
+ * @desc: layer type & id
+ * @device: driver device
+ * @regs: layer registers
+ * @ops: layer functions
+ * @zorder: layer z-order
+ * @mixer_id: id of the mixer used to display the layer
+ * @enabled: to know if the layer is active or not
+ * @src_x src_y: coordinates of the input (fb) area
+ * @src_w src_h: size of the input (fb) area
+ * @dst_x dst_y: coordinates of the output (crtc) area
+ * @dst_w dst_h: size of the output (crtc) area
+ * @format: format
+ * @pitches: pitch of 'planes' (eg: Y, U, V)
+ * @offsets: offset of 'planes'
+ * @paddr: physical address of the input buffer
+ */
+struct sti_layer {
+ struct drm_plane plane;
+ struct drm_framebuffer *fb;
+ struct drm_display_mode *mode;
+ enum sti_layer_desc desc;
+ struct device *dev;
+ void __iomem *regs;
+ const struct sti_layer_funcs *ops;
+ int zorder;
+ int mixer_id;
+ bool enabled;
+ int src_x, src_y;
+ int src_w, src_h;
+ int dst_x, dst_y;
+ int dst_w, dst_h;
+ uint32_t format;
+ unsigned int pitches[4];
+ unsigned int offsets[4];
+ dma_addr_t paddr;
+};
+
+struct sti_layer *sti_layer_create(struct device *dev, int desc,
+ void __iomem *baseaddr);
+int sti_layer_prepare(struct sti_layer *layer, struct drm_framebuffer *fb,
+ struct drm_display_mode *mode,
+ int mixer_id,
+ int dest_x, int dest_y,
+ int dest_w, int dest_h,
+ int src_x, int src_y,
+ int src_w, int src_h);
+int sti_layer_commit(struct sti_layer *layer);
+int sti_layer_disable(struct sti_layer *layer);
+const uint32_t *sti_layer_get_formats(struct sti_layer *layer);
+unsigned int sti_layer_get_nb_formats(struct sti_layer *layer);
+const char *sti_layer_to_str(struct sti_layer *layer);
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c
new file mode 100644
index 000000000000..79f369db9fb6
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_mixer.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include "sti_compositor.h"
+#include "sti_mixer.h"
+#include "sti_vtg.h"
+
+/* Identity: G=Y , B=Cb , R=Cr */
+static const u32 mixerColorSpaceMatIdentity[] = {
+ 0x10000000, 0x00000000, 0x10000000, 0x00001000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+/* regs offset */
+#define GAM_MIXER_CTL 0x00
+#define GAM_MIXER_BKC 0x04
+#define GAM_MIXER_BCO 0x0C
+#define GAM_MIXER_BCS 0x10
+#define GAM_MIXER_AVO 0x28
+#define GAM_MIXER_AVS 0x2C
+#define GAM_MIXER_CRB 0x34
+#define GAM_MIXER_ACT 0x38
+#define GAM_MIXER_MBP 0x3C
+#define GAM_MIXER_MX0 0x80
+
+/* id for depth of CRB reg */
+#define GAM_DEPTH_VID0_ID 1
+#define GAM_DEPTH_VID1_ID 2
+#define GAM_DEPTH_GDP0_ID 3
+#define GAM_DEPTH_GDP1_ID 4
+#define GAM_DEPTH_GDP2_ID 5
+#define GAM_DEPTH_GDP3_ID 6
+#define GAM_DEPTH_MASK_ID 7
+
+/* mask in CTL reg */
+#define GAM_CTL_BACK_MASK BIT(0)
+#define GAM_CTL_VID0_MASK BIT(1)
+#define GAM_CTL_VID1_MASK BIT(2)
+#define GAM_CTL_GDP0_MASK BIT(3)
+#define GAM_CTL_GDP1_MASK BIT(4)
+#define GAM_CTL_GDP2_MASK BIT(5)
+#define GAM_CTL_GDP3_MASK BIT(6)
+
+const char *sti_mixer_to_str(struct sti_mixer *mixer)
+{
+ switch (mixer->id) {
+ case STI_MIXER_MAIN:
+ return "MAIN_MIXER";
+ case STI_MIXER_AUX:
+ return "AUX_MIXER";
+ default:
+ return "<UNKNOWN MIXER>";
+ }
+}
+
+static inline u32 sti_mixer_reg_read(struct sti_mixer *mixer, u32 reg_id)
+{
+ return readl(mixer->regs + reg_id);
+}
+
+static inline void sti_mixer_reg_write(struct sti_mixer *mixer,
+ u32 reg_id, u32 val)
+{
+ writel(val, mixer->regs + reg_id);
+}
+
+void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable)
+{
+ u32 val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL);
+
+ val &= ~GAM_CTL_BACK_MASK;
+ val |= enable;
+ sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val);
+}
+
+static void sti_mixer_set_background_color(struct sti_mixer *mixer,
+ u8 red, u8 green, u8 blue)
+{
+ u32 val = (red << 16) | (green << 8) | blue;
+
+ sti_mixer_reg_write(mixer, GAM_MIXER_BKC, val);
+}
+
+static void sti_mixer_set_background_area(struct sti_mixer *mixer,
+ struct drm_display_mode *mode)
+{
+ u32 ydo, xdo, yds, xds;
+
+ ydo = sti_vtg_get_line_number(*mode, 0);
+ yds = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);
+ xdo = sti_vtg_get_pixel_number(*mode, 0);
+ xds = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);
+
+ sti_mixer_reg_write(mixer, GAM_MIXER_BCO, ydo << 16 | xdo);
+ sti_mixer_reg_write(mixer, GAM_MIXER_BCS, yds << 16 | xds);
+}
+
+int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer)
+{
+ int layer_id = 0, depth = layer->zorder;
+ u32 mask, val;
+
+ if (depth >= GAM_MIXER_NB_DEPTH_LEVEL)
+ return 1;
+
+ switch (layer->desc) {
+ case STI_GDP_0:
+ layer_id = GAM_DEPTH_GDP0_ID;
+ break;
+ case STI_GDP_1:
+ layer_id = GAM_DEPTH_GDP1_ID;
+ break;
+ case STI_GDP_2:
+ layer_id = GAM_DEPTH_GDP2_ID;
+ break;
+ case STI_GDP_3:
+ layer_id = GAM_DEPTH_GDP3_ID;
+ break;
+ case STI_VID_0:
+ layer_id = GAM_DEPTH_VID0_ID;
+ break;
+ case STI_VID_1:
+ layer_id = GAM_DEPTH_VID1_ID;
+ break;
+ default:
+ DRM_ERROR("Unknown layer %d\n", layer->desc);
+ return 1;
+ }
+ mask = GAM_DEPTH_MASK_ID << (3 * depth);
+ layer_id = layer_id << (3 * depth);
+
+ DRM_DEBUG_DRIVER("%s %s depth=%d\n", sti_mixer_to_str(mixer),
+ sti_layer_to_str(layer), depth);
+ dev_dbg(mixer->dev, "GAM_MIXER_CRB val 0x%x mask 0x%x\n",
+ layer_id, mask);
+
+ val = sti_mixer_reg_read(mixer, GAM_MIXER_CRB);
+ val &= ~mask;
+ val |= layer_id;
+ sti_mixer_reg_write(mixer, GAM_MIXER_CRB, val);
+
+ dev_dbg(mixer->dev, "Read GAM_MIXER_CRB 0x%x\n",
+ sti_mixer_reg_read(mixer, GAM_MIXER_CRB));
+ return 0;
+}
+
+int sti_mixer_active_video_area(struct sti_mixer *mixer,
+ struct drm_display_mode *mode)
+{
+ u32 ydo, xdo, yds, xds;
+
+ ydo = sti_vtg_get_line_number(*mode, 0);
+ yds = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);
+ xdo = sti_vtg_get_pixel_number(*mode, 0);
+ xds = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);
+
+ DRM_DEBUG_DRIVER("%s active video area xdo:%d ydo:%d xds:%d yds:%d\n",
+ sti_mixer_to_str(mixer), xdo, ydo, xds, yds);
+ sti_mixer_reg_write(mixer, GAM_MIXER_AVO, ydo << 16 | xdo);
+ sti_mixer_reg_write(mixer, GAM_MIXER_AVS, yds << 16 | xds);
+
+ sti_mixer_set_background_color(mixer, 0xFF, 0, 0);
+
+ sti_mixer_set_background_area(mixer, mode);
+ sti_mixer_set_background_status(mixer, true);
+ return 0;
+}
+
+static u32 sti_mixer_get_layer_mask(struct sti_layer *layer)
+{
+ switch (layer->desc) {
+ case STI_BACK:
+ return GAM_CTL_BACK_MASK;
+ case STI_GDP_0:
+ return GAM_CTL_GDP0_MASK;
+ case STI_GDP_1:
+ return GAM_CTL_GDP1_MASK;
+ case STI_GDP_2:
+ return GAM_CTL_GDP2_MASK;
+ case STI_GDP_3:
+ return GAM_CTL_GDP3_MASK;
+ case STI_VID_0:
+ return GAM_CTL_VID0_MASK;
+ case STI_VID_1:
+ return GAM_CTL_VID1_MASK;
+ default:
+ return 0;
+ }
+}
+
+int sti_mixer_set_layer_status(struct sti_mixer *mixer,
+ struct sti_layer *layer, bool status)
+{
+ u32 mask, val;
+
+ DRM_DEBUG_DRIVER("%s %s %s\n", status ? "enable" : "disable",
+ sti_mixer_to_str(mixer), sti_layer_to_str(layer));
+
+ mask = sti_mixer_get_layer_mask(layer);
+ if (!mask) {
+ DRM_ERROR("Can not find layer mask\n");
+ return -EINVAL;
+ }
+
+ val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL);
+ val &= ~mask;
+ val |= status ? mask : 0;
+ sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val);
+
+ return 0;
+}
+
+void sti_mixer_set_matrix(struct sti_mixer *mixer)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mixerColorSpaceMatIdentity); i++)
+ sti_mixer_reg_write(mixer, GAM_MIXER_MX0 + (i * 4),
+ mixerColorSpaceMatIdentity[i]);
+}
+
+struct sti_mixer *sti_mixer_create(struct device *dev, int id,
+ void __iomem *baseaddr)
+{
+ struct sti_mixer *mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
+ struct device_node *np = dev->of_node;
+
+ dev_dbg(dev, "%s\n", __func__);
+ if (!mixer) {
+ DRM_ERROR("Failed to allocated memory for mixer\n");
+ return NULL;
+ }
+ mixer->regs = baseaddr;
+ mixer->dev = dev;
+ mixer->id = id;
+
+ if (of_device_is_compatible(np, "st,stih416-compositor"))
+ sti_mixer_set_matrix(mixer);
+
+ DRM_DEBUG_DRIVER("%s created. Regs=%p\n",
+ sti_mixer_to_str(mixer), mixer->regs);
+
+ return mixer;
+}
diff --git a/drivers/gpu/drm/sti/sti_mixer.h b/drivers/gpu/drm/sti/sti_mixer.h
new file mode 100644
index 000000000000..874372102e52
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_mixer.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_MIXER_H_
+#define _STI_MIXER_H_
+
+#include <drm/drmP.h>
+
+#include "sti_layer.h"
+
+#define to_sti_mixer(x) container_of(x, struct sti_mixer, drm_crtc)
+
+/**
+ * STI Mixer subdevice structure
+ *
+ * @dev: driver device
+ * @regs: mixer registers
+ * @id: id of the mixer
+ * @drm_crtc: crtc object link to the mixer
+ * @pending_event: set if a flip event is pending on crtc
+ */
+struct sti_mixer {
+ struct device *dev;
+ void __iomem *regs;
+ int id;
+ struct drm_crtc drm_crtc;
+ struct drm_pending_vblank_event *pending_event;
+};
+
+const char *sti_mixer_to_str(struct sti_mixer *mixer);
+
+struct sti_mixer *sti_mixer_create(struct device *dev, int id,
+ void __iomem *baseaddr);
+
+int sti_mixer_set_layer_status(struct sti_mixer *mixer,
+ struct sti_layer *layer, bool status);
+int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer);
+int sti_mixer_active_video_area(struct sti_mixer *mixer,
+ struct drm_display_mode *mode);
+
+void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable);
+
+/* depth in Cross-bar control = z order */
+#define GAM_MIXER_NB_DEPTH_LEVEL 7
+
+#define STI_MIXER_MAIN 0
+#define STI_MIXER_AUX 1
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c
new file mode 100644
index 000000000000..b8afe490356a
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_tvout.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Vincent Abriou <vincent.abriou@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+/* glue registers */
+#define TVO_CSC_MAIN_M0 0x000
+#define TVO_CSC_MAIN_M1 0x004
+#define TVO_CSC_MAIN_M2 0x008
+#define TVO_CSC_MAIN_M3 0x00c
+#define TVO_CSC_MAIN_M4 0x010
+#define TVO_CSC_MAIN_M5 0x014
+#define TVO_CSC_MAIN_M6 0x018
+#define TVO_CSC_MAIN_M7 0x01c
+#define TVO_MAIN_IN_VID_FORMAT 0x030
+#define TVO_CSC_AUX_M0 0x100
+#define TVO_CSC_AUX_M1 0x104
+#define TVO_CSC_AUX_M2 0x108
+#define TVO_CSC_AUX_M3 0x10c
+#define TVO_CSC_AUX_M4 0x110
+#define TVO_CSC_AUX_M5 0x114
+#define TVO_CSC_AUX_M6 0x118
+#define TVO_CSC_AUX_M7 0x11c
+#define TVO_AUX_IN_VID_FORMAT 0x130
+#define TVO_VIP_HDF 0x400
+#define TVO_HD_SYNC_SEL 0x418
+#define TVO_HD_DAC_CFG_OFF 0x420
+#define TVO_VIP_HDMI 0x500
+#define TVO_HDMI_FORCE_COLOR_0 0x504
+#define TVO_HDMI_FORCE_COLOR_1 0x508
+#define TVO_HDMI_CLIP_VALUE_B_CB 0x50c
+#define TVO_HDMI_CLIP_VALUE_Y_G 0x510
+#define TVO_HDMI_CLIP_VALUE_R_CR 0x514
+#define TVO_HDMI_SYNC_SEL 0x518
+#define TVO_HDMI_DFV_OBS 0x540
+
+#define TVO_IN_FMT_SIGNED BIT(0)
+#define TVO_SYNC_EXT BIT(4)
+
+#define TVO_VIP_REORDER_R_SHIFT 24
+#define TVO_VIP_REORDER_G_SHIFT 20
+#define TVO_VIP_REORDER_B_SHIFT 16
+#define TVO_VIP_REORDER_MASK 0x3
+#define TVO_VIP_REORDER_Y_G_SEL 0
+#define TVO_VIP_REORDER_CB_B_SEL 1
+#define TVO_VIP_REORDER_CR_R_SEL 2
+
+#define TVO_VIP_CLIP_SHIFT 8
+#define TVO_VIP_CLIP_MASK 0x7
+#define TVO_VIP_CLIP_DISABLED 0
+#define TVO_VIP_CLIP_EAV_SAV 1
+#define TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y 2
+#define TVO_VIP_CLIP_LIMITED_RANGE_CB_CR 3
+#define TVO_VIP_CLIP_PROG_RANGE 4
+
+#define TVO_VIP_RND_SHIFT 4
+#define TVO_VIP_RND_MASK 0x3
+#define TVO_VIP_RND_8BIT_ROUNDED 0
+#define TVO_VIP_RND_10BIT_ROUNDED 1
+#define TVO_VIP_RND_12BIT_ROUNDED 2
+
+#define TVO_VIP_SEL_INPUT_MASK 0xf
+#define TVO_VIP_SEL_INPUT_MAIN 0x0
+#define TVO_VIP_SEL_INPUT_AUX 0x8
+#define TVO_VIP_SEL_INPUT_FORCE_COLOR 0xf
+#define TVO_VIP_SEL_INPUT_BYPASS_MASK 0x1
+#define TVO_VIP_SEL_INPUT_BYPASSED 1
+
+#define TVO_SYNC_MAIN_VTG_SET_REF 0x00
+#define TVO_SYNC_MAIN_VTG_SET_1 0x01
+#define TVO_SYNC_MAIN_VTG_SET_2 0x02
+#define TVO_SYNC_MAIN_VTG_SET_3 0x03
+#define TVO_SYNC_MAIN_VTG_SET_4 0x04
+#define TVO_SYNC_MAIN_VTG_SET_5 0x05
+#define TVO_SYNC_MAIN_VTG_SET_6 0x06
+#define TVO_SYNC_AUX_VTG_SET_REF 0x10
+#define TVO_SYNC_AUX_VTG_SET_1 0x11
+#define TVO_SYNC_AUX_VTG_SET_2 0x12
+#define TVO_SYNC_AUX_VTG_SET_3 0x13
+#define TVO_SYNC_AUX_VTG_SET_4 0x14
+#define TVO_SYNC_AUX_VTG_SET_5 0x15
+#define TVO_SYNC_AUX_VTG_SET_6 0x16
+
+#define TVO_SYNC_HD_DCS_SHIFT 8
+
+#define ENCODER_MAIN_CRTC_MASK BIT(0)
+
+/* enum listing the supported output data format */
+enum sti_tvout_video_out_type {
+ STI_TVOUT_VIDEO_OUT_RGB,
+ STI_TVOUT_VIDEO_OUT_YUV,
+};
+
+struct sti_tvout {
+ struct device *dev;
+ struct drm_device *drm_dev;
+ void __iomem *regs;
+ struct reset_control *reset;
+ struct drm_encoder *hdmi;
+ struct drm_encoder *hda;
+};
+
+struct sti_tvout_encoder {
+ struct drm_encoder encoder;
+ struct sti_tvout *tvout;
+};
+
+#define to_sti_tvout_encoder(x) \
+ container_of(x, struct sti_tvout_encoder, encoder)
+
+#define to_sti_tvout(x) to_sti_tvout_encoder(x)->tvout
+
+/* preformatter conversion matrix */
+static const u32 rgb_to_ycbcr_601[8] = {
+ 0xF927082E, 0x04C9FEAB, 0x01D30964, 0xFA95FD3D,
+ 0x0000082E, 0x00002000, 0x00002000, 0x00000000
+};
+
+/* 709 RGB to YCbCr */
+static const u32 rgb_to_ycbcr_709[8] = {
+ 0xF891082F, 0x0367FF40, 0x01280B71, 0xF9B1FE20,
+ 0x0000082F, 0x00002000, 0x00002000, 0x00000000
+};
+
+static u32 tvout_read(struct sti_tvout *tvout, int offset)
+{
+ return readl(tvout->regs + offset);
+}
+
+static void tvout_write(struct sti_tvout *tvout, u32 val, int offset)
+{
+ writel(val, tvout->regs + offset);
+}
+
+/**
+ * Set the clipping mode of a VIP
+ *
+ * @tvout: tvout structure
+ * @cr_r:
+ * @y_g:
+ * @cb_b:
+ */
+static void tvout_vip_set_color_order(struct sti_tvout *tvout,
+ u32 cr_r, u32 y_g, u32 cb_b)
+{
+ u32 val = tvout_read(tvout, TVO_VIP_HDMI);
+
+ val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT);
+ val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT);
+ val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT);
+ val |= cr_r << TVO_VIP_REORDER_R_SHIFT;
+ val |= y_g << TVO_VIP_REORDER_G_SHIFT;
+ val |= cb_b << TVO_VIP_REORDER_B_SHIFT;
+
+ tvout_write(tvout, val, TVO_VIP_HDMI);
+}
+
+/**
+ * Set the clipping mode of a VIP
+ *
+ * @tvout: tvout structure
+ * @range: clipping range
+ */
+static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, u32 range)
+{
+ u32 val = tvout_read(tvout, TVO_VIP_HDMI);
+
+ val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT);
+ val |= range << TVO_VIP_CLIP_SHIFT;
+ tvout_write(tvout, val, TVO_VIP_HDMI);
+}
+
+/**
+ * Set the rounded value of a VIP
+ *
+ * @tvout: tvout structure
+ * @rnd: rounded val per component
+ */
+static void tvout_vip_set_rnd(struct sti_tvout *tvout, u32 rnd)
+{
+ u32 val = tvout_read(tvout, TVO_VIP_HDMI);
+
+ val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT);
+ val |= rnd << TVO_VIP_RND_SHIFT;
+ tvout_write(tvout, val, TVO_VIP_HDMI);
+}
+
+/**
+ * Select the VIP input
+ *
+ * @tvout: tvout structure
+ * @sel_input: selected_input (main/aux + conv)
+ */
+static void tvout_vip_set_sel_input(struct sti_tvout *tvout,
+ bool main_path,
+ bool sel_input_logic_inverted,
+ enum sti_tvout_video_out_type video_out)
+{
+ u32 sel_input;
+ u32 val = tvout_read(tvout, TVO_VIP_HDMI);
+
+ if (main_path)
+ sel_input = TVO_VIP_SEL_INPUT_MAIN;
+ else
+ sel_input = TVO_VIP_SEL_INPUT_AUX;
+
+ switch (video_out) {
+ case STI_TVOUT_VIDEO_OUT_RGB:
+ sel_input |= TVO_VIP_SEL_INPUT_BYPASSED;
+ break;
+ case STI_TVOUT_VIDEO_OUT_YUV:
+ sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED;
+ break;
+ }
+
+ /* on stih407 chip the sel_input bypass mode logic is inverted */
+ if (sel_input_logic_inverted)
+ sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK;
+
+ val &= ~TVO_VIP_SEL_INPUT_MASK;
+ val |= sel_input;
+ tvout_write(tvout, val, TVO_VIP_HDMI);
+}
+
+/**
+ * Select the input video signed or unsigned
+ *
+ * @tvout: tvout structure
+ * @in_vid_signed: used video input format
+ */
+static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, u32 in_vid_fmt)
+{
+ u32 val = tvout_read(tvout, TVO_VIP_HDMI);
+
+ val &= ~TVO_IN_FMT_SIGNED;
+ val |= in_vid_fmt;
+ tvout_write(tvout, val, TVO_MAIN_IN_VID_FORMAT);
+}
+
+/**
+ * Start VIP block for HDMI output
+ *
+ * @tvout: pointer on tvout structure
+ * @main_path: true if main path has to be used in the vip configuration
+ * else aux path is used.
+ */
+static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
+{
+ struct device_node *node = tvout->dev->of_node;
+ bool sel_input_logic_inverted = false;
+
+ dev_dbg(tvout->dev, "%s\n", __func__);
+
+ if (main_path) {
+ DRM_DEBUG_DRIVER("main vip for hdmi\n");
+ /* select the input sync for hdmi = VTG set 1 */
+ tvout_write(tvout, TVO_SYNC_MAIN_VTG_SET_1, TVO_HDMI_SYNC_SEL);
+ } else {
+ DRM_DEBUG_DRIVER("aux vip for hdmi\n");
+ /* select the input sync for hdmi = VTG set 1 */
+ tvout_write(tvout, TVO_SYNC_AUX_VTG_SET_1, TVO_HDMI_SYNC_SEL);
+ }
+
+ /* set color channel order */
+ tvout_vip_set_color_order(tvout,
+ TVO_VIP_REORDER_CR_R_SEL,
+ TVO_VIP_REORDER_Y_G_SEL,
+ TVO_VIP_REORDER_CB_B_SEL);
+
+ /* set clipping mode (Limited range RGB/Y) */
+ tvout_vip_set_clip_mode(tvout, TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y);
+
+ /* set round mode (rounded to 8-bit per component) */
+ tvout_vip_set_rnd(tvout, TVO_VIP_RND_8BIT_ROUNDED);
+
+ if (of_device_is_compatible(node, "st,stih407-tvout")) {
+ /* set input video format */
+ tvout_vip_set_in_vid_fmt(tvout->regs + TVO_MAIN_IN_VID_FORMAT,
+ TVO_IN_FMT_SIGNED);
+ sel_input_logic_inverted = true;
+ }
+
+ /* input selection */
+ tvout_vip_set_sel_input(tvout, main_path,
+ sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB);
+}
+
+/**
+ * Start HDF VIP and HD DAC
+ *
+ * @tvout: pointer on tvout structure
+ * @main_path: true if main path has to be used in the vip configuration
+ * else aux path is used.
+ */
+static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
+{
+ struct device_node *node = tvout->dev->of_node;
+ bool sel_input_logic_inverted = false;
+
+ dev_dbg(tvout->dev, "%s\n", __func__);
+
+ if (!main_path) {
+ DRM_ERROR("HD Analog on aux not implemented\n");
+ return;
+ }
+
+ DRM_DEBUG_DRIVER("main vip for HDF\n");
+
+ /* set color channel order */
+ tvout_vip_set_color_order(tvout->regs + TVO_VIP_HDF,
+ TVO_VIP_REORDER_CR_R_SEL,
+ TVO_VIP_REORDER_Y_G_SEL,
+ TVO_VIP_REORDER_CB_B_SEL);
+
+ /* set clipping mode (Limited range RGB/Y) */
+ tvout_vip_set_clip_mode(tvout->regs + TVO_VIP_HDF,
+ TVO_VIP_CLIP_LIMITED_RANGE_CB_CR);
+
+ /* set round mode (rounded to 10-bit per component) */
+ tvout_vip_set_rnd(tvout->regs + TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED);
+
+ if (of_device_is_compatible(node, "st,stih407-tvout")) {
+ /* set input video format */
+ tvout_vip_set_in_vid_fmt(tvout, TVO_IN_FMT_SIGNED);
+ sel_input_logic_inverted = true;
+ }
+
+ /* Input selection */
+ tvout_vip_set_sel_input(tvout->regs + TVO_VIP_HDF,
+ main_path,
+ sel_input_logic_inverted,
+ STI_TVOUT_VIDEO_OUT_YUV);
+
+ /* select the input sync for HD analog = VTG set 3
+ * and HD DCS = VTG set 2 */
+ tvout_write(tvout,
+ (TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT)
+ | TVO_SYNC_MAIN_VTG_SET_3,
+ TVO_HD_SYNC_SEL);
+
+ /* power up HD DAC */
+ tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF);
+}
+
+static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool sti_tvout_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void sti_tvout_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void sti_tvout_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder);
+
+ drm_encoder_cleanup(encoder);
+ kfree(sti_encoder);
+}
+
+static const struct drm_encoder_funcs sti_tvout_encoder_funcs = {
+ .destroy = sti_tvout_encoder_destroy,
+};
+
+static void sti_hda_encoder_commit(struct drm_encoder *encoder)
+{
+ struct sti_tvout *tvout = to_sti_tvout(encoder);
+
+ tvout_hda_start(tvout, true);
+}
+
+static void sti_hda_encoder_disable(struct drm_encoder *encoder)
+{
+ struct sti_tvout *tvout = to_sti_tvout(encoder);
+
+ /* reset VIP register */
+ tvout_write(tvout, 0x0, TVO_VIP_HDF);
+
+ /* power down HD DAC */
+ tvout_write(tvout, 1, TVO_HD_DAC_CFG_OFF);
+}
+
+static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = {
+ .dpms = sti_tvout_encoder_dpms,
+ .mode_fixup = sti_tvout_encoder_mode_fixup,
+ .mode_set = sti_tvout_encoder_mode_set,
+ .prepare = sti_tvout_encoder_prepare,
+ .commit = sti_hda_encoder_commit,
+ .disable = sti_hda_encoder_disable,
+};
+
+static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev,
+ struct sti_tvout *tvout)
+{
+ struct sti_tvout_encoder *encoder;
+ struct drm_encoder *drm_encoder;
+
+ encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
+ if (!encoder)
+ return NULL;
+
+ encoder->tvout = tvout;
+
+ drm_encoder = (struct drm_encoder *) encoder;
+
+ drm_encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK;
+ drm_encoder->possible_clones = 1 << 0;
+
+ drm_encoder_init(dev, drm_encoder,
+ &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC);
+
+ drm_encoder_helper_add(drm_encoder, &sti_hda_encoder_helper_funcs);
+
+ return drm_encoder;
+}
+
+static void sti_hdmi_encoder_commit(struct drm_encoder *encoder)
+{
+ struct sti_tvout *tvout = to_sti_tvout(encoder);
+
+ tvout_hdmi_start(tvout, true);
+}
+
+static void sti_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct sti_tvout *tvout = to_sti_tvout(encoder);
+
+ /* reset VIP register */
+ tvout_write(tvout, 0x0, TVO_VIP_HDMI);
+}
+
+static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = {
+ .dpms = sti_tvout_encoder_dpms,
+ .mode_fixup = sti_tvout_encoder_mode_fixup,
+ .mode_set = sti_tvout_encoder_mode_set,
+ .prepare = sti_tvout_encoder_prepare,
+ .commit = sti_hdmi_encoder_commit,
+ .disable = sti_hdmi_encoder_disable,
+};
+
+static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev,
+ struct sti_tvout *tvout)
+{
+ struct sti_tvout_encoder *encoder;
+ struct drm_encoder *drm_encoder;
+
+ encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
+ if (!encoder)
+ return NULL;
+
+ encoder->tvout = tvout;
+
+ drm_encoder = (struct drm_encoder *) encoder;
+
+ drm_encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK;
+ drm_encoder->possible_clones = 1 << 1;
+
+ drm_encoder_init(dev, drm_encoder,
+ &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS);
+
+ drm_encoder_helper_add(drm_encoder, &sti_hdmi_encoder_helper_funcs);
+
+ return drm_encoder;
+}
+
+static void sti_tvout_create_encoders(struct drm_device *dev,
+ struct sti_tvout *tvout)
+{
+ tvout->hdmi = sti_tvout_create_hdmi_encoder(dev, tvout);
+ tvout->hda = sti_tvout_create_hda_encoder(dev, tvout);
+}
+
+static void sti_tvout_destroy_encoders(struct sti_tvout *tvout)
+{
+ if (tvout->hdmi)
+ drm_encoder_cleanup(tvout->hdmi);
+ tvout->hdmi = NULL;
+
+ if (tvout->hda)
+ drm_encoder_cleanup(tvout->hda);
+ tvout->hda = NULL;
+}
+
+static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
+{
+ struct sti_tvout *tvout = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = data;
+ unsigned int i;
+ int ret;
+
+ tvout->drm_dev = drm_dev;
+
+ /* set preformatter matrix */
+ for (i = 0; i < 8; i++) {
+ tvout_write(tvout, rgb_to_ycbcr_601[i],
+ TVO_CSC_MAIN_M0 + (i * 4));
+ tvout_write(tvout, rgb_to_ycbcr_601[i],
+ TVO_CSC_AUX_M0 + (i * 4));
+ }
+
+ sti_tvout_create_encoders(drm_dev, tvout);
+
+ ret = component_bind_all(dev, drm_dev);
+ if (ret)
+ sti_tvout_destroy_encoders(tvout);
+
+ return ret;
+}
+
+static void sti_tvout_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ /* do nothing */
+}
+
+static const struct component_ops sti_tvout_ops = {
+ .bind = sti_tvout_bind,
+ .unbind = sti_tvout_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static int sti_tvout_master_bind(struct device *dev)
+{
+ return 0;
+}
+
+static void sti_tvout_master_unbind(struct device *dev)
+{
+ /* do nothing */
+}
+
+static const struct component_master_ops sti_tvout_master_ops = {
+ .bind = sti_tvout_master_bind,
+ .unbind = sti_tvout_master_unbind,
+};
+
+static int sti_tvout_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct sti_tvout *tvout;
+ struct resource *res;
+ struct device_node *child_np;
+ struct component_match *match = NULL;
+
+ DRM_INFO("%s\n", __func__);
+
+ if (!node)
+ return -ENODEV;
+
+ tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL);
+ if (!tvout)
+ return -ENOMEM;
+
+ tvout->dev = dev;
+
+ /* get Memory ressources */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
+ if (!res) {
+ DRM_ERROR("Invalid glue resource\n");
+ return -ENOMEM;
+ }
+ tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
+ if (!tvout->regs)
+ return -ENOMEM;
+
+ /* get reset resources */
+ tvout->reset = devm_reset_control_get(dev, "tvout");
+ /* take tvout out of reset */
+ if (!IS_ERR(tvout->reset))
+ reset_control_deassert(tvout->reset);
+
+ platform_set_drvdata(pdev, tvout);
+
+ of_platform_populate(node, NULL, NULL, dev);
+
+ child_np = of_get_next_available_child(node, NULL);
+
+ while (child_np) {
+ component_match_add(dev, &match, compare_of, child_np);
+ of_node_put(child_np);
+ child_np = of_get_next_available_child(node, child_np);
+ }
+
+ component_master_add_with_match(dev, &sti_tvout_master_ops, match);
+
+ return component_add(dev, &sti_tvout_ops);
+}
+
+static int sti_tvout_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &sti_tvout_master_ops);
+ component_del(&pdev->dev, &sti_tvout_ops);
+ return 0;
+}
+
+static const struct of_device_id tvout_of_match[] = {
+ { .compatible = "st,stih416-tvout", },
+ { .compatible = "st,stih407-tvout", },
+ { /* end node */ }
+};
+MODULE_DEVICE_TABLE(of, tvout_of_match);
+
+struct platform_driver sti_tvout_driver = {
+ .driver = {
+ .name = "sti-tvout",
+ .owner = THIS_MODULE,
+ .of_match_table = tvout_of_match,
+ },
+ .probe = sti_tvout_probe,
+ .remove = sti_tvout_remove,
+};
+
+module_platform_driver(sti_tvout_driver);
+
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sti/sti_vid.c b/drivers/gpu/drm/sti/sti_vid.c
new file mode 100644
index 000000000000..10ced6a479f4
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vid.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <drm/drmP.h>
+
+#include "sti_layer.h"
+#include "sti_vid.h"
+#include "sti_vtg.h"
+
+/* Registers */
+#define VID_CTL 0x00
+#define VID_ALP 0x04
+#define VID_CLF 0x08
+#define VID_VPO 0x0C
+#define VID_VPS 0x10
+#define VID_KEY1 0x28
+#define VID_KEY2 0x2C
+#define VID_MPR0 0x30
+#define VID_MPR1 0x34
+#define VID_MPR2 0x38
+#define VID_MPR3 0x3C
+#define VID_MST 0x68
+#define VID_BC 0x70
+#define VID_TINT 0x74
+#define VID_CSAT 0x78
+
+/* Registers values */
+#define VID_CTL_IGNORE (BIT(31) | BIT(30))
+#define VID_CTL_PSI_ENABLE (BIT(2) | BIT(1) | BIT(0))
+#define VID_ALP_OPAQUE 0x00000080
+#define VID_BC_DFLT 0x00008000
+#define VID_TINT_DFLT 0x00000000
+#define VID_CSAT_DFLT 0x00000080
+/* YCbCr to RGB BT709:
+ * R = Y+1.5391Cr
+ * G = Y-0.4590Cr-0.1826Cb
+ * B = Y+1.8125Cb */
+#define VID_MPR0_BT709 0x0A800000
+#define VID_MPR1_BT709 0x0AC50000
+#define VID_MPR2_BT709 0x07150545
+#define VID_MPR3_BT709 0x00000AE8
+
+static int sti_vid_prepare_layer(struct sti_layer *vid, bool first_prepare)
+{
+ u32 val;
+
+ /* Unmask */
+ val = readl(vid->regs + VID_CTL);
+ val &= ~VID_CTL_IGNORE;
+ writel(val, vid->regs + VID_CTL);
+
+ return 0;
+}
+
+static int sti_vid_commit_layer(struct sti_layer *vid)
+{
+ struct drm_display_mode *mode = vid->mode;
+ u32 ydo, xdo, yds, xds;
+
+ ydo = sti_vtg_get_line_number(*mode, vid->dst_y);
+ yds = sti_vtg_get_line_number(*mode, vid->dst_y + vid->dst_h - 1);
+ xdo = sti_vtg_get_pixel_number(*mode, vid->dst_x);
+ xds = sti_vtg_get_pixel_number(*mode, vid->dst_x + vid->dst_w - 1);
+
+ writel((ydo << 16) | xdo, vid->regs + VID_VPO);
+ writel((yds << 16) | xds, vid->regs + VID_VPS);
+
+ return 0;
+}
+
+static int sti_vid_disable_layer(struct sti_layer *vid)
+{
+ u32 val;
+
+ /* Mask */
+ val = readl(vid->regs + VID_CTL);
+ val |= VID_CTL_IGNORE;
+ writel(val, vid->regs + VID_CTL);
+
+ return 0;
+}
+
+static const uint32_t *sti_vid_get_formats(struct sti_layer *layer)
+{
+ return NULL;
+}
+
+static unsigned int sti_vid_get_nb_formats(struct sti_layer *layer)
+{
+ return 0;
+}
+
+static void sti_vid_init(struct sti_layer *vid)
+{
+ /* Enable PSI, Mask layer */
+ writel(VID_CTL_PSI_ENABLE | VID_CTL_IGNORE, vid->regs + VID_CTL);
+
+ /* Opaque */
+ writel(VID_ALP_OPAQUE, vid->regs + VID_ALP);
+
+ /* Color conversion parameters */
+ writel(VID_MPR0_BT709, vid->regs + VID_MPR0);
+ writel(VID_MPR1_BT709, vid->regs + VID_MPR1);
+ writel(VID_MPR2_BT709, vid->regs + VID_MPR2);
+ writel(VID_MPR3_BT709, vid->regs + VID_MPR3);
+
+ /* Brightness, contrast, tint, saturation */
+ writel(VID_BC_DFLT, vid->regs + VID_BC);
+ writel(VID_TINT_DFLT, vid->regs + VID_TINT);
+ writel(VID_CSAT_DFLT, vid->regs + VID_CSAT);
+}
+
+static const struct sti_layer_funcs vid_ops = {
+ .get_formats = sti_vid_get_formats,
+ .get_nb_formats = sti_vid_get_nb_formats,
+ .init = sti_vid_init,
+ .prepare = sti_vid_prepare_layer,
+ .commit = sti_vid_commit_layer,
+ .disable = sti_vid_disable_layer,
+};
+
+struct sti_layer *sti_vid_create(struct device *dev)
+{
+ struct sti_layer *vid;
+
+ vid = devm_kzalloc(dev, sizeof(*vid), GFP_KERNEL);
+ if (!vid) {
+ DRM_ERROR("Failed to allocate memory for VID\n");
+ return NULL;
+ }
+
+ vid->ops = &vid_ops;
+
+ return vid;
+}
diff --git a/drivers/gpu/drm/sti/sti_vid.h b/drivers/gpu/drm/sti/sti_vid.h
new file mode 100644
index 000000000000..2c0aecd63294
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vid.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_VID_H_
+#define _STI_VID_H_
+
+struct sti_layer *sti_vid_create(struct device *dev);
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_vtac.c b/drivers/gpu/drm/sti/sti_vtac.c
new file mode 100644
index 000000000000..97bcdac23ae1
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtac.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <drm/drmP.h>
+
+/* registers offset */
+#define VTAC_CONFIG 0x00
+#define VTAC_RX_FIFO_CONFIG 0x04
+#define VTAC_FIFO_CONFIG_VAL 0x04
+
+#define VTAC_SYS_CFG8521 0x824
+#define VTAC_SYS_CFG8522 0x828
+
+/* Number of phyts per pixel */
+#define VTAC_2_5_PPP 0x0005
+#define VTAC_3_PPP 0x0006
+#define VTAC_4_PPP 0x0008
+#define VTAC_5_PPP 0x000A
+#define VTAC_6_PPP 0x000C
+#define VTAC_13_PPP 0x001A
+#define VTAC_14_PPP 0x001C
+#define VTAC_15_PPP 0x001E
+#define VTAC_16_PPP 0x0020
+#define VTAC_17_PPP 0x0022
+#define VTAC_18_PPP 0x0024
+
+/* enable bits */
+#define VTAC_ENABLE 0x3003
+
+#define VTAC_TX_PHY_ENABLE_CLK_PHY BIT(0)
+#define VTAC_TX_PHY_ENABLE_CLK_DLL BIT(1)
+#define VTAC_TX_PHY_PLL_NOT_OSC_MODE BIT(3)
+#define VTAC_TX_PHY_RST_N_DLL_SWITCH BIT(4)
+#define VTAC_TX_PHY_PROG_N3 BIT(9)
+
+
+/**
+ * VTAC mode structure
+ *
+ * @vid_in_width: Video Data Resolution
+ * @phyts_width: Width of phyt buses(phyt low and phyt high).
+ * @phyts_per_pixel: Number of phyts sent per pixel
+ */
+struct sti_vtac_mode {
+ u32 vid_in_width;
+ u32 phyts_width;
+ u32 phyts_per_pixel;
+};
+
+static const struct sti_vtac_mode vtac_mode_main = {
+ .vid_in_width = 0x2,
+ .phyts_width = 0x2,
+ .phyts_per_pixel = VTAC_5_PPP,
+};
+static const struct sti_vtac_mode vtac_mode_aux = {
+ .vid_in_width = 0x1,
+ .phyts_width = 0x0,
+ .phyts_per_pixel = VTAC_17_PPP,
+};
+
+/**
+ * VTAC structure
+ *
+ * @dev: pointer to device structure
+ * @regs: ioremapped registers for RX and TX devices
+ * @phy_regs: phy registers for TX device
+ * @clk: clock
+ * @mode: main or auxillary configuration mode
+ */
+struct sti_vtac {
+ struct device *dev;
+ void __iomem *regs;
+ void __iomem *phy_regs;
+ struct clk *clk;
+ const struct sti_vtac_mode *mode;
+};
+
+static void sti_vtac_rx_set_config(struct sti_vtac *vtac)
+{
+ u32 config;
+
+ /* Enable VTAC clock */
+ if (clk_prepare_enable(vtac->clk))
+ DRM_ERROR("Failed to prepare/enable vtac_rx clock.\n");
+
+ writel(VTAC_FIFO_CONFIG_VAL, vtac->regs + VTAC_RX_FIFO_CONFIG);
+
+ config = VTAC_ENABLE;
+ config |= vtac->mode->vid_in_width << 4;
+ config |= vtac->mode->phyts_width << 16;
+ config |= vtac->mode->phyts_per_pixel << 23;
+ writel(config, vtac->regs + VTAC_CONFIG);
+}
+
+static void sti_vtac_tx_set_config(struct sti_vtac *vtac)
+{
+ u32 phy_config;
+ u32 config;
+
+ /* Enable VTAC clock */
+ if (clk_prepare_enable(vtac->clk))
+ DRM_ERROR("Failed to prepare/enable vtac_tx clock.\n");
+
+ /* Configure vtac phy */
+ phy_config = 0x00000000;
+ writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8522);
+ phy_config = VTAC_TX_PHY_ENABLE_CLK_PHY;
+ writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
+ phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
+ phy_config |= VTAC_TX_PHY_PROG_N3;
+ writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
+ phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
+ phy_config |= VTAC_TX_PHY_ENABLE_CLK_DLL;
+ writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
+ phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
+ phy_config |= VTAC_TX_PHY_RST_N_DLL_SWITCH;
+ writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
+ phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
+ phy_config |= VTAC_TX_PHY_PLL_NOT_OSC_MODE;
+ writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
+
+ /* Configure vtac tx */
+ config = VTAC_ENABLE;
+ config |= vtac->mode->vid_in_width << 4;
+ config |= vtac->mode->phyts_width << 16;
+ config |= vtac->mode->phyts_per_pixel << 23;
+ writel(config, vtac->regs + VTAC_CONFIG);
+}
+
+static const struct of_device_id vtac_of_match[] = {
+ {
+ .compatible = "st,vtac-main",
+ .data = &vtac_mode_main,
+ }, {
+ .compatible = "st,vtac-aux",
+ .data = &vtac_mode_aux,
+ }, {
+ /* end node */
+ }
+};
+MODULE_DEVICE_TABLE(of, vtac_of_match);
+
+static int sti_vtac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ const struct of_device_id *id;
+ struct sti_vtac *vtac;
+ struct resource *res;
+
+ vtac = devm_kzalloc(dev, sizeof(*vtac), GFP_KERNEL);
+ if (!vtac)
+ return -ENOMEM;
+
+ vtac->dev = dev;
+
+ id = of_match_node(vtac_of_match, np);
+ if (!id)
+ return -ENOMEM;
+
+ vtac->mode = id->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ DRM_ERROR("Invalid resource\n");
+ return -ENOMEM;
+ }
+ vtac->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(vtac->regs))
+ return PTR_ERR(vtac->regs);
+
+
+ vtac->clk = devm_clk_get(dev, "vtac");
+ if (IS_ERR(vtac->clk)) {
+ DRM_ERROR("Cannot get vtac clock\n");
+ return PTR_ERR(vtac->clk);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ vtac->phy_regs = devm_ioremap_nocache(dev, res->start,
+ resource_size(res));
+ sti_vtac_tx_set_config(vtac);
+ } else {
+
+ sti_vtac_rx_set_config(vtac);
+ }
+
+ platform_set_drvdata(pdev, vtac);
+ DRM_INFO("%s %s\n", __func__, dev_name(vtac->dev));
+
+ return 0;
+}
+
+static int sti_vtac_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver sti_vtac_driver = {
+ .driver = {
+ .name = "sti-vtac",
+ .owner = THIS_MODULE,
+ .of_match_table = vtac_of_match,
+ },
+ .probe = sti_vtac_probe,
+ .remove = sti_vtac_remove,
+};
+
+module_platform_driver(sti_vtac_driver);
+
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c
new file mode 100644
index 000000000000..740d6e347a62
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtg.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * Vincent Abriou <vincent.abriou@st.com>
+ * for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+
+#include <drm/drmP.h>
+
+#include "sti_vtg.h"
+
+#define VTG_TYPE_MASTER 0
+#define VTG_TYPE_SLAVE_BY_EXT0 1
+
+/* registers offset */
+#define VTG_MODE 0x0000
+#define VTG_CLKLN 0x0008
+#define VTG_HLFLN 0x000C
+#define VTG_DRST_AUTOC 0x0010
+#define VTG_VID_TFO 0x0040
+#define VTG_VID_TFS 0x0044
+#define VTG_VID_BFO 0x0048
+#define VTG_VID_BFS 0x004C
+
+#define VTG_HOST_ITS 0x0078
+#define VTG_HOST_ITS_BCLR 0x007C
+#define VTG_HOST_ITM_BCLR 0x0088
+#define VTG_HOST_ITM_BSET 0x008C
+
+#define VTG_H_HD_1 0x00C0
+#define VTG_TOP_V_VD_1 0x00C4
+#define VTG_BOT_V_VD_1 0x00C8
+#define VTG_TOP_V_HD_1 0x00CC
+#define VTG_BOT_V_HD_1 0x00D0
+
+#define VTG_H_HD_2 0x00E0
+#define VTG_TOP_V_VD_2 0x00E4
+#define VTG_BOT_V_VD_2 0x00E8
+#define VTG_TOP_V_HD_2 0x00EC
+#define VTG_BOT_V_HD_2 0x00F0
+
+#define VTG_H_HD_3 0x0100
+#define VTG_TOP_V_VD_3 0x0104
+#define VTG_BOT_V_VD_3 0x0108
+#define VTG_TOP_V_HD_3 0x010C
+#define VTG_BOT_V_HD_3 0x0110
+
+#define VTG_IRQ_BOTTOM BIT(0)
+#define VTG_IRQ_TOP BIT(1)
+#define VTG_IRQ_MASK (VTG_IRQ_TOP | VTG_IRQ_BOTTOM)
+
+/* delay introduced by the Arbitrary Waveform Generator in nb of pixels */
+#define AWG_DELAY_HD (-9)
+#define AWG_DELAY_ED (-8)
+#define AWG_DELAY_SD (-7)
+
+LIST_HEAD(vtg_lookup);
+
+/**
+ * STI VTG structure
+ *
+ * @dev: pointer to device driver
+ * @data: data associated to the device
+ * @irq: VTG irq
+ * @type: VTG type (main or aux)
+ * @notifier_list: notifier callback
+ * @crtc_id: the crtc id for vblank event
+ * @slave: slave vtg
+ * @link: List node to link the structure in lookup list
+ */
+struct sti_vtg {
+ struct device *dev;
+ struct device_node *np;
+ void __iomem *regs;
+ int irq;
+ u32 irq_status;
+ struct raw_notifier_head notifier_list;
+ int crtc_id;
+ struct sti_vtg *slave;
+ struct list_head link;
+};
+
+static void vtg_register(struct sti_vtg *vtg)
+{
+ list_add_tail(&vtg->link, &vtg_lookup);
+}
+
+struct sti_vtg *of_vtg_find(struct device_node *np)
+{
+ struct sti_vtg *vtg;
+
+ list_for_each_entry(vtg, &vtg_lookup, link) {
+ if (vtg->np == np)
+ return vtg;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(of_vtg_find);
+
+static void vtg_reset(struct sti_vtg *vtg)
+{
+ /* reset slave and then master */
+ if (vtg->slave)
+ vtg_reset(vtg->slave);
+
+ writel(1, vtg->regs + VTG_DRST_AUTOC);
+}
+
+static void vtg_set_mode(struct sti_vtg *vtg,
+ int type, const struct drm_display_mode *mode)
+{
+ u32 tmp;
+
+ if (vtg->slave)
+ vtg_set_mode(vtg->slave, VTG_TYPE_SLAVE_BY_EXT0, mode);
+
+ writel(mode->htotal, vtg->regs + VTG_CLKLN);
+ writel(mode->vtotal * 2, vtg->regs + VTG_HLFLN);
+
+ tmp = (mode->vtotal - mode->vsync_start + 1) << 16;
+ tmp |= mode->htotal - mode->hsync_start;
+ writel(tmp, vtg->regs + VTG_VID_TFO);
+ writel(tmp, vtg->regs + VTG_VID_BFO);
+
+ tmp = (mode->vdisplay + mode->vtotal - mode->vsync_start + 1) << 16;
+ tmp |= mode->hdisplay + mode->htotal - mode->hsync_start;
+ writel(tmp, vtg->regs + VTG_VID_TFS);
+ writel(tmp, vtg->regs + VTG_VID_BFS);
+
+ /* prepare VTG set 1 and 2 for HDMI and VTG set 3 for HD DAC */
+ tmp = (mode->hsync_end - mode->hsync_start) << 16;
+ writel(tmp, vtg->regs + VTG_H_HD_1);
+ writel(tmp, vtg->regs + VTG_H_HD_2);
+
+ tmp = (mode->vsync_end - mode->vsync_start + 1) << 16;
+ tmp |= 1;
+ writel(tmp, vtg->regs + VTG_TOP_V_VD_1);
+ writel(tmp, vtg->regs + VTG_BOT_V_VD_1);
+ writel(0, vtg->regs + VTG_TOP_V_HD_1);
+ writel(0, vtg->regs + VTG_BOT_V_HD_1);
+
+ /* prepare VTG set 2 for for HD DCS */
+ writel(tmp, vtg->regs + VTG_TOP_V_VD_2);
+ writel(tmp, vtg->regs + VTG_BOT_V_VD_2);
+ writel(0, vtg->regs + VTG_TOP_V_HD_2);
+ writel(0, vtg->regs + VTG_BOT_V_HD_2);
+
+ /* prepare VTG set 3 for HD Analog in HD mode */
+ tmp = (mode->hsync_end - mode->hsync_start + AWG_DELAY_HD) << 16;
+ tmp |= mode->htotal + AWG_DELAY_HD;
+ writel(tmp, vtg->regs + VTG_H_HD_3);
+
+ tmp = (mode->vsync_end - mode->vsync_start) << 16;
+ tmp |= mode->vtotal;
+ writel(tmp, vtg->regs + VTG_TOP_V_VD_3);
+ writel(tmp, vtg->regs + VTG_BOT_V_VD_3);
+
+ tmp = (mode->htotal + AWG_DELAY_HD) << 16;
+ tmp |= mode->htotal + AWG_DELAY_HD;
+ writel(tmp, vtg->regs + VTG_TOP_V_HD_3);
+ writel(tmp, vtg->regs + VTG_BOT_V_HD_3);
+
+ /* mode */
+ writel(type, vtg->regs + VTG_MODE);
+}
+
+static void vtg_enable_irq(struct sti_vtg *vtg)
+{
+ /* clear interrupt status and mask */
+ writel(0xFFFF, vtg->regs + VTG_HOST_ITS_BCLR);
+ writel(0xFFFF, vtg->regs + VTG_HOST_ITM_BCLR);
+ writel(VTG_IRQ_MASK, vtg->regs + VTG_HOST_ITM_BSET);
+}
+
+void sti_vtg_set_config(struct sti_vtg *vtg,
+ const struct drm_display_mode *mode)
+{
+ /* write configuration */
+ vtg_set_mode(vtg, VTG_TYPE_MASTER, mode);
+
+ vtg_reset(vtg);
+
+ /* enable irq for the vtg vblank synchro */
+ if (vtg->slave)
+ vtg_enable_irq(vtg->slave);
+ else
+ vtg_enable_irq(vtg);
+}
+EXPORT_SYMBOL(sti_vtg_set_config);
+
+/**
+ * sti_vtg_get_line_number
+ *
+ * @mode: display mode to be used
+ * @y: line
+ *
+ * Return the line number according to the display mode taking
+ * into account the Sync and Back Porch information.
+ * Video frame line numbers start at 1, y starts at 0.
+ * In interlaced modes the start line is the field line number of the odd
+ * field, but y is still defined as a progressive frame.
+ */
+u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y)
+{
+ u32 start_line = mode.vtotal - mode.vsync_start + 1;
+
+ if (mode.flags & DRM_MODE_FLAG_INTERLACE)
+ start_line *= 2;
+
+ return start_line + y;
+}
+EXPORT_SYMBOL(sti_vtg_get_line_number);
+
+/**
+ * sti_vtg_get_pixel_number
+ *
+ * @mode: display mode to be used
+ * @x: row
+ *
+ * Return the pixel number according to the display mode taking
+ * into account the Sync and Back Porch information.
+ * Pixels are counted from 0.
+ */
+u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x)
+{
+ return mode.htotal - mode.hsync_start + x;
+}
+EXPORT_SYMBOL(sti_vtg_get_pixel_number);
+
+int sti_vtg_register_client(struct sti_vtg *vtg,
+ struct notifier_block *nb, int crtc_id)
+{
+ if (vtg->slave)
+ return sti_vtg_register_client(vtg->slave, nb, crtc_id);
+
+ vtg->crtc_id = crtc_id;
+ return raw_notifier_chain_register(&vtg->notifier_list, nb);
+}
+EXPORT_SYMBOL(sti_vtg_register_client);
+
+int sti_vtg_unregister_client(struct sti_vtg *vtg, struct notifier_block *nb)
+{
+ if (vtg->slave)
+ return sti_vtg_unregister_client(vtg->slave, nb);
+
+ return raw_notifier_chain_unregister(&vtg->notifier_list, nb);
+}
+EXPORT_SYMBOL(sti_vtg_unregister_client);
+
+static irqreturn_t vtg_irq_thread(int irq, void *arg)
+{
+ struct sti_vtg *vtg = arg;
+ u32 event;
+
+ event = (vtg->irq_status & VTG_IRQ_TOP) ?
+ VTG_TOP_FIELD_EVENT : VTG_BOTTOM_FIELD_EVENT;
+
+ raw_notifier_call_chain(&vtg->notifier_list, event, &vtg->crtc_id);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t vtg_irq(int irq, void *arg)
+{
+ struct sti_vtg *vtg = arg;
+
+ vtg->irq_status = readl(vtg->regs + VTG_HOST_ITS);
+
+ writel(vtg->irq_status, vtg->regs + VTG_HOST_ITS_BCLR);
+
+ /* force sync bus write */
+ readl(vtg->regs + VTG_HOST_ITS);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int vtg_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np;
+ struct sti_vtg *vtg;
+ struct resource *res;
+ char irq_name[32];
+ int ret;
+
+ vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL);
+ if (!vtg)
+ return -ENOMEM;
+
+ vtg->dev = dev;
+ vtg->np = pdev->dev.of_node;
+
+ /* Get Memory ressources */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ DRM_ERROR("Get memory resource failed\n");
+ return -ENOMEM;
+ }
+ vtg->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
+
+ np = of_parse_phandle(pdev->dev.of_node, "st,slave", 0);
+ if (np) {
+ vtg->slave = of_vtg_find(np);
+
+ if (!vtg->slave)
+ return -EPROBE_DEFER;
+ } else {
+ vtg->irq = platform_get_irq(pdev, 0);
+ if (IS_ERR_VALUE(vtg->irq)) {
+ DRM_ERROR("Failed to get VTG interrupt\n");
+ return vtg->irq;
+ }
+
+ snprintf(irq_name, sizeof(irq_name), "vsync-%s",
+ dev_name(vtg->dev));
+
+ RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list);
+
+ ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq,
+ vtg_irq_thread, IRQF_ONESHOT, irq_name, vtg);
+ if (IS_ERR_VALUE(ret)) {
+ DRM_ERROR("Failed to register VTG interrupt\n");
+ return ret;
+ }
+ }
+
+ vtg_register(vtg);
+ platform_set_drvdata(pdev, vtg);
+
+ DRM_INFO("%s %s\n", __func__, dev_name(vtg->dev));
+
+ return 0;
+}
+
+static int vtg_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id vtg_of_match[] = {
+ { .compatible = "st,vtg", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vtg_of_match);
+
+struct platform_driver sti_vtg_driver = {
+ .driver = {
+ .name = "sti-vtg",
+ .owner = THIS_MODULE,
+ .of_match_table = vtg_of_match,
+ },
+ .probe = vtg_probe,
+ .remove = vtg_remove,
+};
+
+module_platform_driver(sti_vtg_driver);
+
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sti/sti_vtg.h b/drivers/gpu/drm/sti/sti_vtg.h
new file mode 100644
index 000000000000..e84d23f1f57f
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtg.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_VTG_H_
+#define _STI_VTG_H_
+
+#define VTG_TOP_FIELD_EVENT 1
+#define VTG_BOTTOM_FIELD_EVENT 2
+
+struct sti_vtg;
+struct drm_display_mode;
+struct notifier_block;
+
+struct sti_vtg *of_vtg_find(struct device_node *np);
+void sti_vtg_set_config(struct sti_vtg *vtg,
+ const struct drm_display_mode *mode);
+int sti_vtg_register_client(struct sti_vtg *vtg,
+ struct notifier_block *nb, int crtc_id);
+int sti_vtg_unregister_client(struct sti_vtg *vtg,
+ struct notifier_block *nb);
+
+u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y);
+u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x);
+
+#endif
diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c
index 3492ca5c46d3..fab5ebcb0fef 100644
--- a/drivers/gpu/drm/tdfx/tdfx_drv.c
+++ b/drivers/gpu/drm/tdfx/tdfx_drv.c
@@ -36,6 +36,7 @@
#include "tdfx_drv.h"
#include <drm/drm_pciids.h>
+#include <drm/drm_legacy.h>
static struct pci_device_id pciidlist[] = {
tdfx_PCI_IDS
@@ -46,7 +47,7 @@ static const struct file_operations tdfx_driver_fops = {
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
- .mmap = drm_mmap,
+ .mmap = drm_legacy_mmap,
.poll = drm_poll,
#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
@@ -55,6 +56,7 @@ static const struct file_operations tdfx_driver_fops = {
};
static struct drm_driver driver = {
+ .set_busid = drm_pci_set_busid,
.fops = &tdfx_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index ef40381f3909..054a79f143ae 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -18,6 +18,8 @@
struct tegra_dc_soc_info {
bool supports_interlacing;
bool supports_cursor;
+ bool supports_block_linear;
+ unsigned int pitch_align;
};
struct tegra_plane {
@@ -212,15 +214,44 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
- if (window->tiled) {
- value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
- DC_WIN_BUFFER_ADDR_MODE_TILE;
+ if (dc->soc->supports_block_linear) {
+ unsigned long height = window->tiling.value;
+
+ switch (window->tiling.mode) {
+ case TEGRA_BO_TILING_MODE_PITCH:
+ value = DC_WINBUF_SURFACE_KIND_PITCH;
+ break;
+
+ case TEGRA_BO_TILING_MODE_TILED:
+ value = DC_WINBUF_SURFACE_KIND_TILED;
+ break;
+
+ case TEGRA_BO_TILING_MODE_BLOCK:
+ value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
+ DC_WINBUF_SURFACE_KIND_BLOCK;
+ break;
+ }
+
+ tegra_dc_writel(dc, value, DC_WINBUF_SURFACE_KIND);
} else {
- value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
- DC_WIN_BUFFER_ADDR_MODE_LINEAR;
- }
+ switch (window->tiling.mode) {
+ case TEGRA_BO_TILING_MODE_PITCH:
+ value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
+ DC_WIN_BUFFER_ADDR_MODE_LINEAR;
+ break;
+
+ case TEGRA_BO_TILING_MODE_TILED:
+ value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
+ DC_WIN_BUFFER_ADDR_MODE_TILE;
+ break;
- tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
+ case TEGRA_BO_TILING_MODE_BLOCK:
+ DRM_ERROR("hardware doesn't support block linear mode\n");
+ return -EINVAL;
+ }
+
+ tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
+ }
value = WIN_ENABLE;
@@ -288,6 +319,7 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
struct tegra_dc *dc = to_tegra_dc(crtc);
struct tegra_dc_window window;
unsigned int i;
+ int err;
memset(&window, 0, sizeof(window));
window.src.x = src_x >> 16;
@@ -301,7 +333,10 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
window.format = tegra_dc_format(fb->pixel_format, &window.swap);
window.bits_per_pixel = fb->bits_per_pixel;
window.bottom_up = tegra_fb_is_bottom_up(fb);
- window.tiled = tegra_fb_is_tiled(fb);
+
+ err = tegra_fb_get_tiling(fb, &window.tiling);
+ if (err < 0)
+ return err;
for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
@@ -402,8 +437,14 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
{
struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
unsigned int h_offset = 0, v_offset = 0;
+ struct tegra_bo_tiling tiling;
unsigned int format, swap;
unsigned long value;
+ int err;
+
+ err = tegra_fb_get_tiling(fb, &tiling);
+ if (err < 0)
+ return err;
tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
@@ -417,15 +458,44 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH);
tegra_dc_writel(dc, swap, DC_WIN_BYTE_SWAP);
- if (tegra_fb_is_tiled(fb)) {
- value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
- DC_WIN_BUFFER_ADDR_MODE_TILE;
+ if (dc->soc->supports_block_linear) {
+ unsigned long height = tiling.value;
+
+ switch (tiling.mode) {
+ case TEGRA_BO_TILING_MODE_PITCH:
+ value = DC_WINBUF_SURFACE_KIND_PITCH;
+ break;
+
+ case TEGRA_BO_TILING_MODE_TILED:
+ value = DC_WINBUF_SURFACE_KIND_TILED;
+ break;
+
+ case TEGRA_BO_TILING_MODE_BLOCK:
+ value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
+ DC_WINBUF_SURFACE_KIND_BLOCK;
+ break;
+ }
+
+ tegra_dc_writel(dc, value, DC_WINBUF_SURFACE_KIND);
} else {
- value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
- DC_WIN_BUFFER_ADDR_MODE_LINEAR;
- }
+ switch (tiling.mode) {
+ case TEGRA_BO_TILING_MODE_PITCH:
+ value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
+ DC_WIN_BUFFER_ADDR_MODE_LINEAR;
+ break;
+
+ case TEGRA_BO_TILING_MODE_TILED:
+ value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
+ DC_WIN_BUFFER_ADDR_MODE_TILE;
+ break;
+
+ case TEGRA_BO_TILING_MODE_BLOCK:
+ DRM_ERROR("hardware doesn't support block linear mode\n");
+ return -EINVAL;
+ }
- tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
+ tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
+ }
/* make sure bottom-up buffers are properly displayed */
if (tegra_fb_is_bottom_up(fb)) {
@@ -666,7 +736,6 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = {
static void tegra_crtc_disable(struct drm_crtc *crtc)
{
- struct tegra_dc *dc = to_tegra_dc(crtc);
struct drm_device *drm = crtc->dev;
struct drm_plane *plane;
@@ -682,7 +751,7 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
}
}
- drm_vblank_off(drm, dc->pipe);
+ drm_crtc_vblank_off(crtc);
}
static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -771,8 +840,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
u32 value;
int err;
- drm_vblank_pre_modeset(crtc->dev, dc->pipe);
-
err = tegra_crtc_setup_clk(crtc, mode);
if (err) {
dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
@@ -826,6 +893,8 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
unsigned int syncpt;
unsigned long value;
+ drm_crtc_vblank_off(crtc);
+
/* hardware initialization */
reset_control_deassert(dc->rst);
usleep_range(10000, 20000);
@@ -873,7 +942,7 @@ static void tegra_crtc_commit(struct drm_crtc *crtc)
value = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
- drm_vblank_post_modeset(crtc->dev, dc->pipe);
+ drm_crtc_vblank_on(crtc);
}
static void tegra_crtc_load_lut(struct drm_crtc *crtc)
@@ -1214,12 +1283,20 @@ static int tegra_dc_init(struct host1x_client *client)
{
struct drm_device *drm = dev_get_drvdata(client->parent);
struct tegra_dc *dc = host1x_client_to_dc(client);
+ struct tegra_drm *tegra = drm->dev_private;
int err;
drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs);
drm_mode_crtc_set_gamma_size(&dc->base, 256);
drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
+ /*
+ * Keep track of the minimum pitch alignment across all display
+ * controllers.
+ */
+ if (dc->soc->pitch_align > tegra->pitch_align)
+ tegra->pitch_align = dc->soc->pitch_align;
+
err = tegra_dc_rgb_init(drm, dc);
if (err < 0 && err != -ENODEV) {
dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
@@ -1277,16 +1354,29 @@ static const struct host1x_client_ops dc_client_ops = {
static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
.supports_interlacing = false,
.supports_cursor = false,
+ .supports_block_linear = false,
+ .pitch_align = 8,
};
static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
.supports_interlacing = false,
.supports_cursor = false,
+ .supports_block_linear = false,
+ .pitch_align = 8,
+};
+
+static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
+ .supports_interlacing = false,
+ .supports_cursor = false,
+ .supports_block_linear = false,
+ .pitch_align = 64,
};
static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
.supports_interlacing = true,
.supports_cursor = true,
+ .supports_block_linear = true,
+ .pitch_align = 64,
};
static const struct of_device_id tegra_dc_of_match[] = {
@@ -1303,6 +1393,7 @@ static const struct of_device_id tegra_dc_of_match[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, tegra_dc_of_match);
static int tegra_dc_parse_dt(struct tegra_dc *dc)
{
@@ -1430,6 +1521,7 @@ static int tegra_dc_remove(struct platform_device *pdev)
return err;
}
+ reset_control_assert(dc->rst);
clk_disable_unprepare(dc->clk);
return 0;
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 78c5feff95d2..705c93b00794 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -428,6 +428,11 @@
#define DC_WINBUF_ADDR_V_OFFSET_NS 0x809
#define DC_WINBUF_UFLOW_STATUS 0x80a
+#define DC_WINBUF_SURFACE_KIND 0x80b
+#define DC_WINBUF_SURFACE_KIND_PITCH (0 << 0)
+#define DC_WINBUF_SURFACE_KIND_TILED (1 << 0)
+#define DC_WINBUF_SURFACE_KIND_BLOCK (2 << 0)
+#define DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(x) (((x) & 0x7) << 4)
#define DC_WINBUF_AD_UFLOW_STATUS 0xbca
#define DC_WINBUF_BD_UFLOW_STATUS 0xdca
diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c
index 3f132e356e9c..d6b55e3e3716 100644
--- a/drivers/gpu/drm/tegra/dpaux.c
+++ b/drivers/gpu/drm/tegra/dpaux.c
@@ -382,6 +382,7 @@ static const struct of_device_id tegra_dpaux_of_match[] = {
{ .compatible = "nvidia,tegra124-dpaux", },
{ },
};
+MODULE_DEVICE_TABLE(of, tegra_dpaux_of_match);
struct platform_driver tegra_dpaux_driver = {
.driver = {
@@ -532,9 +533,9 @@ int tegra_dpaux_train(struct tegra_dpaux *dpaux, struct drm_dp_link *link,
for (i = 0; i < link->num_lanes; i++)
values[i] = DP_TRAIN_MAX_PRE_EMPHASIS_REACHED |
- DP_TRAIN_PRE_EMPHASIS_0 |
+ DP_TRAIN_PRE_EMPH_LEVEL_0 |
DP_TRAIN_MAX_SWING_REACHED |
- DP_TRAIN_VOLTAGE_SWING_400;
+ DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
err = drm_dp_dpcd_write(&dpaux->aux, DP_TRAINING_LANE0_SET, values,
link->num_lanes);
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 3396f9f6a9f7..59736bb810cd 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -40,6 +40,12 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
drm_mode_config_init(drm);
+ err = tegra_drm_fb_prepare(drm);
+ if (err < 0)
+ return err;
+
+ drm_kms_helper_poll_init(drm);
+
err = host1x_device_init(device);
if (err < 0)
return err;
@@ -59,8 +65,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
if (err < 0)
return err;
- drm_kms_helper_poll_init(drm);
-
return 0;
}
@@ -128,6 +132,45 @@ host1x_bo_lookup(struct drm_device *drm, struct drm_file *file, u32 handle)
return &bo->base;
}
+static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
+ struct drm_tegra_reloc __user *src,
+ struct drm_device *drm,
+ struct drm_file *file)
+{
+ u32 cmdbuf, target;
+ int err;
+
+ err = get_user(cmdbuf, &src->cmdbuf.handle);
+ if (err < 0)
+ return err;
+
+ err = get_user(dest->cmdbuf.offset, &src->cmdbuf.offset);
+ if (err < 0)
+ return err;
+
+ err = get_user(target, &src->target.handle);
+ if (err < 0)
+ return err;
+
+ err = get_user(dest->target.offset, &src->cmdbuf.offset);
+ if (err < 0)
+ return err;
+
+ err = get_user(dest->shift, &src->shift);
+ if (err < 0)
+ return err;
+
+ dest->cmdbuf.bo = host1x_bo_lookup(drm, file, cmdbuf);
+ if (!dest->cmdbuf.bo)
+ return -ENOENT;
+
+ dest->target.bo = host1x_bo_lookup(drm, file, target);
+ if (!dest->target.bo)
+ return -ENOENT;
+
+ return 0;
+}
+
int tegra_drm_submit(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file)
@@ -180,26 +223,13 @@ int tegra_drm_submit(struct tegra_drm_context *context,
cmdbufs++;
}
- if (copy_from_user(job->relocarray, relocs,
- sizeof(*relocs) * num_relocs)) {
- err = -EFAULT;
- goto fail;
- }
-
+ /* copy and resolve relocations from submit */
while (num_relocs--) {
- struct host1x_reloc *reloc = &job->relocarray[num_relocs];
- struct host1x_bo *cmdbuf, *target;
-
- cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf);
- target = host1x_bo_lookup(drm, file, (u32)reloc->target);
-
- reloc->cmdbuf = cmdbuf;
- reloc->target = target;
-
- if (!reloc->target || !reloc->cmdbuf) {
- err = -ENOENT;
+ err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs],
+ &relocs[num_relocs], drm,
+ file);
+ if (err < 0)
goto fail;
- }
}
if (copy_from_user(job->waitchk, waitchks,
@@ -451,11 +481,151 @@ static int tegra_get_syncpt_base(struct drm_device *drm, void *data,
return 0;
}
+
+static int tegra_gem_set_tiling(struct drm_device *drm, void *data,
+ struct drm_file *file)
+{
+ struct drm_tegra_gem_set_tiling *args = data;
+ enum tegra_bo_tiling_mode mode;
+ struct drm_gem_object *gem;
+ unsigned long value = 0;
+ struct tegra_bo *bo;
+
+ switch (args->mode) {
+ case DRM_TEGRA_GEM_TILING_MODE_PITCH:
+ mode = TEGRA_BO_TILING_MODE_PITCH;
+
+ if (args->value != 0)
+ return -EINVAL;
+
+ break;
+
+ case DRM_TEGRA_GEM_TILING_MODE_TILED:
+ mode = TEGRA_BO_TILING_MODE_TILED;
+
+ if (args->value != 0)
+ return -EINVAL;
+
+ break;
+
+ case DRM_TEGRA_GEM_TILING_MODE_BLOCK:
+ mode = TEGRA_BO_TILING_MODE_BLOCK;
+
+ if (args->value > 5)
+ return -EINVAL;
+
+ value = args->value;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ gem = drm_gem_object_lookup(drm, file, args->handle);
+ if (!gem)
+ return -ENOENT;
+
+ bo = to_tegra_bo(gem);
+
+ bo->tiling.mode = mode;
+ bo->tiling.value = value;
+
+ drm_gem_object_unreference(gem);
+
+ return 0;
+}
+
+static int tegra_gem_get_tiling(struct drm_device *drm, void *data,
+ struct drm_file *file)
+{
+ struct drm_tegra_gem_get_tiling *args = data;
+ struct drm_gem_object *gem;
+ struct tegra_bo *bo;
+ int err = 0;
+
+ gem = drm_gem_object_lookup(drm, file, args->handle);
+ if (!gem)
+ return -ENOENT;
+
+ bo = to_tegra_bo(gem);
+
+ switch (bo->tiling.mode) {
+ case TEGRA_BO_TILING_MODE_PITCH:
+ args->mode = DRM_TEGRA_GEM_TILING_MODE_PITCH;
+ args->value = 0;
+ break;
+
+ case TEGRA_BO_TILING_MODE_TILED:
+ args->mode = DRM_TEGRA_GEM_TILING_MODE_TILED;
+ args->value = 0;
+ break;
+
+ case TEGRA_BO_TILING_MODE_BLOCK:
+ args->mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
+ args->value = bo->tiling.value;
+ break;
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ drm_gem_object_unreference(gem);
+
+ return err;
+}
+
+static int tegra_gem_set_flags(struct drm_device *drm, void *data,
+ struct drm_file *file)
+{
+ struct drm_tegra_gem_set_flags *args = data;
+ struct drm_gem_object *gem;
+ struct tegra_bo *bo;
+
+ if (args->flags & ~DRM_TEGRA_GEM_FLAGS)
+ return -EINVAL;
+
+ gem = drm_gem_object_lookup(drm, file, args->handle);
+ if (!gem)
+ return -ENOENT;
+
+ bo = to_tegra_bo(gem);
+ bo->flags = 0;
+
+ if (args->flags & DRM_TEGRA_GEM_BOTTOM_UP)
+ bo->flags |= TEGRA_BO_BOTTOM_UP;
+
+ drm_gem_object_unreference(gem);
+
+ return 0;
+}
+
+static int tegra_gem_get_flags(struct drm_device *drm, void *data,
+ struct drm_file *file)
+{
+ struct drm_tegra_gem_get_flags *args = data;
+ struct drm_gem_object *gem;
+ struct tegra_bo *bo;
+
+ gem = drm_gem_object_lookup(drm, file, args->handle);
+ if (!gem)
+ return -ENOENT;
+
+ bo = to_tegra_bo(gem);
+ args->flags = 0;
+
+ if (bo->flags & TEGRA_BO_BOTTOM_UP)
+ args->flags |= DRM_TEGRA_GEM_BOTTOM_UP;
+
+ drm_gem_object_unreference(gem);
+
+ return 0;
+}
#endif
static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
#ifdef CONFIG_DRM_TEGRA_STAGING
- DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_UNLOCKED | DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, DRM_UNLOCKED),
@@ -465,6 +635,10 @@ static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base, DRM_UNLOCKED),
+ DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_TILING, tegra_gem_set_tiling, DRM_UNLOCKED),
+ DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_TILING, tegra_gem_get_tiling, DRM_UNLOCKED),
+ DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_FLAGS, tegra_gem_set_flags, DRM_UNLOCKED),
+ DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_FLAGS, tegra_gem_get_flags, DRM_UNLOCKED),
#endif
};
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 6b8fe9d86ed4..e89c70fa82d5 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -19,6 +19,8 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_fixed.h>
+#include "gem.h"
+
struct reset_control;
struct tegra_fb {
@@ -43,6 +45,8 @@ struct tegra_drm {
#ifdef CONFIG_DRM_TEGRA_FBDEV
struct tegra_fbdev *fbdev;
#endif
+
+ unsigned int pitch_align;
};
struct tegra_drm_client;
@@ -160,7 +164,8 @@ struct tegra_dc_window {
unsigned int stride[2];
unsigned long base[3];
bool bottom_up;
- bool tiled;
+
+ struct tegra_bo_tiling tiling;
};
/* from dc.c */
@@ -279,7 +284,9 @@ int tegra_dpaux_train(struct tegra_dpaux *dpaux, struct drm_dp_link *link,
struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
unsigned int index);
bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
-bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer);
+int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
+ struct tegra_bo_tiling *tiling);
+int tegra_drm_fb_prepare(struct drm_device *drm);
int tegra_drm_fb_init(struct drm_device *drm);
void tegra_drm_fb_exit(struct drm_device *drm);
#ifdef CONFIG_DRM_TEGRA_FBDEV
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index bd56f2affa78..f7874458926a 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -474,7 +474,8 @@ static int tegra_output_dsi_enable(struct tegra_output *output)
tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
value = tegra_dsi_readl(dsi, DSI_CONTROL);
- value |= DSI_CONTROL_HS_CLK_CTRL;
+ if (dsi->flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
+ value |= DSI_CONTROL_HS_CLK_CTRL;
value &= ~DSI_CONTROL_TX_TRIG(3);
value &= ~DSI_CONTROL_DCS_ENABLE;
value |= DSI_CONTROL_VIDEO_ENABLE;
@@ -982,6 +983,7 @@ static const struct of_device_id tegra_dsi_of_match[] = {
{ .compatible = "nvidia,tegra114-dsi", },
{ },
};
+MODULE_DEVICE_TABLE(of, tegra_dsi_of_match);
struct platform_driver tegra_dsi_driver = {
.driver = {
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index 9798a7080322..3513d12d5aa1 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -46,14 +46,15 @@ bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer)
return false;
}
-bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer)
+int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
+ struct tegra_bo_tiling *tiling)
{
struct tegra_fb *fb = to_tegra_fb(framebuffer);
- if (fb->planes[0]->flags & TEGRA_BO_TILED)
- return true;
+ /* TODO: handle YUV formats? */
+ *tiling = fb->planes[0]->tiling;
- return false;
+ return 0;
}
static void tegra_fb_destroy(struct drm_framebuffer *framebuffer)
@@ -193,6 +194,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
struct tegra_fbdev *fbdev = to_tegra_fbdev(helper);
+ struct tegra_drm *tegra = helper->dev->dev_private;
struct drm_device *drm = helper->dev;
struct drm_mode_fb_cmd2 cmd = { 0 };
unsigned int bytes_per_pixel;
@@ -207,7 +209,8 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper,
cmd.width = sizes->surface_width;
cmd.height = sizes->surface_height;
- cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
+ cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel,
+ tegra->pitch_align);
cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
@@ -267,18 +270,13 @@ release:
return err;
}
-static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
.fb_probe = tegra_fbdev_probe,
};
-static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
- unsigned int preferred_bpp,
- unsigned int num_crtc,
- unsigned int max_connectors)
+static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm)
{
- struct drm_fb_helper *helper;
struct tegra_fbdev *fbdev;
- int err;
fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
if (!fbdev) {
@@ -286,13 +284,23 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
return ERR_PTR(-ENOMEM);
}
- fbdev->base.funcs = &tegra_fb_helper_funcs;
- helper = &fbdev->base;
+ drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
+
+ return fbdev;
+}
+
+static int tegra_fbdev_init(struct tegra_fbdev *fbdev,
+ unsigned int preferred_bpp,
+ unsigned int num_crtc,
+ unsigned int max_connectors)
+{
+ struct drm_device *drm = fbdev->base.dev;
+ int err;
err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
if (err < 0) {
dev_err(drm->dev, "failed to initialize DRM FB helper\n");
- goto free;
+ return err;
}
err = drm_fb_helper_single_add_all_connectors(&fbdev->base);
@@ -301,21 +309,17 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
goto fini;
}
- drm_helper_disable_unused_functions(drm);
-
err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp);
if (err < 0) {
dev_err(drm->dev, "failed to set initial configuration\n");
goto fini;
}
- return fbdev;
+ return 0;
fini:
drm_fb_helper_fini(&fbdev->base);
-free:
- kfree(fbdev);
- return ERR_PTR(err);
+ return err;
}
static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
@@ -366,7 +370,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
#endif
};
-int tegra_drm_fb_init(struct drm_device *drm)
+int tegra_drm_fb_prepare(struct drm_device *drm)
{
#ifdef CONFIG_DRM_TEGRA_FBDEV
struct tegra_drm *tegra = drm->dev_private;
@@ -381,8 +385,7 @@ int tegra_drm_fb_init(struct drm_device *drm)
drm->mode_config.funcs = &tegra_drm_mode_funcs;
#ifdef CONFIG_DRM_TEGRA_FBDEV
- tegra->fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc,
- drm->mode_config.num_connector);
+ tegra->fbdev = tegra_fbdev_create(drm);
if (IS_ERR(tegra->fbdev))
return PTR_ERR(tegra->fbdev);
#endif
@@ -390,6 +393,21 @@ int tegra_drm_fb_init(struct drm_device *drm)
return 0;
}
+int tegra_drm_fb_init(struct drm_device *drm)
+{
+#ifdef CONFIG_DRM_TEGRA_FBDEV
+ struct tegra_drm *tegra = drm->dev_private;
+ int err;
+
+ err = tegra_fbdev_init(tegra->fbdev, 32, drm->mode_config.num_crtc,
+ drm->mode_config.num_connector);
+ if (err < 0)
+ return err;
+#endif
+
+ return 0;
+}
+
void tegra_drm_fb_exit(struct drm_device *drm)
{
#ifdef CONFIG_DRM_TEGRA_FBDEV
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c
index 78cc8143760a..ce023fa3e8ae 100644
--- a/drivers/gpu/drm/tegra/gem.c
+++ b/drivers/gpu/drm/tegra/gem.c
@@ -16,6 +16,7 @@
#include <linux/dma-buf.h>
#include <drm/tegra_drm.h>
+#include "drm.h"
#include "gem.h"
static inline struct tegra_bo *host1x_to_tegra_bo(struct host1x_bo *bo)
@@ -126,7 +127,7 @@ struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size,
goto err_mmap;
if (flags & DRM_TEGRA_GEM_CREATE_TILED)
- bo->flags |= TEGRA_BO_TILED;
+ bo->tiling.mode = TEGRA_BO_TILING_MODE_TILED;
if (flags & DRM_TEGRA_GEM_CREATE_BOTTOM_UP)
bo->flags |= TEGRA_BO_BOTTOM_UP;
@@ -259,8 +260,10 @@ int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm,
struct drm_mode_create_dumb *args)
{
int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+ struct tegra_drm *tegra = drm->dev_private;
struct tegra_bo *bo;
+ min_pitch = round_up(min_pitch, tegra->pitch_align);
if (args->pitch < min_pitch)
args->pitch = min_pitch;
diff --git a/drivers/gpu/drm/tegra/gem.h b/drivers/gpu/drm/tegra/gem.h
index 2f3fe96c5154..6538b56780c2 100644
--- a/drivers/gpu/drm/tegra/gem.h
+++ b/drivers/gpu/drm/tegra/gem.h
@@ -15,9 +15,20 @@
#include <drm/drm.h>
#include <drm/drmP.h>
+#include <drm/drm_gem.h>
-#define TEGRA_BO_TILED (1 << 0)
-#define TEGRA_BO_BOTTOM_UP (1 << 1)
+#define TEGRA_BO_BOTTOM_UP (1 << 0)
+
+enum tegra_bo_tiling_mode {
+ TEGRA_BO_TILING_MODE_PITCH,
+ TEGRA_BO_TILING_MODE_TILED,
+ TEGRA_BO_TILING_MODE_BLOCK,
+};
+
+struct tegra_bo_tiling {
+ enum tegra_bo_tiling_mode mode;
+ unsigned long value;
+};
struct tegra_bo {
struct drm_gem_object gem;
@@ -26,6 +37,8 @@ struct tegra_bo {
struct sg_table *sgt;
dma_addr_t paddr;
void *vaddr;
+
+ struct tegra_bo_tiling tiling;
};
static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem)
diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c
index 7c53941f2a9e..02cd3e37a6ec 100644
--- a/drivers/gpu/drm/tegra/gr2d.c
+++ b/drivers/gpu/drm/tegra/gr2d.c
@@ -121,6 +121,7 @@ static const struct of_device_id gr2d_match[] = {
{ .compatible = "nvidia,tegra20-gr2d" },
{ },
};
+MODULE_DEVICE_TABLE(of, gr2d_match);
static const u32 gr2d_addr_regs[] = {
GR2D_UA_BASE_ADDR,
diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c
index 30f5ba9bd6d0..0b3f2b977ba0 100644
--- a/drivers/gpu/drm/tegra/gr3d.c
+++ b/drivers/gpu/drm/tegra/gr3d.c
@@ -12,7 +12,8 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
-#include <linux/tegra-powergate.h>
+
+#include <soc/tegra/pmc.h>
#include "drm.h"
#include "gem.h"
@@ -130,6 +131,7 @@ static const struct of_device_id tegra_gr3d_match[] = {
{ .compatible = "nvidia,tegra20-gr3d" },
{ }
};
+MODULE_DEVICE_TABLE(of, tegra_gr3d_match);
static const u32 gr3d_addr_regs[] = {
GR3D_IDX_ATTRIBUTE( 0),
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index ba067bb767e3..ffe26547328d 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1450,6 +1450,7 @@ static const struct of_device_id tegra_hdmi_of_match[] = {
{ .compatible = "nvidia,tegra20-hdmi", .data = &tegra20_hdmi_config },
{ },
};
+MODULE_DEVICE_TABLE(of, tegra_hdmi_of_match);
static int tegra_hdmi_probe(struct platform_device *pdev)
{
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index a3e4f1eca6f7..0c67d7eebc94 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -105,7 +105,7 @@ static void drm_connector_clear(struct drm_connector *connector)
static void tegra_connector_destroy(struct drm_connector *connector)
{
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
drm_connector_clear(connector);
}
@@ -140,7 +140,9 @@ static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
if (mode != DRM_MODE_DPMS_ON) {
drm_panel_disable(panel);
tegra_output_disable(output);
+ drm_panel_unprepare(panel);
} else {
+ drm_panel_prepare(panel);
tegra_output_enable(output);
drm_panel_enable(panel);
}
@@ -318,7 +320,7 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs);
drm_mode_connector_attach_encoder(&output->connector, &output->encoder);
- drm_sysfs_connector_add(&output->connector);
+ drm_connector_register(&output->connector);
output->encoder.possible_crtcs = 0x3;
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 27c979b50111..7829e81f065d 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -11,7 +11,8 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
-#include <linux/tegra-powergate.h>
+
+#include <soc/tegra/pmc.h>
#include <drm/drm_dp_helper.h>
@@ -516,7 +517,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
if (err < 0) {
dev_err(sor->dev, "failed to probe eDP link: %d\n",
err);
- return err;
+ goto unlock;
}
}
@@ -525,7 +526,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
memset(&config, 0, sizeof(config));
- config.bits_per_pixel = 24; /* XXX: don't hardcode? */
+ config.bits_per_pixel = output->connector.display_info.bpc * 3;
err = tegra_sor_calc_config(sor, mode, &config, &link);
if (err < 0)
@@ -815,12 +816,22 @@ static int tegra_output_sor_enable(struct tegra_output *output)
* configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete
* raster, associate with display controller)
*/
- value = SOR_STATE_ASY_VSYNCPOL |
- SOR_STATE_ASY_HSYNCPOL |
- SOR_STATE_ASY_PROTOCOL_DP_A |
+ value = SOR_STATE_ASY_PROTOCOL_DP_A |
SOR_STATE_ASY_CRC_MODE_COMPLETE |
SOR_STATE_ASY_OWNER(dc->pipe + 1);
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ value &= ~SOR_STATE_ASY_HSYNCPOL;
+
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ value |= SOR_STATE_ASY_HSYNCPOL;
+
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ value &= ~SOR_STATE_ASY_VSYNCPOL;
+
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ value |= SOR_STATE_ASY_VSYNCPOL;
+
switch (config.bits_per_pixel) {
case 24:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444;
@@ -1455,6 +1466,7 @@ static const struct of_device_id tegra_sor_of_match[] = {
{ .compatible = "nvidia,tegra124-sor", },
{ },
};
+MODULE_DEVICE_TABLE(of, tegra_sor_of_match);
struct platform_driver tegra_sor_driver = {
.driver = {
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
index b20b69488dc9..79a34cbd29f5 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
@@ -84,6 +84,7 @@ static int modeset_init(struct drm_device *dev)
if ((priv->num_encoders == 0) || (priv->num_connectors == 0)) {
/* oh nos! */
dev_err(dev->dev, "no encoders/connectors found\n");
+ drm_mode_config_cleanup(dev);
return -ENXIO;
}
@@ -120,8 +121,8 @@ static int cpufreq_transition(struct notifier_block *nb,
static int tilcdc_unload(struct drm_device *dev)
{
struct tilcdc_drm_private *priv = dev->dev_private;
- struct tilcdc_module *mod, *cur;
+ drm_fbdev_cma_fini(priv->fbdev);
drm_kms_helper_poll_fini(dev);
drm_mode_config_cleanup(dev);
drm_vblank_cleanup(dev);
@@ -148,11 +149,6 @@ static int tilcdc_unload(struct drm_device *dev)
pm_runtime_disable(dev->dev);
- list_for_each_entry_safe(mod, cur, &module_list, list) {
- DBG("destroying module: %s", mod->name);
- mod->funcs->destroy(mod);
- }
-
kfree(priv);
return 0;
@@ -177,33 +173,37 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
dev->dev_private = priv;
priv->wq = alloc_ordered_workqueue("tilcdc", 0);
+ if (!priv->wq) {
+ ret = -ENOMEM;
+ goto fail_free_priv;
+ }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev->dev, "failed to get memory resource\n");
ret = -EINVAL;
- goto fail;
+ goto fail_free_wq;
}
priv->mmio = ioremap_nocache(res->start, resource_size(res));
if (!priv->mmio) {
dev_err(dev->dev, "failed to ioremap\n");
ret = -ENOMEM;
- goto fail;
+ goto fail_free_wq;
}
priv->clk = clk_get(dev->dev, "fck");
if (IS_ERR(priv->clk)) {
dev_err(dev->dev, "failed to get functional clock\n");
ret = -ENODEV;
- goto fail;
+ goto fail_iounmap;
}
priv->disp_clk = clk_get(dev->dev, "dpll_disp_ck");
if (IS_ERR(priv->clk)) {
dev_err(dev->dev, "failed to get display clock\n");
ret = -ENODEV;
- goto fail;
+ goto fail_put_clk;
}
#ifdef CONFIG_CPU_FREQ
@@ -213,7 +213,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
CPUFREQ_TRANSITION_NOTIFIER);
if (ret) {
dev_err(dev->dev, "failed to register cpufreq notifier\n");
- goto fail;
+ goto fail_put_disp_clk;
}
#endif
@@ -258,13 +258,13 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
ret = modeset_init(dev);
if (ret < 0) {
dev_err(dev->dev, "failed to initialize mode setting\n");
- goto fail;
+ goto fail_cpufreq_unregister;
}
ret = drm_vblank_init(dev, 1);
if (ret < 0) {
dev_err(dev->dev, "failed to initialize vblank\n");
- goto fail;
+ goto fail_mode_config_cleanup;
}
pm_runtime_get_sync(dev->dev);
@@ -272,7 +272,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
pm_runtime_put_sync(dev->dev);
if (ret < 0) {
dev_err(dev->dev, "failed to install IRQ handler\n");
- goto fail;
+ goto fail_vblank_cleanup;
}
platform_set_drvdata(pdev, dev);
@@ -288,13 +288,48 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
priv->fbdev = drm_fbdev_cma_init(dev, bpp,
dev->mode_config.num_crtc,
dev->mode_config.num_connector);
+ if (IS_ERR(priv->fbdev)) {
+ ret = PTR_ERR(priv->fbdev);
+ goto fail_irq_uninstall;
+ }
drm_kms_helper_poll_init(dev);
return 0;
-fail:
- tilcdc_unload(dev);
+fail_irq_uninstall:
+ pm_runtime_get_sync(dev->dev);
+ drm_irq_uninstall(dev);
+ pm_runtime_put_sync(dev->dev);
+
+fail_vblank_cleanup:
+ drm_vblank_cleanup(dev);
+
+fail_mode_config_cleanup:
+ drm_mode_config_cleanup(dev);
+
+fail_cpufreq_unregister:
+ pm_runtime_disable(dev->dev);
+#ifdef CONFIG_CPU_FREQ
+ cpufreq_unregister_notifier(&priv->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+fail_put_disp_clk:
+ clk_put(priv->disp_clk);
+#endif
+
+fail_put_clk:
+ clk_put(priv->clk);
+
+fail_iounmap:
+ iounmap(priv->mmio);
+
+fail_free_wq:
+ flush_workqueue(priv->wq);
+ destroy_workqueue(priv->wq);
+
+fail_free_priv:
+ dev->dev_private = NULL;
+ kfree(priv);
return ret;
}
@@ -507,6 +542,7 @@ static struct drm_driver tilcdc_driver = {
.unload = tilcdc_unload,
.preclose = tilcdc_preclose,
.lastclose = tilcdc_lastclose,
+ .set_busid = drm_platform_set_busid,
.irq_handler = tilcdc_irq,
.irq_preinstall = tilcdc_irq_preinstall,
.irq_postinstall = tilcdc_irq_postinstall,
@@ -628,13 +664,13 @@ static int __init tilcdc_drm_init(void)
static void __exit tilcdc_drm_fini(void)
{
DBG("fini");
- tilcdc_tfp410_fini();
- tilcdc_slave_fini();
- tilcdc_panel_fini();
platform_driver_unregister(&tilcdc_platform_driver);
+ tilcdc_panel_fini();
+ tilcdc_slave_fini();
+ tilcdc_tfp410_fini();
}
-late_initcall(tilcdc_drm_init);
+module_init(tilcdc_drm_init);
module_exit(tilcdc_drm_fini);
MODULE_AUTHOR("Rob Clark <robdclark@gmail.com");
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h
index 093803683b25..7596c144a9fb 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h
@@ -98,7 +98,6 @@ struct tilcdc_module;
struct tilcdc_module_ops {
/* create appropriate encoders/connectors: */
int (*modeset_init)(struct tilcdc_module *mod, struct drm_device *dev);
- void (*destroy)(struct tilcdc_module *mod);
#ifdef CONFIG_DEBUG_FS
/* create debugfs nodes (can be NULL): */
int (*debugfs_init)(struct tilcdc_module *mod, struct drm_minor *minor);
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c
index 86c67329b605..7a0315855e90 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c
@@ -18,6 +18,7 @@
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/consumer.h>
#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
#include <video/display_timing.h>
#include <video/of_display_timing.h>
#include <video/videomode.h>
@@ -29,6 +30,7 @@ struct panel_module {
struct tilcdc_panel_info *info;
struct display_timings *timings;
struct backlight_device *backlight;
+ struct gpio_desc *enable_gpio;
};
#define to_panel_module(x) container_of(x, struct panel_module, base)
@@ -55,13 +57,17 @@ static void panel_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct panel_encoder *panel_encoder = to_panel_encoder(encoder);
struct backlight_device *backlight = panel_encoder->mod->backlight;
+ struct gpio_desc *gpio = panel_encoder->mod->enable_gpio;
- if (!backlight)
- return;
+ if (backlight) {
+ backlight->props.power = mode == DRM_MODE_DPMS_ON ?
+ FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+ backlight_update_status(backlight);
+ }
- backlight->props.power = mode == DRM_MODE_DPMS_ON
- ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
- backlight_update_status(backlight);
+ if (gpio)
+ gpiod_set_value_cansleep(gpio,
+ mode == DRM_MODE_DPMS_ON ? 1 : 0);
}
static bool panel_encoder_mode_fixup(struct drm_encoder *encoder,
@@ -151,6 +157,7 @@ struct panel_connector {
static void panel_connector_destroy(struct drm_connector *connector)
{
struct panel_connector *panel_connector = to_panel_connector(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(panel_connector);
}
@@ -247,7 +254,7 @@ static struct drm_connector *panel_connector_create(struct drm_device *dev,
if (ret)
goto fail;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return connector;
@@ -281,23 +288,8 @@ static int panel_modeset_init(struct tilcdc_module *mod, struct drm_device *dev)
return 0;
}
-static void panel_destroy(struct tilcdc_module *mod)
-{
- struct panel_module *panel_mod = to_panel_module(mod);
-
- if (panel_mod->timings) {
- display_timings_release(panel_mod->timings);
- kfree(panel_mod->timings);
- }
-
- tilcdc_module_cleanup(mod);
- kfree(panel_mod->info);
- kfree(panel_mod);
-}
-
static const struct tilcdc_module_ops panel_module_ops = {
.modeset_init = panel_modeset_init,
- .destroy = panel_destroy,
};
/*
@@ -325,6 +317,7 @@ static struct tilcdc_panel_info *of_get_panel_info(struct device_node *np)
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
pr_err("%s: allocation failed\n", __func__);
+ of_node_put(info_np);
return NULL;
}
@@ -345,22 +338,21 @@ static struct tilcdc_panel_info *of_get_panel_info(struct device_node *np)
if (ret) {
pr_err("%s: error reading panel-info properties\n", __func__);
kfree(info);
+ of_node_put(info_np);
return NULL;
}
+ of_node_put(info_np);
return info;
}
-static struct of_device_id panel_of_match[];
-
static int panel_probe(struct platform_device *pdev)
{
- struct device_node *node = pdev->dev.of_node;
+ struct device_node *bl_node, *node = pdev->dev.of_node;
struct panel_module *panel_mod;
struct tilcdc_module *mod;
struct pinctrl *pinctrl;
- int ret = -EINVAL;
-
+ int ret;
/* bail out early if no DT data: */
if (!node) {
@@ -368,11 +360,42 @@ static int panel_probe(struct platform_device *pdev)
return -ENXIO;
}
- panel_mod = kzalloc(sizeof(*panel_mod), GFP_KERNEL);
+ panel_mod = devm_kzalloc(&pdev->dev, sizeof(*panel_mod), GFP_KERNEL);
if (!panel_mod)
return -ENOMEM;
+ bl_node = of_parse_phandle(node, "backlight", 0);
+ if (bl_node) {
+ panel_mod->backlight = of_find_backlight_by_node(bl_node);
+ of_node_put(bl_node);
+
+ if (!panel_mod->backlight)
+ return -EPROBE_DEFER;
+
+ dev_info(&pdev->dev, "found backlight\n");
+ }
+
+ panel_mod->enable_gpio = devm_gpiod_get(&pdev->dev, "enable");
+ if (IS_ERR(panel_mod->enable_gpio)) {
+ ret = PTR_ERR(panel_mod->enable_gpio);
+ if (ret != -ENOENT) {
+ dev_err(&pdev->dev, "failed to request enable GPIO\n");
+ goto fail_backlight;
+ }
+
+ /* Optional GPIO is not here, continue silently. */
+ panel_mod->enable_gpio = NULL;
+ } else {
+ ret = gpiod_direction_output(panel_mod->enable_gpio, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to setup GPIO\n");
+ goto fail_backlight;
+ }
+ dev_info(&pdev->dev, "found enable GPIO\n");
+ }
+
mod = &panel_mod->base;
+ pdev->dev.platform_data = mod;
tilcdc_module_init(mod, "panel", &panel_module_ops);
@@ -380,34 +403,50 @@ static int panel_probe(struct platform_device *pdev)
if (IS_ERR(pinctrl))
dev_warn(&pdev->dev, "pins are not configured\n");
-
panel_mod->timings = of_get_display_timings(node);
if (!panel_mod->timings) {
dev_err(&pdev->dev, "could not get panel timings\n");
- goto fail;
+ ret = -EINVAL;
+ goto fail_free;
}
panel_mod->info = of_get_panel_info(node);
if (!panel_mod->info) {
dev_err(&pdev->dev, "could not get panel info\n");
- goto fail;
+ ret = -EINVAL;
+ goto fail_timings;
}
mod->preferred_bpp = panel_mod->info->bpp;
- panel_mod->backlight = of_find_backlight_by_node(node);
- if (panel_mod->backlight)
- dev_info(&pdev->dev, "found backlight\n");
-
return 0;
-fail:
- panel_destroy(mod);
+fail_timings:
+ display_timings_release(panel_mod->timings);
+
+fail_free:
+ tilcdc_module_cleanup(mod);
+
+fail_backlight:
+ if (panel_mod->backlight)
+ put_device(&panel_mod->backlight->dev);
return ret;
}
static int panel_remove(struct platform_device *pdev)
{
+ struct tilcdc_module *mod = dev_get_platdata(&pdev->dev);
+ struct panel_module *panel_mod = to_panel_module(mod);
+ struct backlight_device *backlight = panel_mod->backlight;
+
+ if (backlight)
+ put_device(&backlight->dev);
+
+ display_timings_release(panel_mod->timings);
+
+ tilcdc_module_cleanup(mod);
+ kfree(panel_mod->info);
+
return 0;
}
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c
index 595068ba2d5e..3775fd49dac4 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c
@@ -166,6 +166,7 @@ struct slave_connector {
static void slave_connector_destroy(struct drm_connector *connector)
{
struct slave_connector *slave_connector = to_slave_connector(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(slave_connector);
}
@@ -261,7 +262,7 @@ static struct drm_connector *slave_connector_create(struct drm_device *dev,
if (ret)
goto fail;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return connector;
@@ -295,17 +296,8 @@ static int slave_modeset_init(struct tilcdc_module *mod, struct drm_device *dev)
return 0;
}
-static void slave_destroy(struct tilcdc_module *mod)
-{
- struct slave_module *slave_mod = to_slave_module(mod);
-
- tilcdc_module_cleanup(mod);
- kfree(slave_mod);
-}
-
static const struct tilcdc_module_ops slave_module_ops = {
.modeset_init = slave_modeset_init,
- .destroy = slave_destroy,
};
/*
@@ -355,10 +347,13 @@ static int slave_probe(struct platform_device *pdev)
}
slave_mod = kzalloc(sizeof(*slave_mod), GFP_KERNEL);
- if (!slave_mod)
- return -ENOMEM;
+ if (!slave_mod) {
+ ret = -ENOMEM;
+ goto fail_adapter;
+ }
mod = &slave_mod->base;
+ pdev->dev.platform_data = mod;
mod->preferred_bpp = slave_info.bpp;
@@ -373,10 +368,20 @@ static int slave_probe(struct platform_device *pdev)
tilcdc_slave_probedefer(false);
return 0;
+
+fail_adapter:
+ i2c_put_adapter(slavei2c);
+ return ret;
}
static int slave_remove(struct platform_device *pdev)
{
+ struct tilcdc_module *mod = dev_get_platdata(&pdev->dev);
+ struct slave_module *slave_mod = to_slave_module(mod);
+
+ tilcdc_module_cleanup(mod);
+ kfree(slave_mod);
+
return 0;
}
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c
index c38b56b268ac..354c47ca6374 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c
@@ -167,6 +167,7 @@ struct tfp410_connector {
static void tfp410_connector_destroy(struct drm_connector *connector)
{
struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(tfp410_connector);
}
@@ -261,7 +262,7 @@ static struct drm_connector *tfp410_connector_create(struct drm_device *dev,
if (ret)
goto fail;
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
return connector;
@@ -295,23 +296,8 @@ static int tfp410_modeset_init(struct tilcdc_module *mod, struct drm_device *dev
return 0;
}
-static void tfp410_destroy(struct tilcdc_module *mod)
-{
- struct tfp410_module *tfp410_mod = to_tfp410_module(mod);
-
- if (tfp410_mod->i2c)
- i2c_put_adapter(tfp410_mod->i2c);
-
- if (!IS_ERR_VALUE(tfp410_mod->gpio))
- gpio_free(tfp410_mod->gpio);
-
- tilcdc_module_cleanup(mod);
- kfree(tfp410_mod);
-}
-
static const struct tilcdc_module_ops tfp410_module_ops = {
.modeset_init = tfp410_modeset_init,
- .destroy = tfp410_destroy,
};
/*
@@ -341,6 +327,7 @@ static int tfp410_probe(struct platform_device *pdev)
return -ENOMEM;
mod = &tfp410_mod->base;
+ pdev->dev.platform_data = mod;
tilcdc_module_init(mod, "tfp410", &tfp410_module_ops);
@@ -364,6 +351,7 @@ static int tfp410_probe(struct platform_device *pdev)
tfp410_mod->i2c = of_find_i2c_adapter_by_node(i2c_node);
if (!tfp410_mod->i2c) {
dev_err(&pdev->dev, "could not get i2c\n");
+ of_node_put(i2c_node);
goto fail;
}
@@ -377,19 +365,32 @@ static int tfp410_probe(struct platform_device *pdev)
ret = gpio_request(tfp410_mod->gpio, "DVI_PDn");
if (ret) {
dev_err(&pdev->dev, "could not get DVI_PDn gpio\n");
- goto fail;
+ goto fail_adapter;
}
}
return 0;
+fail_adapter:
+ i2c_put_adapter(tfp410_mod->i2c);
+
fail:
- tfp410_destroy(mod);
+ kfree(tfp410_mod);
+ tilcdc_module_cleanup(mod);
return ret;
}
static int tfp410_remove(struct platform_device *pdev)
{
+ struct tilcdc_module *mod = dev_get_platdata(&pdev->dev);
+ struct tfp410_module *tfp410_mod = to_tfp410_module(mod);
+
+ i2c_put_adapter(tfp410_mod->i2c);
+ gpio_free(tfp410_mod->gpio);
+
+ tilcdc_module_cleanup(mod);
+ kfree(tfp410_mod);
+
return 0;
}
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 4ab9f7171c4f..d395b0bef73b 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -40,6 +40,7 @@
#include <linux/file.h>
#include <linux/module.h>
#include <linux/atomic.h>
+#include <linux/reservation.h>
#define TTM_ASSERT_LOCKED(param)
#define TTM_DEBUG(fmt, arg...)
@@ -53,12 +54,13 @@ static struct attribute ttm_bo_count = {
.mode = S_IRUGO
};
-static inline int ttm_mem_type_from_flags(uint32_t flags, uint32_t *mem_type)
+static inline int ttm_mem_type_from_place(const struct ttm_place *place,
+ uint32_t *mem_type)
{
int i;
for (i = 0; i <= TTM_PL_PRIV5; i++)
- if (flags & (1 << i)) {
+ if (place->flags & (1 << i)) {
*mem_type = i;
return 0;
}
@@ -89,12 +91,12 @@ static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo,
bo, bo->mem.num_pages, bo->mem.size >> 10,
bo->mem.size >> 20);
for (i = 0; i < placement->num_placement; i++) {
- ret = ttm_mem_type_from_flags(placement->placement[i],
+ ret = ttm_mem_type_from_place(&placement->placement[i],
&mem_type);
if (ret)
return;
pr_err(" placement[%d]=0x%08X (%d)\n",
- i, placement->placement[i], mem_type);
+ i, placement->placement[i].flags, mem_type);
ttm_mem_type_debug(bo->bdev, mem_type);
}
}
@@ -141,7 +143,6 @@ static void ttm_bo_release_list(struct kref *list_kref)
BUG_ON(atomic_read(&bo->list_kref.refcount));
BUG_ON(atomic_read(&bo->kref.refcount));
BUG_ON(atomic_read(&bo->cpu_writers));
- BUG_ON(bo->sync_obj != NULL);
BUG_ON(bo->mem.mm_node != NULL);
BUG_ON(!list_empty(&bo->lru));
BUG_ON(!list_empty(&bo->ddestroy));
@@ -402,36 +403,48 @@ static void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo)
ww_mutex_unlock (&bo->resv->lock);
}
+static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo)
+{
+ struct reservation_object_list *fobj;
+ struct fence *fence;
+ int i;
+
+ fobj = reservation_object_get_list(bo->resv);
+ fence = reservation_object_get_excl(bo->resv);
+ if (fence && !fence->ops->signaled)
+ fence_enable_sw_signaling(fence);
+
+ for (i = 0; fobj && i < fobj->shared_count; ++i) {
+ fence = rcu_dereference_protected(fobj->shared[i],
+ reservation_object_held(bo->resv));
+
+ if (!fence->ops->signaled)
+ fence_enable_sw_signaling(fence);
+ }
+}
+
static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo)
{
struct ttm_bo_device *bdev = bo->bdev;
struct ttm_bo_global *glob = bo->glob;
- struct ttm_bo_driver *driver = bdev->driver;
- void *sync_obj = NULL;
int put_count;
int ret;
spin_lock(&glob->lru_lock);
- ret = __ttm_bo_reserve(bo, false, true, false, 0);
-
- spin_lock(&bdev->fence_lock);
- (void) ttm_bo_wait(bo, false, false, true);
- if (!ret && !bo->sync_obj) {
- spin_unlock(&bdev->fence_lock);
- put_count = ttm_bo_del_from_lru(bo);
+ ret = __ttm_bo_reserve(bo, false, true, false, NULL);
- spin_unlock(&glob->lru_lock);
- ttm_bo_cleanup_memtype_use(bo);
+ if (!ret) {
+ if (!ttm_bo_wait(bo, false, false, true)) {
+ put_count = ttm_bo_del_from_lru(bo);
- ttm_bo_list_ref_sub(bo, put_count, true);
+ spin_unlock(&glob->lru_lock);
+ ttm_bo_cleanup_memtype_use(bo);
- return;
- }
- if (bo->sync_obj)
- sync_obj = driver->sync_obj_ref(bo->sync_obj);
- spin_unlock(&bdev->fence_lock);
+ ttm_bo_list_ref_sub(bo, put_count, true);
- if (!ret) {
+ return;
+ } else
+ ttm_bo_flush_all_fences(bo);
/*
* Make NO_EVICT bos immediately available to
@@ -450,10 +463,6 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo)
list_add_tail(&bo->ddestroy, &bdev->ddestroy);
spin_unlock(&glob->lru_lock);
- if (sync_obj) {
- driver->sync_obj_flush(sync_obj);
- driver->sync_obj_unref(&sync_obj);
- }
schedule_delayed_work(&bdev->wq,
((HZ / 100) < 1) ? 1 : HZ / 100);
}
@@ -474,47 +483,29 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,
bool interruptible,
bool no_wait_gpu)
{
- struct ttm_bo_device *bdev = bo->bdev;
- struct ttm_bo_driver *driver = bdev->driver;
struct ttm_bo_global *glob = bo->glob;
int put_count;
int ret;
- spin_lock(&bdev->fence_lock);
ret = ttm_bo_wait(bo, false, false, true);
if (ret && !no_wait_gpu) {
- void *sync_obj;
-
- /*
- * Take a reference to the fence and unreserve,
- * at this point the buffer should be dead, so
- * no new sync objects can be attached.
- */
- sync_obj = driver->sync_obj_ref(bo->sync_obj);
- spin_unlock(&bdev->fence_lock);
-
- __ttm_bo_unreserve(bo);
+ long lret;
+ ww_mutex_unlock(&bo->resv->lock);
spin_unlock(&glob->lru_lock);
- ret = driver->sync_obj_wait(sync_obj, false, interruptible);
- driver->sync_obj_unref(&sync_obj);
- if (ret)
- return ret;
+ lret = reservation_object_wait_timeout_rcu(bo->resv,
+ true,
+ interruptible,
+ 30 * HZ);
- /*
- * remove sync_obj with ttm_bo_wait, the wait should be
- * finished, and no new wait object should have been added.
- */
- spin_lock(&bdev->fence_lock);
- ret = ttm_bo_wait(bo, false, false, true);
- WARN_ON(ret);
- spin_unlock(&bdev->fence_lock);
- if (ret)
- return ret;
+ if (lret < 0)
+ return lret;
+ else if (lret == 0)
+ return -EBUSY;
spin_lock(&glob->lru_lock);
- ret = __ttm_bo_reserve(bo, false, true, false, 0);
+ ret = __ttm_bo_reserve(bo, false, true, false, NULL);
/*
* We raced, and lost, someone else holds the reservation now,
@@ -528,8 +519,14 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo,
spin_unlock(&glob->lru_lock);
return 0;
}
- } else
- spin_unlock(&bdev->fence_lock);
+
+ /*
+ * remove sync_obj with ttm_bo_wait, the wait should be
+ * finished, and no new wait object should have been added.
+ */
+ ret = ttm_bo_wait(bo, false, false, true);
+ WARN_ON(ret);
+ }
if (ret || unlikely(list_empty(&bo->ddestroy))) {
__ttm_bo_unreserve(bo);
@@ -577,11 +574,11 @@ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all)
kref_get(&nentry->list_kref);
}
- ret = __ttm_bo_reserve(entry, false, true, false, 0);
+ ret = __ttm_bo_reserve(entry, false, true, false, NULL);
if (remove_all && ret) {
spin_unlock(&glob->lru_lock);
ret = __ttm_bo_reserve(entry, false, false,
- false, 0);
+ false, NULL);
spin_lock(&glob->lru_lock);
}
@@ -667,9 +664,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
struct ttm_placement placement;
int ret = 0;
- spin_lock(&bdev->fence_lock);
ret = ttm_bo_wait(bo, false, interruptible, no_wait_gpu);
- spin_unlock(&bdev->fence_lock);
if (unlikely(ret != 0)) {
if (ret != -ERESTARTSYS) {
@@ -685,8 +680,6 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
evict_mem.bus.io_reserved_vm = false;
evict_mem.bus.io_reserved_count = 0;
- placement.fpfn = 0;
- placement.lpfn = 0;
placement.num_placement = 0;
placement.num_busy_placement = 0;
bdev->driver->evict_flags(bo, &placement);
@@ -716,6 +709,7 @@ out:
static int ttm_mem_evict_first(struct ttm_bo_device *bdev,
uint32_t mem_type,
+ const struct ttm_place *place,
bool interruptible,
bool no_wait_gpu)
{
@@ -726,9 +720,22 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev,
spin_lock(&glob->lru_lock);
list_for_each_entry(bo, &man->lru, lru) {
- ret = __ttm_bo_reserve(bo, false, true, false, 0);
- if (!ret)
+ ret = __ttm_bo_reserve(bo, false, true, false, NULL);
+ if (!ret) {
+ if (place && (place->fpfn || place->lpfn)) {
+ /* Don't evict this BO if it's outside of the
+ * requested placement range
+ */
+ if (place->fpfn >= (bo->mem.start + bo->mem.size) ||
+ (place->lpfn && place->lpfn <= bo->mem.start)) {
+ __ttm_bo_unreserve(bo);
+ ret = -EBUSY;
+ continue;
+ }
+ }
+
break;
+ }
}
if (ret) {
@@ -774,7 +781,7 @@ EXPORT_SYMBOL(ttm_bo_mem_put);
*/
static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo,
uint32_t mem_type,
- struct ttm_placement *placement,
+ const struct ttm_place *place,
struct ttm_mem_reg *mem,
bool interruptible,
bool no_wait_gpu)
@@ -784,12 +791,12 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo,
int ret;
do {
- ret = (*man->func->get_node)(man, bo, placement, mem);
+ ret = (*man->func->get_node)(man, bo, place, mem);
if (unlikely(ret != 0))
return ret;
if (mem->mm_node)
break;
- ret = ttm_mem_evict_first(bdev, mem_type,
+ ret = ttm_mem_evict_first(bdev, mem_type, place,
interruptible, no_wait_gpu);
if (unlikely(ret != 0))
return ret;
@@ -827,18 +834,18 @@ static uint32_t ttm_bo_select_caching(struct ttm_mem_type_manager *man,
static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man,
uint32_t mem_type,
- uint32_t proposed_placement,
+ const struct ttm_place *place,
uint32_t *masked_placement)
{
uint32_t cur_flags = ttm_bo_type_flags(mem_type);
- if ((cur_flags & proposed_placement & TTM_PL_MASK_MEM) == 0)
+ if ((cur_flags & place->flags & TTM_PL_MASK_MEM) == 0)
return false;
- if ((proposed_placement & man->available_caching) == 0)
+ if ((place->flags & man->available_caching) == 0)
return false;
- cur_flags |= (proposed_placement & man->available_caching);
+ cur_flags |= (place->flags & man->available_caching);
*masked_placement = cur_flags;
return true;
@@ -869,15 +876,14 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
mem->mm_node = NULL;
for (i = 0; i < placement->num_placement; ++i) {
- ret = ttm_mem_type_from_flags(placement->placement[i],
- &mem_type);
+ const struct ttm_place *place = &placement->placement[i];
+
+ ret = ttm_mem_type_from_place(place, &mem_type);
if (ret)
return ret;
man = &bdev->man[mem_type];
- type_ok = ttm_bo_mt_compatible(man,
- mem_type,
- placement->placement[i],
+ type_ok = ttm_bo_mt_compatible(man, mem_type, place,
&cur_flags);
if (!type_ok)
@@ -889,7 +895,7 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
* Use the access and other non-mapping-related flag bits from
* the memory placement flags to the current flags
*/
- ttm_flag_masked(&cur_flags, placement->placement[i],
+ ttm_flag_masked(&cur_flags, place->flags,
~TTM_PL_MASK_MEMTYPE);
if (mem_type == TTM_PL_SYSTEM)
@@ -897,7 +903,7 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
if (man->has_type && man->use_type) {
type_found = true;
- ret = (*man->func->get_node)(man, bo, placement, mem);
+ ret = (*man->func->get_node)(man, bo, place, mem);
if (unlikely(ret))
return ret;
}
@@ -915,17 +921,15 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
return -EINVAL;
for (i = 0; i < placement->num_busy_placement; ++i) {
- ret = ttm_mem_type_from_flags(placement->busy_placement[i],
- &mem_type);
+ const struct ttm_place *place = &placement->busy_placement[i];
+
+ ret = ttm_mem_type_from_place(place, &mem_type);
if (ret)
return ret;
man = &bdev->man[mem_type];
if (!man->has_type)
continue;
- if (!ttm_bo_mt_compatible(man,
- mem_type,
- placement->busy_placement[i],
- &cur_flags))
+ if (!ttm_bo_mt_compatible(man, mem_type, place, &cur_flags))
continue;
cur_flags = ttm_bo_select_caching(man, bo->mem.placement,
@@ -934,10 +938,9 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
* Use the access and other non-mapping-related flag bits from
* the memory placement flags to the current flags
*/
- ttm_flag_masked(&cur_flags, placement->busy_placement[i],
+ ttm_flag_masked(&cur_flags, place->flags,
~TTM_PL_MASK_MEMTYPE);
-
if (mem_type == TTM_PL_SYSTEM) {
mem->mem_type = mem_type;
mem->placement = cur_flags;
@@ -945,7 +948,7 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
return 0;
}
- ret = ttm_bo_mem_force_space(bo, mem_type, placement, mem,
+ ret = ttm_bo_mem_force_space(bo, mem_type, place, mem,
interruptible, no_wait_gpu);
if (ret == 0 && mem->mm_node) {
mem->placement = cur_flags;
@@ -966,7 +969,6 @@ static int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
{
int ret = 0;
struct ttm_mem_reg mem;
- struct ttm_bo_device *bdev = bo->bdev;
lockdep_assert_held(&bo->resv->lock.base);
@@ -975,9 +977,7 @@ static int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
* Have the driver move function wait for idle when necessary,
* instead of doing it here.
*/
- spin_lock(&bdev->fence_lock);
ret = ttm_bo_wait(bo, false, interruptible, no_wait_gpu);
- spin_unlock(&bdev->fence_lock);
if (ret)
return ret;
mem.num_pages = bo->num_pages;
@@ -1006,20 +1006,27 @@ static bool ttm_bo_mem_compat(struct ttm_placement *placement,
{
int i;
- if (mem->mm_node && placement->lpfn != 0 &&
- (mem->start < placement->fpfn ||
- mem->start + mem->num_pages > placement->lpfn))
- return false;
-
for (i = 0; i < placement->num_placement; i++) {
- *new_flags = placement->placement[i];
+ const struct ttm_place *heap = &placement->placement[i];
+ if (mem->mm_node &&
+ (mem->start < heap->fpfn ||
+ (heap->lpfn != 0 && (mem->start + mem->num_pages) > heap->lpfn)))
+ continue;
+
+ *new_flags = heap->flags;
if ((*new_flags & mem->placement & TTM_PL_MASK_CACHING) &&
(*new_flags & mem->placement & TTM_PL_MASK_MEM))
return true;
}
for (i = 0; i < placement->num_busy_placement; i++) {
- *new_flags = placement->busy_placement[i];
+ const struct ttm_place *heap = &placement->busy_placement[i];
+ if (mem->mm_node &&
+ (mem->start < heap->fpfn ||
+ (heap->lpfn != 0 && (mem->start + mem->num_pages) > heap->lpfn)))
+ continue;
+
+ *new_flags = heap->flags;
if ((*new_flags & mem->placement & TTM_PL_MASK_CACHING) &&
(*new_flags & mem->placement & TTM_PL_MASK_MEM))
return true;
@@ -1037,11 +1044,6 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,
uint32_t new_flags;
lockdep_assert_held(&bo->resv->lock.base);
- /* Check that range is valid */
- if (placement->lpfn || placement->fpfn)
- if (placement->fpfn > placement->lpfn ||
- (placement->lpfn - placement->fpfn) < bo->num_pages)
- return -EINVAL;
/*
* Check whether we need to move buffer.
*/
@@ -1070,15 +1072,6 @@ int ttm_bo_validate(struct ttm_buffer_object *bo,
}
EXPORT_SYMBOL(ttm_bo_validate);
-int ttm_bo_check_placement(struct ttm_buffer_object *bo,
- struct ttm_placement *placement)
-{
- BUG_ON((placement->fpfn || placement->lpfn) &&
- (bo->mem.num_pages > (placement->lpfn - placement->fpfn)));
-
- return 0;
-}
-
int ttm_bo_init(struct ttm_bo_device *bdev,
struct ttm_buffer_object *bo,
unsigned long size,
@@ -1089,6 +1082,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
struct file *persistent_swap_storage,
size_t acc_size,
struct sg_table *sg,
+ struct reservation_object *resv,
void (*destroy) (struct ttm_buffer_object *))
{
int ret = 0;
@@ -1142,30 +1136,38 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
bo->persistent_swap_storage = persistent_swap_storage;
bo->acc_size = acc_size;
bo->sg = sg;
- bo->resv = &bo->ttm_resv;
- reservation_object_init(bo->resv);
+ if (resv) {
+ bo->resv = resv;
+ lockdep_assert_held(&bo->resv->lock.base);
+ } else {
+ bo->resv = &bo->ttm_resv;
+ reservation_object_init(&bo->ttm_resv);
+ }
atomic_inc(&bo->glob->bo_count);
drm_vma_node_reset(&bo->vma_node);
- ret = ttm_bo_check_placement(bo, placement);
-
/*
* For ttm_bo_type_device buffers, allocate
* address space from the device.
*/
- if (likely(!ret) &&
- (bo->type == ttm_bo_type_device ||
- bo->type == ttm_bo_type_sg))
+ if (bo->type == ttm_bo_type_device ||
+ bo->type == ttm_bo_type_sg)
ret = drm_vma_offset_add(&bdev->vma_manager, &bo->vma_node,
bo->mem.num_pages);
- locked = ww_mutex_trylock(&bo->resv->lock);
- WARN_ON(!locked);
+ /* passed reservation objects should already be locked,
+ * since otherwise lockdep will be angered in radeon.
+ */
+ if (!resv) {
+ locked = ww_mutex_trylock(&bo->resv->lock);
+ WARN_ON(!locked);
+ }
if (likely(!ret))
ret = ttm_bo_validate(bo, placement, interruptible, false);
- ttm_bo_unreserve(bo);
+ if (!resv)
+ ttm_bo_unreserve(bo);
if (unlikely(ret))
ttm_bo_unref(&bo);
@@ -1223,7 +1225,7 @@ int ttm_bo_create(struct ttm_bo_device *bdev,
acc_size = ttm_bo_acc_size(bdev, size, sizeof(struct ttm_buffer_object));
ret = ttm_bo_init(bdev, bo, size, type, placement, page_alignment,
interruptible, persistent_swap_storage, acc_size,
- NULL, NULL);
+ NULL, NULL, NULL);
if (likely(ret == 0))
*p_bo = bo;
@@ -1245,7 +1247,7 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev,
spin_lock(&glob->lru_lock);
while (!list_empty(&man->lru)) {
spin_unlock(&glob->lru_lock);
- ret = ttm_mem_evict_first(bdev, mem_type, false, false);
+ ret = ttm_mem_evict_first(bdev, mem_type, NULL, false, false);
if (ret) {
if (allow_errors) {
return ret;
@@ -1477,7 +1479,6 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
bdev->glob = glob;
bdev->need_dma32 = need_dma32;
bdev->val_seq = 0;
- spin_lock_init(&bdev->fence_lock);
mutex_lock(&glob->device_list_mutex);
list_add_tail(&bdev->device_list, &glob->device_list);
mutex_unlock(&glob->device_list_mutex);
@@ -1530,77 +1531,66 @@ void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo)
EXPORT_SYMBOL(ttm_bo_unmap_virtual);
-
int ttm_bo_wait(struct ttm_buffer_object *bo,
bool lazy, bool interruptible, bool no_wait)
{
- struct ttm_bo_driver *driver = bo->bdev->driver;
- struct ttm_bo_device *bdev = bo->bdev;
- void *sync_obj;
- int ret = 0;
-
- if (likely(bo->sync_obj == NULL))
- return 0;
+ struct reservation_object_list *fobj;
+ struct reservation_object *resv;
+ struct fence *excl;
+ long timeout = 15 * HZ;
+ int i;
- while (bo->sync_obj) {
+ resv = bo->resv;
+ fobj = reservation_object_get_list(resv);
+ excl = reservation_object_get_excl(resv);
+ if (excl) {
+ if (!fence_is_signaled(excl)) {
+ if (no_wait)
+ return -EBUSY;
- if (driver->sync_obj_signaled(bo->sync_obj)) {
- void *tmp_obj = bo->sync_obj;
- bo->sync_obj = NULL;
- clear_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags);
- spin_unlock(&bdev->fence_lock);
- driver->sync_obj_unref(&tmp_obj);
- spin_lock(&bdev->fence_lock);
- continue;
+ timeout = fence_wait_timeout(excl,
+ interruptible, timeout);
}
+ }
- if (no_wait)
- return -EBUSY;
+ for (i = 0; fobj && timeout > 0 && i < fobj->shared_count; ++i) {
+ struct fence *fence;
+ fence = rcu_dereference_protected(fobj->shared[i],
+ reservation_object_held(resv));
- sync_obj = driver->sync_obj_ref(bo->sync_obj);
- spin_unlock(&bdev->fence_lock);
- ret = driver->sync_obj_wait(sync_obj,
- lazy, interruptible);
- if (unlikely(ret != 0)) {
- driver->sync_obj_unref(&sync_obj);
- spin_lock(&bdev->fence_lock);
- return ret;
- }
- spin_lock(&bdev->fence_lock);
- if (likely(bo->sync_obj == sync_obj)) {
- void *tmp_obj = bo->sync_obj;
- bo->sync_obj = NULL;
- clear_bit(TTM_BO_PRIV_FLAG_MOVING,
- &bo->priv_flags);
- spin_unlock(&bdev->fence_lock);
- driver->sync_obj_unref(&sync_obj);
- driver->sync_obj_unref(&tmp_obj);
- spin_lock(&bdev->fence_lock);
- } else {
- spin_unlock(&bdev->fence_lock);
- driver->sync_obj_unref(&sync_obj);
- spin_lock(&bdev->fence_lock);
+ if (!fence_is_signaled(fence)) {
+ if (no_wait)
+ return -EBUSY;
+
+ timeout = fence_wait_timeout(fence,
+ interruptible, timeout);
}
}
+
+ if (timeout < 0)
+ return timeout;
+
+ if (timeout == 0)
+ return -EBUSY;
+
+ reservation_object_add_excl_fence(resv, NULL);
+ clear_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags);
return 0;
}
EXPORT_SYMBOL(ttm_bo_wait);
int ttm_bo_synccpu_write_grab(struct ttm_buffer_object *bo, bool no_wait)
{
- struct ttm_bo_device *bdev = bo->bdev;
int ret = 0;
/*
* Using ttm_bo_reserve makes sure the lru lists are updated.
*/
- ret = ttm_bo_reserve(bo, true, no_wait, false, 0);
+ ret = ttm_bo_reserve(bo, true, no_wait, false, NULL);
if (unlikely(ret != 0))
return ret;
- spin_lock(&bdev->fence_lock);
ret = ttm_bo_wait(bo, false, true, no_wait);
- spin_unlock(&bdev->fence_lock);
if (likely(ret == 0))
atomic_inc(&bo->cpu_writers);
ttm_bo_unreserve(bo);
@@ -1630,7 +1620,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)
spin_lock(&glob->lru_lock);
list_for_each_entry(bo, &glob->swap_lru, swap) {
- ret = __ttm_bo_reserve(bo, false, true, false, 0);
+ ret = __ttm_bo_reserve(bo, false, true, false, NULL);
if (!ret)
break;
}
@@ -1657,9 +1647,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)
* Wait for GPU, then move to system cached.
*/
- spin_lock(&bo->bdev->fence_lock);
ret = ttm_bo_wait(bo, false, false, false);
- spin_unlock(&bo->bdev->fence_lock);
if (unlikely(ret != 0))
goto out;
diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c
index bd850c9f4bca..964387fc5c8f 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_manager.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c
@@ -49,7 +49,7 @@ struct ttm_range_manager {
static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,
struct ttm_buffer_object *bo,
- struct ttm_placement *placement,
+ const struct ttm_place *place,
struct ttm_mem_reg *mem)
{
struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv;
@@ -59,7 +59,7 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,
unsigned long lpfn;
int ret;
- lpfn = placement->lpfn;
+ lpfn = place->lpfn;
if (!lpfn)
lpfn = man->size;
@@ -67,13 +67,13 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,
if (!node)
return -ENOMEM;
- if (bo->mem.placement & TTM_PL_FLAG_TOPDOWN)
+ if (place->flags & TTM_PL_FLAG_TOPDOWN)
aflags = DRM_MM_CREATE_TOP;
spin_lock(&rman->lock);
ret = drm_mm_insert_node_in_range_generic(mm, node, mem->num_pages,
mem->page_alignment, 0,
- placement->fpfn, lpfn,
+ place->fpfn, lpfn,
DRM_MM_SEARCH_BEST,
aflags);
spin_unlock(&rman->lock);
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index 1df856f78568..882cccdad272 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -37,6 +37,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
+#include <linux/reservation.h>
void ttm_bo_free_old_node(struct ttm_buffer_object *bo)
{
@@ -444,8 +445,6 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
struct ttm_buffer_object **new_obj)
{
struct ttm_buffer_object *fbo;
- struct ttm_bo_device *bdev = bo->bdev;
- struct ttm_bo_driver *driver = bdev->driver;
int ret;
fbo = kmalloc(sizeof(*fbo), GFP_KERNEL);
@@ -466,12 +465,6 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
drm_vma_node_reset(&fbo->vma_node);
atomic_set(&fbo->cpu_writers, 0);
- spin_lock(&bdev->fence_lock);
- if (bo->sync_obj)
- fbo->sync_obj = driver->sync_obj_ref(bo->sync_obj);
- else
- fbo->sync_obj = NULL;
- spin_unlock(&bdev->fence_lock);
kref_init(&fbo->list_kref);
kref_init(&fbo->kref);
fbo->destroy = &ttm_transfered_destroy;
@@ -487,28 +480,24 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
pgprot_t ttm_io_prot(uint32_t caching_flags, pgprot_t tmp)
{
+ /* Cached mappings need no adjustment */
+ if (caching_flags & TTM_PL_FLAG_CACHED)
+ return tmp;
+
#if defined(__i386__) || defined(__x86_64__)
if (caching_flags & TTM_PL_FLAG_WC)
tmp = pgprot_writecombine(tmp);
else if (boot_cpu_data.x86 > 3)
tmp = pgprot_noncached(tmp);
-
-#elif defined(__powerpc__)
- if (!(caching_flags & TTM_PL_FLAG_CACHED)) {
- pgprot_val(tmp) |= _PAGE_NO_CACHE;
- if (caching_flags & TTM_PL_FLAG_UNCACHED)
- pgprot_val(tmp) |= _PAGE_GUARDED;
- }
#endif
-#if defined(__ia64__)
+#if defined(__ia64__) || defined(__arm__) || defined(__powerpc__)
if (caching_flags & TTM_PL_FLAG_WC)
tmp = pgprot_writecombine(tmp);
else
tmp = pgprot_noncached(tmp);
#endif
#if defined(__sparc__) || defined(__mips__)
- if (!(caching_flags & TTM_PL_FLAG_CACHED))
- tmp = pgprot_noncached(tmp);
+ tmp = pgprot_noncached(tmp);
#endif
return tmp;
}
@@ -567,9 +556,7 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo,
* We need to use vmap to get the desired page protection
* or to make the buffer object look contiguous.
*/
- prot = (mem->placement & TTM_PL_FLAG_CACHED) ?
- PAGE_KERNEL :
- ttm_io_prot(mem->placement, PAGE_KERNEL);
+ prot = ttm_io_prot(mem->placement, PAGE_KERNEL);
map->bo_kmap_type = ttm_bo_map_vmap;
map->virtual = vmap(ttm->pages + start_page, num_pages,
0, prot);
@@ -644,30 +631,20 @@ void ttm_bo_kunmap(struct ttm_bo_kmap_obj *map)
EXPORT_SYMBOL(ttm_bo_kunmap);
int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
- void *sync_obj,
+ struct fence *fence,
bool evict,
bool no_wait_gpu,
struct ttm_mem_reg *new_mem)
{
struct ttm_bo_device *bdev = bo->bdev;
- struct ttm_bo_driver *driver = bdev->driver;
struct ttm_mem_type_manager *man = &bdev->man[new_mem->mem_type];
struct ttm_mem_reg *old_mem = &bo->mem;
int ret;
struct ttm_buffer_object *ghost_obj;
- void *tmp_obj = NULL;
- spin_lock(&bdev->fence_lock);
- if (bo->sync_obj) {
- tmp_obj = bo->sync_obj;
- bo->sync_obj = NULL;
- }
- bo->sync_obj = driver->sync_obj_ref(sync_obj);
+ reservation_object_add_excl_fence(bo->resv, fence);
if (evict) {
ret = ttm_bo_wait(bo, false, false, false);
- spin_unlock(&bdev->fence_lock);
- if (tmp_obj)
- driver->sync_obj_unref(&tmp_obj);
if (ret)
return ret;
@@ -688,14 +665,13 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
*/
set_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags);
- spin_unlock(&bdev->fence_lock);
- if (tmp_obj)
- driver->sync_obj_unref(&tmp_obj);
ret = ttm_buffer_object_transfer(bo, &ghost_obj);
if (ret)
return ret;
+ reservation_object_add_excl_fence(ghost_obj->resv, fence);
+
/**
* If we're not moving to fixed memory, the TTM object
* needs to stay alive. Otherwhise hang it on the ghost
diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
index 0ce48e5a9cb4..8fb7213277cc 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -45,10 +45,8 @@ static int ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo,
struct vm_area_struct *vma,
struct vm_fault *vmf)
{
- struct ttm_bo_device *bdev = bo->bdev;
int ret = 0;
- spin_lock(&bdev->fence_lock);
if (likely(!test_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags)))
goto out_unlock;
@@ -82,7 +80,6 @@ static int ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo,
VM_FAULT_NOPAGE;
out_unlock:
- spin_unlock(&bdev->fence_lock);
return ret;
}
@@ -200,9 +197,8 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
cvma.vm_page_prot);
} else {
ttm = bo->ttm;
- if (!(bo->mem.placement & TTM_PL_FLAG_CACHED))
- cvma.vm_page_prot = ttm_io_prot(bo->mem.placement,
- cvma.vm_page_prot);
+ cvma.vm_page_prot = ttm_io_prot(bo->mem.placement,
+ cvma.vm_page_prot);
/* Allocate all page at once, most common usage */
if (ttm->bdev->driver->ttm_tt_populate(ttm)) {
diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
index e8dac8758528..8ce508e76208 100644
--- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c
+++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
@@ -32,20 +32,12 @@
#include <linux/sched.h>
#include <linux/module.h>
-static void ttm_eu_backoff_reservation_locked(struct list_head *list)
+static void ttm_eu_backoff_reservation_reverse(struct list_head *list,
+ struct ttm_validate_buffer *entry)
{
- struct ttm_validate_buffer *entry;
-
- list_for_each_entry(entry, list, head) {
+ list_for_each_entry_continue_reverse(entry, list, head) {
struct ttm_buffer_object *bo = entry->bo;
- if (!entry->reserved)
- continue;
- entry->reserved = false;
- if (entry->removed) {
- ttm_bo_add_to_lru(bo);
- entry->removed = false;
- }
__ttm_bo_unreserve(bo);
}
}
@@ -56,27 +48,9 @@ static void ttm_eu_del_from_lru_locked(struct list_head *list)
list_for_each_entry(entry, list, head) {
struct ttm_buffer_object *bo = entry->bo;
- if (!entry->reserved)
- continue;
-
- if (!entry->removed) {
- entry->put_count = ttm_bo_del_from_lru(bo);
- entry->removed = true;
- }
- }
-}
-
-static void ttm_eu_list_ref_sub(struct list_head *list)
-{
- struct ttm_validate_buffer *entry;
-
- list_for_each_entry(entry, list, head) {
- struct ttm_buffer_object *bo = entry->bo;
+ unsigned put_count = ttm_bo_del_from_lru(bo);
- if (entry->put_count) {
- ttm_bo_list_ref_sub(bo, entry->put_count, true);
- entry->put_count = 0;
- }
+ ttm_bo_list_ref_sub(bo, put_count, true);
}
}
@@ -91,11 +65,18 @@ void ttm_eu_backoff_reservation(struct ww_acquire_ctx *ticket,
entry = list_first_entry(list, struct ttm_validate_buffer, head);
glob = entry->bo->glob;
+
spin_lock(&glob->lru_lock);
- ttm_eu_backoff_reservation_locked(list);
+ list_for_each_entry(entry, list, head) {
+ struct ttm_buffer_object *bo = entry->bo;
+
+ ttm_bo_add_to_lru(bo);
+ __ttm_bo_unreserve(bo);
+ }
+ spin_unlock(&glob->lru_lock);
+
if (ticket)
ww_acquire_fini(ticket);
- spin_unlock(&glob->lru_lock);
}
EXPORT_SYMBOL(ttm_eu_backoff_reservation);
@@ -112,7 +93,7 @@ EXPORT_SYMBOL(ttm_eu_backoff_reservation);
*/
int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
- struct list_head *list)
+ struct list_head *list, bool intr)
{
struct ttm_bo_global *glob;
struct ttm_validate_buffer *entry;
@@ -121,60 +102,64 @@ int ttm_eu_reserve_buffers(struct ww_acquire_ctx *ticket,
if (list_empty(list))
return 0;
- list_for_each_entry(entry, list, head) {
- entry->reserved = false;
- entry->put_count = 0;
- entry->removed = false;
- }
-
entry = list_first_entry(list, struct ttm_validate_buffer, head);
glob = entry->bo->glob;
if (ticket)
ww_acquire_init(ticket, &reservation_ww_class);
-retry:
+
list_for_each_entry(entry, list, head) {
struct ttm_buffer_object *bo = entry->bo;
- /* already slowpath reserved? */
- if (entry->reserved)
- continue;
-
- ret = __ttm_bo_reserve(bo, true, (ticket == NULL), true,
+ ret = __ttm_bo_reserve(bo, intr, (ticket == NULL), true,
ticket);
+ if (!ret && unlikely(atomic_read(&bo->cpu_writers) > 0)) {
+ __ttm_bo_unreserve(bo);
+
+ ret = -EBUSY;
+ }
- if (ret == -EDEADLK) {
- /* uh oh, we lost out, drop every reservation and try
- * to only reserve this buffer, then start over if
- * this succeeds.
- */
- BUG_ON(ticket == NULL);
- spin_lock(&glob->lru_lock);
- ttm_eu_backoff_reservation_locked(list);
- spin_unlock(&glob->lru_lock);
- ttm_eu_list_ref_sub(list);
+ if (!ret) {
+ if (!entry->shared)
+ continue;
+
+ ret = reservation_object_reserve_shared(bo->resv);
+ if (!ret)
+ continue;
+ }
+
+ /* uh oh, we lost out, drop every reservation and try
+ * to only reserve this buffer, then start over if
+ * this succeeds.
+ */
+ ttm_eu_backoff_reservation_reverse(list, entry);
+
+ if (ret == -EDEADLK && intr) {
ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
ticket);
- if (unlikely(ret != 0)) {
- if (ret == -EINTR)
- ret = -ERESTARTSYS;
- goto err_fini;
- }
+ } else if (ret == -EDEADLK) {
+ ww_mutex_lock_slow(&bo->resv->lock, ticket);
+ ret = 0;
+ }
- entry->reserved = true;
- if (unlikely(atomic_read(&bo->cpu_writers) > 0)) {
- ret = -EBUSY;
- goto err;
- }
- goto retry;
- } else if (ret)
- goto err;
+ if (!ret && entry->shared)
+ ret = reservation_object_reserve_shared(bo->resv);
- entry->reserved = true;
- if (unlikely(atomic_read(&bo->cpu_writers) > 0)) {
- ret = -EBUSY;
- goto err;
+ if (unlikely(ret != 0)) {
+ if (ret == -EINTR)
+ ret = -ERESTARTSYS;
+ if (ticket) {
+ ww_acquire_done(ticket);
+ ww_acquire_fini(ticket);
+ }
+ return ret;
}
+
+ /* move this item to the front of the list,
+ * forces correct iteration of the loop without keeping track
+ */
+ list_del(&entry->head);
+ list_add(&entry->head, list);
}
if (ticket)
@@ -182,25 +167,12 @@ retry:
spin_lock(&glob->lru_lock);
ttm_eu_del_from_lru_locked(list);
spin_unlock(&glob->lru_lock);
- ttm_eu_list_ref_sub(list);
return 0;
-
-err:
- spin_lock(&glob->lru_lock);
- ttm_eu_backoff_reservation_locked(list);
- spin_unlock(&glob->lru_lock);
- ttm_eu_list_ref_sub(list);
-err_fini:
- if (ticket) {
- ww_acquire_done(ticket);
- ww_acquire_fini(ticket);
- }
- return ret;
}
EXPORT_SYMBOL(ttm_eu_reserve_buffers);
void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,
- struct list_head *list, void *sync_obj)
+ struct list_head *list, struct fence *fence)
{
struct ttm_validate_buffer *entry;
struct ttm_buffer_object *bo;
@@ -217,24 +189,18 @@ void ttm_eu_fence_buffer_objects(struct ww_acquire_ctx *ticket,
glob = bo->glob;
spin_lock(&glob->lru_lock);
- spin_lock(&bdev->fence_lock);
list_for_each_entry(entry, list, head) {
bo = entry->bo;
- entry->old_sync_obj = bo->sync_obj;
- bo->sync_obj = driver->sync_obj_ref(sync_obj);
+ if (entry->shared)
+ reservation_object_add_shared_fence(bo->resv, fence);
+ else
+ reservation_object_add_excl_fence(bo->resv, fence);
ttm_bo_add_to_lru(bo);
__ttm_bo_unreserve(bo);
- entry->reserved = false;
}
- spin_unlock(&bdev->fence_lock);
spin_unlock(&glob->lru_lock);
if (ticket)
ww_acquire_fini(ticket);
-
- list_for_each_entry(entry, list, head) {
- if (entry->old_sync_obj)
- driver->sync_obj_unref(&entry->old_sync_obj);
- }
}
EXPORT_SYMBOL(ttm_eu_fence_buffer_objects);
diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c
index dbc2def887cd..a1803fbcc898 100644
--- a/drivers/gpu/drm/ttm/ttm_memory.c
+++ b/drivers/gpu/drm/ttm/ttm_memory.c
@@ -300,7 +300,8 @@ static int ttm_mem_init_highmem_zone(struct ttm_mem_global *glob,
zone->glob = glob;
glob->zone_highmem = zone;
ret = kobject_init_and_add(
- &zone->kobj, &ttm_mem_zone_kobj_type, &glob->kobj, zone->name);
+ &zone->kobj, &ttm_mem_zone_kobj_type, &glob->kobj, "%s",
+ zone->name);
if (unlikely(ret != 0)) {
kobject_put(&zone->kobj);
return ret;
diff --git a/drivers/gpu/drm/ttm/ttm_module.c b/drivers/gpu/drm/ttm/ttm_module.c
index d7f92fe9d904..66fc6395eb54 100644
--- a/drivers/gpu/drm/ttm/ttm_module.c
+++ b/drivers/gpu/drm/ttm/ttm_module.c
@@ -35,7 +35,7 @@
#include <drm/drm_sysfs.h>
static DECLARE_WAIT_QUEUE_HEAD(exit_q);
-atomic_t device_released;
+static atomic_t device_released;
static struct device_type ttm_drm_class_type = {
.name = "ttm",
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index 863bef9f9234..09874d695188 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -297,8 +297,10 @@ static void ttm_pool_update_free_locked(struct ttm_page_pool *pool,
*
* @pool: to free the pages from
* @free_all: If set to true will free all pages in pool
+ * @gfp: GFP flags.
**/
-static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free)
+static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free,
+ gfp_t gfp)
{
unsigned long irq_flags;
struct page *p;
@@ -309,8 +311,7 @@ static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free)
if (NUM_PAGES_TO_ALLOC < nr_free)
npages_to_free = NUM_PAGES_TO_ALLOC;
- pages_to_free = kmalloc(npages_to_free * sizeof(struct page *),
- GFP_KERNEL);
+ pages_to_free = kmalloc(npages_to_free * sizeof(struct page *), gfp);
if (!pages_to_free) {
pr_err("Failed to allocate memory for pool free operation\n");
return 0;
@@ -382,32 +383,35 @@ out:
*
* XXX: (dchinner) Deadlock warning!
*
- * ttm_page_pool_free() does memory allocation using GFP_KERNEL. that means
- * this can deadlock when called a sc->gfp_mask that is not equal to
- * GFP_KERNEL.
+ * We need to pass sc->gfp_mask to ttm_page_pool_free().
*
* This code is crying out for a shrinker per pool....
*/
static unsigned long
ttm_pool_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
{
- static atomic_t start_pool = ATOMIC_INIT(0);
+ static DEFINE_MUTEX(lock);
+ static unsigned start_pool;
unsigned i;
- unsigned pool_offset = atomic_add_return(1, &start_pool);
+ unsigned pool_offset;
struct ttm_page_pool *pool;
int shrink_pages = sc->nr_to_scan;
unsigned long freed = 0;
- pool_offset = pool_offset % NUM_POOLS;
+ if (!mutex_trylock(&lock))
+ return SHRINK_STOP;
+ pool_offset = ++start_pool % NUM_POOLS;
/* select start pool in round robin fashion */
for (i = 0; i < NUM_POOLS; ++i) {
unsigned nr_free = shrink_pages;
if (shrink_pages == 0)
break;
pool = &_manager->pools[(i + pool_offset)%NUM_POOLS];
- shrink_pages = ttm_page_pool_free(pool, nr_free);
+ shrink_pages = ttm_page_pool_free(pool, nr_free,
+ sc->gfp_mask);
freed += nr_free - shrink_pages;
}
+ mutex_unlock(&lock);
return freed;
}
@@ -706,7 +710,7 @@ static void ttm_put_pages(struct page **pages, unsigned npages, int flags,
}
spin_unlock_irqrestore(&pool->lock, irq_flags);
if (npages)
- ttm_page_pool_free(pool, npages);
+ ttm_page_pool_free(pool, npages, GFP_KERNEL);
}
/*
@@ -790,7 +794,7 @@ static int ttm_get_pages(struct page **pages, unsigned npages, int flags,
return 0;
}
-static void ttm_page_pool_init_locked(struct ttm_page_pool *pool, int flags,
+static void ttm_page_pool_init_locked(struct ttm_page_pool *pool, gfp_t flags,
char *name)
{
spin_lock_init(&pool->lock);
@@ -846,7 +850,8 @@ void ttm_page_alloc_fini(void)
ttm_pool_mm_shrink_fini(_manager);
for (i = 0; i < NUM_POOLS; ++i)
- ttm_page_pool_free(&_manager->pools[i], FREE_ALL_PAGES);
+ ttm_page_pool_free(&_manager->pools[i], FREE_ALL_PAGES,
+ GFP_KERNEL);
kobject_put(&_manager->kobj);
_manager = NULL;
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
index fb8259f69839..c96db433f8af 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
@@ -411,8 +411,10 @@ static void ttm_dma_page_put(struct dma_pool *pool, struct dma_page *d_page)
*
* @pool: to free the pages from
* @nr_free: If set to true will free all pages in pool
+ * @gfp: GFP flags.
**/
-static unsigned ttm_dma_page_pool_free(struct dma_pool *pool, unsigned nr_free)
+static unsigned ttm_dma_page_pool_free(struct dma_pool *pool, unsigned nr_free,
+ gfp_t gfp)
{
unsigned long irq_flags;
struct dma_page *dma_p, *tmp;
@@ -430,8 +432,7 @@ static unsigned ttm_dma_page_pool_free(struct dma_pool *pool, unsigned nr_free)
npages_to_free, nr_free);
}
#endif
- pages_to_free = kmalloc(npages_to_free * sizeof(struct page *),
- GFP_KERNEL);
+ pages_to_free = kmalloc(npages_to_free * sizeof(struct page *), gfp);
if (!pages_to_free) {
pr_err("%s: Failed to allocate memory for pool free operation\n",
@@ -530,7 +531,7 @@ static void ttm_dma_free_pool(struct device *dev, enum pool_type type)
if (pool->type != type)
continue;
/* Takes a spinlock.. */
- ttm_dma_page_pool_free(pool, FREE_ALL_PAGES);
+ ttm_dma_page_pool_free(pool, FREE_ALL_PAGES, GFP_KERNEL);
WARN_ON(((pool->npages_in_use + pool->npages_free) != 0));
/* This code path is called after _all_ references to the
* struct device has been dropped - so nobody should be
@@ -847,6 +848,7 @@ static int ttm_dma_pool_get_pages(struct dma_pool *pool,
if (count) {
d_page = list_first_entry(&pool->free_list, struct dma_page, page_list);
ttm->pages[index] = d_page->p;
+ ttm_dma->cpu_address[index] = d_page->vaddr;
ttm_dma->dma_address[index] = d_page->dma;
list_move_tail(&d_page->page_list, &ttm_dma->pages_list);
r = 0;
@@ -978,12 +980,13 @@ void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev)
INIT_LIST_HEAD(&ttm_dma->pages_list);
for (i = 0; i < ttm->num_pages; i++) {
ttm->pages[i] = NULL;
+ ttm_dma->cpu_address[i] = 0;
ttm_dma->dma_address[i] = 0;
}
/* shrink pool if necessary (only on !is_cached pools)*/
if (npages)
- ttm_dma_page_pool_free(pool, npages);
+ ttm_dma_page_pool_free(pool, npages, GFP_KERNEL);
ttm->state = tt_unpopulated;
}
EXPORT_SYMBOL_GPL(ttm_dma_unpopulate);
@@ -993,10 +996,7 @@ EXPORT_SYMBOL_GPL(ttm_dma_unpopulate);
*
* XXX: (dchinner) Deadlock warning!
*
- * ttm_dma_page_pool_free() does GFP_KERNEL memory allocation, and so attention
- * needs to be paid to sc->gfp_mask to determine if this can be done or not.
- * GFP_KERNEL memory allocation in a GFP_ATOMIC reclaim context woul dbe really
- * bad.
+ * We need to pass sc->gfp_mask to ttm_dma_page_pool_free().
*
* I'm getting sadder as I hear more pathetical whimpers about needing per-pool
* shrinkers
@@ -1004,9 +1004,9 @@ EXPORT_SYMBOL_GPL(ttm_dma_unpopulate);
static unsigned long
ttm_dma_pool_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
{
- static atomic_t start_pool = ATOMIC_INIT(0);
+ static unsigned start_pool;
unsigned idx = 0;
- unsigned pool_offset = atomic_add_return(1, &start_pool);
+ unsigned pool_offset;
unsigned shrink_pages = sc->nr_to_scan;
struct device_pools *p;
unsigned long freed = 0;
@@ -1014,8 +1014,11 @@ ttm_dma_pool_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
if (list_empty(&_manager->pools))
return SHRINK_STOP;
- mutex_lock(&_manager->lock);
- pool_offset = pool_offset % _manager->npools;
+ if (!mutex_trylock(&_manager->lock))
+ return SHRINK_STOP;
+ if (!_manager->npools)
+ goto out;
+ pool_offset = ++start_pool % _manager->npools;
list_for_each_entry(p, &_manager->pools, pools) {
unsigned nr_free;
@@ -1027,13 +1030,15 @@ ttm_dma_pool_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
if (++idx < pool_offset)
continue;
nr_free = shrink_pages;
- shrink_pages = ttm_dma_page_pool_free(p->pool, nr_free);
+ shrink_pages = ttm_dma_page_pool_free(p->pool, nr_free,
+ sc->gfp_mask);
freed += nr_free - shrink_pages;
pr_debug("%s: (%s:%d) Asked to shrink %d, have %d more to go\n",
p->pool->dev_name, p->pool->name, current->pid,
nr_free, shrink_pages);
}
+out:
mutex_unlock(&_manager->lock);
return freed;
}
@@ -1044,7 +1049,8 @@ ttm_dma_pool_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
struct device_pools *p;
unsigned long count = 0;
- mutex_lock(&_manager->lock);
+ if (!mutex_trylock(&_manager->lock))
+ return 0;
list_for_each_entry(p, &_manager->pools, pools)
count += p->pool->npages_free;
mutex_unlock(&_manager->lock);
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
index 75f319090043..bf080abc86d1 100644
--- a/drivers/gpu/drm/ttm/ttm_tt.c
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
@@ -55,9 +55,12 @@ static void ttm_tt_alloc_page_directory(struct ttm_tt *ttm)
static void ttm_dma_tt_alloc_page_directory(struct ttm_dma_tt *ttm)
{
- ttm->ttm.pages = drm_calloc_large(ttm->ttm.num_pages, sizeof(void*));
- ttm->dma_address = drm_calloc_large(ttm->ttm.num_pages,
- sizeof(*ttm->dma_address));
+ ttm->ttm.pages = drm_calloc_large(ttm->ttm.num_pages,
+ sizeof(*ttm->ttm.pages) +
+ sizeof(*ttm->dma_address) +
+ sizeof(*ttm->cpu_address));
+ ttm->cpu_address = (void *) (ttm->ttm.pages + ttm->ttm.num_pages);
+ ttm->dma_address = (void *) (ttm->cpu_address + ttm->ttm.num_pages);
}
#ifdef CONFIG_X86
@@ -228,7 +231,7 @@ int ttm_dma_tt_init(struct ttm_dma_tt *ttm_dma, struct ttm_bo_device *bdev,
INIT_LIST_HEAD(&ttm_dma->pages_list);
ttm_dma_tt_alloc_page_directory(ttm_dma);
- if (!ttm->pages || !ttm_dma->dma_address) {
+ if (!ttm->pages) {
ttm_tt_destroy(ttm);
pr_err("Failed allocating page table\n");
return -ENOMEM;
@@ -243,7 +246,7 @@ void ttm_dma_tt_fini(struct ttm_dma_tt *ttm_dma)
drm_free_large(ttm->pages);
ttm->pages = NULL;
- drm_free_large(ttm_dma->dma_address);
+ ttm_dma->cpu_address = NULL;
ttm_dma->dma_address = NULL;
}
EXPORT_SYMBOL(ttm_dma_tt_fini);
diff --git a/drivers/gpu/drm/udl/Kconfig b/drivers/gpu/drm/udl/Kconfig
index f02528686cd5..613ab0622d6e 100644
--- a/drivers/gpu/drm/udl/Kconfig
+++ b/drivers/gpu/drm/udl/Kconfig
@@ -1,8 +1,9 @@
config DRM_UDL
tristate "DisplayLink"
depends on DRM
+ depends on USB_SUPPORT
depends on USB_ARCH_HAS_HCD
- select DRM_USB
+ select USB
select FB_SYS_FILLRECT
select FB_SYS_COPYAREA
select FB_SYS_IMAGEBLIT
diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c
index b44d548c56f8..0110d95522f3 100644
--- a/drivers/gpu/drm/udl/udl_connector.c
+++ b/drivers/gpu/drm/udl/udl_connector.c
@@ -34,8 +34,8 @@ static u8 *udl_get_edid(struct udl_device *udl)
goto error;
for (i = 0; i < EDID_LENGTH; i++) {
- ret = usb_control_msg(udl->ddev->usbdev,
- usb_rcvctrlpipe(udl->ddev->usbdev, 0), (0x02),
+ ret = usb_control_msg(udl->udev,
+ usb_rcvctrlpipe(udl->udev, 0), (0x02),
(0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2,
HZ);
if (ret < 1) {
@@ -105,14 +105,7 @@ static struct drm_encoder*
udl_best_single_encoder(struct drm_connector *connector)
{
int enc_id = connector->encoder_ids[0];
- struct drm_mode_object *obj;
- struct drm_encoder *encoder;
-
- obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
- if (!obj)
- return NULL;
- encoder = obj_to_encoder(obj);
- return encoder;
+ return drm_encoder_find(connector->dev, enc_id);
}
static int udl_connector_set_property(struct drm_connector *connector,
@@ -124,7 +117,7 @@ static int udl_connector_set_property(struct drm_connector *connector,
static void udl_connector_destroy(struct drm_connector *connector)
{
- drm_sysfs_connector_remove(connector);
+ drm_connector_unregister(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
@@ -154,7 +147,7 @@ int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder)
drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_DVII);
drm_connector_helper_add(connector, &udl_connector_helper_funcs);
- drm_sysfs_connector_add(connector);
+ drm_connector_register(connector);
drm_mode_connector_attach_encoder(connector, encoder);
drm_object_attach_property(&connector->base,
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c
index 3ddd6cd98ac1..8607e9e513db 100644
--- a/drivers/gpu/drm/udl/udl_drv.c
+++ b/drivers/gpu/drm/udl/udl_drv.c
@@ -7,48 +7,13 @@
*/
#include <linux/module.h>
-#include <drm/drm_usb.h>
+#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include "udl_drv.h"
-static struct drm_driver driver;
-
-/*
- * There are many DisplayLink-based graphics products, all with unique PIDs.
- * So we match on DisplayLink's VID + Vendor-Defined Interface Class (0xff)
- * We also require a match on SubClass (0x00) and Protocol (0x00),
- * which is compatible with all known USB 2.0 era graphics chips and firmware,
- * but allows DisplayLink to increment those for any future incompatible chips
- */
-static struct usb_device_id id_table[] = {
- {.idVendor = 0x17e9, .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0x00,
- .bInterfaceProtocol = 0x00,
- .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
- USB_DEVICE_ID_MATCH_INT_CLASS |
- USB_DEVICE_ID_MATCH_INT_SUBCLASS |
- USB_DEVICE_ID_MATCH_INT_PROTOCOL,},
- {},
-};
-MODULE_DEVICE_TABLE(usb, id_table);
-
-MODULE_LICENSE("GPL");
-
-static int udl_usb_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
+static int udl_driver_set_busid(struct drm_device *d, struct drm_master *m)
{
- return drm_get_usb_dev(interface, id, &driver);
-}
-
-static void udl_usb_disconnect(struct usb_interface *interface)
-{
- struct drm_device *dev = usb_get_intfdata(interface);
-
- drm_kms_helper_poll_disable(dev);
- drm_connector_unplug_all(dev);
- udl_fbdev_unplug(dev);
- udl_drop_usb(dev);
- drm_unplug_dev(dev);
+ return 0;
}
static const struct vm_operations_struct udl_gem_vm_ops = {
@@ -75,6 +40,7 @@ static struct drm_driver driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
.load = udl_driver_load,
.unload = udl_driver_unload,
+ .set_busid = udl_driver_set_busid,
/* gem hooks */
.gem_free_object = udl_gem_free_object,
@@ -96,6 +62,61 @@ static struct drm_driver driver = {
.patchlevel = DRIVER_PATCHLEVEL,
};
+static int udl_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct drm_device *dev;
+ int r;
+
+ dev = drm_dev_alloc(&driver, &interface->dev);
+ if (!dev)
+ return -ENOMEM;
+
+ r = drm_dev_register(dev, (unsigned long)udev);
+ if (r)
+ goto err_free;
+
+ usb_set_intfdata(interface, dev);
+ DRM_INFO("Initialized udl on minor %d\n", dev->primary->index);
+
+ return 0;
+
+err_free:
+ drm_dev_unref(dev);
+ return r;
+}
+
+static void udl_usb_disconnect(struct usb_interface *interface)
+{
+ struct drm_device *dev = usb_get_intfdata(interface);
+
+ drm_kms_helper_poll_disable(dev);
+ drm_connector_unplug_all(dev);
+ udl_fbdev_unplug(dev);
+ udl_drop_usb(dev);
+ drm_unplug_dev(dev);
+}
+
+/*
+ * There are many DisplayLink-based graphics products, all with unique PIDs.
+ * So we match on DisplayLink's VID + Vendor-Defined Interface Class (0xff)
+ * We also require a match on SubClass (0x00) and Protocol (0x00),
+ * which is compatible with all known USB 2.0 era graphics chips and firmware,
+ * but allows DisplayLink to increment those for any future incompatible chips
+ */
+static struct usb_device_id id_table[] = {
+ {.idVendor = 0x17e9, .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0x00,
+ .bInterfaceProtocol = 0x00,
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
+ USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS |
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL,},
+ {},
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
static struct usb_driver udl_driver = {
.name = "udl",
.probe = udl_usb_probe,
@@ -105,13 +126,14 @@ static struct usb_driver udl_driver = {
static int __init udl_init(void)
{
- return drm_usb_init(&driver, &udl_driver);
+ return usb_register(&udl_driver);
}
static void __exit udl_exit(void)
{
- drm_usb_exit(&driver, &udl_driver);
+ usb_deregister(&udl_driver);
}
module_init(udl_init);
module_exit(udl_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h
index 1fbf7b357f16..c7490a2489a7 100644
--- a/drivers/gpu/drm/udl/udl_drv.h
+++ b/drivers/gpu/drm/udl/udl_drv.h
@@ -15,6 +15,7 @@
#define UDL_DRV_H
#include <linux/usb.h>
+#include <drm/drm_gem.h>
#define DRIVER_NAME "udl"
#define DRIVER_DESC "DisplayLink"
@@ -47,6 +48,7 @@ struct udl_fbdev;
struct udl_device {
struct device *dev;
struct drm_device *ddev;
+ struct usb_device *udev;
int sku_pixel_limit;
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index 377176372da8..8cbcb4589bd3 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -472,7 +472,8 @@ udl_framebuffer_init(struct drm_device *dev,
static int udlfb_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
- struct udl_fbdev *ufbdev = (struct udl_fbdev *)helper;
+ struct udl_fbdev *ufbdev =
+ container_of(helper, struct udl_fbdev, helper);
struct drm_device *dev = ufbdev->helper.dev;
struct fb_info *info;
struct device *device = dev->dev;
@@ -550,7 +551,7 @@ out:
return ret;
}
-static struct drm_fb_helper_funcs udl_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs udl_fb_helper_funcs = {
.fb_probe = udlfb_create,
};
@@ -583,7 +584,8 @@ int udl_fbdev_init(struct drm_device *dev)
return -ENOMEM;
udl->fbdev = ufbdev;
- ufbdev->helper.funcs = &udl_fb_helper_funcs;
+
+ drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs);
ret = drm_fb_helper_init(dev, &ufbdev->helper,
1, 1);
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
index c041cd73f399..8044f5fb7c49 100644
--- a/drivers/gpu/drm/udl/udl_gem.c
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -107,14 +107,14 @@ int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
}
}
-static int udl_gem_get_pages(struct udl_gem_object *obj, gfp_t gfpmask)
+static int udl_gem_get_pages(struct udl_gem_object *obj)
{
struct page **pages;
if (obj->pages)
return 0;
- pages = drm_gem_get_pages(&obj->base, gfpmask);
+ pages = drm_gem_get_pages(&obj->base);
if (IS_ERR(pages))
return PTR_ERR(pages);
@@ -147,7 +147,7 @@ int udl_gem_vmap(struct udl_gem_object *obj)
return 0;
}
- ret = udl_gem_get_pages(obj, GFP_KERNEL);
+ ret = udl_gem_get_pages(obj);
if (ret)
return ret;
@@ -205,7 +205,7 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev,
}
gobj = to_udl_bo(obj);
- ret = udl_gem_get_pages(gobj, GFP_KERNEL);
+ ret = udl_gem_get_pages(gobj);
if (ret)
goto out;
ret = drm_gem_create_mmap_offset(obj);
diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c
index 7094b92d1ec7..33dbfb2c4748 100644
--- a/drivers/gpu/drm/udl/udl_main.c
+++ b/drivers/gpu/drm/udl/udl_main.c
@@ -202,7 +202,7 @@ static int udl_alloc_urb_list(struct drm_device *dev, int count, size_t size)
}
unode->urb = urb;
- buf = usb_alloc_coherent(udl->ddev->usbdev, MAX_TRANSFER, GFP_KERNEL,
+ buf = usb_alloc_coherent(udl->udev, MAX_TRANSFER, GFP_KERNEL,
&urb->transfer_dma);
if (!buf) {
kfree(unode);
@@ -211,7 +211,7 @@ static int udl_alloc_urb_list(struct drm_device *dev, int count, size_t size)
}
/* urb->transfer_buffer_length set to actual before submit */
- usb_fill_bulk_urb(urb, udl->ddev->usbdev, usb_sndbulkpipe(udl->ddev->usbdev, 1),
+ usb_fill_bulk_urb(urb, udl->udev, usb_sndbulkpipe(udl->udev, 1),
buf, size, udl_urb_completion, unode);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
@@ -282,6 +282,7 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len)
int udl_driver_load(struct drm_device *dev, unsigned long flags)
{
+ struct usb_device *udev = (void*)flags;
struct udl_device *udl;
int ret = -ENOMEM;
@@ -290,10 +291,11 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags)
if (!udl)
return -ENOMEM;
+ udl->udev = udev;
udl->ddev = dev;
dev->dev_private = udl;
- if (!udl_parse_vendor_descriptor(dev, dev->usbdev)) {
+ if (!udl_parse_vendor_descriptor(dev, udl->udev)) {
ret = -ENODEV;
DRM_ERROR("firmware not recognized. Assume incompatible device\n");
goto err;
@@ -306,10 +308,23 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags)
DRM_DEBUG("\n");
ret = udl_modeset_init(dev);
+ if (ret)
+ goto err;
ret = udl_fbdev_init(dev);
+ if (ret)
+ goto err;
+
+ ret = drm_vblank_init(dev, 1);
+ if (ret)
+ goto err_fb;
+
return 0;
+err_fb:
+ udl_fbdev_cleanup(dev);
err:
+ if (udl->urbs.count)
+ udl_free_urb_list(dev);
kfree(udl);
DRM_ERROR("%d\n", ret);
return ret;
@@ -325,6 +340,8 @@ int udl_driver_unload(struct drm_device *dev)
{
struct udl_device *udl = dev->dev_private;
+ drm_vblank_cleanup(dev);
+
if (udl->urbs.count)
udl_free_urb_list(dev);
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
index cddc4fcf35cf..dc145d320b25 100644
--- a/drivers/gpu/drm/udl/udl_modeset.c
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -363,6 +363,26 @@ static void udl_crtc_destroy(struct drm_crtc *crtc)
kfree(crtc);
}
+static int udl_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event,
+ uint32_t page_flip_flags)
+{
+ struct udl_framebuffer *ufb = to_udl_fb(fb);
+ struct drm_device *dev = crtc->dev;
+ unsigned long flags;
+
+ udl_handle_damage(ufb, 0, 0, fb->width, fb->height);
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ if (event)
+ drm_send_vblank_event(dev, 0, event);
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ crtc->primary->fb = fb;
+
+ return 0;
+}
+
static void udl_crtc_prepare(struct drm_crtc *crtc)
{
}
@@ -384,6 +404,7 @@ static struct drm_crtc_helper_funcs udl_helper_funcs = {
static const struct drm_crtc_funcs udl_crtc_funcs = {
.set_config = drm_crtc_helper_set_config,
.destroy = udl_crtc_destroy,
+ .page_flip = udl_crtc_page_flip,
};
static int udl_crtc_init(struct drm_device *dev)
diff --git a/drivers/gpu/drm/via/via_dma.c b/drivers/gpu/drm/via/via_dma.c
index 6fc0648dd37f..d17d8f245c1a 100644
--- a/drivers/gpu/drm/via/via_dma.c
+++ b/drivers/gpu/drm/via/via_dma.c
@@ -161,7 +161,7 @@ int via_dma_cleanup(struct drm_device *dev)
if (dev_priv->ring.virtual_start) {
via_cmdbuf_reset(dev_priv);
- drm_core_ioremapfree(&dev_priv->ring.map, dev);
+ drm_legacy_ioremapfree(&dev_priv->ring.map, dev);
dev_priv->ring.virtual_start = NULL;
}
@@ -200,7 +200,7 @@ static int via_initialize(struct drm_device *dev,
dev_priv->ring.map.flags = 0;
dev_priv->ring.map.mtrr = 0;
- drm_core_ioremap(&dev_priv->ring.map, dev);
+ drm_legacy_ioremap(&dev_priv->ring.map, dev);
if (dev_priv->ring.map.handle == NULL) {
via_dma_cleanup(dev);
diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c
index 50abc2adfaee..ed8aa8ff861a 100644
--- a/drivers/gpu/drm/via/via_drv.c
+++ b/drivers/gpu/drm/via/via_drv.c
@@ -62,7 +62,7 @@ static const struct file_operations via_driver_fops = {
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
- .mmap = drm_mmap,
+ .mmap = drm_legacy_mmap,
.poll = drm_poll,
#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
@@ -79,6 +79,7 @@ static struct drm_driver driver = {
.open = via_driver_open,
.preclose = via_reclaim_buffers_locked,
.postclose = via_driver_postclose,
+ .set_busid = drm_pci_set_busid,
.context_dtor = via_final_context,
.get_vblank_counter = via_get_vblank_counter,
.enable_vblank = via_enable_vblank,
diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h
index ad0273256beb..ef8c500b4a00 100644
--- a/drivers/gpu/drm/via/via_drv.h
+++ b/drivers/gpu/drm/via/via_drv.h
@@ -25,6 +25,8 @@
#define _VIA_DRV_H_
#include <drm/drm_mm.h>
+#include <drm/drm_legacy.h>
+
#define DRIVER_AUTHOR "Various"
#define DRIVER_NAME "via"
diff --git a/drivers/gpu/drm/via/via_map.c b/drivers/gpu/drm/via/via_map.c
index d0ab3fb32acd..0b3522dba6e8 100644
--- a/drivers/gpu/drm/via/via_map.c
+++ b/drivers/gpu/drm/via/via_map.c
@@ -31,7 +31,7 @@ static int via_do_init_map(struct drm_device *dev, drm_via_init_t *init)
DRM_DEBUG("\n");
- dev_priv->sarea = drm_getsarea(dev);
+ dev_priv->sarea = drm_legacy_getsarea(dev);
if (!dev_priv->sarea) {
DRM_ERROR("could not find sarea!\n");
dev->dev_private = (void *)dev_priv;
@@ -39,14 +39,14 @@ static int via_do_init_map(struct drm_device *dev, drm_via_init_t *init)
return -EINVAL;
}
- dev_priv->fb = drm_core_findmap(dev, init->fb_offset);
+ dev_priv->fb = drm_legacy_findmap(dev, init->fb_offset);
if (!dev_priv->fb) {
DRM_ERROR("could not find framebuffer!\n");
dev->dev_private = (void *)dev_priv;
via_do_cleanup_map(dev);
return -EINVAL;
}
- dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset);
+ dev_priv->mmio = drm_legacy_findmap(dev, init->mmio_offset);
if (!dev_priv->mmio) {
DRM_ERROR("could not find mmio region!\n");
dev->dev_private = (void *)dev_priv;
diff --git a/drivers/gpu/drm/via/via_mm.c b/drivers/gpu/drm/via/via_mm.c
index d70b1e1544bf..4f20742e7788 100644
--- a/drivers/gpu/drm/via/via_mm.c
+++ b/drivers/gpu/drm/via/via_mm.c
@@ -211,12 +211,12 @@ void via_reclaim_buffers_locked(struct drm_device *dev,
if (!(file->minor->master && file->master->lock.hw_lock))
return;
- drm_idlelock_take(&file->master->lock);
+ drm_legacy_idlelock_take(&file->master->lock);
mutex_lock(&dev->struct_mutex);
if (list_empty(&file_priv->obj_list)) {
mutex_unlock(&dev->struct_mutex);
- drm_idlelock_release(&file->master->lock);
+ drm_legacy_idlelock_release(&file->master->lock);
return;
}
@@ -231,7 +231,7 @@ void via_reclaim_buffers_locked(struct drm_device *dev,
}
mutex_unlock(&dev->struct_mutex);
- drm_idlelock_release(&file->master->lock);
+ drm_legacy_idlelock_release(&file->master->lock);
return;
}
diff --git a/drivers/gpu/drm/via/via_verifier.c b/drivers/gpu/drm/via/via_verifier.c
index 9dbc92bd1512..0677bbf4ec7e 100644
--- a/drivers/gpu/drm/via/via_verifier.c
+++ b/drivers/gpu/drm/via/via_verifier.c
@@ -31,6 +31,7 @@
#include "via_3d_reg.h"
#include <drm/drmP.h>
#include <drm/via_drm.h>
+#include <drm/drm_legacy.h>
#include "via_verifier.h"
#include "via_drv.h"
diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile
index 458cdf6d81e8..ce0ab951f507 100644
--- a/drivers/gpu/drm/vmwgfx/Makefile
+++ b/drivers/gpu/drm/vmwgfx/Makefile
@@ -6,6 +6,7 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \
vmwgfx_overlay.o vmwgfx_marker.o vmwgfx_gmrid_manager.o \
vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o vmwgfx_context.o \
- vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o
+ vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o \
+ vmwgfx_cmdbuf_res.o \
obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
diff --git a/drivers/gpu/drm/vmwgfx/svga_reg.h b/drivers/gpu/drm/vmwgfx/svga_reg.h
index 11323dd5196f..e4259c2c1acc 100644
--- a/drivers/gpu/drm/vmwgfx/svga_reg.h
+++ b/drivers/gpu/drm/vmwgfx/svga_reg.h
@@ -35,7 +35,6 @@
/*
* PCI device IDs.
*/
-#define PCI_VENDOR_ID_VMWARE 0x15AD
#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405
/*
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
index 6327cfc36805..cff2bf9db9d2 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
@@ -30,66 +30,101 @@
#include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_page_alloc.h>
-static uint32_t vram_placement_flags = TTM_PL_FLAG_VRAM |
- TTM_PL_FLAG_CACHED;
-
-static uint32_t vram_ne_placement_flags = TTM_PL_FLAG_VRAM |
- TTM_PL_FLAG_CACHED |
- TTM_PL_FLAG_NO_EVICT;
+static struct ttm_place vram_placement_flags = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED
+};
-static uint32_t sys_placement_flags = TTM_PL_FLAG_SYSTEM |
- TTM_PL_FLAG_CACHED;
+static struct ttm_place vram_ne_placement_flags = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
+};
-static uint32_t sys_ne_placement_flags = TTM_PL_FLAG_SYSTEM |
- TTM_PL_FLAG_CACHED |
- TTM_PL_FLAG_NO_EVICT;
+static struct ttm_place sys_placement_flags = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED
+};
-static uint32_t gmr_placement_flags = VMW_PL_FLAG_GMR |
- TTM_PL_FLAG_CACHED;
+static struct ttm_place sys_ne_placement_flags = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
+};
-static uint32_t gmr_ne_placement_flags = VMW_PL_FLAG_GMR |
- TTM_PL_FLAG_CACHED |
- TTM_PL_FLAG_NO_EVICT;
+static struct ttm_place gmr_placement_flags = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+};
-static uint32_t mob_placement_flags = VMW_PL_FLAG_MOB |
- TTM_PL_FLAG_CACHED;
+static struct ttm_place gmr_ne_placement_flags = {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
+};
-struct ttm_placement vmw_vram_placement = {
+static struct ttm_place mob_placement_flags = {
.fpfn = 0,
.lpfn = 0,
+ .flags = VMW_PL_FLAG_MOB | TTM_PL_FLAG_CACHED
+};
+
+struct ttm_placement vmw_vram_placement = {
.num_placement = 1,
.placement = &vram_placement_flags,
.num_busy_placement = 1,
.busy_placement = &vram_placement_flags
};
-static uint32_t vram_gmr_placement_flags[] = {
- TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED,
- VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+static struct ttm_place vram_gmr_placement_flags[] = {
+ {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED
+ }, {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+ }
};
-static uint32_t gmr_vram_placement_flags[] = {
- VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED,
- TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED
+static struct ttm_place gmr_vram_placement_flags[] = {
+ {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+ }, {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED
+ }
};
struct ttm_placement vmw_vram_gmr_placement = {
- .fpfn = 0,
- .lpfn = 0,
.num_placement = 2,
.placement = vram_gmr_placement_flags,
.num_busy_placement = 1,
.busy_placement = &gmr_placement_flags
};
-static uint32_t vram_gmr_ne_placement_flags[] = {
- TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT,
- VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT
+static struct ttm_place vram_gmr_ne_placement_flags[] = {
+ {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED |
+ TTM_PL_FLAG_NO_EVICT
+ }, {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED |
+ TTM_PL_FLAG_NO_EVICT
+ }
};
struct ttm_placement vmw_vram_gmr_ne_placement = {
- .fpfn = 0,
- .lpfn = 0,
.num_placement = 2,
.placement = vram_gmr_ne_placement_flags,
.num_busy_placement = 1,
@@ -97,8 +132,6 @@ struct ttm_placement vmw_vram_gmr_ne_placement = {
};
struct ttm_placement vmw_vram_sys_placement = {
- .fpfn = 0,
- .lpfn = 0,
.num_placement = 1,
.placement = &vram_placement_flags,
.num_busy_placement = 1,
@@ -106,8 +139,6 @@ struct ttm_placement vmw_vram_sys_placement = {
};
struct ttm_placement vmw_vram_ne_placement = {
- .fpfn = 0,
- .lpfn = 0,
.num_placement = 1,
.placement = &vram_ne_placement_flags,
.num_busy_placement = 1,
@@ -115,8 +146,6 @@ struct ttm_placement vmw_vram_ne_placement = {
};
struct ttm_placement vmw_sys_placement = {
- .fpfn = 0,
- .lpfn = 0,
.num_placement = 1,
.placement = &sys_placement_flags,
.num_busy_placement = 1,
@@ -124,24 +153,33 @@ struct ttm_placement vmw_sys_placement = {
};
struct ttm_placement vmw_sys_ne_placement = {
- .fpfn = 0,
- .lpfn = 0,
.num_placement = 1,
.placement = &sys_ne_placement_flags,
.num_busy_placement = 1,
.busy_placement = &sys_ne_placement_flags
};
-static uint32_t evictable_placement_flags[] = {
- TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED,
- TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED,
- VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED,
- VMW_PL_FLAG_MOB | TTM_PL_FLAG_CACHED
+static struct ttm_place evictable_placement_flags[] = {
+ {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED
+ }, {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED
+ }, {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED
+ }, {
+ .fpfn = 0,
+ .lpfn = 0,
+ .flags = VMW_PL_FLAG_MOB | TTM_PL_FLAG_CACHED
+ }
};
struct ttm_placement vmw_evictable_placement = {
- .fpfn = 0,
- .lpfn = 0,
.num_placement = 4,
.placement = evictable_placement_flags,
.num_busy_placement = 1,
@@ -149,8 +187,6 @@ struct ttm_placement vmw_evictable_placement = {
};
struct ttm_placement vmw_srf_placement = {
- .fpfn = 0,
- .lpfn = 0,
.num_placement = 1,
.num_busy_placement = 2,
.placement = &gmr_placement_flags,
@@ -158,8 +194,6 @@ struct ttm_placement vmw_srf_placement = {
};
struct ttm_placement vmw_mob_placement = {
- .fpfn = 0,
- .lpfn = 0,
.num_placement = 1,
.num_busy_placement = 1,
.placement = &mob_placement_flags,
@@ -768,44 +802,6 @@ static int vmw_ttm_fault_reserve_notify(struct ttm_buffer_object *bo)
}
/**
- * FIXME: We're using the old vmware polling method to sync.
- * Do this with fences instead.
- */
-
-static void *vmw_sync_obj_ref(void *sync_obj)
-{
-
- return (void *)
- vmw_fence_obj_reference((struct vmw_fence_obj *) sync_obj);
-}
-
-static void vmw_sync_obj_unref(void **sync_obj)
-{
- vmw_fence_obj_unreference((struct vmw_fence_obj **) sync_obj);
-}
-
-static int vmw_sync_obj_flush(void *sync_obj)
-{
- vmw_fence_obj_flush((struct vmw_fence_obj *) sync_obj);
- return 0;
-}
-
-static bool vmw_sync_obj_signaled(void *sync_obj)
-{
- return vmw_fence_obj_signaled((struct vmw_fence_obj *) sync_obj,
- DRM_VMW_FENCE_FLAG_EXEC);
-
-}
-
-static int vmw_sync_obj_wait(void *sync_obj, bool lazy, bool interruptible)
-{
- return vmw_fence_obj_wait((struct vmw_fence_obj *) sync_obj,
- DRM_VMW_FENCE_FLAG_EXEC,
- lazy, interruptible,
- VMW_FENCE_WAIT_TIMEOUT);
-}
-
-/**
* vmw_move_notify - TTM move_notify_callback
*
* @bo: The TTM buffer object about to move.
@@ -829,11 +825,7 @@ static void vmw_move_notify(struct ttm_buffer_object *bo,
*/
static void vmw_swap_notify(struct ttm_buffer_object *bo)
{
- struct ttm_bo_device *bdev = bo->bdev;
-
- spin_lock(&bdev->fence_lock);
ttm_bo_wait(bo, false, false, false);
- spin_unlock(&bdev->fence_lock);
}
@@ -846,11 +838,6 @@ struct ttm_bo_driver vmw_bo_driver = {
.evict_flags = vmw_evict_flags,
.move = NULL,
.verify_access = vmw_verify_access,
- .sync_obj_signaled = vmw_sync_obj_signaled,
- .sync_obj_wait = vmw_sync_obj_wait,
- .sync_obj_flush = vmw_sync_obj_flush,
- .sync_obj_unref = vmw_sync_obj_unref,
- .sync_obj_ref = vmw_sync_obj_ref,
.move_notify = vmw_move_notify,
.swap_notify = vmw_swap_notify,
.fault_reserve_notify = &vmw_ttm_fault_reserve_notify,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c
new file mode 100644
index 000000000000..21e9b7f8dad0
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c
@@ -0,0 +1,342 @@
+/**************************************************************************
+ *
+ * Copyright © 2014 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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 "vmwgfx_drv.h"
+
+#define VMW_CMDBUF_RES_MAN_HT_ORDER 12
+
+enum vmw_cmdbuf_res_state {
+ VMW_CMDBUF_RES_COMMITED,
+ VMW_CMDBUF_RES_ADD,
+ VMW_CMDBUF_RES_DEL
+};
+
+/**
+ * struct vmw_cmdbuf_res - Command buffer managed resource entry.
+ *
+ * @res: Refcounted pointer to a struct vmw_resource.
+ * @hash: Hash entry for the manager hash table.
+ * @head: List head used either by the staging list or the manager list
+ * of commited resources.
+ * @state: Staging state of this resource entry.
+ * @man: Pointer to a resource manager for this entry.
+ */
+struct vmw_cmdbuf_res {
+ struct vmw_resource *res;
+ struct drm_hash_item hash;
+ struct list_head head;
+ enum vmw_cmdbuf_res_state state;
+ struct vmw_cmdbuf_res_manager *man;
+};
+
+/**
+ * struct vmw_cmdbuf_res_manager - Command buffer resource manager.
+ *
+ * @resources: Hash table containing staged and commited command buffer
+ * resources
+ * @list: List of commited command buffer resources.
+ * @dev_priv: Pointer to a device private structure.
+ *
+ * @resources and @list are protected by the cmdbuf mutex for now.
+ */
+struct vmw_cmdbuf_res_manager {
+ struct drm_open_hash resources;
+ struct list_head list;
+ struct vmw_private *dev_priv;
+};
+
+
+/**
+ * vmw_cmdbuf_res_lookup - Look up a command buffer resource
+ *
+ * @man: Pointer to the command buffer resource manager
+ * @resource_type: The resource type, that combined with the user key
+ * identifies the resource.
+ * @user_key: The user key.
+ *
+ * Returns a valid refcounted struct vmw_resource pointer on success,
+ * an error pointer on failure.
+ */
+struct vmw_resource *
+vmw_cmdbuf_res_lookup(struct vmw_cmdbuf_res_manager *man,
+ enum vmw_cmdbuf_res_type res_type,
+ u32 user_key)
+{
+ struct drm_hash_item *hash;
+ int ret;
+ unsigned long key = user_key | (res_type << 24);
+
+ ret = drm_ht_find_item(&man->resources, key, &hash);
+ if (unlikely(ret != 0))
+ return ERR_PTR(ret);
+
+ return vmw_resource_reference
+ (drm_hash_entry(hash, struct vmw_cmdbuf_res, hash)->res);
+}
+
+/**
+ * vmw_cmdbuf_res_free - Free a command buffer resource.
+ *
+ * @man: Pointer to the command buffer resource manager
+ * @entry: Pointer to a struct vmw_cmdbuf_res.
+ *
+ * Frees a struct vmw_cmdbuf_res entry and drops its reference to the
+ * struct vmw_resource.
+ */
+static void vmw_cmdbuf_res_free(struct vmw_cmdbuf_res_manager *man,
+ struct vmw_cmdbuf_res *entry)
+{
+ list_del(&entry->head);
+ WARN_ON(drm_ht_remove_item(&man->resources, &entry->hash));
+ vmw_resource_unreference(&entry->res);
+ kfree(entry);
+}
+
+/**
+ * vmw_cmdbuf_res_commit - Commit a list of command buffer resource actions
+ *
+ * @list: Caller's list of command buffer resource actions.
+ *
+ * This function commits a list of command buffer resource
+ * additions or removals.
+ * It is typically called when the execbuf ioctl call triggering these
+ * actions has commited the fifo contents to the device.
+ */
+void vmw_cmdbuf_res_commit(struct list_head *list)
+{
+ struct vmw_cmdbuf_res *entry, *next;
+
+ list_for_each_entry_safe(entry, next, list, head) {
+ list_del(&entry->head);
+ switch (entry->state) {
+ case VMW_CMDBUF_RES_ADD:
+ entry->state = VMW_CMDBUF_RES_COMMITED;
+ list_add_tail(&entry->head, &entry->man->list);
+ break;
+ case VMW_CMDBUF_RES_DEL:
+ vmw_resource_unreference(&entry->res);
+ kfree(entry);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ }
+}
+
+/**
+ * vmw_cmdbuf_res_revert - Revert a list of command buffer resource actions
+ *
+ * @man: Pointer to the command buffer resource manager
+ * @list: Caller's list of command buffer resource action
+ *
+ * This function reverts a list of command buffer resource
+ * additions or removals.
+ * It is typically called when the execbuf ioctl call triggering these
+ * actions failed for some reason, and the command stream was never
+ * submitted.
+ */
+void vmw_cmdbuf_res_revert(struct list_head *list)
+{
+ struct vmw_cmdbuf_res *entry, *next;
+ int ret;
+
+ list_for_each_entry_safe(entry, next, list, head) {
+ switch (entry->state) {
+ case VMW_CMDBUF_RES_ADD:
+ vmw_cmdbuf_res_free(entry->man, entry);
+ break;
+ case VMW_CMDBUF_RES_DEL:
+ ret = drm_ht_insert_item(&entry->man->resources,
+ &entry->hash);
+ list_del(&entry->head);
+ list_add_tail(&entry->head, &entry->man->list);
+ entry->state = VMW_CMDBUF_RES_COMMITED;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ }
+}
+
+/**
+ * vmw_cmdbuf_res_add - Stage a command buffer managed resource for addition.
+ *
+ * @man: Pointer to the command buffer resource manager.
+ * @res_type: The resource type.
+ * @user_key: The user-space id of the resource.
+ * @res: Valid (refcount != 0) pointer to a struct vmw_resource.
+ * @list: The staging list.
+ *
+ * This function allocates a struct vmw_cmdbuf_res entry and adds the
+ * resource to the hash table of the manager identified by @man. The
+ * entry is then put on the staging list identified by @list.
+ */
+int vmw_cmdbuf_res_add(struct vmw_cmdbuf_res_manager *man,
+ enum vmw_cmdbuf_res_type res_type,
+ u32 user_key,
+ struct vmw_resource *res,
+ struct list_head *list)
+{
+ struct vmw_cmdbuf_res *cres;
+ int ret;
+
+ cres = kzalloc(sizeof(*cres), GFP_KERNEL);
+ if (unlikely(cres == NULL))
+ return -ENOMEM;
+
+ cres->hash.key = user_key | (res_type << 24);
+ ret = drm_ht_insert_item(&man->resources, &cres->hash);
+ if (unlikely(ret != 0))
+ goto out_invalid_key;
+
+ cres->state = VMW_CMDBUF_RES_ADD;
+ cres->res = vmw_resource_reference(res);
+ cres->man = man;
+ list_add_tail(&cres->head, list);
+
+out_invalid_key:
+ return ret;
+}
+
+/**
+ * vmw_cmdbuf_res_remove - Stage a command buffer managed resource for removal.
+ *
+ * @man: Pointer to the command buffer resource manager.
+ * @res_type: The resource type.
+ * @user_key: The user-space id of the resource.
+ * @list: The staging list.
+ *
+ * This function looks up the struct vmw_cmdbuf_res entry from the manager
+ * hash table and, if it exists, removes it. Depending on its current staging
+ * state it then either removes the entry from the staging list or adds it
+ * to it with a staging state of removal.
+ */
+int vmw_cmdbuf_res_remove(struct vmw_cmdbuf_res_manager *man,
+ enum vmw_cmdbuf_res_type res_type,
+ u32 user_key,
+ struct list_head *list)
+{
+ struct vmw_cmdbuf_res *entry;
+ struct drm_hash_item *hash;
+ int ret;
+
+ ret = drm_ht_find_item(&man->resources, user_key | (res_type << 24),
+ &hash);
+ if (likely(ret != 0))
+ return -EINVAL;
+
+ entry = drm_hash_entry(hash, struct vmw_cmdbuf_res, hash);
+
+ switch (entry->state) {
+ case VMW_CMDBUF_RES_ADD:
+ vmw_cmdbuf_res_free(man, entry);
+ break;
+ case VMW_CMDBUF_RES_COMMITED:
+ (void) drm_ht_remove_item(&man->resources, &entry->hash);
+ list_del(&entry->head);
+ entry->state = VMW_CMDBUF_RES_DEL;
+ list_add_tail(&entry->head, list);
+ break;
+ default:
+ BUG();
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * vmw_cmdbuf_res_man_create - Allocate a command buffer managed resource
+ * manager.
+ *
+ * @dev_priv: Pointer to a struct vmw_private
+ *
+ * Allocates and initializes a command buffer managed resource manager. Returns
+ * an error pointer on failure.
+ */
+struct vmw_cmdbuf_res_manager *
+vmw_cmdbuf_res_man_create(struct vmw_private *dev_priv)
+{
+ struct vmw_cmdbuf_res_manager *man;
+ int ret;
+
+ man = kzalloc(sizeof(*man), GFP_KERNEL);
+ if (man == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ man->dev_priv = dev_priv;
+ INIT_LIST_HEAD(&man->list);
+ ret = drm_ht_create(&man->resources, VMW_CMDBUF_RES_MAN_HT_ORDER);
+ if (ret == 0)
+ return man;
+
+ kfree(man);
+ return ERR_PTR(ret);
+}
+
+/**
+ * vmw_cmdbuf_res_man_destroy - Destroy a command buffer managed resource
+ * manager.
+ *
+ * @man: Pointer to the manager to destroy.
+ *
+ * This function destroys a command buffer managed resource manager and
+ * unreferences / frees all command buffer managed resources and -entries
+ * associated with it.
+ */
+void vmw_cmdbuf_res_man_destroy(struct vmw_cmdbuf_res_manager *man)
+{
+ struct vmw_cmdbuf_res *entry, *next;
+
+ list_for_each_entry_safe(entry, next, &man->list, head)
+ vmw_cmdbuf_res_free(man, entry);
+
+ kfree(man);
+}
+
+/**
+ *
+ * vmw_cmdbuf_res_man_size - Return the size of a command buffer managed
+ * resource manager
+ *
+ * Returns the approximate allocation size of a command buffer managed
+ * resource manager.
+ */
+size_t vmw_cmdbuf_res_man_size(void)
+{
+ static size_t res_man_size;
+
+ if (unlikely(res_man_size == 0))
+ res_man_size =
+ ttm_round_pot(sizeof(struct vmw_cmdbuf_res_manager)) +
+ ttm_round_pot(sizeof(struct hlist_head) <<
+ VMW_CMDBUF_RES_MAN_HT_ORDER);
+
+ return res_man_size;
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c
index 8bb26dcd9eae..5ac92874404d 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c
@@ -33,6 +33,7 @@ struct vmw_user_context {
struct ttm_base_object base;
struct vmw_resource res;
struct vmw_ctx_binding_state cbs;
+ struct vmw_cmdbuf_res_manager *man;
};
@@ -103,7 +104,8 @@ static const vmw_scrub_func vmw_scrub_funcs[vmw_ctx_binding_max] = {
static void vmw_hw_context_destroy(struct vmw_resource *res)
{
-
+ struct vmw_user_context *uctx =
+ container_of(res, struct vmw_user_context, res);
struct vmw_private *dev_priv = res->dev_priv;
struct {
SVGA3dCmdHeader header;
@@ -113,9 +115,9 @@ static void vmw_hw_context_destroy(struct vmw_resource *res)
if (res->func->destroy == vmw_gb_context_destroy) {
mutex_lock(&dev_priv->cmdbuf_mutex);
+ vmw_cmdbuf_res_man_destroy(uctx->man);
mutex_lock(&dev_priv->binding_mutex);
- (void) vmw_context_binding_state_kill
- (&container_of(res, struct vmw_user_context, res)->cbs);
+ (void) vmw_context_binding_state_kill(&uctx->cbs);
(void) vmw_gb_context_destroy(res);
mutex_unlock(&dev_priv->binding_mutex);
if (dev_priv->pinned_bo != NULL &&
@@ -152,13 +154,16 @@ static int vmw_gb_context_init(struct vmw_private *dev_priv,
ret = vmw_resource_init(dev_priv, res, true,
res_free, &vmw_gb_context_func);
res->backup_size = SVGA3D_CONTEXT_DATA_SIZE;
+ if (unlikely(ret != 0))
+ goto out_err;
- if (unlikely(ret != 0)) {
- if (res_free)
- res_free(res);
- else
- kfree(res);
- return ret;
+ if (dev_priv->has_mob) {
+ uctx->man = vmw_cmdbuf_res_man_create(dev_priv);
+ if (unlikely(IS_ERR(uctx->man))) {
+ ret = PTR_ERR(uctx->man);
+ uctx->man = NULL;
+ goto out_err;
+ }
}
memset(&uctx->cbs, 0, sizeof(uctx->cbs));
@@ -166,6 +171,13 @@ static int vmw_gb_context_init(struct vmw_private *dev_priv,
vmw_resource_activate(res, vmw_hw_context_destroy);
return 0;
+
+out_err:
+ if (res_free)
+ res_free(res);
+ else
+ kfree(res);
+ return ret;
}
static int vmw_context_init(struct vmw_private *dev_priv,
@@ -471,7 +483,8 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data,
*/
if (unlikely(vmw_user_context_size == 0))
- vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128;
+ vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128 +
+ ((dev_priv->has_mob) ? vmw_cmdbuf_res_man_size() : 0);
ret = ttm_read_lock(&dev_priv->reservation_sem, true);
if (unlikely(ret != 0))
@@ -901,3 +914,8 @@ struct list_head *vmw_context_binding_list(struct vmw_resource *ctx)
{
return &(container_of(ctx, struct vmw_user_context, res)->cbs.list);
}
+
+struct vmw_cmdbuf_res_manager *vmw_context_res_man(struct vmw_resource *ctx)
+{
+ return container_of(ctx, struct vmw_user_context, res)->man;
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
index 70ddce8358b0..914b375763dc 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
@@ -61,7 +61,7 @@ int vmw_dmabuf_to_placement(struct vmw_private *dev_priv,
vmw_execbuf_release_pinned_bo(dev_priv);
- ret = ttm_bo_reserve(bo, interruptible, false, false, 0);
+ ret = ttm_bo_reserve(bo, interruptible, false, false, NULL);
if (unlikely(ret != 0))
goto err;
@@ -105,7 +105,7 @@ int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv,
if (pin)
vmw_execbuf_release_pinned_bo(dev_priv);
- ret = ttm_bo_reserve(bo, interruptible, false, false, 0);
+ ret = ttm_bo_reserve(bo, interruptible, false, false, NULL);
if (unlikely(ret != 0))
goto err;
@@ -198,13 +198,19 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *dev_priv,
{
struct ttm_buffer_object *bo = &buf->base;
struct ttm_placement placement;
+ struct ttm_place place;
int ret = 0;
if (pin)
- placement = vmw_vram_ne_placement;
+ place = vmw_vram_ne_placement.placement[0];
else
- placement = vmw_vram_placement;
- placement.lpfn = bo->num_pages;
+ place = vmw_vram_placement.placement[0];
+ place.lpfn = bo->num_pages;
+
+ placement.num_placement = 1;
+ placement.placement = &place;
+ placement.num_busy_placement = 1;
+ placement.busy_placement = &place;
ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
if (unlikely(ret != 0))
@@ -212,7 +218,7 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *dev_priv,
if (pin)
vmw_execbuf_release_pinned_bo(dev_priv);
- ret = ttm_bo_reserve(bo, interruptible, false, false, 0);
+ ret = ttm_bo_reserve(bo, interruptible, false, false, NULL);
if (unlikely(ret != 0))
goto err_unlock;
@@ -293,21 +299,23 @@ void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *bo,
*/
void vmw_bo_pin(struct ttm_buffer_object *bo, bool pin)
{
- uint32_t pl_flags;
+ struct ttm_place pl;
struct ttm_placement placement;
uint32_t old_mem_type = bo->mem.mem_type;
int ret;
lockdep_assert_held(&bo->resv->lock.base);
- pl_flags = TTM_PL_FLAG_VRAM | VMW_PL_FLAG_GMR | VMW_PL_FLAG_MOB
+ pl.fpfn = 0;
+ pl.lpfn = 0;
+ pl.flags = TTM_PL_FLAG_VRAM | VMW_PL_FLAG_GMR | VMW_PL_FLAG_MOB
| TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED;
if (pin)
- pl_flags |= TTM_PL_FLAG_NO_EVICT;
+ pl.flags |= TTM_PL_FLAG_NO_EVICT;
memset(&placement, 0, sizeof(placement));
placement.num_placement = 1;
- placement.placement = &pl_flags;
+ placement.placement = &pl;
ret = ttm_bo_validate(bo, &placement, false, true);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 246a62bab378..25f3c250fd98 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -316,7 +316,7 @@ static int vmw_dummy_query_bo_create(struct vmw_private *dev_priv)
if (unlikely(ret != 0))
return ret;
- ret = ttm_bo_reserve(bo, false, true, false, 0);
+ ret = ttm_bo_reserve(bo, false, true, false, NULL);
BUG_ON(ret != 0);
ret = ttm_bo_kmap(bo, 0, 1, &map);
@@ -688,7 +688,11 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
goto out_err0;
}
- if (unlikely(dev_priv->prim_bb_mem < dev_priv->vram_size))
+ /*
+ * Limit back buffer size to VRAM size. Remove this once
+ * screen targets are implemented.
+ */
+ if (dev_priv->prim_bb_mem > dev_priv->vram_size)
dev_priv->prim_bb_mem = dev_priv->vram_size;
mutex_unlock(&dev_priv->hw_mutex);
@@ -946,7 +950,6 @@ static void vmw_postclose(struct drm_device *dev,
drm_master_put(&vmw_fp->locked_master);
}
- vmw_compat_shader_man_destroy(vmw_fp->shman);
ttm_object_file_release(&vmw_fp->tfile);
kfree(vmw_fp);
}
@@ -966,16 +969,10 @@ static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv)
if (unlikely(vmw_fp->tfile == NULL))
goto out_no_tfile;
- vmw_fp->shman = vmw_compat_shader_man_create(dev_priv);
- if (IS_ERR(vmw_fp->shman))
- goto out_no_shman;
-
file_priv->driver_priv = vmw_fp;
return 0;
-out_no_shman:
- ttm_object_file_release(&vmw_fp->tfile);
out_no_tfile:
kfree(vmw_fp);
return ret;
@@ -1425,6 +1422,7 @@ static struct drm_driver driver = {
.open = vmw_driver_open,
.preclose = vmw_preclose,
.postclose = vmw_postclose,
+ .set_busid = drm_pci_set_busid,
.dumb_create = vmw_dumb_create,
.dumb_map_offset = vmw_dumb_map_offset,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index c886c024c637..4ee799b43d5d 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -40,10 +40,10 @@
#include <drm/ttm/ttm_module.h>
#include "vmwgfx_fence.h"
-#define VMWGFX_DRIVER_DATE "20140325"
+#define VMWGFX_DRIVER_DATE "20140704"
#define VMWGFX_DRIVER_MAJOR 2
#define VMWGFX_DRIVER_MINOR 6
-#define VMWGFX_DRIVER_PATCHLEVEL 0
+#define VMWGFX_DRIVER_PATCHLEVEL 1
#define VMWGFX_FILE_PAGE_OFFSET 0x00100000
#define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
#define VMWGFX_MAX_RELOCATIONS 2048
@@ -75,14 +75,11 @@
#define VMW_RES_FENCE ttm_driver_type3
#define VMW_RES_SHADER ttm_driver_type4
-struct vmw_compat_shader_manager;
-
struct vmw_fpriv {
struct drm_master *locked_master;
struct ttm_object_file *tfile;
struct list_head fence_events;
bool gb_aware;
- struct vmw_compat_shader_manager *shman;
};
struct vmw_dma_buffer {
@@ -124,6 +121,10 @@ struct vmw_resource {
void (*hw_destroy) (struct vmw_resource *res);
};
+
+/*
+ * Resources that are managed using ioctls.
+ */
enum vmw_res_type {
vmw_res_context,
vmw_res_surface,
@@ -132,6 +133,15 @@ enum vmw_res_type {
vmw_res_max
};
+/*
+ * Resources that are managed using command streams.
+ */
+enum vmw_cmdbuf_res_type {
+ vmw_cmdbuf_res_compat_shader
+};
+
+struct vmw_cmdbuf_res_manager;
+
struct vmw_cursor_snooper {
struct drm_crtc *crtc;
size_t age;
@@ -332,7 +342,6 @@ struct vmw_sw_context{
uint32_t *cmd_bounce;
uint32_t cmd_bounce_size;
struct list_head resource_list;
- uint32_t fence_flags;
struct ttm_buffer_object *cur_query_bo;
struct list_head res_relocations;
uint32_t *buf_start;
@@ -341,7 +350,7 @@ struct vmw_sw_context{
bool needs_post_query_barrier;
struct vmw_resource *error_resource;
struct vmw_ctx_binding_state staged_bindings;
- struct list_head staged_shaders;
+ struct list_head staged_cmd_res;
};
struct vmw_legacy_display;
@@ -694,6 +703,7 @@ extern void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes);
extern void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes);
extern int vmw_fifo_send_fence(struct vmw_private *dev_priv,
uint32_t *seqno);
+extern void vmw_fifo_ping_host_locked(struct vmw_private *, uint32_t reason);
extern void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason);
extern bool vmw_fifo_have_3d(struct vmw_private *dev_priv);
extern bool vmw_fifo_have_pitchlock(struct vmw_private *dev_priv);
@@ -974,7 +984,8 @@ extern void vmw_context_binding_res_list_kill(struct list_head *head);
extern void vmw_context_binding_res_list_scrub(struct list_head *head);
extern int vmw_context_rebind_all(struct vmw_resource *ctx);
extern struct list_head *vmw_context_binding_list(struct vmw_resource *ctx);
-
+extern struct vmw_cmdbuf_res_manager *
+vmw_context_res_man(struct vmw_resource *ctx);
/*
* Surface management - vmwgfx_surface.c
*/
@@ -1008,27 +1019,42 @@ extern int vmw_shader_define_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
-extern int vmw_compat_shader_lookup(struct vmw_compat_shader_manager *man,
- SVGA3dShaderType shader_type,
- u32 *user_key);
-extern void vmw_compat_shaders_commit(struct vmw_compat_shader_manager *man,
- struct list_head *list);
-extern void vmw_compat_shaders_revert(struct vmw_compat_shader_manager *man,
- struct list_head *list);
-extern int vmw_compat_shader_remove(struct vmw_compat_shader_manager *man,
- u32 user_key,
- SVGA3dShaderType shader_type,
- struct list_head *list);
-extern int vmw_compat_shader_add(struct vmw_compat_shader_manager *man,
+extern int vmw_compat_shader_add(struct vmw_private *dev_priv,
+ struct vmw_cmdbuf_res_manager *man,
u32 user_key, const void *bytecode,
SVGA3dShaderType shader_type,
size_t size,
- struct ttm_object_file *tfile,
struct list_head *list);
-extern struct vmw_compat_shader_manager *
-vmw_compat_shader_man_create(struct vmw_private *dev_priv);
-extern void
-vmw_compat_shader_man_destroy(struct vmw_compat_shader_manager *man);
+extern int vmw_compat_shader_remove(struct vmw_cmdbuf_res_manager *man,
+ u32 user_key, SVGA3dShaderType shader_type,
+ struct list_head *list);
+extern struct vmw_resource *
+vmw_compat_shader_lookup(struct vmw_cmdbuf_res_manager *man,
+ u32 user_key, SVGA3dShaderType shader_type);
+
+/*
+ * Command buffer managed resources - vmwgfx_cmdbuf_res.c
+ */
+
+extern struct vmw_cmdbuf_res_manager *
+vmw_cmdbuf_res_man_create(struct vmw_private *dev_priv);
+extern void vmw_cmdbuf_res_man_destroy(struct vmw_cmdbuf_res_manager *man);
+extern size_t vmw_cmdbuf_res_man_size(void);
+extern struct vmw_resource *
+vmw_cmdbuf_res_lookup(struct vmw_cmdbuf_res_manager *man,
+ enum vmw_cmdbuf_res_type res_type,
+ u32 user_key);
+extern void vmw_cmdbuf_res_revert(struct list_head *list);
+extern void vmw_cmdbuf_res_commit(struct list_head *list);
+extern int vmw_cmdbuf_res_add(struct vmw_cmdbuf_res_manager *man,
+ enum vmw_cmdbuf_res_type res_type,
+ u32 user_key,
+ struct vmw_resource *res,
+ struct list_head *list);
+extern int vmw_cmdbuf_res_remove(struct vmw_cmdbuf_res_manager *man,
+ enum vmw_cmdbuf_res_type res_type,
+ u32 user_key,
+ struct list_head *list);
/**
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index 87df0b3674fd..596cd6dafd33 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -346,13 +346,11 @@ static int vmw_bo_to_validate_list(struct vmw_sw_context *sw_context,
++sw_context->cur_val_buf;
val_buf = &vval_buf->base;
val_buf->bo = ttm_bo_reference(bo);
- val_buf->reserved = false;
+ val_buf->shared = false;
list_add_tail(&val_buf->head, &sw_context->validate_nodes);
vval_buf->validate_as_mob = validate_as_mob;
}
- sw_context->fence_flags |= DRM_VMW_FENCE_FLAG_EXEC;
-
if (p_val_node)
*p_val_node = val_node;
@@ -422,28 +420,90 @@ static int vmw_resources_validate(struct vmw_sw_context *sw_context)
return 0;
}
+
+/**
+ * vmw_cmd_res_reloc_add - Add a resource to a software context's
+ * relocation- and validation lists.
+ *
+ * @dev_priv: Pointer to a struct vmw_private identifying the device.
+ * @sw_context: Pointer to the software context.
+ * @res_type: Resource type.
+ * @id_loc: Pointer to where the id that needs translation is located.
+ * @res: Valid pointer to a struct vmw_resource.
+ * @p_val: If non null, a pointer to the struct vmw_resource_validate_node
+ * used for this resource is returned here.
+ */
+static int vmw_cmd_res_reloc_add(struct vmw_private *dev_priv,
+ struct vmw_sw_context *sw_context,
+ enum vmw_res_type res_type,
+ uint32_t *id_loc,
+ struct vmw_resource *res,
+ struct vmw_resource_val_node **p_val)
+{
+ int ret;
+ struct vmw_resource_val_node *node;
+
+ *p_val = NULL;
+ ret = vmw_resource_relocation_add(&sw_context->res_relocations,
+ res,
+ id_loc - sw_context->buf_start);
+ if (unlikely(ret != 0))
+ return ret;
+
+ ret = vmw_resource_val_add(sw_context, res, &node);
+ if (unlikely(ret != 0))
+ return ret;
+
+ if (res_type == vmw_res_context && dev_priv->has_mob &&
+ node->first_usage) {
+
+ /*
+ * Put contexts first on the list to be able to exit
+ * list traversal for contexts early.
+ */
+ list_del(&node->head);
+ list_add(&node->head, &sw_context->resource_list);
+
+ ret = vmw_resource_context_res_add(dev_priv, sw_context, res);
+ if (unlikely(ret != 0))
+ return ret;
+ node->staged_bindings =
+ kzalloc(sizeof(*node->staged_bindings), GFP_KERNEL);
+ if (node->staged_bindings == NULL) {
+ DRM_ERROR("Failed to allocate context binding "
+ "information.\n");
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&node->staged_bindings->list);
+ }
+
+ if (p_val)
+ *p_val = node;
+
+ return 0;
+}
+
+
/**
- * vmw_cmd_compat_res_check - Check that a resource is present and if so, put it
+ * vmw_cmd_res_check - Check that a resource is present and if so, put it
* on the resource validate list unless it's already there.
*
* @dev_priv: Pointer to a device private structure.
* @sw_context: Pointer to the software context.
* @res_type: Resource type.
* @converter: User-space visisble type specific information.
- * @id: user-space resource id handle.
* @id_loc: Pointer to the location in the command buffer currently being
* parsed from where the user-space resource id handle is located.
* @p_val: Pointer to pointer to resource validalidation node. Populated
* on exit.
*/
static int
-vmw_cmd_compat_res_check(struct vmw_private *dev_priv,
- struct vmw_sw_context *sw_context,
- enum vmw_res_type res_type,
- const struct vmw_user_resource_conv *converter,
- uint32_t id,
- uint32_t *id_loc,
- struct vmw_resource_val_node **p_val)
+vmw_cmd_res_check(struct vmw_private *dev_priv,
+ struct vmw_sw_context *sw_context,
+ enum vmw_res_type res_type,
+ const struct vmw_user_resource_conv *converter,
+ uint32_t *id_loc,
+ struct vmw_resource_val_node **p_val)
{
struct vmw_res_cache_entry *rcache =
&sw_context->res_cache[res_type];
@@ -451,7 +511,7 @@ vmw_cmd_compat_res_check(struct vmw_private *dev_priv,
struct vmw_resource_val_node *node;
int ret;
- if (id == SVGA3D_INVALID_ID) {
+ if (*id_loc == SVGA3D_INVALID_ID) {
if (p_val)
*p_val = NULL;
if (res_type == vmw_res_context) {
@@ -466,7 +526,7 @@ vmw_cmd_compat_res_check(struct vmw_private *dev_priv,
* resource
*/
- if (likely(rcache->valid && id == rcache->handle)) {
+ if (likely(rcache->valid && *id_loc == rcache->handle)) {
const struct vmw_resource *res = rcache->res;
rcache->node->first_usage = false;
@@ -480,49 +540,28 @@ vmw_cmd_compat_res_check(struct vmw_private *dev_priv,
ret = vmw_user_resource_lookup_handle(dev_priv,
sw_context->fp->tfile,
- id,
+ *id_loc,
converter,
&res);
if (unlikely(ret != 0)) {
DRM_ERROR("Could not find or use resource 0x%08x.\n",
- (unsigned) id);
+ (unsigned) *id_loc);
dump_stack();
return ret;
}
rcache->valid = true;
rcache->res = res;
- rcache->handle = id;
-
- ret = vmw_resource_relocation_add(&sw_context->res_relocations,
- res,
- id_loc - sw_context->buf_start);
- if (unlikely(ret != 0))
- goto out_no_reloc;
+ rcache->handle = *id_loc;
- ret = vmw_resource_val_add(sw_context, res, &node);
+ ret = vmw_cmd_res_reloc_add(dev_priv, sw_context, res_type, id_loc,
+ res, &node);
if (unlikely(ret != 0))
goto out_no_reloc;
rcache->node = node;
if (p_val)
*p_val = node;
-
- if (dev_priv->has_mob && node->first_usage &&
- res_type == vmw_res_context) {
- ret = vmw_resource_context_res_add(dev_priv, sw_context, res);
- if (unlikely(ret != 0))
- goto out_no_reloc;
- node->staged_bindings =
- kzalloc(sizeof(*node->staged_bindings), GFP_KERNEL);
- if (node->staged_bindings == NULL) {
- DRM_ERROR("Failed to allocate context binding "
- "information.\n");
- goto out_no_reloc;
- }
- INIT_LIST_HEAD(&node->staged_bindings->list);
- }
-
vmw_resource_unreference(&res);
return 0;
@@ -534,31 +573,6 @@ out_no_reloc:
}
/**
- * vmw_cmd_res_check - Check that a resource is present and if so, put it
- * on the resource validate list unless it's already there.
- *
- * @dev_priv: Pointer to a device private structure.
- * @sw_context: Pointer to the software context.
- * @res_type: Resource type.
- * @converter: User-space visisble type specific information.
- * @id_loc: Pointer to the location in the command buffer currently being
- * parsed from where the user-space resource id handle is located.
- * @p_val: Pointer to pointer to resource validalidation node. Populated
- * on exit.
- */
-static int
-vmw_cmd_res_check(struct vmw_private *dev_priv,
- struct vmw_sw_context *sw_context,
- enum vmw_res_type res_type,
- const struct vmw_user_resource_conv *converter,
- uint32_t *id_loc,
- struct vmw_resource_val_node **p_val)
-{
- return vmw_cmd_compat_res_check(dev_priv, sw_context, res_type,
- converter, *id_loc, id_loc, p_val);
-}
-
-/**
* vmw_rebind_contexts - Rebind all resources previously bound to
* referenced contexts.
*
@@ -572,8 +586,8 @@ static int vmw_rebind_contexts(struct vmw_sw_context *sw_context)
int ret;
list_for_each_entry(val, &sw_context->resource_list, head) {
- if (likely(!val->staged_bindings))
- continue;
+ if (unlikely(!val->staged_bindings))
+ break;
ret = vmw_context_rebind_all(val->res);
if (unlikely(ret != 0)) {
@@ -1626,13 +1640,14 @@ static int vmw_cmd_shader_define(struct vmw_private *dev_priv,
} *cmd;
int ret;
size_t size;
+ struct vmw_resource_val_node *val;
cmd = container_of(header, struct vmw_shader_define_cmd,
header);
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context,
user_context_converter, &cmd->body.cid,
- NULL);
+ &val);
if (unlikely(ret != 0))
return ret;
@@ -1640,11 +1655,11 @@ static int vmw_cmd_shader_define(struct vmw_private *dev_priv,
return 0;
size = cmd->header.size - sizeof(cmd->body);
- ret = vmw_compat_shader_add(sw_context->fp->shman,
+ ret = vmw_compat_shader_add(dev_priv,
+ vmw_context_res_man(val->res),
cmd->body.shid, cmd + 1,
cmd->body.type, size,
- sw_context->fp->tfile,
- &sw_context->staged_shaders);
+ &sw_context->staged_cmd_res);
if (unlikely(ret != 0))
return ret;
@@ -1672,23 +1687,24 @@ static int vmw_cmd_shader_destroy(struct vmw_private *dev_priv,
SVGA3dCmdDestroyShader body;
} *cmd;
int ret;
+ struct vmw_resource_val_node *val;
cmd = container_of(header, struct vmw_shader_destroy_cmd,
header);
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context,
user_context_converter, &cmd->body.cid,
- NULL);
+ &val);
if (unlikely(ret != 0))
return ret;
if (unlikely(!dev_priv->has_mob))
return 0;
- ret = vmw_compat_shader_remove(sw_context->fp->shman,
+ ret = vmw_compat_shader_remove(vmw_context_res_man(val->res),
cmd->body.shid,
cmd->body.type,
- &sw_context->staged_shaders);
+ &sw_context->staged_cmd_res);
if (unlikely(ret != 0))
return ret;
@@ -1715,7 +1731,9 @@ static int vmw_cmd_set_shader(struct vmw_private *dev_priv,
SVGA3dCmdHeader header;
SVGA3dCmdSetShader body;
} *cmd;
- struct vmw_resource_val_node *ctx_node;
+ struct vmw_resource_val_node *ctx_node, *res_node = NULL;
+ struct vmw_ctx_bindinfo bi;
+ struct vmw_resource *res = NULL;
int ret;
cmd = container_of(header, struct vmw_set_shader_cmd,
@@ -1727,32 +1745,40 @@ static int vmw_cmd_set_shader(struct vmw_private *dev_priv,
if (unlikely(ret != 0))
return ret;
- if (dev_priv->has_mob) {
- struct vmw_ctx_bindinfo bi;
- struct vmw_resource_val_node *res_node;
- u32 shid = cmd->body.shid;
-
- if (shid != SVGA3D_INVALID_ID)
- (void) vmw_compat_shader_lookup(sw_context->fp->shman,
- cmd->body.type,
- &shid);
-
- ret = vmw_cmd_compat_res_check(dev_priv, sw_context,
- vmw_res_shader,
- user_shader_converter,
- shid,
- &cmd->body.shid, &res_node);
+ if (!dev_priv->has_mob)
+ return 0;
+
+ if (cmd->body.shid != SVGA3D_INVALID_ID) {
+ res = vmw_compat_shader_lookup
+ (vmw_context_res_man(ctx_node->res),
+ cmd->body.shid,
+ cmd->body.type);
+
+ if (!IS_ERR(res)) {
+ ret = vmw_cmd_res_reloc_add(dev_priv, sw_context,
+ vmw_res_shader,
+ &cmd->body.shid, res,
+ &res_node);
+ vmw_resource_unreference(&res);
+ if (unlikely(ret != 0))
+ return ret;
+ }
+ }
+
+ if (!res_node) {
+ ret = vmw_cmd_res_check(dev_priv, sw_context,
+ vmw_res_shader,
+ user_shader_converter,
+ &cmd->body.shid, &res_node);
if (unlikely(ret != 0))
return ret;
-
- bi.ctx = ctx_node->res;
- bi.res = res_node ? res_node->res : NULL;
- bi.bt = vmw_ctx_binding_shader;
- bi.i1.shader_type = cmd->body.type;
- return vmw_context_binding_add(ctx_node->staged_bindings, &bi);
}
- return 0;
+ bi.ctx = ctx_node->res;
+ bi.res = res_node ? res_node->res : NULL;
+ bi.bt = vmw_ctx_binding_shader;
+ bi.i1.shader_type = cmd->body.type;
+ return vmw_context_binding_add(ctx_node->staged_bindings, &bi);
}
/**
@@ -2309,13 +2335,9 @@ int vmw_execbuf_fence_commands(struct drm_file *file_priv,
if (p_handle != NULL)
ret = vmw_user_fence_create(file_priv, dev_priv->fman,
- sequence,
- DRM_VMW_FENCE_FLAG_EXEC,
- p_fence, p_handle);
+ sequence, p_fence, p_handle);
else
- ret = vmw_fence_create(dev_priv->fman, sequence,
- DRM_VMW_FENCE_FLAG_EXEC,
- p_fence);
+ ret = vmw_fence_create(dev_priv->fman, sequence, p_fence);
if (unlikely(ret != 0 && !synced)) {
(void) vmw_fallback_wait(dev_priv, false, false,
@@ -2367,7 +2389,7 @@ vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
BUG_ON(fence == NULL);
fence_rep.handle = fence_handle;
- fence_rep.seqno = fence->seqno;
+ fence_rep.seqno = fence->base.seqno;
vmw_update_seqno(dev_priv, &dev_priv->fifo);
fence_rep.passed_seqno = dev_priv->last_read_seqno;
}
@@ -2388,12 +2410,13 @@ vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
ttm_ref_object_base_unref(vmw_fp->tfile,
fence_handle, TTM_REF_USAGE);
DRM_ERROR("Fence copy error. Syncing.\n");
- (void) vmw_fence_obj_wait(fence, fence->signal_mask,
- false, false,
+ (void) vmw_fence_obj_wait(fence, false, false,
VMW_FENCE_WAIT_TIMEOUT);
}
}
+
+
int vmw_execbuf_process(struct drm_file *file_priv,
struct vmw_private *dev_priv,
void __user *user_commands,
@@ -2439,7 +2462,6 @@ int vmw_execbuf_process(struct drm_file *file_priv,
sw_context->fp = vmw_fpriv(file_priv);
sw_context->cur_reloc = 0;
sw_context->cur_val_buf = 0;
- sw_context->fence_flags = 0;
INIT_LIST_HEAD(&sw_context->resource_list);
sw_context->cur_query_bo = dev_priv->pinned_bo;
sw_context->last_query_ctx = NULL;
@@ -2453,7 +2475,7 @@ int vmw_execbuf_process(struct drm_file *file_priv,
goto out_unlock;
sw_context->res_ht_initialized = true;
}
- INIT_LIST_HEAD(&sw_context->staged_shaders);
+ INIT_LIST_HEAD(&sw_context->staged_cmd_res);
INIT_LIST_HEAD(&resource_list);
ret = vmw_cmd_check_all(dev_priv, sw_context, kernel_commands,
@@ -2465,7 +2487,7 @@ int vmw_execbuf_process(struct drm_file *file_priv,
if (unlikely(ret != 0))
goto out_err_nores;
- ret = ttm_eu_reserve_buffers(&ticket, &sw_context->validate_nodes);
+ ret = ttm_eu_reserve_buffers(&ticket, &sw_context->validate_nodes, true);
if (unlikely(ret != 0))
goto out_err;
@@ -2548,8 +2570,7 @@ int vmw_execbuf_process(struct drm_file *file_priv,
}
list_splice_init(&sw_context->resource_list, &resource_list);
- vmw_compat_shaders_commit(sw_context->fp->shman,
- &sw_context->staged_shaders);
+ vmw_cmdbuf_res_commit(&sw_context->staged_cmd_res);
mutex_unlock(&dev_priv->cmdbuf_mutex);
/*
@@ -2576,8 +2597,7 @@ out_unlock:
list_splice_init(&sw_context->resource_list, &resource_list);
error_resource = sw_context->error_resource;
sw_context->error_resource = NULL;
- vmw_compat_shaders_revert(sw_context->fp->shman,
- &sw_context->staged_shaders);
+ vmw_cmdbuf_res_revert(&sw_context->staged_cmd_res);
mutex_unlock(&dev_priv->cmdbuf_mutex);
/*
@@ -2650,15 +2670,14 @@ void __vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv,
INIT_LIST_HEAD(&validate_list);
pinned_val.bo = ttm_bo_reference(dev_priv->pinned_bo);
+ pinned_val.shared = false;
list_add_tail(&pinned_val.head, &validate_list);
query_val.bo = ttm_bo_reference(dev_priv->dummy_query_bo);
+ query_val.shared = false;
list_add_tail(&query_val.head, &validate_list);
- do {
- ret = ttm_eu_reserve_buffers(&ticket, &validate_list);
- } while (ret == -ERESTARTSYS);
-
+ ret = ttm_eu_reserve_buffers(&ticket, &validate_list, false);
if (unlikely(ret != 0)) {
vmw_execbuf_unpin_panic(dev_priv);
goto out_no_reserve;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index b031b48dbb3c..0a474f391fad 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -374,10 +374,16 @@ static int vmw_fb_create_bo(struct vmw_private *vmw_priv,
size_t size, struct vmw_dma_buffer **out)
{
struct vmw_dma_buffer *vmw_bo;
- struct ttm_placement ne_placement = vmw_vram_ne_placement;
+ struct ttm_place ne_place = vmw_vram_ne_placement.placement[0];
+ struct ttm_placement ne_placement;
int ret;
- ne_placement.lpfn = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ ne_placement.num_placement = 1;
+ ne_placement.placement = &ne_place;
+ ne_placement.num_busy_placement = 1;
+ ne_placement.busy_placement = &ne_place;
+
+ ne_place.lpfn = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
(void) ttm_write_lock(&vmw_priv->reservation_sem, false);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index 436b013b4231..197164fd7803 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -35,7 +35,7 @@ struct vmw_fence_manager {
struct vmw_private *dev_priv;
spinlock_t lock;
struct list_head fence_list;
- struct work_struct work;
+ struct work_struct work, ping_work;
u32 user_fence_size;
u32 fence_size;
u32 event_fence_action_size;
@@ -46,6 +46,7 @@ struct vmw_fence_manager {
bool goal_irq_on; /* Protected by @goal_irq_mutex */
bool seqno_valid; /* Protected by @lock, and may not be set to true
without the @goal_irq_mutex held. */
+ unsigned ctx;
};
struct vmw_user_fence {
@@ -80,6 +81,12 @@ struct vmw_event_fence_action {
uint32_t *tv_usec;
};
+static struct vmw_fence_manager *
+fman_from_fence(struct vmw_fence_obj *fence)
+{
+ return container_of(fence->base.lock, struct vmw_fence_manager, lock);
+}
+
/**
* Note on fencing subsystem usage of irqs:
* Typically the vmw_fences_update function is called
@@ -102,25 +109,143 @@ struct vmw_event_fence_action {
* objects with actions attached to them.
*/
-static void vmw_fence_obj_destroy_locked(struct kref *kref)
+static void vmw_fence_obj_destroy(struct fence *f)
{
struct vmw_fence_obj *fence =
- container_of(kref, struct vmw_fence_obj, kref);
+ container_of(f, struct vmw_fence_obj, base);
- struct vmw_fence_manager *fman = fence->fman;
- unsigned int num_fences;
+ struct vmw_fence_manager *fman = fman_from_fence(fence);
+ unsigned long irq_flags;
+ spin_lock_irqsave(&fman->lock, irq_flags);
list_del_init(&fence->head);
- num_fences = --fman->num_fence_objects;
- spin_unlock_irq(&fman->lock);
- if (fence->destroy)
- fence->destroy(fence);
- else
- kfree(fence);
+ --fman->num_fence_objects;
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
+ fence->destroy(fence);
+}
- spin_lock_irq(&fman->lock);
+static const char *vmw_fence_get_driver_name(struct fence *f)
+{
+ return "vmwgfx";
+}
+
+static const char *vmw_fence_get_timeline_name(struct fence *f)
+{
+ return "svga";
+}
+
+static void vmw_fence_ping_func(struct work_struct *work)
+{
+ struct vmw_fence_manager *fman =
+ container_of(work, struct vmw_fence_manager, ping_work);
+
+ vmw_fifo_ping_host(fman->dev_priv, SVGA_SYNC_GENERIC);
+}
+
+static bool vmw_fence_enable_signaling(struct fence *f)
+{
+ struct vmw_fence_obj *fence =
+ container_of(f, struct vmw_fence_obj, base);
+
+ struct vmw_fence_manager *fman = fman_from_fence(fence);
+ struct vmw_private *dev_priv = fman->dev_priv;
+
+ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
+ u32 seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE);
+ if (seqno - fence->base.seqno < VMW_FENCE_WRAP)
+ return false;
+
+ if (mutex_trylock(&dev_priv->hw_mutex)) {
+ vmw_fifo_ping_host_locked(dev_priv, SVGA_SYNC_GENERIC);
+ mutex_unlock(&dev_priv->hw_mutex);
+ } else
+ schedule_work(&fman->ping_work);
+
+ return true;
+}
+
+struct vmwgfx_wait_cb {
+ struct fence_cb base;
+ struct task_struct *task;
+};
+
+static void
+vmwgfx_wait_cb(struct fence *fence, struct fence_cb *cb)
+{
+ struct vmwgfx_wait_cb *wait =
+ container_of(cb, struct vmwgfx_wait_cb, base);
+
+ wake_up_process(wait->task);
+}
+
+static void __vmw_fences_update(struct vmw_fence_manager *fman);
+
+static long vmw_fence_wait(struct fence *f, bool intr, signed long timeout)
+{
+ struct vmw_fence_obj *fence =
+ container_of(f, struct vmw_fence_obj, base);
+
+ struct vmw_fence_manager *fman = fman_from_fence(fence);
+ struct vmw_private *dev_priv = fman->dev_priv;
+ struct vmwgfx_wait_cb cb;
+ long ret = timeout;
+ unsigned long irq_flags;
+
+ if (likely(vmw_fence_obj_signaled(fence)))
+ return timeout;
+
+ vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
+ vmw_seqno_waiter_add(dev_priv);
+
+ spin_lock_irqsave(f->lock, irq_flags);
+
+ if (intr && signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+
+ cb.base.func = vmwgfx_wait_cb;
+ cb.task = current;
+ list_add(&cb.base.node, &f->cb_list);
+
+ while (ret > 0) {
+ __vmw_fences_update(fman);
+ if (test_bit(FENCE_FLAG_SIGNALED_BIT, &f->flags))
+ break;
+
+ if (intr)
+ __set_current_state(TASK_INTERRUPTIBLE);
+ else
+ __set_current_state(TASK_UNINTERRUPTIBLE);
+ spin_unlock_irqrestore(f->lock, irq_flags);
+
+ ret = schedule_timeout(ret);
+
+ spin_lock_irqsave(f->lock, irq_flags);
+ if (ret > 0 && intr && signal_pending(current))
+ ret = -ERESTARTSYS;
+ }
+
+ if (!list_empty(&cb.base.node))
+ list_del(&cb.base.node);
+ __set_current_state(TASK_RUNNING);
+
+out:
+ spin_unlock_irqrestore(f->lock, irq_flags);
+
+ vmw_seqno_waiter_remove(dev_priv);
+
+ return ret;
}
+static struct fence_ops vmw_fence_ops = {
+ .get_driver_name = vmw_fence_get_driver_name,
+ .get_timeline_name = vmw_fence_get_timeline_name,
+ .enable_signaling = vmw_fence_enable_signaling,
+ .wait = vmw_fence_wait,
+ .release = vmw_fence_obj_destroy,
+};
+
/**
* Execute signal actions on fences recently signaled.
@@ -180,12 +305,14 @@ struct vmw_fence_manager *vmw_fence_manager_init(struct vmw_private *dev_priv)
INIT_LIST_HEAD(&fman->fence_list);
INIT_LIST_HEAD(&fman->cleanup_list);
INIT_WORK(&fman->work, &vmw_fence_work_func);
+ INIT_WORK(&fman->ping_work, &vmw_fence_ping_func);
fman->fifo_down = true;
fman->user_fence_size = ttm_round_pot(sizeof(struct vmw_user_fence));
fman->fence_size = ttm_round_pot(sizeof(struct vmw_fence_obj));
fman->event_fence_action_size =
ttm_round_pot(sizeof(struct vmw_event_fence_action));
mutex_init(&fman->goal_irq_mutex);
+ fman->ctx = fence_context_alloc(1);
return fman;
}
@@ -196,6 +323,7 @@ void vmw_fence_manager_takedown(struct vmw_fence_manager *fman)
bool lists_empty;
(void) cancel_work_sync(&fman->work);
+ (void) cancel_work_sync(&fman->ping_work);
spin_lock_irqsave(&fman->lock, irq_flags);
lists_empty = list_empty(&fman->fence_list) &&
@@ -207,23 +335,16 @@ void vmw_fence_manager_takedown(struct vmw_fence_manager *fman)
}
static int vmw_fence_obj_init(struct vmw_fence_manager *fman,
- struct vmw_fence_obj *fence,
- u32 seqno,
- uint32_t mask,
+ struct vmw_fence_obj *fence, u32 seqno,
void (*destroy) (struct vmw_fence_obj *fence))
{
unsigned long irq_flags;
- unsigned int num_fences;
int ret = 0;
- fence->seqno = seqno;
+ fence_init(&fence->base, &vmw_fence_ops, &fman->lock,
+ fman->ctx, seqno);
INIT_LIST_HEAD(&fence->seq_passed_actions);
- fence->fman = fman;
- fence->signaled = 0;
- fence->signal_mask = mask;
- kref_init(&fence->kref);
fence->destroy = destroy;
- init_waitqueue_head(&fence->queue);
spin_lock_irqsave(&fman->lock, irq_flags);
if (unlikely(fman->fifo_down)) {
@@ -231,7 +352,7 @@ static int vmw_fence_obj_init(struct vmw_fence_manager *fman,
goto out_unlock;
}
list_add_tail(&fence->head, &fman->fence_list);
- num_fences = ++fman->num_fence_objects;
+ ++fman->num_fence_objects;
out_unlock:
spin_unlock_irqrestore(&fman->lock, irq_flags);
@@ -239,38 +360,6 @@ out_unlock:
}
-struct vmw_fence_obj *vmw_fence_obj_reference(struct vmw_fence_obj *fence)
-{
- if (unlikely(fence == NULL))
- return NULL;
-
- kref_get(&fence->kref);
- return fence;
-}
-
-/**
- * vmw_fence_obj_unreference
- *
- * Note that this function may not be entered with disabled irqs since
- * it may re-enable them in the destroy function.
- *
- */
-void vmw_fence_obj_unreference(struct vmw_fence_obj **fence_p)
-{
- struct vmw_fence_obj *fence = *fence_p;
- struct vmw_fence_manager *fman;
-
- if (unlikely(fence == NULL))
- return;
-
- fman = fence->fman;
- *fence_p = NULL;
- spin_lock_irq(&fman->lock);
- BUG_ON(atomic_read(&fence->kref.refcount) == 0);
- kref_put(&fence->kref, vmw_fence_obj_destroy_locked);
- spin_unlock_irq(&fman->lock);
-}
-
static void vmw_fences_perform_actions(struct vmw_fence_manager *fman,
struct list_head *list)
{
@@ -326,7 +415,7 @@ static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman,
list_for_each_entry(fence, &fman->fence_list, head) {
if (!list_empty(&fence->seq_passed_actions)) {
fman->seqno_valid = true;
- iowrite32(fence->seqno,
+ iowrite32(fence->base.seqno,
fifo_mem + SVGA_FIFO_FENCE_GOAL);
break;
}
@@ -353,27 +442,27 @@ static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman,
*/
static bool vmw_fence_goal_check_locked(struct vmw_fence_obj *fence)
{
+ struct vmw_fence_manager *fman = fman_from_fence(fence);
u32 goal_seqno;
__le32 __iomem *fifo_mem;
- if (fence->signaled & DRM_VMW_FENCE_FLAG_EXEC)
+ if (fence_is_signaled_locked(&fence->base))
return false;
- fifo_mem = fence->fman->dev_priv->mmio_virt;
+ fifo_mem = fman->dev_priv->mmio_virt;
goal_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE_GOAL);
- if (likely(fence->fman->seqno_valid &&
- goal_seqno - fence->seqno < VMW_FENCE_WRAP))
+ if (likely(fman->seqno_valid &&
+ goal_seqno - fence->base.seqno < VMW_FENCE_WRAP))
return false;
- iowrite32(fence->seqno, fifo_mem + SVGA_FIFO_FENCE_GOAL);
- fence->fman->seqno_valid = true;
+ iowrite32(fence->base.seqno, fifo_mem + SVGA_FIFO_FENCE_GOAL);
+ fman->seqno_valid = true;
return true;
}
-void vmw_fences_update(struct vmw_fence_manager *fman)
+static void __vmw_fences_update(struct vmw_fence_manager *fman)
{
- unsigned long flags;
struct vmw_fence_obj *fence, *next_fence;
struct list_head action_list;
bool needs_rerun;
@@ -382,32 +471,25 @@ void vmw_fences_update(struct vmw_fence_manager *fman)
seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE);
rerun:
- spin_lock_irqsave(&fman->lock, flags);
list_for_each_entry_safe(fence, next_fence, &fman->fence_list, head) {
- if (seqno - fence->seqno < VMW_FENCE_WRAP) {
+ if (seqno - fence->base.seqno < VMW_FENCE_WRAP) {
list_del_init(&fence->head);
- fence->signaled |= DRM_VMW_FENCE_FLAG_EXEC;
+ fence_signal_locked(&fence->base);
INIT_LIST_HEAD(&action_list);
list_splice_init(&fence->seq_passed_actions,
&action_list);
vmw_fences_perform_actions(fman, &action_list);
- wake_up_all(&fence->queue);
} else
break;
}
- needs_rerun = vmw_fence_goal_new_locked(fman, seqno);
-
- if (!list_empty(&fman->cleanup_list))
- (void) schedule_work(&fman->work);
- spin_unlock_irqrestore(&fman->lock, flags);
-
/*
* Rerun if the fence goal seqno was updated, and the
* hardware might have raced with that update, so that
* we missed a fence_goal irq.
*/
+ needs_rerun = vmw_fence_goal_new_locked(fman, seqno);
if (unlikely(needs_rerun)) {
new_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE);
if (new_seqno != seqno) {
@@ -415,79 +497,58 @@ rerun:
goto rerun;
}
}
+
+ if (!list_empty(&fman->cleanup_list))
+ (void) schedule_work(&fman->work);
}
-bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence,
- uint32_t flags)
+void vmw_fences_update(struct vmw_fence_manager *fman)
{
- struct vmw_fence_manager *fman = fence->fman;
unsigned long irq_flags;
- uint32_t signaled;
spin_lock_irqsave(&fman->lock, irq_flags);
- signaled = fence->signaled;
+ __vmw_fences_update(fman);
spin_unlock_irqrestore(&fman->lock, irq_flags);
+}
- flags &= fence->signal_mask;
- if ((signaled & flags) == flags)
- return 1;
+bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence)
+{
+ struct vmw_fence_manager *fman = fman_from_fence(fence);
- if ((signaled & DRM_VMW_FENCE_FLAG_EXEC) == 0)
- vmw_fences_update(fman);
+ if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags))
+ return 1;
- spin_lock_irqsave(&fman->lock, irq_flags);
- signaled = fence->signaled;
- spin_unlock_irqrestore(&fman->lock, irq_flags);
+ vmw_fences_update(fman);
- return ((signaled & flags) == flags);
+ return fence_is_signaled(&fence->base);
}
-int vmw_fence_obj_wait(struct vmw_fence_obj *fence,
- uint32_t flags, bool lazy,
+int vmw_fence_obj_wait(struct vmw_fence_obj *fence, bool lazy,
bool interruptible, unsigned long timeout)
{
- struct vmw_private *dev_priv = fence->fman->dev_priv;
- long ret;
+ long ret = fence_wait_timeout(&fence->base, interruptible, timeout);
- if (likely(vmw_fence_obj_signaled(fence, flags)))
+ if (likely(ret > 0))
return 0;
-
- vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
- vmw_seqno_waiter_add(dev_priv);
-
- if (interruptible)
- ret = wait_event_interruptible_timeout
- (fence->queue,
- vmw_fence_obj_signaled(fence, flags),
- timeout);
+ else if (ret == 0)
+ return -EBUSY;
else
- ret = wait_event_timeout
- (fence->queue,
- vmw_fence_obj_signaled(fence, flags),
- timeout);
-
- vmw_seqno_waiter_remove(dev_priv);
-
- if (unlikely(ret == 0))
- ret = -EBUSY;
- else if (likely(ret > 0))
- ret = 0;
-
- return ret;
+ return ret;
}
void vmw_fence_obj_flush(struct vmw_fence_obj *fence)
{
- struct vmw_private *dev_priv = fence->fman->dev_priv;
+ struct vmw_private *dev_priv = fman_from_fence(fence)->dev_priv;
vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
}
static void vmw_fence_destroy(struct vmw_fence_obj *fence)
{
- struct vmw_fence_manager *fman = fence->fman;
+ struct vmw_fence_manager *fman = fman_from_fence(fence);
+
+ fence_free(&fence->base);
- kfree(fence);
/*
* Free kernel space accounting.
*/
@@ -497,7 +558,6 @@ static void vmw_fence_destroy(struct vmw_fence_obj *fence)
int vmw_fence_create(struct vmw_fence_manager *fman,
uint32_t seqno,
- uint32_t mask,
struct vmw_fence_obj **p_fence)
{
struct ttm_mem_global *mem_glob = vmw_mem_glob(fman->dev_priv);
@@ -515,7 +575,7 @@ int vmw_fence_create(struct vmw_fence_manager *fman,
goto out_no_object;
}
- ret = vmw_fence_obj_init(fman, fence, seqno, mask,
+ ret = vmw_fence_obj_init(fman, fence, seqno,
vmw_fence_destroy);
if (unlikely(ret != 0))
goto out_err_init;
@@ -535,7 +595,7 @@ static void vmw_user_fence_destroy(struct vmw_fence_obj *fence)
{
struct vmw_user_fence *ufence =
container_of(fence, struct vmw_user_fence, fence);
- struct vmw_fence_manager *fman = fence->fman;
+ struct vmw_fence_manager *fman = fman_from_fence(fence);
ttm_base_object_kfree(ufence, base);
/*
@@ -559,7 +619,6 @@ static void vmw_user_fence_base_release(struct ttm_base_object **p_base)
int vmw_user_fence_create(struct drm_file *file_priv,
struct vmw_fence_manager *fman,
uint32_t seqno,
- uint32_t mask,
struct vmw_fence_obj **p_fence,
uint32_t *p_handle)
{
@@ -586,7 +645,7 @@ int vmw_user_fence_create(struct drm_file *file_priv,
}
ret = vmw_fence_obj_init(fman, &ufence->fence, seqno,
- mask, vmw_user_fence_destroy);
+ vmw_user_fence_destroy);
if (unlikely(ret != 0)) {
kfree(ufence);
goto out_no_object;
@@ -629,7 +688,6 @@ out_no_object:
void vmw_fence_fifo_down(struct vmw_fence_manager *fman)
{
- unsigned long irq_flags;
struct list_head action_list;
int ret;
@@ -638,35 +696,32 @@ void vmw_fence_fifo_down(struct vmw_fence_manager *fman)
* restart when we've released the fman->lock.
*/
- spin_lock_irqsave(&fman->lock, irq_flags);
+ spin_lock_irq(&fman->lock);
fman->fifo_down = true;
while (!list_empty(&fman->fence_list)) {
struct vmw_fence_obj *fence =
list_entry(fman->fence_list.prev, struct vmw_fence_obj,
head);
- kref_get(&fence->kref);
+ fence_get(&fence->base);
spin_unlock_irq(&fman->lock);
- ret = vmw_fence_obj_wait(fence, fence->signal_mask,
- false, false,
+ ret = vmw_fence_obj_wait(fence, false, false,
VMW_FENCE_WAIT_TIMEOUT);
if (unlikely(ret != 0)) {
list_del_init(&fence->head);
- fence->signaled |= DRM_VMW_FENCE_FLAG_EXEC;
+ fence_signal(&fence->base);
INIT_LIST_HEAD(&action_list);
list_splice_init(&fence->seq_passed_actions,
&action_list);
vmw_fences_perform_actions(fman, &action_list);
- wake_up_all(&fence->queue);
}
- spin_lock_irq(&fman->lock);
-
BUG_ON(!list_empty(&fence->head));
- kref_put(&fence->kref, vmw_fence_obj_destroy_locked);
+ fence_put(&fence->base);
+ spin_lock_irq(&fman->lock);
}
- spin_unlock_irqrestore(&fman->lock, irq_flags);
+ spin_unlock_irq(&fman->lock);
}
void vmw_fence_fifo_up(struct vmw_fence_manager *fman)
@@ -716,14 +771,14 @@ int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data,
timeout = jiffies;
if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie)) {
- ret = ((vmw_fence_obj_signaled(fence, arg->flags)) ?
+ ret = ((vmw_fence_obj_signaled(fence)) ?
0 : -EBUSY);
goto out;
}
timeout = (unsigned long)arg->kernel_cookie - timeout;
- ret = vmw_fence_obj_wait(fence, arg->flags, arg->lazy, true, timeout);
+ ret = vmw_fence_obj_wait(fence, arg->lazy, true, timeout);
out:
ttm_base_object_unref(&base);
@@ -758,12 +813,12 @@ int vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data,
}
fence = &(container_of(base, struct vmw_user_fence, base)->fence);
- fman = fence->fman;
+ fman = fman_from_fence(fence);
- arg->signaled = vmw_fence_obj_signaled(fence, arg->flags);
- spin_lock_irq(&fman->lock);
+ arg->signaled = vmw_fence_obj_signaled(fence);
- arg->signaled_flags = fence->signaled;
+ arg->signaled_flags = arg->flags;
+ spin_lock_irq(&fman->lock);
arg->passed_seqno = dev_priv->last_read_seqno;
spin_unlock_irq(&fman->lock);
@@ -876,7 +931,7 @@ static void vmw_event_fence_action_cleanup(struct vmw_fence_action *action)
{
struct vmw_event_fence_action *eaction =
container_of(action, struct vmw_event_fence_action, action);
- struct vmw_fence_manager *fman = eaction->fence->fman;
+ struct vmw_fence_manager *fman = fman_from_fence(eaction->fence);
unsigned long irq_flags;
spin_lock_irqsave(&fman->lock, irq_flags);
@@ -900,7 +955,7 @@ static void vmw_event_fence_action_cleanup(struct vmw_fence_action *action)
static void vmw_fence_obj_add_action(struct vmw_fence_obj *fence,
struct vmw_fence_action *action)
{
- struct vmw_fence_manager *fman = fence->fman;
+ struct vmw_fence_manager *fman = fman_from_fence(fence);
unsigned long irq_flags;
bool run_update = false;
@@ -908,7 +963,7 @@ static void vmw_fence_obj_add_action(struct vmw_fence_obj *fence,
spin_lock_irqsave(&fman->lock, irq_flags);
fman->pending_actions[action->type]++;
- if (fence->signaled & DRM_VMW_FENCE_FLAG_EXEC) {
+ if (fence_is_signaled_locked(&fence->base)) {
struct list_head action_list;
INIT_LIST_HEAD(&action_list);
@@ -960,7 +1015,7 @@ int vmw_event_fence_action_queue(struct drm_file *file_priv,
bool interruptible)
{
struct vmw_event_fence_action *eaction;
- struct vmw_fence_manager *fman = fence->fman;
+ struct vmw_fence_manager *fman = fman_from_fence(fence);
struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
unsigned long irq_flags;
@@ -1000,7 +1055,8 @@ static int vmw_event_fence_action_create(struct drm_file *file_priv,
bool interruptible)
{
struct vmw_event_fence_pending *event;
- struct drm_device *dev = fence->fman->dev_priv->dev;
+ struct vmw_fence_manager *fman = fman_from_fence(fence);
+ struct drm_device *dev = fman->dev_priv->dev;
unsigned long irq_flags;
int ret;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
index faf2e7873860..26a4add39208 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
@@ -27,6 +27,8 @@
#ifndef _VMWGFX_FENCE_H_
+#include <linux/fence.h>
+
#define VMW_FENCE_WAIT_TIMEOUT (5*HZ)
struct vmw_private;
@@ -50,16 +52,11 @@ struct vmw_fence_action {
};
struct vmw_fence_obj {
- struct kref kref;
- u32 seqno;
+ struct fence base;
- struct vmw_fence_manager *fman;
struct list_head head;
- uint32_t signaled;
- uint32_t signal_mask;
struct list_head seq_passed_actions;
void (*destroy)(struct vmw_fence_obj *fence);
- wait_queue_head_t queue;
};
extern struct vmw_fence_manager *
@@ -67,17 +64,29 @@ vmw_fence_manager_init(struct vmw_private *dev_priv);
extern void vmw_fence_manager_takedown(struct vmw_fence_manager *fman);
-extern void vmw_fence_obj_unreference(struct vmw_fence_obj **fence_p);
+static inline void
+vmw_fence_obj_unreference(struct vmw_fence_obj **fence_p)
+{
+ struct vmw_fence_obj *fence = *fence_p;
+
+ *fence_p = NULL;
+ if (fence)
+ fence_put(&fence->base);
+}
-extern struct vmw_fence_obj *
-vmw_fence_obj_reference(struct vmw_fence_obj *fence);
+static inline struct vmw_fence_obj *
+vmw_fence_obj_reference(struct vmw_fence_obj *fence)
+{
+ if (fence)
+ fence_get(&fence->base);
+ return fence;
+}
extern void vmw_fences_update(struct vmw_fence_manager *fman);
-extern bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence,
- uint32_t flags);
+extern bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence);
-extern int vmw_fence_obj_wait(struct vmw_fence_obj *fence, uint32_t flags,
+extern int vmw_fence_obj_wait(struct vmw_fence_obj *fence,
bool lazy,
bool interruptible, unsigned long timeout);
@@ -85,13 +94,11 @@ extern void vmw_fence_obj_flush(struct vmw_fence_obj *fence);
extern int vmw_fence_create(struct vmw_fence_manager *fman,
uint32_t seqno,
- uint32_t mask,
struct vmw_fence_obj **p_fence);
extern int vmw_user_fence_create(struct drm_file *file_priv,
struct vmw_fence_manager *fman,
uint32_t sequence,
- uint32_t mask,
struct vmw_fence_obj **p_fence,
uint32_t *p_handle);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
index 6ccd993e26bf..09e10aefcd8e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
@@ -160,16 +160,21 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
return vmw_fifo_send_fence(dev_priv, &dummy);
}
-void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason)
+void vmw_fifo_ping_host_locked(struct vmw_private *dev_priv, uint32_t reason)
{
__le32 __iomem *fifo_mem = dev_priv->mmio_virt;
- mutex_lock(&dev_priv->hw_mutex);
-
if (unlikely(ioread32(fifo_mem + SVGA_FIFO_BUSY) == 0)) {
iowrite32(1, fifo_mem + SVGA_FIFO_BUSY);
vmw_write(dev_priv, SVGA_REG_SYNC, reason);
}
+}
+
+void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason)
+{
+ mutex_lock(&dev_priv->hw_mutex);
+
+ vmw_fifo_ping_host_locked(dev_priv, reason);
mutex_unlock(&dev_priv->hw_mutex);
}
@@ -180,8 +185,9 @@ void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
mutex_lock(&dev_priv->hw_mutex);
+ vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC);
while (vmw_read(dev_priv, SVGA_REG_BUSY) != 0)
- vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC);
+ ;
dev_priv->last_read_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
index b1273e8e9a69..170b61be1e4e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
@@ -46,7 +46,7 @@ struct vmwgfx_gmrid_man {
static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man,
struct ttm_buffer_object *bo,
- struct ttm_placement *placement,
+ const struct ttm_place *place,
struct ttm_mem_reg *mem)
{
struct vmwgfx_gmrid_man *gman =
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 8f3edc4710f2..941a7bc0b791 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -75,7 +75,7 @@ void vmw_display_unit_cleanup(struct vmw_display_unit *du)
vmw_surface_unreference(&du->cursor_surface);
if (du->cursor_dmabuf)
vmw_dmabuf_unreference(&du->cursor_dmabuf);
- drm_sysfs_connector_remove(&du->connector);
+ drm_connector_unregister(&du->connector);
drm_crtc_cleanup(&du->crtc);
drm_encoder_cleanup(&du->encoder);
drm_connector_cleanup(&du->connector);
@@ -136,7 +136,7 @@ int vmw_cursor_update_dmabuf(struct vmw_private *dev_priv,
kmap_offset = 0;
kmap_num = (width*height*4 + PAGE_SIZE - 1) >> PAGE_SHIFT;
- ret = ttm_bo_reserve(&dmabuf->base, true, false, false, 0);
+ ret = ttm_bo_reserve(&dmabuf->base, true, false, false, NULL);
if (unlikely(ret != 0)) {
DRM_ERROR("reserve failed\n");
return -EINVAL;
@@ -187,7 +187,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
* can do this since the caller in the drm core doesn't check anything
* which is protected by any looks.
*/
- drm_modeset_unlock(&crtc->mutex);
+ drm_modeset_unlock_crtc(crtc);
drm_modeset_lock_all(dev_priv->dev);
/* A lot of the code assumes this */
@@ -252,7 +252,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
ret = 0;
out:
drm_modeset_unlock_all(dev_priv->dev);
- drm_modeset_lock(&crtc->mutex, NULL);
+ drm_modeset_lock_crtc(crtc);
return ret;
}
@@ -273,7 +273,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
* can do this since the caller in the drm core doesn't check anything
* which is protected by any looks.
*/
- drm_modeset_unlock(&crtc->mutex);
+ drm_modeset_unlock_crtc(crtc);
drm_modeset_lock_all(dev_priv->dev);
vmw_cursor_update_position(dev_priv, shown,
@@ -281,7 +281,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
du->cursor_y + du->hotspot_y);
drm_modeset_unlock_all(dev_priv->dev);
- drm_modeset_lock(&crtc->mutex, NULL);
+ drm_modeset_lock_crtc(crtc);
return 0;
}
@@ -343,7 +343,7 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,
kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT;
kmap_num = (64*64*4) >> PAGE_SHIFT;
- ret = ttm_bo_reserve(bo, true, false, false, 0);
+ ret = ttm_bo_reserve(bo, true, false, false, NULL);
if (unlikely(ret != 0)) {
DRM_ERROR("reserve failed\n");
return;
@@ -1501,7 +1501,6 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
{
struct drm_vmw_cursor_bypass_arg *arg = data;
struct vmw_display_unit *du;
- struct drm_mode_object *obj;
struct drm_crtc *crtc;
int ret = 0;
@@ -1519,13 +1518,12 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
return 0;
}
- obj = drm_mode_object_find(dev, arg->crtc_id, DRM_MODE_OBJECT_CRTC);
- if (!obj) {
+ crtc = drm_crtc_find(dev, arg->crtc_id);
+ if (!crtc) {
ret = -ENOENT;
goto out;
}
- crtc = obj_to_crtc(obj);
du = vmw_crtc_to_du(crtc);
du->hotspot_x = arg->xhot;
@@ -1952,6 +1950,14 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC)
};
int i;
+ u32 assumed_bpp = 2;
+
+ /*
+ * If using screen objects, then assume 32-bpp because that's what the
+ * SVGA device is assuming
+ */
+ if (dev_priv->sou_priv)
+ assumed_bpp = 4;
/* Add preferred mode */
{
@@ -1962,8 +1968,9 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
mode->vdisplay = du->pref_height;
vmw_guess_mode_timing(mode);
- if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2,
- mode->vdisplay)) {
+ if (vmw_kms_validate_mode_vram(dev_priv,
+ mode->hdisplay * assumed_bpp,
+ mode->vdisplay)) {
drm_mode_probed_add(connector, mode);
} else {
drm_mode_destroy(dev, mode);
@@ -1985,7 +1992,8 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
bmode->vdisplay > max_height)
continue;
- if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2,
+ if (!vmw_kms_validate_mode_vram(dev_priv,
+ bmode->hdisplay * assumed_bpp,
bmode->vdisplay))
continue;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index b2b9bd23aeee..15e185ae4c99 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -371,7 +371,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
encoder->possible_crtcs = (1 << unit);
encoder->possible_clones = 0;
- (void) drm_sysfs_connector_add(connector);
+ (void) drm_connector_register(connector);
drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 01d68f0a69dc..026de7cea0f6 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -127,12 +127,13 @@ static void vmw_resource_release(struct kref *kref)
if (res->backup) {
struct ttm_buffer_object *bo = &res->backup->base;
- ttm_bo_reserve(bo, false, false, false, 0);
+ ttm_bo_reserve(bo, false, false, false, NULL);
if (!list_empty(&res->mob_head) &&
res->func->unbind != NULL) {
struct ttm_validate_buffer val_buf;
val_buf.bo = bo;
+ val_buf.shared = false;
res->func->unbind(res, false, &val_buf);
}
res->backup_dirty = false;
@@ -429,7 +430,7 @@ int vmw_dmabuf_init(struct vmw_private *dev_priv,
ret = ttm_bo_init(bdev, &vmw_bo->base, size,
ttm_bo_type_device, placement,
0, interruptible,
- NULL, acc_size, NULL, bo_free);
+ NULL, acc_size, NULL, NULL, bo_free);
return ret;
}
@@ -567,13 +568,18 @@ static int vmw_user_dmabuf_synccpu_grab(struct vmw_user_dma_buffer *user_bo,
int ret;
if (flags & drm_vmw_synccpu_allow_cs) {
- struct ttm_bo_device *bdev = bo->bdev;
+ bool nonblock = !!(flags & drm_vmw_synccpu_dontblock);
+ long lret;
- spin_lock(&bdev->fence_lock);
- ret = ttm_bo_wait(bo, false, true,
- !!(flags & drm_vmw_synccpu_dontblock));
- spin_unlock(&bdev->fence_lock);
- return ret;
+ if (nonblock)
+ return reservation_object_test_signaled_rcu(bo->resv, true) ? 0 : -EBUSY;
+
+ lret = reservation_object_wait_timeout_rcu(bo->resv, true, true, MAX_SCHEDULE_TIMEOUT);
+ if (!lret)
+ return -EBUSY;
+ else if (lret < 0)
+ return lret;
+ return 0;
}
ret = ttm_bo_synccpu_write_grab
@@ -1214,8 +1220,9 @@ vmw_resource_check_buffer(struct vmw_resource *res,
INIT_LIST_HEAD(&val_list);
val_buf->bo = ttm_bo_reference(&res->backup->base);
+ val_buf->shared = false;
list_add_tail(&val_buf->head, &val_list);
- ret = ttm_eu_reserve_buffers(NULL, &val_list);
+ ret = ttm_eu_reserve_buffers(NULL, &val_list, interruptible);
if (unlikely(ret != 0))
goto out_no_reserve;
@@ -1307,6 +1314,7 @@ int vmw_resource_do_evict(struct vmw_resource *res, bool interruptible)
BUG_ON(!func->may_evict);
val_buf.bo = NULL;
+ val_buf.shared = false;
ret = vmw_resource_check_buffer(res, interruptible, &val_buf);
if (unlikely(ret != 0))
return ret;
@@ -1352,6 +1360,7 @@ int vmw_resource_validate(struct vmw_resource *res)
return 0;
val_buf.bo = NULL;
+ val_buf.shared = false;
if (res->backup)
val_buf.bo = &res->backup->base;
do {
@@ -1419,25 +1428,16 @@ void vmw_fence_single_bo(struct ttm_buffer_object *bo,
struct vmw_fence_obj *fence)
{
struct ttm_bo_device *bdev = bo->bdev;
- struct ttm_bo_driver *driver = bdev->driver;
- struct vmw_fence_obj *old_fence_obj;
+
struct vmw_private *dev_priv =
container_of(bdev, struct vmw_private, bdev);
- if (fence == NULL)
+ if (fence == NULL) {
vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL);
- else
- driver->sync_obj_ref(fence);
-
- spin_lock(&bdev->fence_lock);
-
- old_fence_obj = bo->sync_obj;
- bo->sync_obj = fence;
-
- spin_unlock(&bdev->fence_lock);
-
- if (old_fence_obj)
- vmw_fence_obj_unreference(&old_fence_obj);
+ reservation_object_add_excl_fence(bo->resv, &fence->base);
+ fence_put(&fence->base);
+ } else
+ reservation_object_add_excl_fence(bo->resv, &fence->base);
}
/**
@@ -1475,10 +1475,10 @@ void vmw_resource_move_notify(struct ttm_buffer_object *bo,
if (mem->mem_type != VMW_PL_MOB) {
struct vmw_resource *res, *n;
- struct ttm_bo_device *bdev = bo->bdev;
struct ttm_validate_buffer val_buf;
val_buf.bo = bo;
+ val_buf.shared = false;
list_for_each_entry_safe(res, n, &dma_buf->res_list, mob_head) {
@@ -1491,9 +1491,7 @@ void vmw_resource_move_notify(struct ttm_buffer_object *bo,
list_del_init(&res->mob_head);
}
- spin_lock(&bdev->fence_lock);
(void) ttm_bo_wait(bo, false, false, false);
- spin_unlock(&bdev->fence_lock);
}
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index a95d3a0cabe4..b295463a60b3 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -467,7 +467,7 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
encoder->possible_crtcs = (1 << unit);
encoder->possible_clones = 0;
- (void) drm_sysfs_connector_add(connector);
+ (void) drm_connector_register(connector);
drm_crtc_init(dev, crtc, &vmw_screen_object_crtc_funcs);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
index c1559eeaffe9..8719fb3cccc9 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
@@ -29,8 +29,6 @@
#include "vmwgfx_resource_priv.h"
#include "ttm/ttm_placement.h"
-#define VMW_COMPAT_SHADER_HT_ORDER 12
-
struct vmw_shader {
struct vmw_resource res;
SVGA3dShaderType type;
@@ -42,49 +40,8 @@ struct vmw_user_shader {
struct vmw_shader shader;
};
-/**
- * enum vmw_compat_shader_state - Staging state for compat shaders
- */
-enum vmw_compat_shader_state {
- VMW_COMPAT_COMMITED,
- VMW_COMPAT_ADD,
- VMW_COMPAT_DEL
-};
-
-/**
- * struct vmw_compat_shader - Metadata for compat shaders.
- *
- * @handle: The TTM handle of the guest backed shader.
- * @tfile: The struct ttm_object_file the guest backed shader is registered
- * with.
- * @hash: Hash item for lookup.
- * @head: List head for staging lists or the compat shader manager list.
- * @state: Staging state.
- *
- * The structure is protected by the cmdbuf lock.
- */
-struct vmw_compat_shader {
- u32 handle;
- struct ttm_object_file *tfile;
- struct drm_hash_item hash;
- struct list_head head;
- enum vmw_compat_shader_state state;
-};
-
-/**
- * struct vmw_compat_shader_manager - Compat shader manager.
- *
- * @shaders: Hash table containing staged and commited compat shaders
- * @list: List of commited shaders.
- * @dev_priv: Pointer to a device private structure.
- *
- * @shaders and @list are protected by the cmdbuf mutex for now.
- */
-struct vmw_compat_shader_manager {
- struct drm_open_hash shaders;
- struct list_head list;
- struct vmw_private *dev_priv;
-};
+static uint64_t vmw_user_shader_size;
+static uint64_t vmw_shader_size;
static void vmw_user_shader_free(struct vmw_resource *res);
static struct vmw_resource *
@@ -98,8 +55,6 @@ static int vmw_gb_shader_unbind(struct vmw_resource *res,
struct ttm_validate_buffer *val_buf);
static int vmw_gb_shader_destroy(struct vmw_resource *res);
-static uint64_t vmw_user_shader_size;
-
static const struct vmw_user_resource_conv user_shader_conv = {
.object_type = VMW_RES_SHADER,
.base_obj_to_res = vmw_user_shader_base_to_res,
@@ -347,6 +302,16 @@ static void vmw_user_shader_free(struct vmw_resource *res)
vmw_user_shader_size);
}
+static void vmw_shader_free(struct vmw_resource *res)
+{
+ struct vmw_shader *shader = vmw_res_to_shader(res);
+ struct vmw_private *dev_priv = res->dev_priv;
+
+ kfree(shader);
+ ttm_mem_global_free(vmw_mem_glob(dev_priv),
+ vmw_shader_size);
+}
+
/**
* This function is called when user space has no more references on the
* base object. It releases the base-object's reference on the resource object.
@@ -371,13 +336,13 @@ int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data,
TTM_REF_USAGE);
}
-static int vmw_shader_alloc(struct vmw_private *dev_priv,
- struct vmw_dma_buffer *buffer,
- size_t shader_size,
- size_t offset,
- SVGA3dShaderType shader_type,
- struct ttm_object_file *tfile,
- u32 *handle)
+static int vmw_user_shader_alloc(struct vmw_private *dev_priv,
+ struct vmw_dma_buffer *buffer,
+ size_t shader_size,
+ size_t offset,
+ SVGA3dShaderType shader_type,
+ struct ttm_object_file *tfile,
+ u32 *handle)
{
struct vmw_user_shader *ushader;
struct vmw_resource *res, *tmp;
@@ -442,6 +407,56 @@ out:
}
+struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv,
+ struct vmw_dma_buffer *buffer,
+ size_t shader_size,
+ size_t offset,
+ SVGA3dShaderType shader_type)
+{
+ struct vmw_shader *shader;
+ struct vmw_resource *res;
+ int ret;
+
+ /*
+ * Approximate idr memory usage with 128 bytes. It will be limited
+ * by maximum number_of shaders anyway.
+ */
+ if (unlikely(vmw_shader_size == 0))
+ vmw_shader_size =
+ ttm_round_pot(sizeof(struct vmw_shader)) + 128;
+
+ ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv),
+ vmw_shader_size,
+ false, true);
+ if (unlikely(ret != 0)) {
+ if (ret != -ERESTARTSYS)
+ DRM_ERROR("Out of graphics memory for shader "
+ "creation.\n");
+ goto out_err;
+ }
+
+ shader = kzalloc(sizeof(*shader), GFP_KERNEL);
+ if (unlikely(shader == NULL)) {
+ ttm_mem_global_free(vmw_mem_glob(dev_priv),
+ vmw_shader_size);
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ res = &shader->res;
+
+ /*
+ * From here on, the destructor takes over resource freeing.
+ */
+ ret = vmw_gb_shader_init(dev_priv, res, shader_size,
+ offset, shader_type, buffer,
+ vmw_shader_free);
+
+out_err:
+ return ret ? ERR_PTR(ret) : res;
+}
+
+
int vmw_shader_define_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -490,8 +505,8 @@ int vmw_shader_define_ioctl(struct drm_device *dev, void *data,
if (unlikely(ret != 0))
goto out_bad_arg;
- ret = vmw_shader_alloc(dev_priv, buffer, arg->size, arg->offset,
- shader_type, tfile, &arg->shader_handle);
+ ret = vmw_user_shader_alloc(dev_priv, buffer, arg->size, arg->offset,
+ shader_type, tfile, &arg->shader_handle);
ttm_read_unlock(&dev_priv->reservation_sem);
out_bad_arg:
@@ -500,202 +515,83 @@ out_bad_arg:
}
/**
- * vmw_compat_shader_lookup - Look up a compat shader
- *
- * @man: Pointer to the compat shader manager.
- * @shader_type: The shader type, that combined with the user_key identifies
- * the shader.
- * @user_key: On entry, this should be a pointer to the user_key.
- * On successful exit, it will contain the guest-backed shader's TTM handle.
+ * vmw_compat_shader_id_ok - Check whether a compat shader user key and
+ * shader type are within valid bounds.
*
- * Returns 0 on success. Non-zero on failure, in which case the value pointed
- * to by @user_key is unmodified.
- */
-int vmw_compat_shader_lookup(struct vmw_compat_shader_manager *man,
- SVGA3dShaderType shader_type,
- u32 *user_key)
-{
- struct drm_hash_item *hash;
- int ret;
- unsigned long key = *user_key | (shader_type << 24);
-
- ret = drm_ht_find_item(&man->shaders, key, &hash);
- if (unlikely(ret != 0))
- return ret;
-
- *user_key = drm_hash_entry(hash, struct vmw_compat_shader,
- hash)->handle;
-
- return 0;
-}
-
-/**
- * vmw_compat_shader_free - Free a compat shader.
- *
- * @man: Pointer to the compat shader manager.
- * @entry: Pointer to a struct vmw_compat_shader.
- *
- * Frees a struct vmw_compat_shder entry and drops its reference to the
- * guest backed shader.
- */
-static void vmw_compat_shader_free(struct vmw_compat_shader_manager *man,
- struct vmw_compat_shader *entry)
-{
- list_del(&entry->head);
- WARN_ON(drm_ht_remove_item(&man->shaders, &entry->hash));
- WARN_ON(ttm_ref_object_base_unref(entry->tfile, entry->handle,
- TTM_REF_USAGE));
- kfree(entry);
-}
-
-/**
- * vmw_compat_shaders_commit - Commit a list of compat shader actions.
- *
- * @man: Pointer to the compat shader manager.
- * @list: Caller's list of compat shader actions.
+ * @user_key: User space id of the shader.
+ * @shader_type: Shader type.
*
- * This function commits a list of compat shader additions or removals.
- * It is typically called when the execbuf ioctl call triggering these
- * actions has commited the fifo contents to the device.
+ * Returns true if valid false if not.
*/
-void vmw_compat_shaders_commit(struct vmw_compat_shader_manager *man,
- struct list_head *list)
+static bool vmw_compat_shader_id_ok(u32 user_key, SVGA3dShaderType shader_type)
{
- struct vmw_compat_shader *entry, *next;
-
- list_for_each_entry_safe(entry, next, list, head) {
- list_del(&entry->head);
- switch (entry->state) {
- case VMW_COMPAT_ADD:
- entry->state = VMW_COMPAT_COMMITED;
- list_add_tail(&entry->head, &man->list);
- break;
- case VMW_COMPAT_DEL:
- ttm_ref_object_base_unref(entry->tfile, entry->handle,
- TTM_REF_USAGE);
- kfree(entry);
- break;
- default:
- BUG();
- break;
- }
- }
+ return user_key <= ((1 << 20) - 1) && (unsigned) shader_type < 16;
}
/**
- * vmw_compat_shaders_revert - Revert a list of compat shader actions
+ * vmw_compat_shader_key - Compute a hash key suitable for a compat shader.
*
- * @man: Pointer to the compat shader manager.
- * @list: Caller's list of compat shader actions.
+ * @user_key: User space id of the shader.
+ * @shader_type: Shader type.
*
- * This function reverts a list of compat shader additions or removals.
- * It is typically called when the execbuf ioctl call triggering these
- * actions failed for some reason, and the command stream was never
- * submitted.
+ * Returns a hash key suitable for a command buffer managed resource
+ * manager hash table.
*/
-void vmw_compat_shaders_revert(struct vmw_compat_shader_manager *man,
- struct list_head *list)
+static u32 vmw_compat_shader_key(u32 user_key, SVGA3dShaderType shader_type)
{
- struct vmw_compat_shader *entry, *next;
- int ret;
-
- list_for_each_entry_safe(entry, next, list, head) {
- switch (entry->state) {
- case VMW_COMPAT_ADD:
- vmw_compat_shader_free(man, entry);
- break;
- case VMW_COMPAT_DEL:
- ret = drm_ht_insert_item(&man->shaders, &entry->hash);
- list_del(&entry->head);
- list_add_tail(&entry->head, &man->list);
- entry->state = VMW_COMPAT_COMMITED;
- break;
- default:
- BUG();
- break;
- }
- }
+ return user_key | (shader_type << 20);
}
/**
* vmw_compat_shader_remove - Stage a compat shader for removal.
*
- * @man: Pointer to the compat shader manager
+ * @man: Pointer to the compat shader manager identifying the shader namespace.
* @user_key: The key that is used to identify the shader. The key is
* unique to the shader type.
* @shader_type: Shader type.
- * @list: Caller's list of staged shader actions.
- *
- * This function stages a compat shader for removal and removes the key from
- * the shader manager's hash table. If the shader was previously only staged
- * for addition it is completely removed (But the execbuf code may keep a
- * reference if it was bound to a context between addition and removal). If
- * it was previously commited to the manager, it is staged for removal.
+ * @list: Caller's list of staged command buffer resource actions.
*/
-int vmw_compat_shader_remove(struct vmw_compat_shader_manager *man,
+int vmw_compat_shader_remove(struct vmw_cmdbuf_res_manager *man,
u32 user_key, SVGA3dShaderType shader_type,
struct list_head *list)
{
- struct vmw_compat_shader *entry;
- struct drm_hash_item *hash;
- int ret;
-
- ret = drm_ht_find_item(&man->shaders, user_key | (shader_type << 24),
- &hash);
- if (likely(ret != 0))
+ if (!vmw_compat_shader_id_ok(user_key, shader_type))
return -EINVAL;
- entry = drm_hash_entry(hash, struct vmw_compat_shader, hash);
-
- switch (entry->state) {
- case VMW_COMPAT_ADD:
- vmw_compat_shader_free(man, entry);
- break;
- case VMW_COMPAT_COMMITED:
- (void) drm_ht_remove_item(&man->shaders, &entry->hash);
- list_del(&entry->head);
- entry->state = VMW_COMPAT_DEL;
- list_add_tail(&entry->head, list);
- break;
- default:
- BUG();
- break;
- }
-
- return 0;
+ return vmw_cmdbuf_res_remove(man, vmw_cmdbuf_res_compat_shader,
+ vmw_compat_shader_key(user_key,
+ shader_type),
+ list);
}
/**
- * vmw_compat_shader_add - Create a compat shader and add the
- * key to the manager
+ * vmw_compat_shader_add - Create a compat shader and stage it for addition
+ * as a command buffer managed resource.
*
- * @man: Pointer to the compat shader manager
+ * @man: Pointer to the compat shader manager identifying the shader namespace.
* @user_key: The key that is used to identify the shader. The key is
* unique to the shader type.
* @bytecode: Pointer to the bytecode of the shader.
* @shader_type: Shader type.
* @tfile: Pointer to a struct ttm_object_file that the guest-backed shader is
* to be created with.
- * @list: Caller's list of staged shader actions.
+ * @list: Caller's list of staged command buffer resource actions.
*
- * Note that only the key is added to the shader manager's hash table.
- * The shader is not yet added to the shader manager's list of shaders.
*/
-int vmw_compat_shader_add(struct vmw_compat_shader_manager *man,
+int vmw_compat_shader_add(struct vmw_private *dev_priv,
+ struct vmw_cmdbuf_res_manager *man,
u32 user_key, const void *bytecode,
SVGA3dShaderType shader_type,
size_t size,
- struct ttm_object_file *tfile,
struct list_head *list)
{
struct vmw_dma_buffer *buf;
struct ttm_bo_kmap_obj map;
bool is_iomem;
- struct vmw_compat_shader *compat;
- u32 handle;
int ret;
+ struct vmw_resource *res;
- if (user_key > ((1 << 24) - 1) || (unsigned) shader_type > 16)
+ if (!vmw_compat_shader_id_ok(user_key, shader_type))
return -EINVAL;
/* Allocate and pin a DMA buffer */
@@ -703,7 +599,7 @@ int vmw_compat_shader_add(struct vmw_compat_shader_manager *man,
if (unlikely(buf == NULL))
return -ENOMEM;
- ret = vmw_dmabuf_init(man->dev_priv, buf, size, &vmw_sys_ne_placement,
+ ret = vmw_dmabuf_init(dev_priv, buf, size, &vmw_sys_ne_placement,
true, vmw_dmabuf_bo_free);
if (unlikely(ret != 0))
goto out;
@@ -728,84 +624,40 @@ int vmw_compat_shader_add(struct vmw_compat_shader_manager *man,
WARN_ON(ret != 0);
ttm_bo_unreserve(&buf->base);
- /* Create a guest-backed shader container backed by the dma buffer */
- ret = vmw_shader_alloc(man->dev_priv, buf, size, 0, shader_type,
- tfile, &handle);
- vmw_dmabuf_unreference(&buf);
+ res = vmw_shader_alloc(dev_priv, buf, size, 0, shader_type);
if (unlikely(ret != 0))
goto no_reserve;
- /*
- * Create a compat shader structure and stage it for insertion
- * in the manager
- */
- compat = kzalloc(sizeof(*compat), GFP_KERNEL);
- if (compat == NULL)
- goto no_compat;
-
- compat->hash.key = user_key | (shader_type << 24);
- ret = drm_ht_insert_item(&man->shaders, &compat->hash);
- if (unlikely(ret != 0))
- goto out_invalid_key;
-
- compat->state = VMW_COMPAT_ADD;
- compat->handle = handle;
- compat->tfile = tfile;
- list_add_tail(&compat->head, list);
-
- return 0;
-out_invalid_key:
- kfree(compat);
-no_compat:
- ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE);
+ ret = vmw_cmdbuf_res_add(man, vmw_cmdbuf_res_compat_shader,
+ vmw_compat_shader_key(user_key, shader_type),
+ res, list);
+ vmw_resource_unreference(&res);
no_reserve:
+ vmw_dmabuf_unreference(&buf);
out:
return ret;
}
/**
- * vmw_compat_shader_man_create - Create a compat shader manager
- *
- * @dev_priv: Pointer to a device private structure.
- *
- * Typically done at file open time. If successful returns a pointer to a
- * compat shader manager. Otherwise returns an error pointer.
- */
-struct vmw_compat_shader_manager *
-vmw_compat_shader_man_create(struct vmw_private *dev_priv)
-{
- struct vmw_compat_shader_manager *man;
- int ret;
-
- man = kzalloc(sizeof(*man), GFP_KERNEL);
- if (man == NULL)
- return ERR_PTR(-ENOMEM);
-
- man->dev_priv = dev_priv;
- INIT_LIST_HEAD(&man->list);
- ret = drm_ht_create(&man->shaders, VMW_COMPAT_SHADER_HT_ORDER);
- if (ret == 0)
- return man;
-
- kfree(man);
- return ERR_PTR(ret);
-}
-
-/**
- * vmw_compat_shader_man_destroy - Destroy a compat shader manager
+ * vmw_compat_shader_lookup - Look up a compat shader
*
- * @man: Pointer to the shader manager to destroy.
+ * @man: Pointer to the command buffer managed resource manager identifying
+ * the shader namespace.
+ * @user_key: The user space id of the shader.
+ * @shader_type: The shader type.
*
- * Typically done at file close time.
+ * Returns a refcounted pointer to a struct vmw_resource if the shader was
+ * found. An error pointer otherwise.
*/
-void vmw_compat_shader_man_destroy(struct vmw_compat_shader_manager *man)
+struct vmw_resource *
+vmw_compat_shader_lookup(struct vmw_cmdbuf_res_manager *man,
+ u32 user_key,
+ SVGA3dShaderType shader_type)
{
- struct vmw_compat_shader *entry, *next;
-
- mutex_lock(&man->dev_priv->cmdbuf_mutex);
- list_for_each_entry_safe(entry, next, &man->list, head)
- vmw_compat_shader_free(man, entry);
+ if (!vmw_compat_shader_id_ok(user_key, shader_type))
+ return ERR_PTR(-EINVAL);
- mutex_unlock(&man->dev_priv->cmdbuf_mutex);
- kfree(man);
+ return vmw_cmdbuf_res_lookup(man, vmw_cmdbuf_res_compat_shader,
+ vmw_compat_shader_key(user_key,
+ shader_type));
}
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c
index 112f27e51bc7..63bd63f3c7df 100644
--- a/drivers/gpu/host1x/job.c
+++ b/drivers/gpu/host1x/job.c
@@ -185,16 +185,16 @@ static unsigned int pin_job(struct host1x_job *job)
struct sg_table *sgt;
dma_addr_t phys_addr;
- reloc->target = host1x_bo_get(reloc->target);
- if (!reloc->target)
+ reloc->target.bo = host1x_bo_get(reloc->target.bo);
+ if (!reloc->target.bo)
goto unpin;
- phys_addr = host1x_bo_pin(reloc->target, &sgt);
+ phys_addr = host1x_bo_pin(reloc->target.bo, &sgt);
if (!phys_addr)
goto unpin;
job->addr_phys[job->num_unpins] = phys_addr;
- job->unpins[job->num_unpins].bo = reloc->target;
+ job->unpins[job->num_unpins].bo = reloc->target.bo;
job->unpins[job->num_unpins].sgt = sgt;
job->num_unpins++;
}
@@ -235,21 +235,21 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
for (i = 0; i < job->num_relocs; i++) {
struct host1x_reloc *reloc = &job->relocarray[i];
u32 reloc_addr = (job->reloc_addr_phys[i] +
- reloc->target_offset) >> reloc->shift;
+ reloc->target.offset) >> reloc->shift;
u32 *target;
/* skip all other gathers */
- if (cmdbuf != reloc->cmdbuf)
+ if (cmdbuf != reloc->cmdbuf.bo)
continue;
- if (last_page != reloc->cmdbuf_offset >> PAGE_SHIFT) {
+ if (last_page != reloc->cmdbuf.offset >> PAGE_SHIFT) {
if (cmdbuf_page_addr)
host1x_bo_kunmap(cmdbuf, last_page,
cmdbuf_page_addr);
cmdbuf_page_addr = host1x_bo_kmap(cmdbuf,
- reloc->cmdbuf_offset >> PAGE_SHIFT);
- last_page = reloc->cmdbuf_offset >> PAGE_SHIFT;
+ reloc->cmdbuf.offset >> PAGE_SHIFT);
+ last_page = reloc->cmdbuf.offset >> PAGE_SHIFT;
if (unlikely(!cmdbuf_page_addr)) {
pr_err("Could not map cmdbuf for relocation\n");
@@ -257,7 +257,7 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
}
}
- target = cmdbuf_page_addr + (reloc->cmdbuf_offset & ~PAGE_MASK);
+ target = cmdbuf_page_addr + (reloc->cmdbuf.offset & ~PAGE_MASK);
*target = reloc_addr;
}
@@ -272,7 +272,7 @@ static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
{
offset *= sizeof(u32);
- if (reloc->cmdbuf != cmdbuf || reloc->cmdbuf_offset != offset)
+ if (reloc->cmdbuf.bo != cmdbuf || reloc->cmdbuf.offset != offset)
return false;
return true;
diff --git a/drivers/gpu/ipu-v3/Kconfig b/drivers/gpu/ipu-v3/Kconfig
index 2f228a2f2a48..aefdff95356d 100644
--- a/drivers/gpu/ipu-v3/Kconfig
+++ b/drivers/gpu/ipu-v3/Kconfig
@@ -1,7 +1,8 @@
config IMX_IPUV3_CORE
tristate "IPUv3 core support"
- depends on SOC_IMX5 || SOC_IMX6Q || SOC_IMX6SL || ARCH_MULTIPLATFORM
+ depends on SOC_IMX5 || SOC_IMX6Q || ARCH_MULTIPLATFORM
depends on RESET_CONTROLLER
+ select GENERIC_IRQ_CHIP
help
Choose this if you have a i.MX5/6 system and want to use the Image
Processing Unit. This option only enables IPU base support.
diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile
index 1887972b4ac2..107ec236a4a6 100644
--- a/drivers/gpu/ipu-v3/Makefile
+++ b/drivers/gpu/ipu-v3/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
-imx-ipu-v3-objs := ipu-common.o ipu-dc.o ipu-di.o ipu-dp.o ipu-dmfc.o ipu-smfc.o
+imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \
+ ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 04e7b2eafbdd..f707d25ae78f 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -44,17 +44,6 @@ static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
writel(value, ipu->cm_reg + offset);
}
-static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset)
-{
- return readl(ipu->idmac_reg + offset);
-}
-
-static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value,
- unsigned offset)
-{
- writel(value, ipu->idmac_reg + offset);
-}
-
void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
{
u32 val;
@@ -65,457 +54,184 @@ void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
}
EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update);
-struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel)
-{
- struct ipu_soc *ipu = channel->ipu;
-
- return ipu->cpmem_base + channel->num;
-}
-EXPORT_SYMBOL_GPL(ipu_get_cpmem);
-
-void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel)
-{
- struct ipu_soc *ipu = channel->ipu;
- struct ipu_ch_param __iomem *p = ipu_get_cpmem(channel);
- u32 val;
-
- if (ipu->ipu_type == IPUV3EX)
- ipu_ch_param_write_field(p, IPU_FIELD_ID, 1);
-
- val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(channel->num));
- val |= 1 << (channel->num % 32);
- ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(channel->num));
-};
-EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority);
-
-void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v)
-{
- u32 bit = (wbs >> 8) % 160;
- u32 size = wbs & 0xff;
- u32 word = (wbs >> 8) / 160;
- u32 i = bit / 32;
- u32 ofs = bit % 32;
- u32 mask = (1 << size) - 1;
- u32 val;
-
- pr_debug("%s %d %d %d\n", __func__, word, bit , size);
-
- val = readl(&base->word[word].data[i]);
- val &= ~(mask << ofs);
- val |= v << ofs;
- writel(val, &base->word[word].data[i]);
-
- if ((bit + size - 1) / 32 > i) {
- val = readl(&base->word[word].data[i + 1]);
- val &= ~(mask >> (ofs ? (32 - ofs) : 0));
- val |= v >> (ofs ? (32 - ofs) : 0);
- writel(val, &base->word[word].data[i + 1]);
- }
-}
-EXPORT_SYMBOL_GPL(ipu_ch_param_write_field);
-
-u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs)
-{
- u32 bit = (wbs >> 8) % 160;
- u32 size = wbs & 0xff;
- u32 word = (wbs >> 8) / 160;
- u32 i = bit / 32;
- u32 ofs = bit % 32;
- u32 mask = (1 << size) - 1;
- u32 val = 0;
-
- pr_debug("%s %d %d %d\n", __func__, word, bit , size);
-
- val = (readl(&base->word[word].data[i]) >> ofs) & mask;
-
- if ((bit + size - 1) / 32 > i) {
- u32 tmp;
- tmp = readl(&base->word[word].data[i + 1]);
- tmp &= mask >> (ofs ? (32 - ofs) : 0);
- val |= tmp << (ofs ? (32 - ofs) : 0);
- }
-
- return val;
-}
-EXPORT_SYMBOL_GPL(ipu_ch_param_read_field);
-
-int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *p,
- const struct ipu_rgb *rgb)
-{
- int bpp = 0, npb = 0, ro, go, bo, to;
-
- ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset;
- go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset;
- bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset;
- to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset;
-
- ipu_ch_param_write_field(p, IPU_FIELD_WID0, rgb->red.length - 1);
- ipu_ch_param_write_field(p, IPU_FIELD_OFS0, ro);
- ipu_ch_param_write_field(p, IPU_FIELD_WID1, rgb->green.length - 1);
- ipu_ch_param_write_field(p, IPU_FIELD_OFS1, go);
- ipu_ch_param_write_field(p, IPU_FIELD_WID2, rgb->blue.length - 1);
- ipu_ch_param_write_field(p, IPU_FIELD_OFS2, bo);
-
- if (rgb->transp.length) {
- ipu_ch_param_write_field(p, IPU_FIELD_WID3,
- rgb->transp.length - 1);
- ipu_ch_param_write_field(p, IPU_FIELD_OFS3, to);
- } else {
- ipu_ch_param_write_field(p, IPU_FIELD_WID3, 7);
- ipu_ch_param_write_field(p, IPU_FIELD_OFS3,
- rgb->bits_per_pixel);
- }
-
- switch (rgb->bits_per_pixel) {
- case 32:
- bpp = 0;
- npb = 15;
- break;
- case 24:
- bpp = 1;
- npb = 19;
- break;
- case 16:
- bpp = 3;
- npb = 31;
- break;
- case 8:
- bpp = 5;
- npb = 63;
- break;
- default:
- return -EINVAL;
- }
- ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp);
- ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb);
- ipu_ch_param_write_field(p, IPU_FIELD_PFS, 7); /* rgb mode */
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb);
-
-int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p,
- int width)
+enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
{
- int bpp = 0, npb = 0;
-
- switch (width) {
- case 32:
- bpp = 0;
- npb = 15;
- break;
- case 24:
- bpp = 1;
- npb = 19;
- break;
- case 16:
- bpp = 3;
- npb = 31;
- break;
- case 8:
- bpp = 5;
- npb = 63;
- break;
+ switch (drm_fourcc) {
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_BGR888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_BGRA8888:
+ return IPUV3_COLORSPACE_RGB;
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_NV12:
+ case DRM_FORMAT_NV21:
+ case DRM_FORMAT_NV16:
+ case DRM_FORMAT_NV61:
+ return IPUV3_COLORSPACE_YUV;
default:
- return -EINVAL;
+ return IPUV3_COLORSPACE_UNKNOWN;
}
-
- ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp);
- ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb);
- ipu_ch_param_write_field(p, IPU_FIELD_PFS, 6); /* raw mode */
-
- return 0;
}
-EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough);
+EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace);
-void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p,
- u32 pixel_format)
+enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
{
- switch (pixel_format) {
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_YUV422P:
case V4L2_PIX_FMT_UYVY:
- ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3); /* bits/pixel */
- ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0xA); /* pix format */
- ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31); /* burst size */
- break;
case V4L2_PIX_FMT_YUYV:
- ipu_ch_param_write_field(p, IPU_FIELD_BPP, 3); /* bits/pixel */
- ipu_ch_param_write_field(p, IPU_FIELD_PFS, 0x8); /* pix format */
- ipu_ch_param_write_field(p, IPU_FIELD_NPB, 31); /* burst size */
- break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ return IPUV3_COLORSPACE_YUV;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB565:
+ return IPUV3_COLORSPACE_RGB;
+ default:
+ return IPUV3_COLORSPACE_UNKNOWN;
}
}
-EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved);
+EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
-void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p,
- u32 pixel_format, int stride, int u_offset, int v_offset)
+bool ipu_pixelformat_is_planar(u32 pixelformat)
{
- switch (pixel_format) {
+ switch (pixelformat) {
case V4L2_PIX_FMT_YUV420:
- ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1);
- ipu_ch_param_write_field(p, IPU_FIELD_UBO, u_offset / 8);
- ipu_ch_param_write_field(p, IPU_FIELD_VBO, v_offset / 8);
- break;
case V4L2_PIX_FMT_YVU420:
- ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1);
- ipu_ch_param_write_field(p, IPU_FIELD_UBO, v_offset / 8);
- ipu_ch_param_write_field(p, IPU_FIELD_VBO, u_offset / 8);
- break;
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ return true;
}
-}
-EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full);
-
-void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format,
- int stride, int height)
-{
- int u_offset, v_offset;
- int uv_stride = 0;
- switch (pixel_format) {
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- uv_stride = stride / 2;
- u_offset = stride * height;
- v_offset = u_offset + (uv_stride * height / 2);
- ipu_cpmem_set_yuv_planar_full(p, pixel_format, stride,
- u_offset, v_offset);
- break;
- }
+ return false;
}
-EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar);
-
-static const struct ipu_rgb def_rgb_32 = {
- .red = { .offset = 16, .length = 8, },
- .green = { .offset = 8, .length = 8, },
- .blue = { .offset = 0, .length = 8, },
- .transp = { .offset = 24, .length = 8, },
- .bits_per_pixel = 32,
-};
-
-static const struct ipu_rgb def_bgr_32 = {
- .red = { .offset = 0, .length = 8, },
- .green = { .offset = 8, .length = 8, },
- .blue = { .offset = 16, .length = 8, },
- .transp = { .offset = 24, .length = 8, },
- .bits_per_pixel = 32,
-};
-
-static const struct ipu_rgb def_rgb_24 = {
- .red = { .offset = 16, .length = 8, },
- .green = { .offset = 8, .length = 8, },
- .blue = { .offset = 0, .length = 8, },
- .transp = { .offset = 0, .length = 0, },
- .bits_per_pixel = 24,
-};
-
-static const struct ipu_rgb def_bgr_24 = {
- .red = { .offset = 0, .length = 8, },
- .green = { .offset = 8, .length = 8, },
- .blue = { .offset = 16, .length = 8, },
- .transp = { .offset = 0, .length = 0, },
- .bits_per_pixel = 24,
-};
-
-static const struct ipu_rgb def_rgb_16 = {
- .red = { .offset = 11, .length = 5, },
- .green = { .offset = 5, .length = 6, },
- .blue = { .offset = 0, .length = 5, },
- .transp = { .offset = 0, .length = 0, },
- .bits_per_pixel = 16,
-};
-
-static const struct ipu_rgb def_bgr_16 = {
- .red = { .offset = 0, .length = 5, },
- .green = { .offset = 5, .length = 6, },
- .blue = { .offset = 11, .length = 5, },
- .transp = { .offset = 0, .length = 0, },
- .bits_per_pixel = 16,
-};
-
-#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y))
-#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \
- (pix->width * (y) / 4) + (x) / 2)
-#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \
- (pix->width * pix->height / 4) + \
- (pix->width * (y) / 4) + (x) / 2)
+EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar);
-int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 drm_fourcc)
+enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code)
{
- switch (drm_fourcc) {
- case DRM_FORMAT_YUV420:
- case DRM_FORMAT_YVU420:
- /* pix format */
- ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 2);
- /* burst size */
- ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 63);
- break;
- case DRM_FORMAT_UYVY:
- /* bits/pixel */
- ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3);
- /* pix format */
- ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0xA);
- /* burst size */
- ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31);
- break;
- case DRM_FORMAT_YUYV:
- /* bits/pixel */
- ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3);
- /* pix format */
- ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0x8);
- /* burst size */
- ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31);
- break;
- case DRM_FORMAT_ABGR8888:
- case DRM_FORMAT_XBGR8888:
- ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32);
- break;
- case DRM_FORMAT_ARGB8888:
- case DRM_FORMAT_XRGB8888:
- ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32);
- break;
- case DRM_FORMAT_BGR888:
- ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24);
- break;
- case DRM_FORMAT_RGB888:
- ipu_cpmem_set_format_rgb(cpmem, &def_rgb_24);
- break;
- case DRM_FORMAT_RGB565:
- ipu_cpmem_set_format_rgb(cpmem, &def_rgb_16);
- break;
- case DRM_FORMAT_BGR565:
- ipu_cpmem_set_format_rgb(cpmem, &def_bgr_16);
- break;
+ switch (mbus_code & 0xf000) {
+ case 0x1000:
+ return IPUV3_COLORSPACE_RGB;
+ case 0x2000:
+ return IPUV3_COLORSPACE_YUV;
default:
- return -EINVAL;
+ return IPUV3_COLORSPACE_UNKNOWN;
}
-
- return 0;
}
-EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
+EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace);
-/*
- * The V4L2 spec defines packed RGB formats in memory byte order, which from
- * point of view of the IPU corresponds to little-endian words with the first
- * component in the least significant bits.
- * The DRM pixel formats and IPU internal representation are ordered the other
- * way around, with the first named component ordered at the most significant
- * bits. Further, V4L2 formats are not well defined:
- * http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html
- * We choose the interpretation which matches GStreamer behavior.
- */
-static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat)
+int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat)
{
switch (pixelformat) {
- case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
/*
- * Here we choose the 'corrected' interpretation of RGBP, a
- * little-endian 16-bit word with the red component at the most
- * significant bits:
- * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B
+ * for the planar YUV formats, the stride passed to
+ * cpmem must be the stride in bytes of the Y plane.
+ * And all the planar YUV formats have an 8-bit
+ * Y component.
*/
- return DRM_FORMAT_RGB565;
+ return (8 * pixel_stride) >> 3;
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ return (16 * pixel_stride) >> 3;
case V4L2_PIX_FMT_BGR24:
- /* B G R <=> [24:0] R:G:B */
- return DRM_FORMAT_RGB888;
case V4L2_PIX_FMT_RGB24:
- /* R G B <=> [24:0] B:G:R */
- return DRM_FORMAT_BGR888;
+ return (24 * pixel_stride) >> 3;
case V4L2_PIX_FMT_BGR32:
- /* B G R A <=> [32:0] A:B:G:R */
- return DRM_FORMAT_XRGB8888;
case V4L2_PIX_FMT_RGB32:
- /* R G B A <=> [32:0] A:B:G:R */
- return DRM_FORMAT_XBGR8888;
- case V4L2_PIX_FMT_UYVY:
- return DRM_FORMAT_UYVY;
- case V4L2_PIX_FMT_YUYV:
- return DRM_FORMAT_YUYV;
- case V4L2_PIX_FMT_YUV420:
- return DRM_FORMAT_YUV420;
- case V4L2_PIX_FMT_YVU420:
- return DRM_FORMAT_YVU420;
+ return (32 * pixel_stride) >> 3;
+ default:
+ break;
}
return -EINVAL;
}
+EXPORT_SYMBOL_GPL(ipu_stride_to_bytes);
-enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
+int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees,
+ bool hflip, bool vflip)
{
- switch (drm_fourcc) {
- case DRM_FORMAT_RGB565:
- case DRM_FORMAT_BGR565:
- case DRM_FORMAT_RGB888:
- case DRM_FORMAT_BGR888:
- case DRM_FORMAT_XRGB8888:
- case DRM_FORMAT_XBGR8888:
- case DRM_FORMAT_RGBX8888:
- case DRM_FORMAT_BGRX8888:
- case DRM_FORMAT_ARGB8888:
- case DRM_FORMAT_ABGR8888:
- case DRM_FORMAT_RGBA8888:
- case DRM_FORMAT_BGRA8888:
- return IPUV3_COLORSPACE_RGB;
- case DRM_FORMAT_YUYV:
- case DRM_FORMAT_UYVY:
- case DRM_FORMAT_YUV420:
- case DRM_FORMAT_YVU420:
- return IPUV3_COLORSPACE_YUV;
+ u32 r90, vf, hf;
+
+ switch (degrees) {
+ case 0:
+ vf = hf = r90 = 0;
+ break;
+ case 90:
+ vf = hf = 0;
+ r90 = 1;
+ break;
+ case 180:
+ vf = hf = 1;
+ r90 = 0;
+ break;
+ case 270:
+ vf = hf = r90 = 1;
+ break;
default:
- return IPUV3_COLORSPACE_UNKNOWN;
+ return -EINVAL;
}
-}
-EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace);
-int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem,
- struct ipu_image *image)
-{
- struct v4l2_pix_format *pix = &image->pix;
- int y_offset, u_offset, v_offset;
+ hf ^= (u32)hflip;
+ vf ^= (u32)vflip;
- pr_debug("%s: resolution: %dx%d stride: %d\n",
- __func__, pix->width, pix->height,
- pix->bytesperline);
+ *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode);
- ipu_cpmem_set_resolution(cpmem, image->rect.width,
- image->rect.height);
- ipu_cpmem_set_stride(cpmem, pix->bytesperline);
+int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode,
+ bool hflip, bool vflip)
+{
+ u32 r90, vf, hf;
- ipu_cpmem_set_fmt(cpmem, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat));
+ r90 = ((u32)mode >> 2) & 0x1;
+ hf = ((u32)mode >> 1) & 0x1;
+ vf = ((u32)mode >> 0) & 0x1;
+ hf ^= (u32)hflip;
+ vf ^= (u32)vflip;
- switch (pix->pixelformat) {
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- y_offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
- u_offset = U_OFFSET(pix, image->rect.left,
- image->rect.top) - y_offset;
- v_offset = V_OFFSET(pix, image->rect.left,
- image->rect.top) - y_offset;
-
- ipu_cpmem_set_yuv_planar_full(cpmem, pix->pixelformat,
- pix->bytesperline, u_offset, v_offset);
- ipu_cpmem_set_buffer(cpmem, 0, image->phys + y_offset);
+ switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) {
+ case IPU_ROTATE_NONE:
+ *degrees = 0;
break;
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_YUYV:
- ipu_cpmem_set_buffer(cpmem, 0, image->phys +
- image->rect.left * 2 +
- image->rect.top * image->pix.bytesperline);
+ case IPU_ROTATE_90_RIGHT:
+ *degrees = 90;
break;
- case V4L2_PIX_FMT_RGB32:
- case V4L2_PIX_FMT_BGR32:
- ipu_cpmem_set_buffer(cpmem, 0, image->phys +
- image->rect.left * 4 +
- image->rect.top * image->pix.bytesperline);
+ case IPU_ROTATE_180:
+ *degrees = 180;
break;
- case V4L2_PIX_FMT_RGB565:
- ipu_cpmem_set_buffer(cpmem, 0, image->phys +
- image->rect.left * 2 +
- image->rect.top * image->pix.bytesperline);
- break;
- case V4L2_PIX_FMT_RGB24:
- case V4L2_PIX_FMT_BGR24:
- ipu_cpmem_set_buffer(cpmem, 0, image->phys +
- image->rect.left * 3 +
- image->rect.top * image->pix.bytesperline);
+ case IPU_ROTATE_90_LEFT:
+ *degrees = 270;
break;
default:
return -EINVAL;
@@ -523,27 +239,7 @@ int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem,
return 0;
}
-EXPORT_SYMBOL_GPL(ipu_cpmem_set_image);
-
-enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
-{
- switch (pixelformat) {
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_YUYV:
- return IPUV3_COLORSPACE_YUV;
- case V4L2_PIX_FMT_RGB32:
- case V4L2_PIX_FMT_BGR32:
- case V4L2_PIX_FMT_RGB24:
- case V4L2_PIX_FMT_BGR24:
- case V4L2_PIX_FMT_RGB565:
- return IPUV3_COLORSPACE_RGB;
- default:
- return IPUV3_COLORSPACE_UNKNOWN;
- }
-}
-EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
+EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees);
struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
{
@@ -587,7 +283,26 @@ void ipu_idmac_put(struct ipuv3_channel *channel)
}
EXPORT_SYMBOL_GPL(ipu_idmac_put);
-#define idma_mask(ch) (1 << (ch & 0x1f))
+#define idma_mask(ch) (1 << ((ch) & 0x1f))
+
+/*
+ * This is an undocumented feature, a write one to a channel bit in
+ * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's
+ * internal current buffer pointer so that transfers start from buffer
+ * 0 on the next channel enable (that's the theory anyway, the imx6 TRM
+ * only says these are read-only registers). This operation is required
+ * for channel linking to work correctly, for instance video capture
+ * pipelines that carry out image rotations will fail after the first
+ * streaming unless this function is called for each channel before
+ * re-enabling the channels.
+ */
+static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ unsigned int chno = channel->num;
+
+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno));
+}
void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
bool doublebuffer)
@@ -605,10 +320,81 @@ void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
reg &= ~idma_mask(channel->num);
ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
+ __ipu_idmac_reset_current_buffer(channel);
+
spin_unlock_irqrestore(&ipu->lock, flags);
}
EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
+static const struct {
+ int chnum;
+ u32 reg;
+ int shift;
+} idmac_lock_en_info[] = {
+ { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, },
+ { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, },
+ { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, },
+ { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, },
+ { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, },
+ { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, },
+ { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, },
+ { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, },
+ { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, },
+ { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, },
+ { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, },
+ { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, },
+ { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, },
+ { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, },
+ { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, },
+ { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, },
+ { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, },
+};
+
+int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ unsigned long flags;
+ u32 bursts, regval;
+ int i;
+
+ switch (num_bursts) {
+ case 0:
+ case 1:
+ bursts = 0x00; /* locking disabled */
+ break;
+ case 2:
+ bursts = 0x01;
+ break;
+ case 4:
+ bursts = 0x02;
+ break;
+ case 8:
+ bursts = 0x03;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) {
+ if (channel->num == idmac_lock_en_info[i].chnum)
+ break;
+ }
+ if (i >= ARRAY_SIZE(idmac_lock_en_info))
+ return -EINVAL;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg);
+ regval &= ~(0x03 << idmac_lock_en_info[i].shift);
+ regval |= (bursts << idmac_lock_en_info[i].shift);
+ ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg);
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable);
+
int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
{
unsigned long lock_flags;
@@ -661,30 +447,6 @@ int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
}
EXPORT_SYMBOL_GPL(ipu_module_disable);
-int ipu_csi_enable(struct ipu_soc *ipu, int csi)
-{
- return ipu_module_enable(ipu, csi ? IPU_CONF_CSI1_EN : IPU_CONF_CSI0_EN);
-}
-EXPORT_SYMBOL_GPL(ipu_csi_enable);
-
-int ipu_csi_disable(struct ipu_soc *ipu, int csi)
-{
- return ipu_module_disable(ipu, csi ? IPU_CONF_CSI1_EN : IPU_CONF_CSI0_EN);
-}
-EXPORT_SYMBOL_GPL(ipu_csi_disable);
-
-int ipu_smfc_enable(struct ipu_soc *ipu)
-{
- return ipu_module_enable(ipu, IPU_CONF_SMFC_EN);
-}
-EXPORT_SYMBOL_GPL(ipu_smfc_enable);
-
-int ipu_smfc_disable(struct ipu_soc *ipu)
-{
- return ipu_module_disable(ipu, IPU_CONF_SMFC_EN);
-}
-EXPORT_SYMBOL_GPL(ipu_smfc_disable);
-
int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
{
struct ipu_soc *ipu = channel->ipu;
@@ -694,6 +456,30 @@ int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel)
}
EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
+bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ unsigned long flags;
+ u32 reg = 0;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+ switch (buf_num) {
+ case 0:
+ reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num));
+ break;
+ case 1:
+ reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num));
+ break;
+ case 2:
+ reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num));
+ break;
+ }
+ spin_unlock_irqrestore(&ipu->lock, flags);
+
+ return ((reg & idma_mask(channel->num)) != 0);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready);
+
void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
{
struct ipu_soc *ipu = channel->ipu;
@@ -712,6 +498,34 @@ void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
}
EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
+void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ unsigned int chno = channel->num;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */
+ switch (buf_num) {
+ case 0:
+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
+ break;
+ case 1:
+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
+ break;
+ case 2:
+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno));
+ break;
+ default:
+ break;
+ }
+ ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer);
+
int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
{
struct ipu_soc *ipu = channel->ipu;
@@ -782,6 +596,8 @@ int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
val &= ~idma_mask(channel->num);
ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
+ __ipu_idmac_reset_current_buffer(channel);
+
/* Set channel buffers NOT to be ready */
ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
@@ -810,6 +626,31 @@ int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
}
EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
+/*
+ * The imx6 rev. D TRM says that enabling the WM feature will increase
+ * a channel's priority. Refer to Table 36-8 Calculated priority value.
+ * The sub-module that is the sink or source for the channel must enable
+ * watermark signal for this to take effect (SMFC_WM for instance).
+ */
+void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num));
+ if (enable)
+ val |= 1 << (channel->num % 32);
+ else
+ val &= ~(1 << (channel->num % 32));
+ ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark);
+
static int ipu_memory_reset(struct ipu_soc *ipu)
{
unsigned long timeout;
@@ -826,12 +667,66 @@ static int ipu_memory_reset(struct ipu_soc *ipu)
return 0;
}
+/*
+ * Set the source mux for the given CSI. Selects either parallel or
+ * MIPI CSI2 sources.
+ */
+void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2)
+{
+ unsigned long flags;
+ u32 val, mask;
+
+ mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE :
+ IPU_CONF_CSI0_DATA_SOURCE;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ val = ipu_cm_read(ipu, IPU_CONF);
+ if (mipi_csi2)
+ val |= mask;
+ else
+ val &= ~mask;
+ ipu_cm_write(ipu, val, IPU_CONF);
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux);
+
+/*
+ * Set the source mux for the IC. Selects either CSI[01] or the VDI.
+ */
+void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ val = ipu_cm_read(ipu, IPU_CONF);
+ if (vdi) {
+ val |= IPU_CONF_IC_INPUT;
+ } else {
+ val &= ~IPU_CONF_IC_INPUT;
+ if (csi_id == 1)
+ val |= IPU_CONF_CSI_SEL;
+ else
+ val &= ~IPU_CONF_CSI_SEL;
+ }
+ ipu_cm_write(ipu, val, IPU_CONF);
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
+
struct ipu_devtype {
const char *name;
unsigned long cm_ofs;
unsigned long cpmem_ofs;
unsigned long srm_ofs;
unsigned long tpm_ofs;
+ unsigned long csi0_ofs;
+ unsigned long csi1_ofs;
+ unsigned long ic_ofs;
unsigned long disp0_ofs;
unsigned long disp1_ofs;
unsigned long dc_tmpl_ofs;
@@ -845,6 +740,9 @@ static struct ipu_devtype ipu_type_imx51 = {
.cpmem_ofs = 0x1f000000,
.srm_ofs = 0x1f040000,
.tpm_ofs = 0x1f060000,
+ .csi0_ofs = 0x1f030000,
+ .csi1_ofs = 0x1f038000,
+ .ic_ofs = 0x1f020000,
.disp0_ofs = 0x1e040000,
.disp1_ofs = 0x1e048000,
.dc_tmpl_ofs = 0x1f080000,
@@ -858,6 +756,9 @@ static struct ipu_devtype ipu_type_imx53 = {
.cpmem_ofs = 0x07000000,
.srm_ofs = 0x07040000,
.tpm_ofs = 0x07060000,
+ .csi0_ofs = 0x07030000,
+ .csi1_ofs = 0x07038000,
+ .ic_ofs = 0x07020000,
.disp0_ofs = 0x06040000,
.disp1_ofs = 0x06048000,
.dc_tmpl_ofs = 0x07080000,
@@ -871,6 +772,9 @@ static struct ipu_devtype ipu_type_imx6q = {
.cpmem_ofs = 0x00300000,
.srm_ofs = 0x00340000,
.tpm_ofs = 0x00360000,
+ .csi0_ofs = 0x00230000,
+ .csi1_ofs = 0x00238000,
+ .ic_ofs = 0x00220000,
.disp0_ofs = 0x00240000,
.disp1_ofs = 0x00248000,
.dc_tmpl_ofs = 0x00380000,
@@ -895,8 +799,36 @@ static int ipu_submodules_init(struct ipu_soc *ipu,
struct device *dev = &pdev->dev;
const struct ipu_devtype *devtype = ipu->devtype;
+ ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs);
+ if (ret) {
+ unit = "cpmem";
+ goto err_cpmem;
+ }
+
+ ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs,
+ IPU_CONF_CSI0_EN, ipu_clk);
+ if (ret) {
+ unit = "csi0";
+ goto err_csi_0;
+ }
+
+ ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs,
+ IPU_CONF_CSI1_EN, ipu_clk);
+ if (ret) {
+ unit = "csi1";
+ goto err_csi_1;
+ }
+
+ ret = ipu_ic_init(ipu, dev,
+ ipu_base + devtype->ic_ofs,
+ ipu_base + devtype->tpm_ofs);
+ if (ret) {
+ unit = "ic";
+ goto err_ic;
+ }
+
ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
- IPU_CONF_DI0_EN, ipu_clk);
+ IPU_CONF_DI0_EN, ipu_clk);
if (ret) {
unit = "di0";
goto err_di_0;
@@ -949,6 +881,14 @@ err_dc:
err_di_1:
ipu_di_exit(ipu, 0);
err_di_0:
+ ipu_ic_exit(ipu);
+err_ic:
+ ipu_csi_exit(ipu, 1);
+err_csi_1:
+ ipu_csi_exit(ipu, 0);
+err_csi_0:
+ ipu_cpmem_exit(ipu);
+err_cpmem:
dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
return ret;
}
@@ -1025,6 +965,10 @@ static void ipu_submodules_exit(struct ipu_soc *ipu)
ipu_dc_exit(ipu);
ipu_di_exit(ipu, 1);
ipu_di_exit(ipu, 0);
+ ipu_ic_exit(ipu);
+ ipu_csi_exit(ipu, 1);
+ ipu_csi_exit(ipu, 0);
+ ipu_cpmem_exit(ipu);
}
static int platform_remove_devices_fn(struct device *dev, void *unused)
@@ -1116,8 +1060,10 @@ static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
id++, &reg->pdata, sizeof(reg->pdata));
}
- if (IS_ERR(pdev))
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
goto err_register;
+ }
}
return 0;
@@ -1201,6 +1147,44 @@ static void ipu_irq_exit(struct ipu_soc *ipu)
irq_domain_remove(ipu->domain);
}
+void ipu_dump(struct ipu_soc *ipu)
+{
+ int i;
+
+ dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n",
+ ipu_cm_read(ipu, IPU_CONF));
+ dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n",
+ ipu_idmac_read(ipu, IDMAC_CONF));
+ dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n",
+ ipu_idmac_read(ipu, IDMAC_CHA_EN(0)));
+ dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n",
+ ipu_idmac_read(ipu, IDMAC_CHA_EN(32)));
+ dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n",
+ ipu_idmac_read(ipu, IDMAC_CHA_PRI(0)));
+ dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n",
+ ipu_idmac_read(ipu, IDMAC_CHA_PRI(32)));
+ dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n",
+ ipu_idmac_read(ipu, IDMAC_BAND_EN(0)));
+ dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n",
+ ipu_idmac_read(ipu, IDMAC_BAND_EN(32)));
+ dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
+ ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)));
+ dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
+ ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)));
+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW1));
+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW2));
+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW3));
+ dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
+ ipu_cm_read(ipu, IPU_FS_DISP_FLOW1));
+ for (i = 0; i < 15; i++)
+ dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i,
+ ipu_cm_read(ipu, IPU_INT_CTRL(i)));
+}
+EXPORT_SYMBOL_GPL(ipu_dump);
+
static int ipu_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
@@ -1243,6 +1227,12 @@ static int ipu_probe(struct platform_device *pdev)
ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n",
ipu_base + devtype->cpmem_ofs);
+ dev_dbg(&pdev->dev, "csi0: 0x%08lx\n",
+ ipu_base + devtype->csi0_ofs);
+ dev_dbg(&pdev->dev, "csi1: 0x%08lx\n",
+ ipu_base + devtype->csi1_ofs);
+ dev_dbg(&pdev->dev, "ic: 0x%08lx\n",
+ ipu_base + devtype->ic_ofs);
dev_dbg(&pdev->dev, "disp0: 0x%08lx\n",
ipu_base + devtype->disp0_ofs);
dev_dbg(&pdev->dev, "disp1: 0x%08lx\n",
@@ -1265,10 +1255,8 @@ static int ipu_probe(struct platform_device *pdev)
ipu->idmac_reg = devm_ioremap(&pdev->dev,
ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
PAGE_SIZE);
- ipu->cpmem_base = devm_ioremap(&pdev->dev,
- ipu_base + devtype->cpmem_ofs, PAGE_SIZE);
- if (!ipu->cm_reg || !ipu->idmac_reg || !ipu->cpmem_base)
+ if (!ipu->cm_reg || !ipu->idmac_reg)
return -ENOMEM;
ipu->clk = devm_clk_get(&pdev->dev, "bus");
diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
new file mode 100644
index 000000000000..3bf05bc4ab67
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -0,0 +1,764 @@
+/*
+ * Copyright (C) 2012 Mentor Graphics Inc.
+ * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/types.h>
+#include <linux/bitrev.h>
+#include <linux/io.h>
+#include <drm/drm_fourcc.h>
+#include "ipu-prv.h"
+
+struct ipu_cpmem_word {
+ u32 data[5];
+ u32 res[3];
+};
+
+struct ipu_ch_param {
+ struct ipu_cpmem_word word[2];
+};
+
+struct ipu_cpmem {
+ struct ipu_ch_param __iomem *base;
+ u32 module;
+ spinlock_t lock;
+ int use_count;
+ struct ipu_soc *ipu;
+};
+
+#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size))
+
+#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22)
+#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22)
+#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4)
+#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1)
+#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1)
+#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14)
+#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14)
+
+#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10)
+#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9)
+#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13)
+#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12)
+#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1)
+#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1)
+#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12)
+#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11)
+#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10)
+#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7)
+#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10)
+#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1)
+#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1)
+#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7)
+#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1)
+#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1)
+#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3)
+#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2)
+#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1)
+#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3)
+#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2)
+#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1)
+#define IPU_FIELD_ROT_HF_VF IPU_CPMEM_WORD(0, 119, 3)
+#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1)
+#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1)
+#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1)
+#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1)
+#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1)
+#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13)
+#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12)
+#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29)
+#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29)
+#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20)
+#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7)
+#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4)
+#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1)
+#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3)
+#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2)
+#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7)
+#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14)
+#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3)
+#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3)
+#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3)
+#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3)
+#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5)
+#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5)
+#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5)
+#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5)
+#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1)
+#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1)
+#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1)
+
+static inline struct ipu_ch_param __iomem *
+ipu_get_cpmem(struct ipuv3_channel *ch)
+{
+ struct ipu_cpmem *cpmem = ch->ipu->cpmem_priv;
+
+ return cpmem->base + ch->num;
+}
+
+static void ipu_ch_param_write_field(struct ipuv3_channel *ch, u32 wbs, u32 v)
+{
+ struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch);
+ u32 bit = (wbs >> 8) % 160;
+ u32 size = wbs & 0xff;
+ u32 word = (wbs >> 8) / 160;
+ u32 i = bit / 32;
+ u32 ofs = bit % 32;
+ u32 mask = (1 << size) - 1;
+ u32 val;
+
+ pr_debug("%s %d %d %d\n", __func__, word, bit , size);
+
+ val = readl(&base->word[word].data[i]);
+ val &= ~(mask << ofs);
+ val |= v << ofs;
+ writel(val, &base->word[word].data[i]);
+
+ if ((bit + size - 1) / 32 > i) {
+ val = readl(&base->word[word].data[i + 1]);
+ val &= ~(mask >> (ofs ? (32 - ofs) : 0));
+ val |= v >> (ofs ? (32 - ofs) : 0);
+ writel(val, &base->word[word].data[i + 1]);
+ }
+}
+
+static u32 ipu_ch_param_read_field(struct ipuv3_channel *ch, u32 wbs)
+{
+ struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch);
+ u32 bit = (wbs >> 8) % 160;
+ u32 size = wbs & 0xff;
+ u32 word = (wbs >> 8) / 160;
+ u32 i = bit / 32;
+ u32 ofs = bit % 32;
+ u32 mask = (1 << size) - 1;
+ u32 val = 0;
+
+ pr_debug("%s %d %d %d\n", __func__, word, bit , size);
+
+ val = (readl(&base->word[word].data[i]) >> ofs) & mask;
+
+ if ((bit + size - 1) / 32 > i) {
+ u32 tmp;
+
+ tmp = readl(&base->word[word].data[i + 1]);
+ tmp &= mask >> (ofs ? (32 - ofs) : 0);
+ val |= tmp << (ofs ? (32 - ofs) : 0);
+ }
+
+ return val;
+}
+
+/*
+ * The V4L2 spec defines packed RGB formats in memory byte order, which from
+ * point of view of the IPU corresponds to little-endian words with the first
+ * component in the least significant bits.
+ * The DRM pixel formats and IPU internal representation are ordered the other
+ * way around, with the first named component ordered at the most significant
+ * bits. Further, V4L2 formats are not well defined:
+ * http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html
+ * We choose the interpretation which matches GStreamer behavior.
+ */
+static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat)
+{
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ /*
+ * Here we choose the 'corrected' interpretation of RGBP, a
+ * little-endian 16-bit word with the red component at the most
+ * significant bits:
+ * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B
+ */
+ return DRM_FORMAT_RGB565;
+ case V4L2_PIX_FMT_BGR24:
+ /* B G R <=> [24:0] R:G:B */
+ return DRM_FORMAT_RGB888;
+ case V4L2_PIX_FMT_RGB24:
+ /* R G B <=> [24:0] B:G:R */
+ return DRM_FORMAT_BGR888;
+ case V4L2_PIX_FMT_BGR32:
+ /* B G R A <=> [32:0] A:B:G:R */
+ return DRM_FORMAT_XRGB8888;
+ case V4L2_PIX_FMT_RGB32:
+ /* R G B A <=> [32:0] A:B:G:R */
+ return DRM_FORMAT_XBGR8888;
+ case V4L2_PIX_FMT_UYVY:
+ return DRM_FORMAT_UYVY;
+ case V4L2_PIX_FMT_YUYV:
+ return DRM_FORMAT_YUYV;
+ case V4L2_PIX_FMT_YUV420:
+ return DRM_FORMAT_YUV420;
+ case V4L2_PIX_FMT_YUV422P:
+ return DRM_FORMAT_YUV422;
+ case V4L2_PIX_FMT_YVU420:
+ return DRM_FORMAT_YVU420;
+ case V4L2_PIX_FMT_NV12:
+ return DRM_FORMAT_NV12;
+ case V4L2_PIX_FMT_NV16:
+ return DRM_FORMAT_NV16;
+ }
+
+ return -EINVAL;
+}
+
+void ipu_cpmem_zero(struct ipuv3_channel *ch)
+{
+ struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch);
+ void __iomem *base = p;
+ int i;
+
+ for (i = 0; i < sizeof(*p) / sizeof(u32); i++)
+ writel(0, base + i * sizeof(u32));
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_zero);
+
+void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres)
+{
+ ipu_ch_param_write_field(ch, IPU_FIELD_FW, xres - 1);
+ ipu_ch_param_write_field(ch, IPU_FIELD_FH, yres - 1);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_resolution);
+
+void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride)
+{
+ ipu_ch_param_write_field(ch, IPU_FIELD_SLY, stride - 1);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_stride);
+
+void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch)
+{
+ struct ipu_soc *ipu = ch->ipu;
+ u32 val;
+
+ if (ipu->ipu_type == IPUV3EX)
+ ipu_ch_param_write_field(ch, IPU_FIELD_ID, 1);
+
+ val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(ch->num));
+ val |= 1 << (ch->num % 32);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(ch->num));
+};
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority);
+
+void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
+{
+ if (bufnum)
+ ipu_ch_param_write_field(ch, IPU_FIELD_EBA1, buf >> 3);
+ else
+ ipu_ch_param_write_field(ch, IPU_FIELD_EBA0, buf >> 3);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
+
+void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride)
+{
+ ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1);
+ ipu_ch_param_write_field(ch, IPU_FIELD_ILO, stride / 8);
+ ipu_ch_param_write_field(ch, IPU_FIELD_SLY, (stride * 2) - 1);
+};
+EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan);
+
+void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id)
+{
+ id &= 0x3;
+ ipu_ch_param_write_field(ch, IPU_FIELD_ID, id);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id);
+
+void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize)
+{
+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1);
+};
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_burstsize);
+
+void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch)
+{
+ ipu_ch_param_write_field(ch, IPU_FIELD_BM, 1);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_block_mode);
+
+void ipu_cpmem_set_rotation(struct ipuv3_channel *ch,
+ enum ipu_rotate_mode rot)
+{
+ u32 temp_rot = bitrev8(rot) >> 5;
+
+ ipu_ch_param_write_field(ch, IPU_FIELD_ROT_HF_VF, temp_rot);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_rotation);
+
+int ipu_cpmem_set_format_rgb(struct ipuv3_channel *ch,
+ const struct ipu_rgb *rgb)
+{
+ int bpp = 0, npb = 0, ro, go, bo, to;
+
+ ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset;
+ go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset;
+ bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset;
+ to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset;
+
+ ipu_ch_param_write_field(ch, IPU_FIELD_WID0, rgb->red.length - 1);
+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS0, ro);
+ ipu_ch_param_write_field(ch, IPU_FIELD_WID1, rgb->green.length - 1);
+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS1, go);
+ ipu_ch_param_write_field(ch, IPU_FIELD_WID2, rgb->blue.length - 1);
+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS2, bo);
+
+ if (rgb->transp.length) {
+ ipu_ch_param_write_field(ch, IPU_FIELD_WID3,
+ rgb->transp.length - 1);
+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, to);
+ } else {
+ ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7);
+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS3,
+ rgb->bits_per_pixel);
+ }
+
+ switch (rgb->bits_per_pixel) {
+ case 32:
+ bpp = 0;
+ npb = 15;
+ break;
+ case 24:
+ bpp = 1;
+ npb = 19;
+ break;
+ case 16:
+ bpp = 3;
+ npb = 31;
+ break;
+ case 8:
+ bpp = 5;
+ npb = 63;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp);
+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb);
+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 7); /* rgb mode */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb);
+
+int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, int width)
+{
+ int bpp = 0, npb = 0;
+
+ switch (width) {
+ case 32:
+ bpp = 0;
+ npb = 15;
+ break;
+ case 24:
+ bpp = 1;
+ npb = 19;
+ break;
+ case 16:
+ bpp = 3;
+ npb = 31;
+ break;
+ case 8:
+ bpp = 5;
+ npb = 63;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp);
+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb);
+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 6); /* raw mode */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough);
+
+void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format)
+{
+ switch (pixel_format) {
+ case V4L2_PIX_FMT_UYVY:
+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */
+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);/* pix fmt */
+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */
+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);/* pix fmt */
+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved);
+
+void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch,
+ u32 pixel_format, int stride,
+ int u_offset, int v_offset)
+{
+ switch (pixel_format) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YUV422P:
+ ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, (stride / 2) - 1);
+ ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8);
+ ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_offset / 8);
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, (stride / 2) - 1);
+ ipu_ch_param_write_field(ch, IPU_FIELD_UBO, v_offset / 8);
+ ipu_ch_param_write_field(ch, IPU_FIELD_VBO, u_offset / 8);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, stride - 1);
+ ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8);
+ ipu_ch_param_write_field(ch, IPU_FIELD_VBO, u_offset / 8);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full);
+
+void ipu_cpmem_set_yuv_planar(struct ipuv3_channel *ch,
+ u32 pixel_format, int stride, int height)
+{
+ int u_offset, v_offset;
+ int uv_stride = 0;
+
+ switch (pixel_format) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ uv_stride = stride / 2;
+ u_offset = stride * height;
+ v_offset = u_offset + (uv_stride * height / 2);
+ ipu_cpmem_set_yuv_planar_full(ch, pixel_format, stride,
+ u_offset, v_offset);
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ uv_stride = stride / 2;
+ u_offset = stride * height;
+ v_offset = u_offset + (uv_stride * height);
+ ipu_cpmem_set_yuv_planar_full(ch, pixel_format, stride,
+ u_offset, v_offset);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ u_offset = stride * height;
+ ipu_cpmem_set_yuv_planar_full(ch, pixel_format, stride,
+ u_offset, 0);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar);
+
+static const struct ipu_rgb def_rgb_32 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 24, .length = 8, },
+ .bits_per_pixel = 32,
+};
+
+static const struct ipu_rgb def_bgr_32 = {
+ .red = { .offset = 0, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 16, .length = 8, },
+ .transp = { .offset = 24, .length = 8, },
+ .bits_per_pixel = 32,
+};
+
+static const struct ipu_rgb def_rgb_24 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 24,
+};
+
+static const struct ipu_rgb def_bgr_24 = {
+ .red = { .offset = 0, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 16, .length = 8, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 24,
+};
+
+static const struct ipu_rgb def_rgb_16 = {
+ .red = { .offset = 11, .length = 5, },
+ .green = { .offset = 5, .length = 6, },
+ .blue = { .offset = 0, .length = 5, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 16,
+};
+
+static const struct ipu_rgb def_bgr_16 = {
+ .red = { .offset = 0, .length = 5, },
+ .green = { .offset = 5, .length = 6, },
+ .blue = { .offset = 11, .length = 5, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 16,
+};
+
+#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y))
+#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * (y) / 4) + (x) / 2)
+#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * pix->height / 4) + \
+ (pix->width * (y) / 4) + (x) / 2)
+#define U2_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * (y) / 2) + (x) / 2)
+#define V2_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * pix->height / 2) + \
+ (pix->width * (y) / 2) + (x) / 2)
+#define UV_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * (y) / 2) + (x))
+#define UV2_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * y) + (x))
+
+int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc)
+{
+ switch (drm_fourcc) {
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_YVU420:
+ /* pix format */
+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 2);
+ /* burst size */
+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
+ break;
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_YVU422:
+ /* pix format */
+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 1);
+ /* burst size */
+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
+ break;
+ case DRM_FORMAT_NV12:
+ /* pix format */
+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 4);
+ /* burst size */
+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
+ break;
+ case DRM_FORMAT_NV16:
+ /* pix format */
+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 3);
+ /* burst size */
+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
+ break;
+ case DRM_FORMAT_UYVY:
+ /* bits/pixel */
+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3);
+ /* pix format */
+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);
+ /* burst size */
+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
+ break;
+ case DRM_FORMAT_YUYV:
+ /* bits/pixel */
+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3);
+ /* pix format */
+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);
+ /* burst size */
+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);
+ break;
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_XBGR8888:
+ ipu_cpmem_set_format_rgb(ch, &def_bgr_32);
+ break;
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB8888:
+ ipu_cpmem_set_format_rgb(ch, &def_rgb_32);
+ break;
+ case DRM_FORMAT_BGR888:
+ ipu_cpmem_set_format_rgb(ch, &def_bgr_24);
+ break;
+ case DRM_FORMAT_RGB888:
+ ipu_cpmem_set_format_rgb(ch, &def_rgb_24);
+ break;
+ case DRM_FORMAT_RGB565:
+ ipu_cpmem_set_format_rgb(ch, &def_rgb_16);
+ break;
+ case DRM_FORMAT_BGR565:
+ ipu_cpmem_set_format_rgb(ch, &def_bgr_16);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
+
+int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image)
+{
+ struct v4l2_pix_format *pix = &image->pix;
+ int offset, u_offset, v_offset;
+
+ pr_debug("%s: resolution: %dx%d stride: %d\n",
+ __func__, pix->width, pix->height,
+ pix->bytesperline);
+
+ ipu_cpmem_set_resolution(ch, image->rect.width, image->rect.height);
+ ipu_cpmem_set_stride(ch, pix->bytesperline);
+
+ ipu_cpmem_set_fmt(ch, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat));
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
+ u_offset = U_OFFSET(pix, image->rect.left,
+ image->rect.top) - offset;
+ v_offset = V_OFFSET(pix, image->rect.left,
+ image->rect.top) - offset;
+
+ ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
+ pix->bytesperline,
+ u_offset, v_offset);
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
+ u_offset = U2_OFFSET(pix, image->rect.left,
+ image->rect.top) - offset;
+ v_offset = V2_OFFSET(pix, image->rect.left,
+ image->rect.top) - offset;
+
+ ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
+ pix->bytesperline,
+ u_offset, v_offset);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
+ u_offset = UV_OFFSET(pix, image->rect.left,
+ image->rect.top) - offset;
+ v_offset = 0;
+
+ ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
+ pix->bytesperline,
+ u_offset, v_offset);
+ break;
+ case V4L2_PIX_FMT_NV16:
+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
+ u_offset = UV2_OFFSET(pix, image->rect.left,
+ image->rect.top) - offset;
+ v_offset = 0;
+
+ ipu_cpmem_set_yuv_planar_full(ch, pix->pixelformat,
+ pix->bytesperline,
+ u_offset, v_offset);
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_RGB565:
+ offset = image->rect.left * 2 +
+ image->rect.top * pix->bytesperline;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ offset = image->rect.left * 4 +
+ image->rect.top * pix->bytesperline;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ offset = image->rect.left * 3 +
+ image->rect.top * pix->bytesperline;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ipu_cpmem_set_buffer(ch, 0, image->phys0 + offset);
+ ipu_cpmem_set_buffer(ch, 1, image->phys1 + offset);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_image);
+
+void ipu_cpmem_dump(struct ipuv3_channel *ch)
+{
+ struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch);
+ struct ipu_soc *ipu = ch->ipu;
+ int chno = ch->num;
+
+ dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", chno,
+ readl(&p->word[0].data[0]),
+ readl(&p->word[0].data[1]),
+ readl(&p->word[0].data[2]),
+ readl(&p->word[0].data[3]),
+ readl(&p->word[0].data[4]));
+ dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", chno,
+ readl(&p->word[1].data[0]),
+ readl(&p->word[1].data[1]),
+ readl(&p->word[1].data[2]),
+ readl(&p->word[1].data[3]),
+ readl(&p->word[1].data[4]));
+ dev_dbg(ipu->dev, "PFS 0x%x, ",
+ ipu_ch_param_read_field(ch, IPU_FIELD_PFS));
+ dev_dbg(ipu->dev, "BPP 0x%x, ",
+ ipu_ch_param_read_field(ch, IPU_FIELD_BPP));
+ dev_dbg(ipu->dev, "NPB 0x%x\n",
+ ipu_ch_param_read_field(ch, IPU_FIELD_NPB));
+
+ dev_dbg(ipu->dev, "FW %d, ",
+ ipu_ch_param_read_field(ch, IPU_FIELD_FW));
+ dev_dbg(ipu->dev, "FH %d, ",
+ ipu_ch_param_read_field(ch, IPU_FIELD_FH));
+ dev_dbg(ipu->dev, "EBA0 0x%x\n",
+ ipu_ch_param_read_field(ch, IPU_FIELD_EBA0) << 3);
+ dev_dbg(ipu->dev, "EBA1 0x%x\n",
+ ipu_ch_param_read_field(ch, IPU_FIELD_EBA1) << 3);
+ dev_dbg(ipu->dev, "Stride %d\n",
+ ipu_ch_param_read_field(ch, IPU_FIELD_SL));
+ dev_dbg(ipu->dev, "scan_order %d\n",
+ ipu_ch_param_read_field(ch, IPU_FIELD_SO));
+ dev_dbg(ipu->dev, "uv_stride %d\n",
+ ipu_ch_param_read_field(ch, IPU_FIELD_SLUV));
+ dev_dbg(ipu->dev, "u_offset 0x%x\n",
+ ipu_ch_param_read_field(ch, IPU_FIELD_UBO) << 3);
+ dev_dbg(ipu->dev, "v_offset 0x%x\n",
+ ipu_ch_param_read_field(ch, IPU_FIELD_VBO) << 3);
+
+ dev_dbg(ipu->dev, "Width0 %d+1, ",
+ ipu_ch_param_read_field(ch, IPU_FIELD_WID0));
+ dev_dbg(ipu->dev, "Width1 %d+1, ",
+ ipu_ch_param_read_field(ch, IPU_FIELD_WID1));
+ dev_dbg(ipu->dev, "Width2 %d+1, ",
+ ipu_ch_param_read_field(ch, IPU_FIELD_WID2));
+ dev_dbg(ipu->dev, "Width3 %d+1, ",
+ ipu_ch_param_read_field(ch, IPU_FIELD_WID3));
+ dev_dbg(ipu->dev, "Offset0 %d, ",
+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS0));
+ dev_dbg(ipu->dev, "Offset1 %d, ",
+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS1));
+ dev_dbg(ipu->dev, "Offset2 %d, ",
+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS2));
+ dev_dbg(ipu->dev, "Offset3 %d\n",
+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS3));
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_dump);
+
+int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base)
+{
+ struct ipu_cpmem *cpmem;
+
+ cpmem = devm_kzalloc(dev, sizeof(*cpmem), GFP_KERNEL);
+ if (!cpmem)
+ return -ENOMEM;
+
+ ipu->cpmem_priv = cpmem;
+
+ spin_lock_init(&cpmem->lock);
+ cpmem->base = devm_ioremap(dev, base, SZ_128K);
+ if (!cpmem->base)
+ return -ENOMEM;
+
+ dev_dbg(dev, "CPMEM base: 0x%08lx remapped to %p\n",
+ base, cpmem->base);
+ cpmem->ipu = ipu;
+
+ return 0;
+}
+
+void ipu_cpmem_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
new file mode 100644
index 000000000000..d6f56471bd2a
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -0,0 +1,741 @@
+/*
+ * Copyright (C) 2012-2014 Mentor Graphics Inc.
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * 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/export.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <uapi/linux/v4l2-mediabus.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+#include "ipu-prv.h"
+
+struct ipu_csi {
+ void __iomem *base;
+ int id;
+ u32 module;
+ struct clk *clk_ipu; /* IPU bus clock */
+ spinlock_t lock;
+ bool inuse;
+ struct ipu_soc *ipu;
+};
+
+/* CSI Register Offsets */
+#define CSI_SENS_CONF 0x0000
+#define CSI_SENS_FRM_SIZE 0x0004
+#define CSI_ACT_FRM_SIZE 0x0008
+#define CSI_OUT_FRM_CTRL 0x000c
+#define CSI_TST_CTRL 0x0010
+#define CSI_CCIR_CODE_1 0x0014
+#define CSI_CCIR_CODE_2 0x0018
+#define CSI_CCIR_CODE_3 0x001c
+#define CSI_MIPI_DI 0x0020
+#define CSI_SKIP 0x0024
+#define CSI_CPD_CTRL 0x0028
+#define CSI_CPD_RC(n) (0x002c + ((n)*4))
+#define CSI_CPD_RS(n) (0x004c + ((n)*4))
+#define CSI_CPD_GRC(n) (0x005c + ((n)*4))
+#define CSI_CPD_GRS(n) (0x007c + ((n)*4))
+#define CSI_CPD_GBC(n) (0x008c + ((n)*4))
+#define CSI_CPD_GBS(n) (0x00Ac + ((n)*4))
+#define CSI_CPD_BC(n) (0x00Bc + ((n)*4))
+#define CSI_CPD_BS(n) (0x00Dc + ((n)*4))
+#define CSI_CPD_OFFSET1 0x00ec
+#define CSI_CPD_OFFSET2 0x00f0
+
+/* CSI Register Fields */
+#define CSI_SENS_CONF_DATA_FMT_SHIFT 8
+#define CSI_SENS_CONF_DATA_FMT_MASK 0x00000700
+#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 0L
+#define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV 1L
+#define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY 2L
+#define CSI_SENS_CONF_DATA_FMT_BAYER 3L
+#define CSI_SENS_CONF_DATA_FMT_RGB565 4L
+#define CSI_SENS_CONF_DATA_FMT_RGB555 5L
+#define CSI_SENS_CONF_DATA_FMT_RGB444 6L
+#define CSI_SENS_CONF_DATA_FMT_JPEG 7L
+
+#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0
+#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1
+#define CSI_SENS_CONF_DATA_POL_SHIFT 2
+#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3
+#define CSI_SENS_CONF_SENS_PRTCL_MASK 0x00000070
+#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4
+#define CSI_SENS_CONF_PACK_TIGHT_SHIFT 7
+#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 11
+#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15
+#define CSI_SENS_CONF_DIVRATIO_SHIFT 16
+
+#define CSI_SENS_CONF_DIVRATIO_MASK 0x00ff0000
+#define CSI_SENS_CONF_DATA_DEST_SHIFT 24
+#define CSI_SENS_CONF_DATA_DEST_MASK 0x07000000
+#define CSI_SENS_CONF_JPEG8_EN_SHIFT 27
+#define CSI_SENS_CONF_JPEG_EN_SHIFT 28
+#define CSI_SENS_CONF_FORCE_EOF_SHIFT 29
+#define CSI_SENS_CONF_DATA_EN_POL_SHIFT 31
+
+#define CSI_DATA_DEST_IC 2
+#define CSI_DATA_DEST_IDMAC 4
+
+#define CSI_CCIR_ERR_DET_EN 0x01000000
+#define CSI_HORI_DOWNSIZE_EN 0x80000000
+#define CSI_VERT_DOWNSIZE_EN 0x40000000
+#define CSI_TEST_GEN_MODE_EN 0x01000000
+
+#define CSI_HSC_MASK 0x1fff0000
+#define CSI_HSC_SHIFT 16
+#define CSI_VSC_MASK 0x00000fff
+#define CSI_VSC_SHIFT 0
+
+#define CSI_TEST_GEN_R_MASK 0x000000ff
+#define CSI_TEST_GEN_R_SHIFT 0
+#define CSI_TEST_GEN_G_MASK 0x0000ff00
+#define CSI_TEST_GEN_G_SHIFT 8
+#define CSI_TEST_GEN_B_MASK 0x00ff0000
+#define CSI_TEST_GEN_B_SHIFT 16
+
+#define CSI_MAX_RATIO_SKIP_SMFC_MASK 0x00000007
+#define CSI_MAX_RATIO_SKIP_SMFC_SHIFT 0
+#define CSI_SKIP_SMFC_MASK 0x000000f8
+#define CSI_SKIP_SMFC_SHIFT 3
+#define CSI_ID_2_SKIP_MASK 0x00000300
+#define CSI_ID_2_SKIP_SHIFT 8
+
+#define CSI_COLOR_FIRST_ROW_MASK 0x00000002
+#define CSI_COLOR_FIRST_COMP_MASK 0x00000001
+
+/* MIPI CSI-2 data types */
+#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */
+#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */
+#define MIPI_DT_YUV422 0x1e /* UYVY... */
+#define MIPI_DT_RGB444 0x20
+#define MIPI_DT_RGB555 0x21
+#define MIPI_DT_RGB565 0x22
+#define MIPI_DT_RGB666 0x23
+#define MIPI_DT_RGB888 0x24
+#define MIPI_DT_RAW6 0x28
+#define MIPI_DT_RAW7 0x29
+#define MIPI_DT_RAW8 0x2a
+#define MIPI_DT_RAW10 0x2b
+#define MIPI_DT_RAW12 0x2c
+#define MIPI_DT_RAW14 0x2d
+
+/*
+ * Bitfield of CSI bus signal polarities and modes.
+ */
+struct ipu_csi_bus_config {
+ unsigned data_width:4;
+ unsigned clk_mode:3;
+ unsigned ext_vsync:1;
+ unsigned vsync_pol:1;
+ unsigned hsync_pol:1;
+ unsigned pixclk_pol:1;
+ unsigned data_pol:1;
+ unsigned sens_clksrc:1;
+ unsigned pack_tight:1;
+ unsigned force_eof:1;
+ unsigned data_en_pol:1;
+
+ unsigned data_fmt;
+ unsigned mipi_dt;
+};
+
+/*
+ * Enumeration of CSI data bus widths.
+ */
+enum ipu_csi_data_width {
+ IPU_CSI_DATA_WIDTH_4 = 0,
+ IPU_CSI_DATA_WIDTH_8 = 1,
+ IPU_CSI_DATA_WIDTH_10 = 3,
+ IPU_CSI_DATA_WIDTH_12 = 5,
+ IPU_CSI_DATA_WIDTH_16 = 9,
+};
+
+/*
+ * Enumeration of CSI clock modes.
+ */
+enum ipu_csi_clk_mode {
+ IPU_CSI_CLK_MODE_GATED_CLK,
+ IPU_CSI_CLK_MODE_NONGATED_CLK,
+ IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE,
+ IPU_CSI_CLK_MODE_CCIR656_INTERLACED,
+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR,
+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR,
+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR,
+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR,
+};
+
+static inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset)
+{
+ return readl(csi->base + offset);
+}
+
+static inline void ipu_csi_write(struct ipu_csi *csi, u32 value,
+ unsigned offset)
+{
+ writel(value, csi->base + offset);
+}
+
+/*
+ * Set mclk division ratio for generating test mode mclk. Only used
+ * for test generator.
+ */
+static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk,
+ u32 ipu_clk)
+{
+ u32 temp;
+ u32 div_ratio;
+
+ div_ratio = (ipu_clk / pixel_clk) - 1;
+
+ if (div_ratio > 0xFF || div_ratio < 0) {
+ dev_err(csi->ipu->dev,
+ "value of pixel_clk extends normal range\n");
+ return -EINVAL;
+ }
+
+ temp = ipu_csi_read(csi, CSI_SENS_CONF);
+ temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
+ ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
+ CSI_SENS_CONF);
+
+ return 0;
+}
+
+/*
+ * Find the CSI data format and data width for the given V4L2 media
+ * bus pixel format code.
+ */
+static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code)
+{
+ switch (mbus_code) {
+ case V4L2_MBUS_FMT_BGR565_2X8_BE:
+ case V4L2_MBUS_FMT_BGR565_2X8_LE:
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
+ cfg->mipi_dt = MIPI_DT_RGB565;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_8;
+ break;
+ case V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE:
+ case V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE:
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444;
+ cfg->mipi_dt = MIPI_DT_RGB444;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_8;
+ break;
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
+ cfg->mipi_dt = MIPI_DT_RGB555;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_8;
+ break;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
+ cfg->mipi_dt = MIPI_DT_YUV422;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_8;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
+ cfg->mipi_dt = MIPI_DT_YUV422;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_8;
+ break;
+ case V4L2_MBUS_FMT_UYVY8_1X16:
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
+ cfg->mipi_dt = MIPI_DT_YUV422;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_16;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
+ cfg->mipi_dt = MIPI_DT_YUV422;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_16;
+ break;
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ case V4L2_MBUS_FMT_SGBRG8_1X8:
+ case V4L2_MBUS_FMT_SGRBG8_1X8:
+ case V4L2_MBUS_FMT_SRGGB8_1X8:
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
+ cfg->mipi_dt = MIPI_DT_RAW8;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_8;
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE:
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE:
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE:
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
+ cfg->mipi_dt = MIPI_DT_RAW10;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_8;
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ case V4L2_MBUS_FMT_SGBRG10_1X10:
+ case V4L2_MBUS_FMT_SGRBG10_1X10:
+ case V4L2_MBUS_FMT_SRGGB10_1X10:
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
+ cfg->mipi_dt = MIPI_DT_RAW10;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_10;
+ break;
+ case V4L2_MBUS_FMT_SBGGR12_1X12:
+ case V4L2_MBUS_FMT_SGBRG12_1X12:
+ case V4L2_MBUS_FMT_SGRBG12_1X12:
+ case V4L2_MBUS_FMT_SRGGB12_1X12:
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
+ cfg->mipi_dt = MIPI_DT_RAW12;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_12;
+ break;
+ case V4L2_MBUS_FMT_JPEG_1X8:
+ /* TODO */
+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG;
+ cfg->mipi_dt = MIPI_DT_RAW8;
+ cfg->data_width = IPU_CSI_DATA_WIDTH_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Fill a CSI bus config struct from mbus_config and mbus_framefmt.
+ */
+static void fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg,
+ struct v4l2_mbus_config *mbus_cfg,
+ struct v4l2_mbus_framefmt *mbus_fmt)
+{
+ memset(csicfg, 0, sizeof(*csicfg));
+
+ mbus_code_to_bus_cfg(csicfg, mbus_fmt->code);
+
+ switch (mbus_cfg->type) {
+ case V4L2_MBUS_PARALLEL:
+ csicfg->ext_vsync = 1;
+ csicfg->vsync_pol = (mbus_cfg->flags &
+ V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0;
+ csicfg->hsync_pol = (mbus_cfg->flags &
+ V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0;
+ csicfg->pixclk_pol = (mbus_cfg->flags &
+ V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0;
+ csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK;
+ break;
+ case V4L2_MBUS_BT656:
+ csicfg->ext_vsync = 0;
+ if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field))
+ csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED;
+ else
+ csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE;
+ break;
+ case V4L2_MBUS_CSI2:
+ /*
+ * MIPI CSI-2 requires non gated clock mode, all other
+ * parameters are not applicable for MIPI CSI-2 bus.
+ */
+ csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK;
+ break;
+ default:
+ /* will never get here, keep compiler quiet */
+ break;
+ }
+}
+
+int ipu_csi_init_interface(struct ipu_csi *csi,
+ struct v4l2_mbus_config *mbus_cfg,
+ struct v4l2_mbus_framefmt *mbus_fmt)
+{
+ struct ipu_csi_bus_config cfg;
+ unsigned long flags;
+ u32 data = 0;
+
+ fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt);
+
+ /* Set the CSI_SENS_CONF register remaining fields */
+ data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
+ cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
+ cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
+ cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
+ cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
+ cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
+ cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
+ cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
+ cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
+ cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
+ cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;
+
+ spin_lock_irqsave(&csi->lock, flags);
+
+ ipu_csi_write(csi, data, CSI_SENS_CONF);
+
+ /* Setup sensor frame size */
+ ipu_csi_write(csi,
+ (mbus_fmt->width - 1) | ((mbus_fmt->height - 1) << 16),
+ CSI_SENS_FRM_SIZE);
+
+ /* Set CCIR registers */
+
+ switch (cfg.clk_mode) {
+ case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
+ ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1);
+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
+ break;
+ case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
+ if (mbus_fmt->width == 720 && mbus_fmt->height == 576) {
+ /*
+ * PAL case
+ *
+ * Field0BlankEnd = 0x6, Field0BlankStart = 0x2,
+ * Field0ActiveEnd = 0x4, Field0ActiveStart = 0
+ * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
+ * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
+ */
+ ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
+ CSI_CCIR_CODE_1);
+ ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
+
+ } else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) {
+ /*
+ * NTSC case
+ *
+ * Field0BlankEnd = 0x7, Field0BlankStart = 0x3,
+ * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1
+ * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
+ * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
+ */
+ ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
+ CSI_CCIR_CODE_1);
+ ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
+ } else {
+ dev_err(csi->ipu->dev,
+ "Unsupported CCIR656 interlaced video mode\n");
+ spin_unlock_irqrestore(&csi->lock, flags);
+ return -EINVAL;
+ }
+ break;
+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
+ ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN,
+ CSI_CCIR_CODE_1);
+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
+ break;
+ case IPU_CSI_CLK_MODE_GATED_CLK:
+ case IPU_CSI_CLK_MODE_NONGATED_CLK:
+ ipu_csi_write(csi, 0, CSI_CCIR_CODE_1);
+ break;
+ }
+
+ dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
+ ipu_csi_read(csi, CSI_SENS_CONF));
+ dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
+ ipu_csi_read(csi, CSI_ACT_FRM_SIZE));
+
+ spin_unlock_irqrestore(&csi->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_csi_init_interface);
+
+bool ipu_csi_is_interlaced(struct ipu_csi *csi)
+{
+ unsigned long flags;
+ u32 sensor_protocol;
+
+ spin_lock_irqsave(&csi->lock, flags);
+ sensor_protocol =
+ (ipu_csi_read(csi, CSI_SENS_CONF) &
+ CSI_SENS_CONF_SENS_PRTCL_MASK) >>
+ CSI_SENS_CONF_SENS_PRTCL_SHIFT;
+ spin_unlock_irqrestore(&csi->lock, flags);
+
+ switch (sensor_protocol) {
+ case IPU_CSI_CLK_MODE_GATED_CLK:
+ case IPU_CSI_CLK_MODE_NONGATED_CLK:
+ case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
+ return false;
+ case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
+ return true;
+ default:
+ dev_err(csi->ipu->dev,
+ "CSI %d sensor protocol unsupported\n", csi->id);
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced);
+
+void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w)
+{
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&csi->lock, flags);
+
+ reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE);
+ w->width = (reg & 0xFFFF) + 1;
+ w->height = (reg >> 16 & 0xFFFF) + 1;
+
+ reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL);
+ w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT;
+ w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT;
+
+ spin_unlock_irqrestore(&csi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_csi_get_window);
+
+void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w)
+{
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&csi->lock, flags);
+
+ ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16),
+ CSI_ACT_FRM_SIZE);
+
+ reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL);
+ reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK);
+ reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT));
+ ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL);
+
+ spin_unlock_irqrestore(&csi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_csi_set_window);
+
+void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active,
+ u32 r_value, u32 g_value, u32 b_value,
+ u32 pix_clk)
+{
+ unsigned long flags;
+ u32 ipu_clk = clk_get_rate(csi->clk_ipu);
+ u32 temp;
+
+ spin_lock_irqsave(&csi->lock, flags);
+
+ temp = ipu_csi_read(csi, CSI_TST_CTRL);
+
+ if (active == false) {
+ temp &= ~CSI_TEST_GEN_MODE_EN;
+ ipu_csi_write(csi, temp, CSI_TST_CTRL);
+ } else {
+ /* Set sensb_mclk div_ratio */
+ ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk);
+
+ temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
+ CSI_TEST_GEN_B_MASK);
+ temp |= CSI_TEST_GEN_MODE_EN;
+ temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
+ (g_value << CSI_TEST_GEN_G_SHIFT) |
+ (b_value << CSI_TEST_GEN_B_SHIFT);
+ ipu_csi_write(csi, temp, CSI_TST_CTRL);
+ }
+
+ spin_unlock_irqrestore(&csi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator);
+
+int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc,
+ struct v4l2_mbus_framefmt *mbus_fmt)
+{
+ struct ipu_csi_bus_config cfg;
+ unsigned long flags;
+ u32 temp;
+
+ if (vc > 3)
+ return -EINVAL;
+
+ mbus_code_to_bus_cfg(&cfg, mbus_fmt->code);
+
+ spin_lock_irqsave(&csi->lock, flags);
+
+ temp = ipu_csi_read(csi, CSI_MIPI_DI);
+ temp &= ~(0xff << (vc * 8));
+ temp |= (cfg.mipi_dt << (vc * 8));
+ ipu_csi_write(csi, temp, CSI_MIPI_DI);
+
+ spin_unlock_irqrestore(&csi->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype);
+
+int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
+ u32 max_ratio, u32 id)
+{
+ unsigned long flags;
+ u32 temp;
+
+ if (max_ratio > 5 || id > 3)
+ return -EINVAL;
+
+ spin_lock_irqsave(&csi->lock, flags);
+
+ temp = ipu_csi_read(csi, CSI_SKIP);
+ temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK |
+ CSI_SKIP_SMFC_MASK);
+ temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) |
+ (id << CSI_ID_2_SKIP_SHIFT) |
+ (skip << CSI_SKIP_SMFC_SHIFT);
+ ipu_csi_write(csi, temp, CSI_SKIP);
+
+ spin_unlock_irqrestore(&csi->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc);
+
+int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest)
+{
+ unsigned long flags;
+ u32 csi_sens_conf, dest;
+
+ if (csi_dest == IPU_CSI_DEST_IDMAC)
+ dest = CSI_DATA_DEST_IDMAC;
+ else
+ dest = CSI_DATA_DEST_IC; /* IC or VDIC */
+
+ spin_lock_irqsave(&csi->lock, flags);
+
+ csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF);
+ csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK;
+ csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT);
+ ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF);
+
+ spin_unlock_irqrestore(&csi->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_csi_set_dest);
+
+int ipu_csi_enable(struct ipu_csi *csi)
+{
+ ipu_module_enable(csi->ipu, csi->module);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_csi_enable);
+
+int ipu_csi_disable(struct ipu_csi *csi)
+{
+ ipu_module_disable(csi->ipu, csi->module);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_csi_disable);
+
+struct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id)
+{
+ unsigned long flags;
+ struct ipu_csi *csi, *ret;
+
+ if (id > 1)
+ return ERR_PTR(-EINVAL);
+
+ csi = ipu->csi_priv[id];
+ ret = csi;
+
+ spin_lock_irqsave(&csi->lock, flags);
+
+ if (csi->inuse) {
+ ret = ERR_PTR(-EBUSY);
+ goto unlock;
+ }
+
+ csi->inuse = true;
+unlock:
+ spin_unlock_irqrestore(&csi->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_csi_get);
+
+void ipu_csi_put(struct ipu_csi *csi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&csi->lock, flags);
+ csi->inuse = false;
+ spin_unlock_irqrestore(&csi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_csi_put);
+
+int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id,
+ unsigned long base, u32 module, struct clk *clk_ipu)
+{
+ struct ipu_csi *csi;
+
+ if (id > 1)
+ return -ENODEV;
+
+ csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL);
+ if (!csi)
+ return -ENOMEM;
+
+ ipu->csi_priv[id] = csi;
+
+ spin_lock_init(&csi->lock);
+ csi->module = module;
+ csi->id = id;
+ csi->clk_ipu = clk_ipu;
+ csi->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!csi->base)
+ return -ENOMEM;
+
+ dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n",
+ id, base, csi->base);
+ csi->ipu = ipu;
+
+ return 0;
+}
+
+void ipu_csi_exit(struct ipu_soc *ipu, int id)
+{
+}
+
+void ipu_csi_dump(struct ipu_csi *csi)
+{
+ dev_dbg(csi->ipu->dev, "CSI_SENS_CONF: %08x\n",
+ ipu_csi_read(csi, CSI_SENS_CONF));
+ dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n",
+ ipu_csi_read(csi, CSI_SENS_FRM_SIZE));
+ dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE: %08x\n",
+ ipu_csi_read(csi, CSI_ACT_FRM_SIZE));
+ dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL: %08x\n",
+ ipu_csi_read(csi, CSI_OUT_FRM_CTRL));
+ dev_dbg(csi->ipu->dev, "CSI_TST_CTRL: %08x\n",
+ ipu_csi_read(csi, CSI_TST_CTRL));
+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1: %08x\n",
+ ipu_csi_read(csi, CSI_CCIR_CODE_1));
+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2: %08x\n",
+ ipu_csi_read(csi, CSI_CCIR_CODE_2));
+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3: %08x\n",
+ ipu_csi_read(csi, CSI_CCIR_CODE_3));
+ dev_dbg(csi->ipu->dev, "CSI_MIPI_DI: %08x\n",
+ ipu_csi_read(csi, CSI_MIPI_DI));
+ dev_dbg(csi->ipu->dev, "CSI_SKIP: %08x\n",
+ ipu_csi_read(csi, CSI_SKIP));
+}
+EXPORT_SYMBOL_GPL(ipu_csi_dump);
diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
new file mode 100644
index 000000000000..ad75588e1629
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -0,0 +1,778 @@
+/*
+ * Copyright (C) 2012-2014 Mentor Graphics Inc.
+ * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/bitrev.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include "ipu-prv.h"
+
+/* IC Register Offsets */
+#define IC_CONF 0x0000
+#define IC_PRP_ENC_RSC 0x0004
+#define IC_PRP_VF_RSC 0x0008
+#define IC_PP_RSC 0x000C
+#define IC_CMBP_1 0x0010
+#define IC_CMBP_2 0x0014
+#define IC_IDMAC_1 0x0018
+#define IC_IDMAC_2 0x001C
+#define IC_IDMAC_3 0x0020
+#define IC_IDMAC_4 0x0024
+
+/* IC Register Fields */
+#define IC_CONF_PRPENC_EN (1 << 0)
+#define IC_CONF_PRPENC_CSC1 (1 << 1)
+#define IC_CONF_PRPENC_ROT_EN (1 << 2)
+#define IC_CONF_PRPVF_EN (1 << 8)
+#define IC_CONF_PRPVF_CSC1 (1 << 9)
+#define IC_CONF_PRPVF_CSC2 (1 << 10)
+#define IC_CONF_PRPVF_CMB (1 << 11)
+#define IC_CONF_PRPVF_ROT_EN (1 << 12)
+#define IC_CONF_PP_EN (1 << 16)
+#define IC_CONF_PP_CSC1 (1 << 17)
+#define IC_CONF_PP_CSC2 (1 << 18)
+#define IC_CONF_PP_CMB (1 << 19)
+#define IC_CONF_PP_ROT_EN (1 << 20)
+#define IC_CONF_IC_GLB_LOC_A (1 << 28)
+#define IC_CONF_KEY_COLOR_EN (1 << 29)
+#define IC_CONF_RWS_EN (1 << 30)
+#define IC_CONF_CSI_MEM_WR_EN (1 << 31)
+
+#define IC_IDMAC_1_CB0_BURST_16 (1 << 0)
+#define IC_IDMAC_1_CB1_BURST_16 (1 << 1)
+#define IC_IDMAC_1_CB2_BURST_16 (1 << 2)
+#define IC_IDMAC_1_CB3_BURST_16 (1 << 3)
+#define IC_IDMAC_1_CB4_BURST_16 (1 << 4)
+#define IC_IDMAC_1_CB5_BURST_16 (1 << 5)
+#define IC_IDMAC_1_CB6_BURST_16 (1 << 6)
+#define IC_IDMAC_1_CB7_BURST_16 (1 << 7)
+#define IC_IDMAC_1_PRPENC_ROT_MASK (0x7 << 11)
+#define IC_IDMAC_1_PRPENC_ROT_OFFSET 11
+#define IC_IDMAC_1_PRPVF_ROT_MASK (0x7 << 14)
+#define IC_IDMAC_1_PRPVF_ROT_OFFSET 14
+#define IC_IDMAC_1_PP_ROT_MASK (0x7 << 17)
+#define IC_IDMAC_1_PP_ROT_OFFSET 17
+#define IC_IDMAC_1_PP_FLIP_RS (1 << 22)
+#define IC_IDMAC_1_PRPVF_FLIP_RS (1 << 21)
+#define IC_IDMAC_1_PRPENC_FLIP_RS (1 << 20)
+
+#define IC_IDMAC_2_PRPENC_HEIGHT_MASK (0x3ff << 0)
+#define IC_IDMAC_2_PRPENC_HEIGHT_OFFSET 0
+#define IC_IDMAC_2_PRPVF_HEIGHT_MASK (0x3ff << 10)
+#define IC_IDMAC_2_PRPVF_HEIGHT_OFFSET 10
+#define IC_IDMAC_2_PP_HEIGHT_MASK (0x3ff << 20)
+#define IC_IDMAC_2_PP_HEIGHT_OFFSET 20
+
+#define IC_IDMAC_3_PRPENC_WIDTH_MASK (0x3ff << 0)
+#define IC_IDMAC_3_PRPENC_WIDTH_OFFSET 0
+#define IC_IDMAC_3_PRPVF_WIDTH_MASK (0x3ff << 10)
+#define IC_IDMAC_3_PRPVF_WIDTH_OFFSET 10
+#define IC_IDMAC_3_PP_WIDTH_MASK (0x3ff << 20)
+#define IC_IDMAC_3_PP_WIDTH_OFFSET 20
+
+struct ic_task_regoffs {
+ u32 rsc;
+ u32 tpmem_csc[2];
+};
+
+struct ic_task_bitfields {
+ u32 ic_conf_en;
+ u32 ic_conf_rot_en;
+ u32 ic_conf_cmb_en;
+ u32 ic_conf_csc1_en;
+ u32 ic_conf_csc2_en;
+ u32 ic_cmb_galpha_bit;
+};
+
+static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
+ [IC_TASK_ENCODER] = {
+ .rsc = IC_PRP_ENC_RSC,
+ .tpmem_csc = {0x2008, 0},
+ },
+ [IC_TASK_VIEWFINDER] = {
+ .rsc = IC_PRP_VF_RSC,
+ .tpmem_csc = {0x4028, 0x4040},
+ },
+ [IC_TASK_POST_PROCESSOR] = {
+ .rsc = IC_PP_RSC,
+ .tpmem_csc = {0x6060, 0x6078},
+ },
+};
+
+static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = {
+ [IC_TASK_ENCODER] = {
+ .ic_conf_en = IC_CONF_PRPENC_EN,
+ .ic_conf_rot_en = IC_CONF_PRPENC_ROT_EN,
+ .ic_conf_cmb_en = 0, /* NA */
+ .ic_conf_csc1_en = IC_CONF_PRPENC_CSC1,
+ .ic_conf_csc2_en = 0, /* NA */
+ .ic_cmb_galpha_bit = 0, /* NA */
+ },
+ [IC_TASK_VIEWFINDER] = {
+ .ic_conf_en = IC_CONF_PRPVF_EN,
+ .ic_conf_rot_en = IC_CONF_PRPVF_ROT_EN,
+ .ic_conf_cmb_en = IC_CONF_PRPVF_CMB,
+ .ic_conf_csc1_en = IC_CONF_PRPVF_CSC1,
+ .ic_conf_csc2_en = IC_CONF_PRPVF_CSC2,
+ .ic_cmb_galpha_bit = 0,
+ },
+ [IC_TASK_POST_PROCESSOR] = {
+ .ic_conf_en = IC_CONF_PP_EN,
+ .ic_conf_rot_en = IC_CONF_PP_ROT_EN,
+ .ic_conf_cmb_en = IC_CONF_PP_CMB,
+ .ic_conf_csc1_en = IC_CONF_PP_CSC1,
+ .ic_conf_csc2_en = IC_CONF_PP_CSC2,
+ .ic_cmb_galpha_bit = 8,
+ },
+};
+
+struct ipu_ic_priv;
+
+struct ipu_ic {
+ enum ipu_ic_task task;
+ const struct ic_task_regoffs *reg;
+ const struct ic_task_bitfields *bit;
+
+ enum ipu_color_space in_cs, g_in_cs;
+ enum ipu_color_space out_cs;
+ bool graphics;
+ bool rotation;
+ bool in_use;
+
+ struct ipu_ic_priv *priv;
+};
+
+struct ipu_ic_priv {
+ void __iomem *base;
+ void __iomem *tpmem_base;
+ spinlock_t lock;
+ struct ipu_soc *ipu;
+ int use_count;
+ struct ipu_ic task[IC_NUM_TASKS];
+};
+
+static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset)
+{
+ return readl(ic->priv->base + offset);
+}
+
+static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset)
+{
+ writel(value, ic->priv->base + offset);
+}
+
+struct ic_csc_params {
+ s16 coeff[3][3]; /* signed 9-bit integer coefficients */
+ s16 offset[3]; /* signed 11+2-bit fixed point offset */
+ u8 scale:2; /* scale coefficients * 2^(scale-1) */
+ bool sat:1; /* saturate to (16, 235(Y) / 240(U, V)) */
+};
+
+/*
+ * Y = R * .299 + G * .587 + B * .114;
+ * U = R * -.169 + G * -.332 + B * .500 + 128.;
+ * V = R * .500 + G * -.419 + B * -.0813 + 128.;
+ */
+static const struct ic_csc_params ic_csc_rgb2ycbcr = {
+ .coeff = {
+ { 77, 150, 29 },
+ { 469, 427, 128 },
+ { 128, 405, 491 },
+ },
+ .offset = { 0, 512, 512 },
+ .scale = 1,
+};
+
+/* transparent RGB->RGB matrix for graphics combining */
+static const struct ic_csc_params ic_csc_rgb2rgb = {
+ .coeff = {
+ { 128, 0, 0 },
+ { 0, 128, 0 },
+ { 0, 0, 128 },
+ },
+ .scale = 2,
+};
+
+/*
+ * R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
+ * G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
+ * B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128);
+ */
+static const struct ic_csc_params ic_csc_ycbcr2rgb = {
+ .coeff = {
+ { 149, 0, 204 },
+ { 149, 462, 408 },
+ { 149, 255, 0 },
+ },
+ .offset = { -446, 266, -554 },
+ .scale = 2,
+};
+
+static int init_csc(struct ipu_ic *ic,
+ enum ipu_color_space inf,
+ enum ipu_color_space outf,
+ int csc_index)
+{
+ struct ipu_ic_priv *priv = ic->priv;
+ const struct ic_csc_params *params;
+ u32 __iomem *base;
+ const u16 (*c)[3];
+ const u16 *a;
+ u32 param;
+
+ base = (u32 __iomem *)
+ (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]);
+
+ if (inf == IPUV3_COLORSPACE_YUV && outf == IPUV3_COLORSPACE_RGB)
+ params = &ic_csc_ycbcr2rgb;
+ else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_YUV)
+ params = &ic_csc_rgb2ycbcr;
+ else if (inf == IPUV3_COLORSPACE_RGB && outf == IPUV3_COLORSPACE_RGB)
+ params = &ic_csc_rgb2rgb;
+ else {
+ dev_err(priv->ipu->dev, "Unsupported color space conversion\n");
+ return -EINVAL;
+ }
+
+ /* Cast to unsigned */
+ c = (const u16 (*)[3])params->coeff;
+ a = (const u16 *)params->offset;
+
+ param = ((a[0] & 0x1f) << 27) | ((c[0][0] & 0x1ff) << 18) |
+ ((c[1][1] & 0x1ff) << 9) | (c[2][2] & 0x1ff);
+ writel(param, base++);
+
+ param = ((a[0] & 0x1fe0) >> 5) | (params->scale << 8) |
+ (params->sat << 9);
+ writel(param, base++);
+
+ param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) |
+ ((c[1][0] & 0x1ff) << 9) | (c[2][0] & 0x1ff);
+ writel(param, base++);
+
+ param = ((a[1] & 0x1fe0) >> 5);
+ writel(param, base++);
+
+ param = ((a[2] & 0x1f) << 27) | ((c[0][2] & 0x1ff) << 18) |
+ ((c[1][2] & 0x1ff) << 9) | (c[2][1] & 0x1ff);
+ writel(param, base++);
+
+ param = ((a[2] & 0x1fe0) >> 5);
+ writel(param, base++);
+
+ return 0;
+}
+
+static int calc_resize_coeffs(struct ipu_ic *ic,
+ u32 in_size, u32 out_size,
+ u32 *resize_coeff,
+ u32 *downsize_coeff)
+{
+ struct ipu_ic_priv *priv = ic->priv;
+ struct ipu_soc *ipu = priv->ipu;
+ u32 temp_size, temp_downsize;
+
+ /*
+ * Input size cannot be more than 4096, and output size cannot
+ * be more than 1024
+ */
+ if (in_size > 4096) {
+ dev_err(ipu->dev, "Unsupported resize (in_size > 4096)\n");
+ return -EINVAL;
+ }
+ if (out_size > 1024) {
+ dev_err(ipu->dev, "Unsupported resize (out_size > 1024)\n");
+ return -EINVAL;
+ }
+
+ /* Cannot downsize more than 8:1 */
+ if ((out_size << 3) < in_size) {
+ dev_err(ipu->dev, "Unsupported downsize\n");
+ return -EINVAL;
+ }
+
+ /* Compute downsizing coefficient */
+ temp_downsize = 0;
+ temp_size = in_size;
+ while (((temp_size > 1024) || (temp_size >= out_size * 2)) &&
+ (temp_downsize < 2)) {
+ temp_size >>= 1;
+ temp_downsize++;
+ }
+ *downsize_coeff = temp_downsize;
+
+ /*
+ * compute resizing coefficient using the following equation:
+ * resize_coeff = M * (SI - 1) / (SO - 1)
+ * where M = 2^13, SI = input size, SO = output size
+ */
+ *resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1);
+ if (*resize_coeff >= 16384L) {
+ dev_err(ipu->dev, "Warning! Overflow on resize coeff.\n");
+ *resize_coeff = 0x3FFF;
+ }
+
+ return 0;
+}
+
+void ipu_ic_task_enable(struct ipu_ic *ic)
+{
+ struct ipu_ic_priv *priv = ic->priv;
+ unsigned long flags;
+ u32 ic_conf;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ ic_conf = ipu_ic_read(ic, IC_CONF);
+
+ ic_conf |= ic->bit->ic_conf_en;
+
+ if (ic->rotation)
+ ic_conf |= ic->bit->ic_conf_rot_en;
+
+ if (ic->in_cs != ic->out_cs)
+ ic_conf |= ic->bit->ic_conf_csc1_en;
+
+ if (ic->graphics) {
+ ic_conf |= ic->bit->ic_conf_cmb_en;
+ ic_conf |= ic->bit->ic_conf_csc1_en;
+
+ if (ic->g_in_cs != ic->out_cs)
+ ic_conf |= ic->bit->ic_conf_csc2_en;
+ }
+
+ ipu_ic_write(ic, ic_conf, IC_CONF);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_ic_task_enable);
+
+void ipu_ic_task_disable(struct ipu_ic *ic)
+{
+ struct ipu_ic_priv *priv = ic->priv;
+ unsigned long flags;
+ u32 ic_conf;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ ic_conf = ipu_ic_read(ic, IC_CONF);
+
+ ic_conf &= ~(ic->bit->ic_conf_en |
+ ic->bit->ic_conf_csc1_en |
+ ic->bit->ic_conf_rot_en);
+ if (ic->bit->ic_conf_csc2_en)
+ ic_conf &= ~ic->bit->ic_conf_csc2_en;
+ if (ic->bit->ic_conf_cmb_en)
+ ic_conf &= ~ic->bit->ic_conf_cmb_en;
+
+ ipu_ic_write(ic, ic_conf, IC_CONF);
+
+ ic->rotation = ic->graphics = false;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_ic_task_disable);
+
+int ipu_ic_task_graphics_init(struct ipu_ic *ic,
+ enum ipu_color_space in_g_cs,
+ bool galpha_en, u32 galpha,
+ bool colorkey_en, u32 colorkey)
+{
+ struct ipu_ic_priv *priv = ic->priv;
+ unsigned long flags;
+ u32 reg, ic_conf;
+ int ret = 0;
+
+ if (ic->task == IC_TASK_ENCODER)
+ return -EINVAL;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ ic_conf = ipu_ic_read(ic, IC_CONF);
+
+ if (!(ic_conf & ic->bit->ic_conf_csc1_en)) {
+ /* need transparent CSC1 conversion */
+ ret = init_csc(ic, IPUV3_COLORSPACE_RGB,
+ IPUV3_COLORSPACE_RGB, 0);
+ if (ret)
+ goto unlock;
+ }
+
+ ic->g_in_cs = in_g_cs;
+
+ if (ic->g_in_cs != ic->out_cs) {
+ ret = init_csc(ic, ic->g_in_cs, ic->out_cs, 1);
+ if (ret)
+ goto unlock;
+ }
+
+ if (galpha_en) {
+ ic_conf |= IC_CONF_IC_GLB_LOC_A;
+ reg = ipu_ic_read(ic, IC_CMBP_1);
+ reg &= ~(0xff << ic->bit->ic_cmb_galpha_bit);
+ reg |= (galpha << ic->bit->ic_cmb_galpha_bit);
+ ipu_ic_write(ic, reg, IC_CMBP_1);
+ } else
+ ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
+
+ if (colorkey_en) {
+ ic_conf |= IC_CONF_KEY_COLOR_EN;
+ ipu_ic_write(ic, colorkey, IC_CMBP_2);
+ } else
+ ic_conf &= ~IC_CONF_KEY_COLOR_EN;
+
+ ipu_ic_write(ic, ic_conf, IC_CONF);
+
+ ic->graphics = true;
+unlock:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_ic_task_graphics_init);
+
+int ipu_ic_task_init(struct ipu_ic *ic,
+ int in_width, int in_height,
+ int out_width, int out_height,
+ enum ipu_color_space in_cs,
+ enum ipu_color_space out_cs)
+{
+ struct ipu_ic_priv *priv = ic->priv;
+ u32 reg, downsize_coeff, resize_coeff;
+ unsigned long flags;
+ int ret = 0;
+
+ /* Setup vertical resizing */
+ ret = calc_resize_coeffs(ic, in_height, out_height,
+ &resize_coeff, &downsize_coeff);
+ if (ret)
+ return ret;
+
+ reg = (downsize_coeff << 30) | (resize_coeff << 16);
+
+ /* Setup horizontal resizing */
+ ret = calc_resize_coeffs(ic, in_width, out_width,
+ &resize_coeff, &downsize_coeff);
+ if (ret)
+ return ret;
+
+ reg |= (downsize_coeff << 14) | resize_coeff;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ ipu_ic_write(ic, reg, ic->reg->rsc);
+
+ /* Setup color space conversion */
+ ic->in_cs = in_cs;
+ ic->out_cs = out_cs;
+
+ if (ic->in_cs != ic->out_cs) {
+ ret = init_csc(ic, ic->in_cs, ic->out_cs, 0);
+ if (ret)
+ goto unlock;
+ }
+
+unlock:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_ic_task_init);
+
+int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
+ u32 width, u32 height, int burst_size,
+ enum ipu_rotate_mode rot)
+{
+ struct ipu_ic_priv *priv = ic->priv;
+ struct ipu_soc *ipu = priv->ipu;
+ u32 ic_idmac_1, ic_idmac_2, ic_idmac_3;
+ u32 temp_rot = bitrev8(rot) >> 5;
+ bool need_hor_flip = false;
+ unsigned long flags;
+ int ret = 0;
+
+ if ((burst_size != 8) && (burst_size != 16)) {
+ dev_err(ipu->dev, "Illegal burst length for IC\n");
+ return -EINVAL;
+ }
+
+ width--;
+ height--;
+
+ if (temp_rot & 0x2) /* Need horizontal flip */
+ need_hor_flip = true;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ ic_idmac_1 = ipu_ic_read(ic, IC_IDMAC_1);
+ ic_idmac_2 = ipu_ic_read(ic, IC_IDMAC_2);
+ ic_idmac_3 = ipu_ic_read(ic, IC_IDMAC_3);
+
+ switch (channel->num) {
+ case IPUV3_CHANNEL_IC_PP_MEM:
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16;
+
+ if (need_hor_flip)
+ ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS;
+
+ ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK;
+ ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET;
+
+ ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK;
+ ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET;
+ break;
+ case IPUV3_CHANNEL_MEM_IC_PP:
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16;
+ break;
+ case IPUV3_CHANNEL_MEM_ROT_PP:
+ ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK;
+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET;
+ break;
+ case IPUV3_CHANNEL_MEM_IC_PRP_VF:
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16;
+ break;
+ case IPUV3_CHANNEL_IC_PRP_ENC_MEM:
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16;
+
+ if (need_hor_flip)
+ ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS;
+
+ ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK;
+ ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET;
+
+ ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK;
+ ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET;
+ break;
+ case IPUV3_CHANNEL_MEM_ROT_ENC:
+ ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK;
+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET;
+ break;
+ case IPUV3_CHANNEL_IC_PRP_VF_MEM:
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16;
+
+ if (need_hor_flip)
+ ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS;
+
+ ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK;
+ ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET;
+
+ ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK;
+ ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET;
+ break;
+ case IPUV3_CHANNEL_MEM_ROT_VF:
+ ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK;
+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET;
+ break;
+ case IPUV3_CHANNEL_G_MEM_IC_PRP_VF:
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16;
+ break;
+ case IPUV3_CHANNEL_G_MEM_IC_PP:
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16;
+ break;
+ case IPUV3_CHANNEL_VDI_MEM_IC_VF:
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16;
+ break;
+ default:
+ goto unlock;
+ }
+
+ ipu_ic_write(ic, ic_idmac_1, IC_IDMAC_1);
+ ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2);
+ ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3);
+
+ if (rot >= IPU_ROTATE_90_RIGHT)
+ ic->rotation = true;
+
+unlock:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
+
+int ipu_ic_enable(struct ipu_ic *ic)
+{
+ struct ipu_ic_priv *priv = ic->priv;
+ unsigned long flags;
+ u32 module = IPU_CONF_IC_EN;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (ic->rotation)
+ module |= IPU_CONF_ROT_EN;
+
+ if (!priv->use_count)
+ ipu_module_enable(priv->ipu, module);
+
+ priv->use_count++;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_ic_enable);
+
+int ipu_ic_disable(struct ipu_ic *ic)
+{
+ struct ipu_ic_priv *priv = ic->priv;
+ unsigned long flags;
+ u32 module = IPU_CONF_IC_EN | IPU_CONF_ROT_EN;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ priv->use_count--;
+
+ if (!priv->use_count)
+ ipu_module_disable(priv->ipu, module);
+
+ if (priv->use_count < 0)
+ priv->use_count = 0;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_ic_disable);
+
+struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task)
+{
+ struct ipu_ic_priv *priv = ipu->ic_priv;
+ unsigned long flags;
+ struct ipu_ic *ic, *ret;
+
+ if (task >= IC_NUM_TASKS)
+ return ERR_PTR(-EINVAL);
+
+ ic = &priv->task[task];
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (ic->in_use) {
+ ret = ERR_PTR(-EBUSY);
+ goto unlock;
+ }
+
+ ic->in_use = true;
+ ret = ic;
+
+unlock:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_ic_get);
+
+void ipu_ic_put(struct ipu_ic *ic)
+{
+ struct ipu_ic_priv *priv = ic->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ ic->in_use = false;
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_ic_put);
+
+int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
+ unsigned long base, unsigned long tpmem_base)
+{
+ struct ipu_ic_priv *priv;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ipu->ic_priv = priv;
+
+ spin_lock_init(&priv->lock);
+ priv->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!priv->base)
+ return -ENOMEM;
+ priv->tpmem_base = devm_ioremap(dev, tpmem_base, SZ_64K);
+ if (!priv->tpmem_base)
+ return -ENOMEM;
+
+ dev_dbg(dev, "IC base: 0x%08lx remapped to %p\n", base, priv->base);
+
+ priv->ipu = ipu;
+
+ for (i = 0; i < IC_NUM_TASKS; i++) {
+ priv->task[i].task = i;
+ priv->task[i].priv = priv;
+ priv->task[i].reg = &ic_task_reg[i];
+ priv->task[i].bit = &ic_task_bit[i];
+ }
+
+ return 0;
+}
+
+void ipu_ic_exit(struct ipu_soc *ipu)
+{
+}
+
+void ipu_ic_dump(struct ipu_ic *ic)
+{
+ struct ipu_ic_priv *priv = ic->priv;
+ struct ipu_soc *ipu = priv->ipu;
+
+ dev_dbg(ipu->dev, "IC_CONF = \t0x%08X\n",
+ ipu_ic_read(ic, IC_CONF));
+ dev_dbg(ipu->dev, "IC_PRP_ENC_RSC = \t0x%08X\n",
+ ipu_ic_read(ic, IC_PRP_ENC_RSC));
+ dev_dbg(ipu->dev, "IC_PRP_VF_RSC = \t0x%08X\n",
+ ipu_ic_read(ic, IC_PRP_VF_RSC));
+ dev_dbg(ipu->dev, "IC_PP_RSC = \t0x%08X\n",
+ ipu_ic_read(ic, IC_PP_RSC));
+ dev_dbg(ipu->dev, "IC_CMBP_1 = \t0x%08X\n",
+ ipu_ic_read(ic, IC_CMBP_1));
+ dev_dbg(ipu->dev, "IC_CMBP_2 = \t0x%08X\n",
+ ipu_ic_read(ic, IC_CMBP_2));
+ dev_dbg(ipu->dev, "IC_IDMAC_1 = \t0x%08X\n",
+ ipu_ic_read(ic, IC_IDMAC_1));
+ dev_dbg(ipu->dev, "IC_IDMAC_2 = \t0x%08X\n",
+ ipu_ic_read(ic, IC_IDMAC_2));
+ dev_dbg(ipu->dev, "IC_IDMAC_3 = \t0x%08X\n",
+ ipu_ic_read(ic, IC_IDMAC_3));
+ dev_dbg(ipu->dev, "IC_IDMAC_4 = \t0x%08X\n",
+ ipu_ic_read(ic, IC_IDMAC_4));
+}
+EXPORT_SYMBOL_GPL(ipu_ic_dump);
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index c93f50ec04f7..bfb1e8a4483f 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -24,23 +24,6 @@ struct ipu_soc;
#include <video/imx-ipu-v3.h>
-#define IPUV3_CHANNEL_CSI0 0
-#define IPUV3_CHANNEL_CSI1 1
-#define IPUV3_CHANNEL_CSI2 2
-#define IPUV3_CHANNEL_CSI3 3
-#define IPUV3_CHANNEL_MEM_BG_SYNC 23
-#define IPUV3_CHANNEL_MEM_FG_SYNC 27
-#define IPUV3_CHANNEL_MEM_DC_SYNC 28
-#define IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA 31
-#define IPUV3_CHANNEL_MEM_DC_ASYNC 41
-#define IPUV3_CHANNEL_ROT_ENC_MEM 45
-#define IPUV3_CHANNEL_ROT_VF_MEM 46
-#define IPUV3_CHANNEL_ROT_PP_MEM 47
-#define IPUV3_CHANNEL_ROT_ENC_MEM_OUT 48
-#define IPUV3_CHANNEL_ROT_VF_MEM_OUT 49
-#define IPUV3_CHANNEL_ROT_PP_MEM_OUT 50
-#define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA 51
-
#define IPU_MCU_T_DEFAULT 8
#define IPU_CM_IDMAC_REG_OFS 0x00008000
#define IPU_CM_IC_REG_OFS 0x00020000
@@ -85,6 +68,7 @@ struct ipu_soc;
#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254)
#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32))
#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32))
+#define IPU_CHA_BUF2_RDY(ch) IPU_CM_REG(0x0288 + 4 * ((ch) / 32))
#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32))
#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32))
@@ -148,9 +132,12 @@ struct ipuv3_channel {
struct ipu_soc *ipu;
};
+struct ipu_cpmem;
+struct ipu_csi;
struct ipu_dc_priv;
struct ipu_dmfc_priv;
struct ipu_di;
+struct ipu_ic_priv;
struct ipu_smfc_priv;
struct ipu_devtype;
@@ -164,7 +151,6 @@ struct ipu_soc {
void __iomem *cm_reg;
void __iomem *idmac_reg;
- struct ipu_ch_param __iomem *cpmem_base;
int usecount;
@@ -176,13 +162,27 @@ struct ipu_soc {
int irq_err;
struct irq_domain *domain;
+ struct ipu_cpmem *cpmem_priv;
struct ipu_dc_priv *dc_priv;
struct ipu_dp_priv *dp_priv;
struct ipu_dmfc_priv *dmfc_priv;
struct ipu_di *di_priv[2];
+ struct ipu_csi *csi_priv[2];
+ struct ipu_ic_priv *ic_priv;
struct ipu_smfc_priv *smfc_priv;
};
+static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset)
+{
+ return readl(ipu->idmac_reg + offset);
+}
+
+static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value,
+ unsigned offset)
+{
+ writel(value, ipu->idmac_reg + offset);
+}
+
void ipu_srm_dp_sync_update(struct ipu_soc *ipu);
int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
@@ -191,6 +191,14 @@ int ipu_module_disable(struct ipu_soc *ipu, u32 mask);
bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno);
int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms);
+int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id,
+ unsigned long base, u32 module, struct clk *clk_ipu);
+void ipu_csi_exit(struct ipu_soc *ipu, int id);
+
+int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
+ unsigned long base, unsigned long tpmem_base);
+void ipu_ic_exit(struct ipu_soc *ipu);
+
int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
unsigned long base, u32 module, struct clk *ipu_clk);
void ipu_di_exit(struct ipu_soc *ipu, int id);
diff --git a/drivers/gpu/ipu-v3/ipu-smfc.c b/drivers/gpu/ipu-v3/ipu-smfc.c
index e4f85ad286fc..4ef910991413 100644
--- a/drivers/gpu/ipu-v3/ipu-smfc.c
+++ b/drivers/gpu/ipu-v3/ipu-smfc.c
@@ -8,7 +8,6 @@
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
-#define DEBUG
#include <linux/export.h>
#include <linux/types.h>
#include <linux/init.h>
@@ -21,9 +20,18 @@
#include "ipu-prv.h"
+struct ipu_smfc {
+ struct ipu_smfc_priv *priv;
+ int chno;
+ bool inuse;
+};
+
struct ipu_smfc_priv {
void __iomem *base;
spinlock_t lock;
+ struct ipu_soc *ipu;
+ struct ipu_smfc channel[4];
+ int use_count;
};
/*SMFC Registers */
@@ -31,63 +39,166 @@ struct ipu_smfc_priv {
#define SMFC_WMC 0x0004
#define SMFC_BS 0x0008
-int ipu_smfc_set_burstsize(struct ipu_soc *ipu, int channel, int burstsize)
+int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize)
{
- struct ipu_smfc_priv *smfc = ipu->smfc_priv;
+ struct ipu_smfc_priv *priv = smfc->priv;
unsigned long flags;
u32 val, shift;
- spin_lock_irqsave(&smfc->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
- shift = channel * 4;
- val = readl(smfc->base + SMFC_BS);
+ shift = smfc->chno * 4;
+ val = readl(priv->base + SMFC_BS);
val &= ~(0xf << shift);
val |= burstsize << shift;
- writel(val, smfc->base + SMFC_BS);
+ writel(val, priv->base + SMFC_BS);
- spin_unlock_irqrestore(&smfc->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize);
-int ipu_smfc_map_channel(struct ipu_soc *ipu, int channel, int csi_id, int mipi_id)
+int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id)
{
- struct ipu_smfc_priv *smfc = ipu->smfc_priv;
+ struct ipu_smfc_priv *priv = smfc->priv;
unsigned long flags;
u32 val, shift;
- spin_lock_irqsave(&smfc->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
- shift = channel * 3;
- val = readl(smfc->base + SMFC_MAP);
+ shift = smfc->chno * 3;
+ val = readl(priv->base + SMFC_MAP);
val &= ~(0x7 << shift);
val |= ((csi_id << 2) | mipi_id) << shift;
- writel(val, smfc->base + SMFC_MAP);
+ writel(val, priv->base + SMFC_MAP);
- spin_unlock_irqrestore(&smfc->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_smfc_map_channel);
+int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level)
+{
+ struct ipu_smfc_priv *priv = smfc->priv;
+ unsigned long flags;
+ u32 val, shift;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0);
+ val = readl(priv->base + SMFC_WMC);
+ val &= ~(0x3f << shift);
+ val |= ((clr_level << 3) | set_level) << shift;
+ writel(val, priv->base + SMFC_WMC);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark);
+
+int ipu_smfc_enable(struct ipu_smfc *smfc)
+{
+ struct ipu_smfc_priv *priv = smfc->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (!priv->use_count)
+ ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN);
+
+ priv->use_count++;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_smfc_enable);
+
+int ipu_smfc_disable(struct ipu_smfc *smfc)
+{
+ struct ipu_smfc_priv *priv = smfc->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ priv->use_count--;
+
+ if (!priv->use_count)
+ ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN);
+
+ if (priv->use_count < 0)
+ priv->use_count = 0;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_smfc_disable);
+
+struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno)
+{
+ struct ipu_smfc_priv *priv = ipu->smfc_priv;
+ struct ipu_smfc *smfc, *ret;
+ unsigned long flags;
+
+ if (chno >= 4)
+ return ERR_PTR(-EINVAL);
+
+ smfc = &priv->channel[chno];
+ ret = smfc;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (smfc->inuse) {
+ ret = ERR_PTR(-EBUSY);
+ goto unlock;
+ }
+
+ smfc->inuse = true;
+unlock:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_smfc_get);
+
+void ipu_smfc_put(struct ipu_smfc *smfc)
+{
+ struct ipu_smfc_priv *priv = smfc->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ smfc->inuse = false;
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_smfc_put);
+
int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev,
unsigned long base)
{
- struct ipu_smfc_priv *smfc;
+ struct ipu_smfc_priv *priv;
+ int i;
- smfc = devm_kzalloc(dev, sizeof(*smfc), GFP_KERNEL);
- if (!smfc)
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
return -ENOMEM;
- ipu->smfc_priv = smfc;
- spin_lock_init(&smfc->lock);
+ ipu->smfc_priv = priv;
+ spin_lock_init(&priv->lock);
+ priv->ipu = ipu;
- smfc->base = devm_ioremap(dev, base, PAGE_SIZE);
- if (!smfc->base)
+ priv->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!priv->base)
return -ENOMEM;
- pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, smfc->base);
+ for (i = 0; i < 4; i++) {
+ priv->channel[i].priv = priv;
+ priv->channel[i].chno = i;
+ }
+
+ pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base);
return 0;
}
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index 6866448083b2..37ac7b5dbd06 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -660,6 +660,12 @@ int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *
}
EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_ops);
+void vga_switcheroo_fini_domain_pm_ops(struct device *dev)
+{
+ dev->pm_domain = NULL;
+}
+EXPORT_SYMBOL(vga_switcheroo_fini_domain_pm_ops);
+
static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c
index af0259708358..7bcbf863656e 100644
--- a/drivers/gpu/vga/vgaarb.c
+++ b/drivers/gpu/vga/vgaarb.c
@@ -41,6 +41,7 @@
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
+#include <linux/screen_info.h>
#include <linux/uaccess.h>
@@ -112,10 +113,8 @@ both:
return 1;
}
-#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
/* this is only used a cookie - it should not be dereferenced */
static struct pci_dev *vga_default;
-#endif
static void vga_arb_device_card_gone(struct pci_dev *pdev);
@@ -131,7 +130,6 @@ static struct vga_device *vgadev_find(struct pci_dev *pdev)
}
/* Returns the default VGA device (vgacon's babe) */
-#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
struct pci_dev *vga_default_device(void)
{
return vga_default;
@@ -147,7 +145,6 @@ void vga_set_default_device(struct pci_dev *pdev)
pci_dev_put(vga_default);
vga_default = pci_dev_get(pdev);
}
-#endif
static inline void vga_irq_set_state(struct vga_device *vgadev, bool state)
{
@@ -237,12 +234,10 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev,
if (conflict->locks & lwants)
return conflict;
- /* Ok, now check if he owns the resource we want. We don't need
- * to check "decodes" since it should be impossible to own
- * own legacy resources you don't decode unless I have a bug
- * in this code...
+ /* Ok, now check if it owns the resource we want. We can
+ * lock resources that are not decoded, therefore a device
+ * can own resources it doesn't decode.
*/
- WARN_ON(conflict->owns & ~conflict->decodes);
match = lwants & conflict->owns;
if (!match)
continue;
@@ -254,13 +249,19 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev,
flags = 0;
pci_bits = 0;
+ /* If we can't control legacy resources via the bridge, we
+ * also need to disable normal decoding.
+ */
if (!conflict->bridge_has_one_vga) {
- vga_irq_set_state(conflict, false);
- flags |= PCI_VGA_STATE_CHANGE_DECODES;
- if (match & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
+ if ((match & conflict->decodes) & VGA_RSRC_LEGACY_MEM)
pci_bits |= PCI_COMMAND_MEMORY;
- if (match & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
+ if ((match & conflict->decodes) & VGA_RSRC_LEGACY_IO)
pci_bits |= PCI_COMMAND_IO;
+
+ if (pci_bits) {
+ vga_irq_set_state(conflict, false);
+ flags |= PCI_VGA_STATE_CHANGE_DECODES;
+ }
}
if (change_bridge)
@@ -268,18 +269,19 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev,
pci_set_vga_state(conflict->pdev, false, pci_bits, flags);
conflict->owns &= ~match;
- /* If he also owned non-legacy, that is no longer the case */
- if (match & VGA_RSRC_LEGACY_MEM)
+
+ /* If we disabled normal decoding, reflect it in owns */
+ if (pci_bits & PCI_COMMAND_MEMORY)
conflict->owns &= ~VGA_RSRC_NORMAL_MEM;
- if (match & VGA_RSRC_LEGACY_IO)
+ if (pci_bits & PCI_COMMAND_IO)
conflict->owns &= ~VGA_RSRC_NORMAL_IO;
}
enable_them:
/* ok dude, we got it, everybody conflicting has been disabled, let's
- * enable us. Make sure we don't mark a bit in "owns" that we don't
- * also have in "decodes". We can lock resources we don't decode but
- * not own them.
+ * enable us. Mark any bits in "owns" regardless of whether we
+ * decoded them. We can lock resources we don't decode, therefore
+ * we must track them via "owns".
*/
flags = 0;
pci_bits = 0;
@@ -291,7 +293,7 @@ enable_them:
if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
pci_bits |= PCI_COMMAND_IO;
}
- if (!!(wants & VGA_RSRC_LEGACY_MASK))
+ if (wants & VGA_RSRC_LEGACY_MASK)
flags |= PCI_VGA_STATE_CHANGE_BRIDGE;
pci_set_vga_state(vgadev->pdev, true, pci_bits, flags);
@@ -299,7 +301,7 @@ enable_them:
if (!vgadev->bridge_has_one_vga) {
vga_irq_set_state(vgadev, true);
}
- vgadev->owns |= (wants & vgadev->decodes);
+ vgadev->owns |= wants;
lock_them:
vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK);
if (rsrc & VGA_RSRC_LEGACY_IO)
@@ -398,7 +400,6 @@ int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible)
}
schedule();
remove_wait_queue(&vga_wait_queue, &wait);
- set_current_state(TASK_RUNNING);
}
return rc;
}
@@ -578,11 +579,12 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
/* Deal with VGA default device. Use first enabled one
* by default if arch doesn't have it's own hook
*/
-#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
if (vga_default == NULL &&
- ((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK))
+ ((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK)) {
+ pr_info("vgaarb: setting as boot device: PCI:%s\n",
+ pci_name(pdev));
vga_set_default_device(pdev);
-#endif
+ }
vga_arbiter_check_bridge_sharing(vgadev);
@@ -616,10 +618,8 @@ static bool vga_arbiter_del_pci_device(struct pci_dev *pdev)
goto bail;
}
-#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
if (vga_default == pdev)
vga_set_default_device(NULL);
-#endif
if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM))
vga_decode_count--;
@@ -649,7 +649,6 @@ static inline void vga_update_device_decodes(struct vga_device *vgadev,
old_decodes = vgadev->decodes;
decodes_removed = ~new_decodes & old_decodes;
decodes_unlocked = vgadev->locks & decodes_removed;
- vgadev->owns &= ~decodes_removed;
vgadev->decodes = new_decodes;
pr_info("vgaarb: device changed decodes: PCI:%s,olddecodes=%s,decodes=%s:owns=%s\n",
@@ -1316,6 +1315,38 @@ static int __init vga_arb_device_init(void)
pr_info("vgaarb: loaded\n");
list_for_each_entry(vgadev, &vga_list, list) {
+#if defined(CONFIG_X86) || defined(CONFIG_IA64)
+ /* Override I/O based detection done by vga_arbiter_add_pci_device()
+ * as it may take the wrong device (e.g. on Apple system under EFI).
+ *
+ * Select the device owning the boot framebuffer if there is one.
+ */
+ resource_size_t start, end;
+ int i;
+
+ /* Does firmware framebuffer belong to us? */
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+ if (!(pci_resource_flags(vgadev->pdev, i) & IORESOURCE_MEM))
+ continue;
+
+ start = pci_resource_start(vgadev->pdev, i);
+ end = pci_resource_end(vgadev->pdev, i);
+
+ if (!start || !end)
+ continue;
+
+ if (screen_info.lfb_base < start ||
+ (screen_info.lfb_base + screen_info.lfb_size) >= end)
+ continue;
+ if (!vga_default_device())
+ pr_info("vgaarb: setting as boot device: PCI:%s\n",
+ pci_name(vgadev->pdev));
+ else if (vgadev->pdev != vga_default_device())
+ pr_info("vgaarb: overriding boot device: PCI:%s\n",
+ pci_name(vgadev->pdev));
+ vga_set_default_device(vgadev->pdev);
+ }
+#endif
if (vgadev->bridge_has_one_vga)
pr_info("vgaarb: bridge control possible %s\n", pci_name(vgadev->pdev));
else