summaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/Makefile1
-rw-r--r--drivers/gpu/drm/Kconfig4
-rw-r--r--drivers/gpu/drm/Makefile5
-rw-r--r--drivers/gpu/drm/armada/armada_drv.c4
-rw-r--r--drivers/gpu/drm/armada/armada_fbdev.c4
-rw-r--r--drivers/gpu/drm/armada/armada_gem.c4
-rw-r--r--drivers/gpu/drm/ast/Makefile4
-rw-r--r--drivers/gpu/drm/ast/ast_dp501.c410
-rw-r--r--drivers/gpu/drm/ast/ast_drv.c3
-rw-r--r--drivers/gpu/drm/ast/ast_drv.h24
-rw-r--r--drivers/gpu/drm/ast/ast_main.c97
-rw-r--r--drivers/gpu/drm/ast/ast_mode.c100
-rw-r--r--drivers/gpu/drm/ast/ast_post.c902
-rw-r--r--drivers/gpu/drm/ast/ast_tables.h67
-rw-r--r--drivers/gpu/drm/bochs/bochs_mm.c6
-rw-r--r--drivers/gpu/drm/bridge/ptn3460.c7
-rw-r--r--drivers/gpu/drm/cirrus/cirrus_main.c6
-rw-r--r--drivers/gpu/drm/cirrus/cirrus_mode.c8
-rw-r--r--drivers/gpu/drm/drm_bufs.c34
-rw-r--r--drivers/gpu/drm/drm_cache.c6
-rw-r--r--drivers/gpu/drm/drm_crtc.c421
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c27
-rw-r--r--drivers/gpu/drm/drm_dp_helper.c25
-rw-r--r--drivers/gpu/drm/drm_edid.c293
-rw-r--r--drivers/gpu/drm/drm_edid_load.c23
-rw-r--r--drivers/gpu/drm/drm_fb_cma_helper.c9
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c77
-rw-r--r--drivers/gpu/drm/drm_fops.c9
-rw-r--r--drivers/gpu/drm/drm_gem.c19
-rw-r--r--drivers/gpu/drm/drm_info.c6
-rw-r--r--drivers/gpu/drm/drm_ioctl.c37
-rw-r--r--drivers/gpu/drm/drm_irq.c461
-rw-r--r--drivers/gpu/drm/drm_mipi_dsi.c10
-rw-r--r--drivers/gpu/drm/drm_modes.c9
-rw-r--r--drivers/gpu/drm/drm_modeset_lock.c247
-rw-r--r--drivers/gpu/drm/drm_pci.c159
-rw-r--r--drivers/gpu/drm/drm_plane_helper.c154
-rw-r--r--drivers/gpu/drm/drm_platform.c40
-rw-r--r--drivers/gpu/drm/drm_probe_helper.c76
-rw-r--r--drivers/gpu/drm/drm_stub.c61
-rw-r--r--drivers/gpu/drm/drm_sysfs.c6
-rw-r--r--drivers/gpu/drm/drm_usb.c34
-rw-r--r--drivers/gpu/drm/exynos/Kconfig8
-rw-r--r--drivers/gpu/drm/exynos/exynos_ddc.c63
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.c211
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.h60
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_reg.c46
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_core.c216
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c17
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.h4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dpi.c74
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c446
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h87
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dsi.c114
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c14
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimc.c427
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c211
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.c22
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gsc.c10
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.c258
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.h12
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_rotator.c11
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c108
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c652
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.h23
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmiphy.c65
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c67
-rw-r--r--drivers/gpu/drm/exynos/regs-hdmi.h16
-rw-r--r--drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c2
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.c8
-rw-r--r--drivers/gpu/drm/i2c/tda998x_drv.c6
-rw-r--r--drivers/gpu/drm/i810/i810_dma.c2
-rw-r--r--drivers/gpu/drm/i915/Kconfig3
-rw-r--r--drivers/gpu/drm/i915/Makefile8
-rw-r--r--drivers/gpu/drm/i915/dvo_ch7xxx.c2
-rw-r--r--drivers/gpu/drm/i915/dvo_ivch.c2
-rw-r--r--drivers/gpu/drm/i915/dvo_ns2501.c24
-rw-r--r--drivers/gpu/drm/i915/dvo_sil164.c2
-rw-r--r--drivers/gpu/drm/i915/dvo_tfp410.c2
-rw-r--r--drivers/gpu/drm/i915/i915_cmd_parser.c758
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c269
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c74
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c626
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h479
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c559
-rw-r--r--drivers/gpu/drm/i915/i915_gem_context.c95
-rw-r--r--drivers/gpu/drm/i915/i915_gem_dmabuf.c14
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c136
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.c212
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.h284
-rw-r--r--drivers/gpu/drm/i915/i915_gem_render_state.c198
-rw-r--r--drivers/gpu/drm/i915/i915_gem_userptr.c711
-rw-r--r--drivers/gpu/drm/i915/i915_gpu_error.c55
-rw-r--r--drivers/gpu/drm/i915/i915_ioc32.c2
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c1146
-rw-r--r--drivers/gpu/drm/i915/i915_params.c8
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h780
-rw-r--r--drivers/gpu/drm/i915/i915_suspend.c2
-rw-r--r--drivers/gpu/drm/i915/i915_sysfs.c6
-rw-r--r--drivers/gpu/drm/i915/i915_trace.h101
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c331
-rw-r--r--drivers/gpu/drm/i915/intel_bios.h64
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c83
-rw-r--r--drivers/gpu/drm/i915/intel_ddi.c97
-rw-r--r--drivers/gpu/drm/i915/intel_display.c1854
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c581
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h96
-rw-r--r--drivers/gpu/drm/i915/intel_dsi.c190
-rw-r--r--drivers/gpu/drm/i915/intel_dsi.h19
-rw-r--r--drivers/gpu/drm/i915/intel_dsi_cmd.c4
-rw-r--r--drivers/gpu/drm/i915/intel_dsi_cmd.h5
-rw-r--r--drivers/gpu/drm/i915/intel_dsi_panel_vbt.c589
-rw-r--r--drivers/gpu/drm/i915/intel_dvo.c6
-rw-r--r--drivers/gpu/drm/i915/intel_fbdev.c40
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c260
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c23
-rw-r--r--drivers/gpu/drm/i915/intel_opregion.c4
-rw-r--r--drivers/gpu/drm/i915/intel_overlay.c16
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c168
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c770
-rw-r--r--drivers/gpu/drm/i915/intel_renderstate.h48
-rw-r--r--drivers/gpu/drm/i915/intel_renderstate_gen6.c289
-rw-r--r--drivers/gpu/drm/i915/intel_renderstate_gen7.c253
-rw-r--r--drivers/gpu/drm/i915/intel_renderstate_gen8.c479
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c820
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.h168
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c45
-rw-r--r--drivers/gpu/drm/i915/intel_sideband.c59
-rw-r--r--drivers/gpu/drm/i915/intel_sprite.c239
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c221
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.c108
-rw-r--r--drivers/gpu/drm/mga/mga_ioc32.c2
-rw-r--r--drivers/gpu/drm/mga/mga_state.c4
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_main.c6
-rw-r--r--drivers/gpu/drm/msm/Kconfig2
-rw-r--r--drivers/gpu/drm/msm/Makefile2
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.c20
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_connector.c52
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c2
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c2
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c22
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c5
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c56
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h17
-rw-r--r--drivers/gpu/drm/msm/msm_gem.h1
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c1
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c107
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h31
-rw-r--r--drivers/gpu/drm/msm/msm_perf.c275
-rw-r--r--drivers/gpu/drm/msm/msm_rd.c337
-rw-r--r--drivers/gpu/drm/nouveau/Makefile14
-rw-r--r--drivers/gpu/drm/nouveau/core/core/event.c83
-rw-r--r--drivers/gpu/drm/nouveau/core/core/object.c10
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/gm100.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv04.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv10.c32
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv20.c16
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv30.c20
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv40.c64
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nv50.c56
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nvc0.c36
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/device/nve0.c74
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/base.c124
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/conn.c172
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/conn.h59
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/dport.c292
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/dport.h45
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/gm107.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv04.c16
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv50.c286
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv50.h14
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv84.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nv94.c11
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nva0.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nva3.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c206
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nve0.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/outp.c137
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/outp.h59
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c276
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h65
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c122
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/priv.h32
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c28
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c85
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c72
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/base.c6
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/gk20a.c35
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c6
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c6
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/fifo/nve0.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c53
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h9
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c14
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c47
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nv50.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c14
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/graph/nve4.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/software/nv50.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/software/nv50.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/engine/software/nvc0.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/class.h4
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/event.h29
-rw-r--r--drivers/gpu/drm/nouveau/core/include/engine/disp.h30
-rw-r--r--drivers/gpu/drm/nouveau/core/include/engine/fifo.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/include/engine/graph.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h22
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h9
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/clock.h8
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/fb.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/gpio.h34
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/i2c.h83
-rw-r--r--drivers/gpu/drm/nouveau/core/include/subdev/ibus.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bar/base.c6
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c114
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/base.c9
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/conn.c62
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/dp.c23
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/bios/init.c9
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/base.c7
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c3
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c3
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c3
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c3
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c3
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c3
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c56
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/priv.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c152
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/base.c130
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c115
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c152
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c74
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c56
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c137
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h62
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c27
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c36
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/base.c212
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c8
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c33
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c33
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c30
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c108
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c38
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c72
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c84
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h58
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c35
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c86
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/port.h15
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h85
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/ibus/gk20a.c103
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c5
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c5
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c5
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c2
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/ic.c6
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/dac.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/dfp.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/disp.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/tvnv04.c3
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/tvnv17.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c212
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.h10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_crtc.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c18
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c14
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ioc32.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vga.c11
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c107
-rw-r--r--drivers/gpu/drm/omapdrm/omap_crtc.c10
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c4
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fb.c1
-rw-r--r--drivers/gpu/drm/panel/panel-ld9040.c3
-rw-r--r--drivers/gpu/drm/panel/panel-s6e8aa0.c1
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c99
-rw-r--r--drivers/gpu/drm/qxl/qxl_display.c6
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.c1
-rw-r--r--drivers/gpu/drm/qxl/qxl_ioctl.c2
-rw-r--r--drivers/gpu/drm/qxl/qxl_irq.c2
-rw-r--r--drivers/gpu/drm/qxl/qxl_ttm.c6
-rw-r--r--drivers/gpu/drm/r128/r128_ioc32.c2
-rw-r--r--drivers/gpu/drm/r128/r128_state.c4
-rw-r--r--drivers/gpu/drm/radeon/Makefile2
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c92
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c40
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c5
-rw-r--r--drivers/gpu/drm/radeon/atombios_i2c.c17
-rw-r--r--drivers/gpu/drm/radeon/cik.c33
-rw-r--r--drivers/gpu/drm/radeon/cik_sdma.c43
-rw-r--r--drivers/gpu/drm/radeon/cikd.h1
-rw-r--r--drivers/gpu/drm/radeon/clearstate_cayman.h8
-rw-r--r--drivers/gpu/drm/radeon/clearstate_ci.h4
-rw-r--r--drivers/gpu/drm/radeon/clearstate_si.h4
-rw-r--r--drivers/gpu/drm/radeon/dce3_1_afmt.c244
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c72
-rw-r--r--drivers/gpu/drm/radeon/evergreen_hdmi.c48
-rw-r--r--drivers/gpu/drm/radeon/evergreend.h3
-rw-r--r--drivers/gpu/drm/radeon/ni.c17
-rw-r--r--drivers/gpu/drm/radeon/nid.h1
-rw-r--r--drivers/gpu/drm/radeon/r100.c63
-rw-r--r--drivers/gpu/drm/radeon/r300.c7
-rw-r--r--drivers/gpu/drm/radeon/r600.c15
-rw-r--r--drivers/gpu/drm/radeon/r600_hdmi.c341
-rw-r--r--drivers/gpu/drm/radeon/r600d.h17
-rw-r--r--drivers/gpu/drm/radeon/radeon.h55
-rw-r--r--drivers/gpu/drm/radeon/radeon_agp.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c72
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h40
-rw-r--r--drivers/gpu/drm/radeon/radeon_bios.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c78
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c55
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c282
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c12
-rw-r--r--drivers/gpu/drm/radeon/radeon_fence.c22
-rw-r--r--drivers/gpu/drm/radeon/radeon_i2c.c11
-rw-r--r--drivers/gpu/drm/radeon/radeon_ioc32.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_irq_kms.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c18
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h7
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c11
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_state.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_uvd.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_vce.c1
-rw-r--r--drivers/gpu/drm/radeon/radeon_vm.c123
-rw-r--r--drivers/gpu/drm/radeon/rs400.c7
-rw-r--r--drivers/gpu/drm/radeon/rs600.c44
-rw-r--r--drivers/gpu/drm/radeon/rv770.c13
-rw-r--r--drivers/gpu/drm/radeon/si.c33
-rw-r--r--drivers/gpu/drm/radeon/si_dma.c24
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c13
-rw-r--r--drivers/gpu/drm/radeon/sid.h1
-rw-r--r--drivers/gpu/drm/radeon/uvd_v2_2.c2
-rw-r--r--drivers/gpu/drm/rcar-du/Kconfig2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c7
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_vgacon.c7
-rw-r--r--drivers/gpu/drm/savage/savage_bci.c2
-rw-r--r--drivers/gpu/drm/shmobile/Kconfig3
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_crtc.c7
-rw-r--r--drivers/gpu/drm/shmobile/shmob_drm_drv.c2
-rw-r--r--drivers/gpu/drm/sis/sis_mm.c2
-rw-r--r--drivers/gpu/drm/tegra/Makefile1
-rw-r--r--drivers/gpu/drm/tegra/bus.c75
-rw-r--r--drivers/gpu/drm/tegra/dc.c657
-rw-r--r--drivers/gpu/drm/tegra/dc.h33
-rw-r--r--drivers/gpu/drm/tegra/dpaux.c35
-rw-r--r--drivers/gpu/drm/tegra/drm.c36
-rw-r--r--drivers/gpu/drm/tegra/drm.h58
-rw-r--r--drivers/gpu/drm/tegra/dsi.c250
-rw-r--r--drivers/gpu/drm/tegra/dsi.h10
-rw-r--r--drivers/gpu/drm/tegra/fb.c7
-rw-r--r--drivers/gpu/drm/tegra/gem.c3
-rw-r--r--drivers/gpu/drm/tegra/gr2d.c8
-rw-r--r--drivers/gpu/drm/tegra/gr3d.c8
-rw-r--r--drivers/gpu/drm/tegra/hdmi.c202
-rw-r--r--drivers/gpu/drm/tegra/hdmi.h5
-rw-r--r--drivers/gpu/drm/tegra/rgb.c31
-rw-r--r--drivers/gpu/drm/tegra/sor.c478
-rw-r--r--drivers/gpu/drm/tegra/sor.h4
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_drv.c2
-rw-r--r--drivers/gpu/drm/udl/udl_main.c1
-rw-r--r--drivers/gpu/drm/via/via_dma.c2
-rw-r--r--drivers/gpu/drm/via/via_mm.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/Kconfig7
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c4
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c10
-rw-r--r--drivers/gpu/host1x/bus.c12
-rw-r--r--drivers/gpu/ipu-v3/Kconfig7
-rw-r--r--drivers/gpu/ipu-v3/Makefile3
-rw-r--r--drivers/gpu/ipu-v3/ipu-common.c1362
-rw-r--r--drivers/gpu/ipu-v3/ipu-dc.c460
-rw-r--r--drivers/gpu/ipu-v3/ipu-di.c730
-rw-r--r--drivers/gpu/ipu-v3/ipu-dmfc.c436
-rw-r--r--drivers/gpu/ipu-v3/ipu-dp.c363
-rw-r--r--drivers/gpu/ipu-v3/ipu-prv.h215
-rw-r--r--drivers/gpu/ipu-v3/ipu-smfc.c97
-rw-r--r--drivers/gpu/vga/vga_switcheroo.c3
396 files changed, 26660 insertions, 9398 deletions
diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile
index d8a22c2a579d..70da9eb52a42 100644
--- a/drivers/gpu/Makefile
+++ b/drivers/gpu/Makefile
@@ -1,2 +1,3 @@
obj-y += drm/ vga/
obj-$(CONFIG_TEGRA_HOST1X) += host1x/
+obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index d1cc2f613a78..f5120046ff80 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -83,6 +83,8 @@ config DRM_KMS_CMA_HELPER
source "drivers/gpu/drm/i2c/Kconfig"
+source "drivers/gpu/drm/bridge/Kconfig"
+
config DRM_TDFX
tristate "3dfx Banshee/Voodoo3+"
depends on DRM && PCI
@@ -199,5 +201,3 @@ source "drivers/gpu/drm/msm/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig"
source "drivers/gpu/drm/panel/Kconfig"
-
-source "drivers/gpu/drm/bridge/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 48e38ba22783..dd2ba4269740 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -14,7 +14,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
drm_info.o drm_debugfs.o drm_encoder_slave.o \
drm_trace_points.o drm_global.o drm_prime.o \
drm_rect.o drm_vma_manager.o drm_flip_work.o \
- drm_plane_helper.o
+ drm_modeset_lock.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
@@ -23,7 +23,8 @@ drm-$(CONFIG_DRM_PANEL) += drm_panel.o
drm-usb-y := drm_usb.o
-drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o
+drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
+ drm_plane_helper.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
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index 32982da82694..8ab3cd1a8cdb 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -173,7 +173,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
if (ret)
goto err_kms;
- ret = drm_irq_install(dev);
+ ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
if (ret)
goto err_kms;
@@ -402,7 +402,7 @@ static struct platform_driver armada_drm_platform_driver = {
static int __init armada_drm_init(void)
{
- armada_drm_driver.num_ioctls = DRM_ARRAY_SIZE(armada_ioctls);
+ armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls);
return platform_driver_register(&armada_drm_platform_driver);
}
module_init(armada_drm_init);
diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
index 948cb14c561e..fd166f532ab9 100644
--- a/drivers/gpu/drm/armada/armada_fbdev.c
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -181,10 +181,8 @@ void armada_fbdev_lastclose(struct drm_device *dev)
{
struct armada_private *priv = dev->dev_private;
- drm_modeset_lock_all(dev);
if (priv->fbdev)
- drm_fb_helper_restore_fbdev_mode(priv->fbdev);
- drm_modeset_unlock_all(dev);
+ drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
}
void armada_fbdev_fini(struct drm_device *dev)
diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
index 887816f43476..bb9b642d8485 100644
--- a/drivers/gpu/drm/armada/armada_gem.c
+++ b/drivers/gpu/drm/armada/armada_gem.c
@@ -433,7 +433,6 @@ armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
if (dobj->obj.filp) {
struct address_space *mapping;
- gfp_t gfp;
int count;
count = dobj->obj.size / PAGE_SIZE;
@@ -441,12 +440,11 @@ armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
goto free_sgt;
mapping = file_inode(dobj->obj.filp)->i_mapping;
- gfp = mapping_gfp_mask(mapping);
for_each_sg(sgt->sgl, sg, count, i) {
struct page *page;
- page = shmem_read_mapping_page_gfp(mapping, i, gfp);
+ page = shmem_read_mapping_page(mapping, i);
if (IS_ERR(page)) {
num = i;
goto release;
diff --git a/drivers/gpu/drm/ast/Makefile b/drivers/gpu/drm/ast/Makefile
index 8df4f284ee24..171aa0622b66 100644
--- a/drivers/gpu/drm/ast/Makefile
+++ b/drivers/gpu/drm/ast/Makefile
@@ -4,6 +4,6 @@
ccflags-y := -Iinclude/drm
-ast-y := ast_drv.o ast_main.o ast_mode.o ast_fb.o ast_ttm.o ast_post.o
+ast-y := ast_drv.o ast_main.o ast_mode.o ast_fb.o ast_ttm.o ast_post.o ast_dp501.o
-obj-$(CONFIG_DRM_AST) := ast.o \ No newline at end of file
+obj-$(CONFIG_DRM_AST) := ast.o
diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c
new file mode 100644
index 000000000000..5da4b62285fa
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_dp501.c
@@ -0,0 +1,410 @@
+
+#include <linux/firmware.h>
+#include <drm/drmP.h>
+#include "ast_drv.h"
+MODULE_FIRMWARE("ast_dp501_fw.bin");
+
+int ast_load_dp501_microcode(struct drm_device *dev)
+{
+ struct ast_private *ast = dev->dev_private;
+ static char *fw_name = "ast_dp501_fw.bin";
+ int err;
+ err = request_firmware(&ast->dp501_fw, fw_name, dev->dev);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static void send_ack(struct ast_private *ast)
+{
+ u8 sendack;
+ sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff);
+ sendack |= 0x80;
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack);
+}
+
+static void send_nack(struct ast_private *ast)
+{
+ u8 sendack;
+ sendack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0xff);
+ sendack &= ~0x80;
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, 0x00, sendack);
+}
+
+static bool wait_ack(struct ast_private *ast)
+{
+ u8 waitack;
+ u32 retry = 0;
+ do {
+ waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
+ waitack &= 0x80;
+ udelay(100);
+ } while ((!waitack) && (retry++ < 1000));
+
+ if (retry < 1000)
+ return true;
+ else
+ return false;
+}
+
+static bool wait_nack(struct ast_private *ast)
+{
+ u8 waitack;
+ u32 retry = 0;
+ do {
+ waitack = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
+ waitack &= 0x80;
+ udelay(100);
+ } while ((waitack) && (retry++ < 1000));
+
+ if (retry < 1000)
+ return true;
+ else
+ return false;
+}
+
+static void set_cmd_trigger(struct ast_private *ast)
+{
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x40);
+}
+
+static void clear_cmd_trigger(struct ast_private *ast)
+{
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9b, ~0x40, 0x00);
+}
+
+#if 0
+static bool wait_fw_ready(struct ast_private *ast)
+{
+ u8 waitready;
+ u32 retry = 0;
+ do {
+ waitready = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd2, 0xff);
+ waitready &= 0x40;
+ udelay(100);
+ } while ((!waitready) && (retry++ < 1000));
+
+ if (retry < 1000)
+ return true;
+ else
+ return false;
+}
+#endif
+
+static bool ast_write_cmd(struct drm_device *dev, u8 data)
+{
+ struct ast_private *ast = dev->dev_private;
+ int retry = 0;
+ if (wait_nack(ast)) {
+ send_nack(ast);
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data);
+ send_ack(ast);
+ set_cmd_trigger(ast);
+ do {
+ if (wait_ack(ast)) {
+ clear_cmd_trigger(ast);
+ send_nack(ast);
+ return true;
+ }
+ } while (retry++ < 100);
+ }
+ clear_cmd_trigger(ast);
+ send_nack(ast);
+ return false;
+}
+
+static bool ast_write_data(struct drm_device *dev, u8 data)
+{
+ struct ast_private *ast = dev->dev_private;
+
+ if (wait_nack(ast)) {
+ send_nack(ast);
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, data);
+ send_ack(ast);
+ if (wait_ack(ast)) {
+ send_nack(ast);
+ return true;
+ }
+ }
+ send_nack(ast);
+ return false;
+}
+
+#if 0
+static bool ast_read_data(struct drm_device *dev, u8 *data)
+{
+ struct ast_private *ast = dev->dev_private;
+ u8 tmp;
+
+ *data = 0;
+
+ if (wait_ack(ast) == false)
+ return false;
+ tmp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd3, 0xff);
+ *data = tmp;
+ if (wait_nack(ast) == false) {
+ send_nack(ast);
+ return false;
+ }
+ send_nack(ast);
+ return true;
+}
+
+static void clear_cmd(struct ast_private *ast)
+{
+ send_nack(ast);
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x9a, 0x00, 0x00);
+}
+#endif
+
+void ast_set_dp501_video_output(struct drm_device *dev, u8 mode)
+{
+ ast_write_cmd(dev, 0x40);
+ ast_write_data(dev, mode);
+
+ msleep(10);
+}
+
+static u32 get_fw_base(struct ast_private *ast)
+{
+ return ast_mindwm(ast, 0x1e6e2104) & 0x7fffffff;
+}
+
+bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size)
+{
+ struct ast_private *ast = dev->dev_private;
+ u32 i, data;
+ u32 boot_address;
+
+ data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
+ if (data) {
+ boot_address = get_fw_base(ast);
+ for (i = 0; i < size; i += 4)
+ *(u32 *)(addr + i) = ast_mindwm(ast, boot_address + i);
+ return true;
+ }
+ return false;
+}
+
+bool ast_launch_m68k(struct drm_device *dev)
+{
+ struct ast_private *ast = dev->dev_private;
+ u32 i, data, len = 0;
+ u32 boot_address;
+ u8 *fw_addr = NULL;
+ u8 jreg;
+
+ data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
+ if (!data) {
+
+ if (ast->dp501_fw_addr) {
+ fw_addr = ast->dp501_fw_addr;
+ len = 32*1024;
+ } else if (ast->dp501_fw) {
+ fw_addr = (u8 *)ast->dp501_fw->data;
+ len = ast->dp501_fw->size;
+ }
+ /* Get BootAddress */
+ ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8);
+ data = ast_mindwm(ast, 0x1e6e0004);
+ switch (data & 0x03) {
+ case 0:
+ boot_address = 0x44000000;
+ break;
+ default:
+ case 1:
+ boot_address = 0x48000000;
+ break;
+ case 2:
+ boot_address = 0x50000000;
+ break;
+ case 3:
+ boot_address = 0x60000000;
+ break;
+ }
+ boot_address -= 0x200000; /* -2MB */
+
+ /* copy image to buffer */
+ for (i = 0; i < len; i += 4) {
+ data = *(u32 *)(fw_addr + i);
+ ast_moutdwm(ast, boot_address + i, data);
+ }
+
+ /* Init SCU */
+ ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8);
+
+ /* Launch FW */
+ ast_moutdwm(ast, 0x1e6e2104, 0x80000000 + boot_address);
+ ast_moutdwm(ast, 0x1e6e2100, 1);
+
+ /* Update Scratch */
+ data = ast_mindwm(ast, 0x1e6e2040) & 0xfffff1ff; /* D[11:9] = 100b: UEFI handling */
+ data |= 0x800;
+ ast_moutdwm(ast, 0x1e6e2040, data);
+
+ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xfc); /* D[1:0]: Reserved Video Buffer */
+ jreg |= 0x02;
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x99, jreg);
+ }
+ return true;
+}
+
+u8 ast_get_dp501_max_clk(struct drm_device *dev)
+{
+ struct ast_private *ast = dev->dev_private;
+ u32 boot_address, offset, data;
+ u8 linkcap[4], linkrate, linklanes, maxclk = 0xff;
+
+ boot_address = get_fw_base(ast);
+
+ /* validate FW version */
+ offset = 0xf000;
+ data = ast_mindwm(ast, boot_address + offset);
+ if ((data & 0xf0) != 0x10) /* version: 1x */
+ return maxclk;
+
+ /* Read Link Capability */
+ offset = 0xf014;
+ *(u32 *)linkcap = ast_mindwm(ast, boot_address + offset);
+ if (linkcap[2] == 0) {
+ linkrate = linkcap[0];
+ linklanes = linkcap[1];
+ data = (linkrate == 0x0a) ? (90 * linklanes) : (54 * linklanes);
+ if (data > 0xff)
+ data = 0xff;
+ maxclk = (u8)data;
+ }
+ return maxclk;
+}
+
+bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata)
+{
+ struct ast_private *ast = dev->dev_private;
+ u32 i, boot_address, offset, data;
+
+ boot_address = get_fw_base(ast);
+
+ /* validate FW version */
+ offset = 0xf000;
+ data = ast_mindwm(ast, boot_address + offset);
+ if ((data & 0xf0) != 0x10)
+ return false;
+
+ /* validate PnP Monitor */
+ offset = 0xf010;
+ data = ast_mindwm(ast, boot_address + offset);
+ if (!(data & 0x01))
+ return false;
+
+ /* Read EDID */
+ offset = 0xf020;
+ for (i = 0; i < 128; i += 4) {
+ data = ast_mindwm(ast, boot_address + offset + i);
+ *(u32 *)(ediddata + i) = data;
+ }
+
+ return true;
+}
+
+static bool ast_init_dvo(struct drm_device *dev)
+{
+ struct ast_private *ast = dev->dev_private;
+ u8 jreg;
+ u32 data;
+ ast_write32(ast, 0xf004, 0x1e6e0000);
+ ast_write32(ast, 0xf000, 0x1);
+ ast_write32(ast, 0x12000, 0x1688a8a8);
+
+ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
+ if (!(jreg & 0x80)) {
+ /* Init SCU DVO Settings */
+ data = ast_read32(ast, 0x12008);
+ /* delay phase */
+ data &= 0xfffff8ff;
+ data |= 0x00000500;
+ ast_write32(ast, 0x12008, data);
+
+ if (ast->chip == AST2300) {
+ data = ast_read32(ast, 0x12084);
+ /* multi-pins for DVO single-edge */
+ data |= 0xfffe0000;
+ ast_write32(ast, 0x12084, data);
+
+ data = ast_read32(ast, 0x12088);
+ /* multi-pins for DVO single-edge */
+ data |= 0x000fffff;
+ ast_write32(ast, 0x12088, data);
+
+ data = ast_read32(ast, 0x12090);
+ /* multi-pins for DVO single-edge */
+ data &= 0xffffffcf;
+ data |= 0x00000020;
+ ast_write32(ast, 0x12090, data);
+ } else { /* AST2400 */
+ data = ast_read32(ast, 0x12088);
+ /* multi-pins for DVO single-edge */
+ data |= 0x30000000;
+ ast_write32(ast, 0x12088, data);
+
+ data = ast_read32(ast, 0x1208c);
+ /* multi-pins for DVO single-edge */
+ data |= 0x000000cf;
+ ast_write32(ast, 0x1208c, data);
+
+ data = ast_read32(ast, 0x120a4);
+ /* multi-pins for DVO single-edge */
+ data |= 0xffff0000;
+ ast_write32(ast, 0x120a4, data);
+
+ data = ast_read32(ast, 0x120a8);
+ /* multi-pins for DVO single-edge */
+ data |= 0x0000000f;
+ ast_write32(ast, 0x120a8, data);
+
+ data = ast_read32(ast, 0x12094);
+ /* multi-pins for DVO single-edge */
+ data |= 0x00000002;
+ ast_write32(ast, 0x12094, data);
+ }
+ }
+
+ /* Force to DVO */
+ data = ast_read32(ast, 0x1202c);
+ data &= 0xfffbffff;
+ ast_write32(ast, 0x1202c, data);
+
+ /* Init VGA DVO Settings */
+ ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80);
+ return true;
+}
+
+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) {
+ case 0x04:
+ ast_init_dvo(dev);
+ break;
+ case 0x08:
+ ast_launch_m68k(dev);
+ break;
+ case 0x0c:
+ ast_init_dvo(dev);
+ break;
+ 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);
+ }
+ }
+ }
+}
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
index 5137f15dba19..44074fbcf7ff 100644
--- a/drivers/gpu/drm/ast/ast_drv.c
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -94,9 +94,7 @@ static int ast_drm_thaw(struct drm_device *dev)
ast_post_gpu(dev);
drm_mode_config_reset(dev);
- drm_modeset_lock_all(dev);
drm_helper_resume_force_mode(dev);
- drm_modeset_unlock_all(dev);
console_lock();
ast_fbdev_set_suspend(dev, 0);
@@ -198,7 +196,6 @@ static const struct file_operations ast_fops = {
static struct drm_driver driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM,
- .dev_priv_size = 0,
.load = ast_driver_load,
.unload = ast_driver_unload,
diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h
index 9833a1b1acc1..5d6a87573c33 100644
--- a/drivers/gpu/drm/ast/ast_drv.h
+++ b/drivers/gpu/drm/ast/ast_drv.h
@@ -61,9 +61,17 @@ enum ast_chip {
AST2200,
AST2150,
AST2300,
+ AST2400,
AST1180,
};
+enum ast_tx_chip {
+ AST_TX_NONE,
+ AST_TX_SIL164,
+ AST_TX_ITE66121,
+ AST_TX_DP501,
+};
+
#define AST_DRAM_512Mx16 0
#define AST_DRAM_1Gx16 1
#define AST_DRAM_512Mx32 2
@@ -102,6 +110,12 @@ struct ast_private {
* we have. */
struct ttm_bo_kmap_obj cache_kmap;
int next_cursor;
+ bool support_wide_screen;
+
+ enum ast_tx_chip tx_chip_type;
+ u8 dp501_maxclk;
+ u8 *dp501_fw_addr;
+ const struct firmware *dp501_fw; /* dp501 fw */
};
int ast_driver_load(struct drm_device *dev, unsigned long flags);
@@ -368,4 +382,14 @@ int ast_mmap(struct file *filp, struct vm_area_struct *vma);
/* ast post */
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);
+/* ast dp501 */
+int ast_load_dp501_microcode(struct drm_device *dev);
+void ast_set_dp501_video_output(struct drm_device *dev, u8 mode);
+bool ast_launch_m68k(struct drm_device *dev);
+bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size);
+bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata);
+u8 ast_get_dp501_max_clk(struct drm_device *dev);
+void ast_init_3rdtx(struct drm_device *dev);
#endif
diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
index 50535fd5a88d..a2cc6be97983 100644
--- a/drivers/gpu/drm/ast/ast_main.c
+++ b/drivers/gpu/drm/ast/ast_main.c
@@ -66,12 +66,16 @@ uint8_t ast_get_index_reg_mask(struct ast_private *ast,
static int ast_detect_chip(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
+ uint32_t data, jreg;
if (dev->pdev->device == PCI_CHIP_AST1180) {
ast->chip = AST1100;
DRM_INFO("AST 1180 detected\n");
} else {
- if (dev->pdev->revision >= 0x20) {
+ if (dev->pdev->revision >= 0x30) {
+ ast->chip = AST2400;
+ DRM_INFO("AST 2400 detected\n");
+ } else if (dev->pdev->revision >= 0x20) {
ast->chip = AST2300;
DRM_INFO("AST 2300 detected\n");
} else if (dev->pdev->revision >= 0x10) {
@@ -104,6 +108,59 @@ static int ast_detect_chip(struct drm_device *dev)
DRM_INFO("AST 2000 detected\n");
}
}
+
+ switch (ast->chip) {
+ case AST1180:
+ ast->support_wide_screen = true;
+ break;
+ case AST2000:
+ ast->support_wide_screen = false;
+ break;
+ default:
+ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff);
+ if (!(jreg & 0x80))
+ ast->support_wide_screen = true;
+ else if (jreg & 0x01)
+ ast->support_wide_screen = true;
+ else {
+ ast->support_wide_screen = false;
+ ast_write32(ast, 0xf004, 0x1e6e0000);
+ ast_write32(ast, 0xf000, 0x1);
+ data = ast_read32(ast, 0x1207c);
+ data &= 0x300;
+ if (ast->chip == AST2300 && data == 0x0) /* ast1300 */
+ ast->support_wide_screen = true;
+ if (ast->chip == AST2400 && data == 0x100) /* ast1400 */
+ ast->support_wide_screen = true;
+ }
+ break;
+ }
+
+ 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;
+ if ((ast->chip == AST2300) || (ast->chip == AST2400)) {
+ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff);
+ switch (jreg) {
+ case 0x04:
+ ast->tx_chip_type = AST_TX_SIL164;
+ break;
+ case 0x08:
+ ast->dp501_fw_addr = kzalloc(32*1024, GFP_KERNEL);
+ if (ast->dp501_fw_addr) {
+ /* backup firmware */
+ if (ast_backup_fw(dev, ast->dp501_fw_addr, 32*1024)) {
+ kfree(ast->dp501_fw_addr);
+ ast->dp501_fw_addr = NULL;
+ }
+ }
+ /* fallthrough */
+ case 0x0c:
+ ast->tx_chip_type = AST_TX_DP501;
+ }
+ }
+
return 0;
}
@@ -129,7 +186,7 @@ static int ast_get_dram_info(struct drm_device *dev)
else
ast->dram_bus_width = 32;
- if (ast->chip == AST2300) {
+ if (ast->chip == AST2300 || ast->chip == AST2400) {
switch (data & 0x03) {
case 0:
ast->dram_type = AST_DRAM_512Mx16;
@@ -257,17 +314,32 @@ static u32 ast_get_vram_info(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
u8 jreg;
-
+ u32 vram_size;
ast_open_key(ast);
+ vram_size = AST_VIDMEM_DEFAULT_SIZE;
jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff);
switch (jreg & 3) {
- case 0: return AST_VIDMEM_SIZE_8M;
- case 1: return AST_VIDMEM_SIZE_16M;
- case 2: return AST_VIDMEM_SIZE_32M;
- case 3: return AST_VIDMEM_SIZE_64M;
+ case 0: vram_size = AST_VIDMEM_SIZE_8M; break;
+ case 1: vram_size = AST_VIDMEM_SIZE_16M; break;
+ case 2: vram_size = AST_VIDMEM_SIZE_32M; break;
+ case 3: vram_size = AST_VIDMEM_SIZE_64M; break;
+ }
+
+ jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xff);
+ switch (jreg & 0x03) {
+ case 1:
+ vram_size -= 0x100000;
+ break;
+ case 2:
+ vram_size -= 0x200000;
+ break;
+ case 3:
+ vram_size -= 0x400000;
+ break;
}
- return AST_VIDMEM_DEFAULT_SIZE;
+
+ return vram_size;
}
int ast_driver_load(struct drm_device *dev, unsigned long flags)
@@ -316,6 +388,7 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags)
if (ast->chip == AST2100 ||
ast->chip == AST2200 ||
ast->chip == AST2300 ||
+ ast->chip == AST2400 ||
ast->chip == AST1180) {
dev->mode_config.max_width = 1920;
dev->mode_config.max_height = 2048;
@@ -343,6 +416,7 @@ int ast_driver_unload(struct drm_device *dev)
{
struct ast_private *ast = dev->dev_private;
+ kfree(ast->dp501_fw_addr);
ast_mode_fini(dev);
ast_fbdev_fini(dev);
drm_mode_config_cleanup(dev);
@@ -411,16 +485,13 @@ static void ast_bo_unref(struct ast_bo **bo)
tbo = &((*bo)->bo);
ttm_bo_unref(&tbo);
- if (tbo == NULL)
- *bo = NULL;
-
+ *bo = NULL;
}
+
void ast_gem_free_object(struct drm_gem_object *obj)
{
struct ast_bo *ast_bo = gem_to_ast_bo(obj);
- if (!ast_bo)
- return;
ast_bo_unref(&ast_bo);
}
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index a4afdc8bb578..114aee941d46 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -115,11 +115,17 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo
else
vbios_mode->enh_table = &res_1280x1024[refresh_rate_index];
break;
+ case 1360:
+ vbios_mode->enh_table = &res_1360x768[refresh_rate_index];
+ break;
case 1440:
vbios_mode->enh_table = &res_1440x900[refresh_rate_index];
break;
case 1600:
- vbios_mode->enh_table = &res_1600x1200[refresh_rate_index];
+ if (crtc->mode.crtc_vdisplay == 900)
+ vbios_mode->enh_table = &res_1600x900[refresh_rate_index];
+ else
+ vbios_mode->enh_table = &res_1600x1200[refresh_rate_index];
break;
case 1680:
vbios_mode->enh_table = &res_1680x1050[refresh_rate_index];
@@ -175,14 +181,17 @@ static bool ast_get_vbios_mode_info(struct drm_crtc *crtc, struct drm_display_mo
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8d, refresh_rate_index & 0xff);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x8e, mode_id & 0xff);
- ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8);
- ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->primary->fb->bits_per_pixel);
- ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000);
- ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay);
- ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8);
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0x00);
+ if (vbios_mode->enh_table->flags & NewModeInfo) {
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x91, 0xa8);
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x92, crtc->primary->fb->bits_per_pixel);
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x93, adjusted_mode->clock / 1000);
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x94, adjusted_mode->crtc_hdisplay);
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x95, adjusted_mode->crtc_hdisplay >> 8);
- ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x96, adjusted_mode->crtc_vdisplay);
- ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x97, adjusted_mode->crtc_vdisplay >> 8);
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x96, adjusted_mode->crtc_vdisplay);
+ ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x97, adjusted_mode->crtc_vdisplay >> 8);
+ }
}
return true;
@@ -389,7 +398,7 @@ static void ast_set_ext_reg(struct drm_crtc *crtc, struct drm_display_mode *mode
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa8, 0xfd, jregA8);
/* Set Threshold */
- if (ast->chip == AST2300) {
+ if (ast->chip == AST2300 || ast->chip == AST2400) {
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa7, 0x78);
ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xa6, 0x60);
} else if (ast->chip == AST2100 ||
@@ -451,9 +460,13 @@ static void ast_crtc_dpms(struct drm_crtc *crtc, int mode)
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0);
+ if (ast->tx_chip_type == AST_TX_DP501)
+ ast_set_dp501_video_output(crtc->dev, 1);
ast_crtc_load_lut(crtc);
break;
case DRM_MODE_DPMS_OFF:
+ if (ast->tx_chip_type == AST_TX_DP501)
+ ast_set_dp501_video_output(crtc->dev, 0);
ast_set_index_reg_mask(ast, AST_IO_SEQ_PORT, 0x1, 0xdf, 0x20);
break;
}
@@ -729,10 +742,24 @@ static int ast_encoder_init(struct drm_device *dev)
static int ast_get_modes(struct drm_connector *connector)
{
struct ast_connector *ast_connector = to_ast_connector(connector);
+ struct ast_private *ast = connector->dev->dev_private;
struct edid *edid;
int ret;
-
- edid = drm_get_edid(connector, &ast_connector->i2c->adapter);
+ bool flags = false;
+ if (ast->tx_chip_type == AST_TX_DP501) {
+ ast->dp501_maxclk = 0xff;
+ edid = kmalloc(128, GFP_KERNEL);
+ if (!edid)
+ return -ENOMEM;
+
+ flags = ast_dp501_read_edid(connector->dev, (u8 *)edid);
+ if (flags)
+ ast->dp501_maxclk = ast_get_dp501_max_clk(connector->dev);
+ else
+ kfree(edid);
+ }
+ if (!flags)
+ edid = drm_get_edid(connector, &ast_connector->i2c->adapter);
if (edid) {
drm_mode_connector_update_edid_property(&ast_connector->base, edid);
ret = drm_add_edid_modes(connector, edid);
@@ -746,7 +773,56 @@ static int ast_get_modes(struct drm_connector *connector)
static int ast_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- return MODE_OK;
+ struct ast_private *ast = connector->dev->dev_private;
+ int flags = MODE_NOMODE;
+ uint32_t jtemp;
+
+ if (ast->support_wide_screen) {
+ if ((mode->hdisplay == 1680) && (mode->vdisplay == 1050))
+ return MODE_OK;
+ if ((mode->hdisplay == 1280) && (mode->vdisplay == 800))
+ return MODE_OK;
+ if ((mode->hdisplay == 1440) && (mode->vdisplay == 900))
+ return MODE_OK;
+ if ((mode->hdisplay == 1360) && (mode->vdisplay == 768))
+ return MODE_OK;
+ if ((mode->hdisplay == 1600) && (mode->vdisplay == 900))
+ return MODE_OK;
+
+ if ((ast->chip == AST2100) || (ast->chip == AST2200) || (ast->chip == AST2300) || (ast->chip == AST2400) || (ast->chip == AST1180)) {
+ if ((mode->hdisplay == 1920) && (mode->vdisplay == 1080))
+ return MODE_OK;
+
+ if ((mode->hdisplay == 1920) && (mode->vdisplay == 1200)) {
+ jtemp = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd1, 0xff);
+ if (jtemp & 0x01)
+ return MODE_NOMODE;
+ else
+ return MODE_OK;
+ }
+ }
+ }
+ switch (mode->hdisplay) {
+ case 640:
+ if (mode->vdisplay == 480) flags = MODE_OK;
+ break;
+ case 800:
+ if (mode->vdisplay == 600) flags = MODE_OK;
+ break;
+ case 1024:
+ if (mode->vdisplay == 768) flags = MODE_OK;
+ break;
+ case 1280:
+ if (mode->vdisplay == 1024) flags = MODE_OK;
+ break;
+ case 1600:
+ if (mode->vdisplay == 1200) flags = MODE_OK;
+ break;
+ default:
+ return flags;
+ }
+
+ return flags;
}
static void ast_connector_destroy(struct drm_connector *connector)
diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c
index 635f6ffc27c2..38d437f3a267 100644
--- a/drivers/gpu/drm/ast/ast_post.c
+++ b/drivers/gpu/drm/ast/ast_post.c
@@ -78,7 +78,7 @@ ast_set_def_ext_reg(struct drm_device *dev)
for (i = 0x81; i <= 0x8f; i++)
ast_set_index_reg(ast, AST_IO_CRTC_PORT, i, 0x00);
- if (ast->chip == AST2300) {
+ if (ast->chip == AST2300 || ast->chip == AST2400) {
if (dev->pdev->revision >= 0x20)
ext_reg_info = extreginfo_ast2300;
else
@@ -102,23 +102,32 @@ ast_set_def_ext_reg(struct drm_device *dev)
/* Enable RAMDAC for A1 */
reg = 0x04;
- if (ast->chip == AST2300)
+ if (ast->chip == AST2300 || ast->chip == AST2400)
reg |= 0x20;
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff, reg);
}
-static inline u32 mindwm(struct ast_private *ast, u32 r)
+u32 ast_mindwm(struct ast_private *ast, u32 r)
{
+ uint32_t data;
+
ast_write32(ast, 0xf004, r & 0xffff0000);
ast_write32(ast, 0xf000, 0x1);
+ do {
+ data = ast_read32(ast, 0xf004) & 0xffff0000;
+ } while (data != (r & 0xffff0000));
return ast_read32(ast, 0x10000 + (r & 0x0000ffff));
}
-static inline void moutdwm(struct ast_private *ast, u32 r, u32 v)
+void ast_moutdwm(struct ast_private *ast, u32 r, u32 v)
{
+ uint32_t data;
ast_write32(ast, 0xf004, r & 0xffff0000);
ast_write32(ast, 0xf000, 0x1);
+ do {
+ data = ast_read32(ast, 0xf004) & 0xffff0000;
+ } while (data != (r & 0xffff0000));
ast_write32(ast, 0x10000 + (r & 0x0000ffff), v);
}
@@ -154,28 +163,28 @@ static u32 mmctestburst2_ast2150(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
- moutdwm(ast, 0x1e6e0070, 0x00000000);
- moutdwm(ast, 0x1e6e0070, 0x00000001 | (datagen << 3));
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000001 | (datagen << 3));
timeout = 0;
do {
- data = mindwm(ast, 0x1e6e0070) & 0x40;
+ data = ast_mindwm(ast, 0x1e6e0070) & 0x40;
if (++timeout > TIMEOUT_AST2150) {
- moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
return 0xffffffff;
}
} while (!data);
- moutdwm(ast, 0x1e6e0070, 0x00000000);
- moutdwm(ast, 0x1e6e0070, 0x00000003 | (datagen << 3));
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000003 | (datagen << 3));
timeout = 0;
do {
- data = mindwm(ast, 0x1e6e0070) & 0x40;
+ data = ast_mindwm(ast, 0x1e6e0070) & 0x40;
if (++timeout > TIMEOUT_AST2150) {
- moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
return 0xffffffff;
}
} while (!data);
- data = (mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
- moutdwm(ast, 0x1e6e0070, 0x00000000);
+ data = (ast_mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
return data;
}
@@ -184,18 +193,18 @@ static u32 mmctestsingle2_ast2150(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
- moutdwm(ast, 0x1e6e0070, 0x00000000);
- moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
timeout = 0;
do {
- data = mindwm(ast, 0x1e6e0070) & 0x40;
+ data = ast_mindwm(ast, 0x1e6e0070) & 0x40;
if (++timeout > TIMEOUT_AST2150) {
- moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
return 0xffffffff;
}
} while (!data);
- data = (mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
- moutdwm(ast, 0x1e6e0070, 0x00000000);
+ data = (ast_mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
return data;
}
#endif
@@ -215,7 +224,7 @@ static int cbrscan_ast2150(struct ast_private *ast, int busw)
u32 patcnt, loop;
for (patcnt = 0; patcnt < CBR_PATNUM_AST2150; patcnt++) {
- moutdwm(ast, 0x1e6e007c, pattern_AST2150[patcnt]);
+ ast_moutdwm(ast, 0x1e6e007c, pattern_AST2150[patcnt]);
for (loop = 0; loop < CBR_PASSNUM_AST2150; loop++) {
if (cbrtest_ast2150(ast))
break;
@@ -237,7 +246,7 @@ cbr_start:
passcnt = 0;
for (dlli = 0; dlli < 100; dlli++) {
- moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
data = cbrscan_ast2150(ast, busw);
if (data != 0) {
if (data & 0x1) {
@@ -254,7 +263,7 @@ cbr_start:
goto cbr_start;
dlli = dll_min[0] + (((dll_max[0] - dll_min[0]) * 7) >> 4);
- moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
}
@@ -365,10 +374,12 @@ void ast_post_gpu(struct drm_device *dev)
ast_open_key(ast);
ast_set_def_ext_reg(dev);
- if (ast->chip == AST2300)
+ if (ast->chip == AST2300 || ast->chip == AST2400)
ast_init_dram_2300(dev);
else
ast_init_dram_reg(dev);
+
+ ast_init_3rdtx(dev);
}
/* AST 2300 DRAM settings */
@@ -403,6 +414,7 @@ struct ast2300_dram_param {
/*
* DQSI DLL CBR Setting
*/
+#define CBR_SIZE0 ((1 << 10) - 1)
#define CBR_SIZE1 ((4 << 10) - 1)
#define CBR_SIZE2 ((64 << 10) - 1)
#define CBR_PASSNUM 5
@@ -423,88 +435,84 @@ static const u32 pattern[8] = {
0x7C61D253
};
-#if 0 /* unused in DDX, included for completeness */
static int mmc_test_burst(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
- moutdwm(ast, 0x1e6e0070, 0x00000000);
- moutdwm(ast, 0x1e6e0070, 0x000000c1 | (datagen << 3));
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x000000c1 | (datagen << 3));
timeout = 0;
do {
- data = mindwm(ast, 0x1e6e0070) & 0x3000;
+ data = ast_mindwm(ast, 0x1e6e0070) & 0x3000;
if (data & 0x2000) {
return 0;
}
if (++timeout > TIMEOUT) {
- moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
return 0;
}
} while (!data);
- moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
return 1;
}
-#endif
static int mmc_test_burst2(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
- moutdwm(ast, 0x1e6e0070, 0x00000000);
- moutdwm(ast, 0x1e6e0070, 0x00000041 | (datagen << 3));
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000041 | (datagen << 3));
timeout = 0;
do {
- data = mindwm(ast, 0x1e6e0070) & 0x1000;
+ data = ast_mindwm(ast, 0x1e6e0070) & 0x1000;
if (++timeout > TIMEOUT) {
- moutdwm(ast, 0x1e6e0070, 0x0);
+ ast_moutdwm(ast, 0x1e6e0070, 0x0);
return -1;
}
} while (!data);
- data = mindwm(ast, 0x1e6e0078);
+ data = ast_mindwm(ast, 0x1e6e0078);
data = (data | (data >> 16)) & 0xffff;
- moutdwm(ast, 0x1e6e0070, 0x0);
+ ast_moutdwm(ast, 0x1e6e0070, 0x0);
return data;
}
-#if 0 /* Unused in DDX here for completeness */
static int mmc_test_single(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
- moutdwm(ast, 0x1e6e0070, 0x00000000);
- moutdwm(ast, 0x1e6e0070, 0x000000c5 | (datagen << 3));
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x000000c5 | (datagen << 3));
timeout = 0;
do {
- data = mindwm(ast, 0x1e6e0070) & 0x3000;
+ data = ast_mindwm(ast, 0x1e6e0070) & 0x3000;
if (data & 0x2000)
return 0;
if (++timeout > TIMEOUT) {
- moutdwm(ast, 0x1e6e0070, 0x0);
+ ast_moutdwm(ast, 0x1e6e0070, 0x0);
return 0;
}
} while (!data);
- moutdwm(ast, 0x1e6e0070, 0x0);
+ ast_moutdwm(ast, 0x1e6e0070, 0x0);
return 1;
}
-#endif
static int mmc_test_single2(struct ast_private *ast, u32 datagen)
{
u32 data, timeout;
- moutdwm(ast, 0x1e6e0070, 0x00000000);
- moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, 0x1e6e0070, 0x00000005 | (datagen << 3));
timeout = 0;
do {
- data = mindwm(ast, 0x1e6e0070) & 0x1000;
+ data = ast_mindwm(ast, 0x1e6e0070) & 0x1000;
if (++timeout > TIMEOUT) {
- moutdwm(ast, 0x1e6e0070, 0x0);
+ ast_moutdwm(ast, 0x1e6e0070, 0x0);
return -1;
}
} while (!data);
- data = mindwm(ast, 0x1e6e0078);
+ data = ast_mindwm(ast, 0x1e6e0078);
data = (data | (data >> 16)) & 0xffff;
- moutdwm(ast, 0x1e6e0070, 0x0);
+ ast_moutdwm(ast, 0x1e6e0070, 0x0);
return data;
}
@@ -533,7 +541,7 @@ static int cbr_scan(struct ast_private *ast)
data2 = 3;
for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
- moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
+ ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
for (loop = 0; loop < CBR_PASSNUM2; loop++) {
if ((data = cbr_test(ast)) != 0) {
data2 &= data;
@@ -568,7 +576,7 @@ static u32 cbr_scan2(struct ast_private *ast)
data2 = 0xffff;
for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
- moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
+ ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
for (loop = 0; loop < CBR_PASSNUM2; loop++) {
if ((data = cbr_test2(ast)) != 0) {
data2 &= data;
@@ -583,106 +591,35 @@ static u32 cbr_scan2(struct ast_private *ast)
return data2;
}
-#if 0 /* unused in DDX - added for completeness */
-static void finetuneDQI(struct ast_private *ast, struct ast2300_dram_param *param)
+static u32 cbr_test3(struct ast_private *ast)
{
- u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt;
-
- gold_sadj[0] = (mindwm(ast, 0x1E6E0024) >> 16) & 0xffff;
- gold_sadj[1] = gold_sadj[0] >> 8;
- gold_sadj[0] = gold_sadj[0] & 0xff;
- gold_sadj[0] = (gold_sadj[0] + gold_sadj[1]) >> 1;
- gold_sadj[1] = gold_sadj[0];
-
- for (cnt = 0; cnt < 16; cnt++) {
- dllmin[cnt] = 0xff;
- dllmax[cnt] = 0x0;
- }
- passcnt = 0;
- for (dlli = 0; dlli < 76; dlli++) {
- moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
- /* Wait DQSI latch phase calibration */
- moutdwm(ast, 0x1E6E0074, 0x00000010);
- moutdwm(ast, 0x1E6E0070, 0x00000003);
- do {
- data = mindwm(ast, 0x1E6E0070);
- } while (!(data & 0x00001000));
- moutdwm(ast, 0x1E6E0070, 0x00000000);
+ if (!mmc_test_burst(ast, 0))
+ return 0;
+ if (!mmc_test_single(ast, 0))
+ return 0;
+ return 1;
+}
- moutdwm(ast, 0x1E6E0074, CBR_SIZE1);
- data = cbr_scan2(ast);
- if (data != 0) {
- mask = 0x00010001;
- for (cnt = 0; cnt < 16; cnt++) {
- if (data & mask) {
- if (dllmin[cnt] > dlli) {
- dllmin[cnt] = dlli;
- }
- if (dllmax[cnt] < dlli) {
- dllmax[cnt] = dlli;
- }
- }
- mask <<= 1;
- }
- passcnt++;
- } else if (passcnt >= CBR_THRESHOLD) {
- break;
- }
- }
- data = 0;
- for (cnt = 0; cnt < 8; cnt++) {
- data >>= 3;
- if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD)) {
- dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
- if (gold_sadj[0] >= dlli) {
- dlli = (gold_sadj[0] - dlli) >> 1;
- if (dlli > 3) {
- dlli = 3;
- }
- } else {
- dlli = (dlli - gold_sadj[0]) >> 1;
- if (dlli > 4) {
- dlli = 4;
- }
- dlli = (8 - dlli) & 0x7;
- }
- data |= dlli << 21;
- }
- }
- moutdwm(ast, 0x1E6E0080, data);
+static u32 cbr_scan3(struct ast_private *ast)
+{
+ u32 patcnt, loop;
- data = 0;
- for (cnt = 8; cnt < 16; cnt++) {
- data >>= 3;
- if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD)) {
- dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
- if (gold_sadj[1] >= dlli) {
- dlli = (gold_sadj[1] - dlli) >> 1;
- if (dlli > 3) {
- dlli = 3;
- } else {
- dlli = (dlli - 1) & 0x7;
- }
- } else {
- dlli = (dlli - gold_sadj[1]) >> 1;
- dlli += 1;
- if (dlli > 4) {
- dlli = 4;
- }
- dlli = (8 - dlli) & 0x7;
- }
- data |= dlli << 21;
+ for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
+ ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
+ for (loop = 0; loop < 2; loop++) {
+ if (cbr_test3(ast))
+ break;
}
+ if (loop == 2)
+ return 0;
}
- moutdwm(ast, 0x1E6E0084, data);
-
-} /* finetuneDQI */
-#endif
+ return 1;
+}
-static void finetuneDQI_L(struct ast_private *ast, struct ast2300_dram_param *param)
+static bool finetuneDQI_L(struct ast_private *ast, struct ast2300_dram_param *param)
{
- u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt;
-
+ u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt, retry = 0;
+ bool status = false;
FINETUNE_START:
for (cnt = 0; cnt < 16; cnt++) {
dllmin[cnt] = 0xff;
@@ -690,16 +627,8 @@ FINETUNE_START:
}
passcnt = 0;
for (dlli = 0; dlli < 76; dlli++) {
- moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
- /* Wait DQSI latch phase calibration */
- moutdwm(ast, 0x1E6E0074, 0x00000010);
- moutdwm(ast, 0x1E6E0070, 0x00000003);
- do {
- data = mindwm(ast, 0x1E6E0070);
- } while (!(data & 0x00001000));
- moutdwm(ast, 0x1E6E0070, 0x00000000);
-
- moutdwm(ast, 0x1E6E0074, CBR_SIZE1);
+ ast_moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE1);
data = cbr_scan2(ast);
if (data != 0) {
mask = 0x00010001;
@@ -727,9 +656,13 @@ FINETUNE_START:
passcnt++;
}
}
+ if (retry++ > 10)
+ goto FINETUNE_DONE;
if (passcnt != 16) {
goto FINETUNE_START;
}
+ status = true;
+FINETUNE_DONE:
gold_sadj[0] = gold_sadj[0] >> 4;
gold_sadj[1] = gold_sadj[0];
@@ -753,7 +686,7 @@ FINETUNE_START:
data |= dlli << 21;
}
}
- moutdwm(ast, 0x1E6E0080, data);
+ ast_moutdwm(ast, 0x1E6E0080, data);
data = 0;
for (cnt = 8; cnt < 16; cnt++) {
@@ -778,162 +711,116 @@ FINETUNE_START:
data |= dlli << 21;
}
}
- moutdwm(ast, 0x1E6E0084, data);
-
+ ast_moutdwm(ast, 0x1E6E0084, data);
+ return status;
} /* finetuneDQI_L */
-static void finetuneDQI_L2(struct ast_private *ast, struct ast2300_dram_param *param)
+static void finetuneDQSI(struct ast_private *ast)
{
- u32 gold_sadj[2], dllmin[16], dllmax[16], dlli, data, cnt, mask, passcnt, data2;
+ u32 dlli, dqsip, dqidly;
+ u32 reg_mcr18, reg_mcr0c, passcnt[2], diff;
+ u32 g_dqidly, g_dqsip, g_margin, g_side;
+ u16 pass[32][2][2];
+ char tag[2][76];
+
+ /* Disable DQI CBR */
+ reg_mcr0c = ast_mindwm(ast, 0x1E6E000C);
+ reg_mcr18 = ast_mindwm(ast, 0x1E6E0018);
+ reg_mcr18 &= 0x0000ffff;
+ ast_moutdwm(ast, 0x1E6E0018, reg_mcr18);
- for (cnt = 0; cnt < 16; cnt++) {
- dllmin[cnt] = 0xff;
- dllmax[cnt] = 0x0;
- }
- passcnt = 0;
for (dlli = 0; dlli < 76; dlli++) {
- moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
- /* Wait DQSI latch phase calibration */
- moutdwm(ast, 0x1E6E0074, 0x00000010);
- moutdwm(ast, 0x1E6E0070, 0x00000003);
- do {
- data = mindwm(ast, 0x1E6E0070);
- } while (!(data & 0x00001000));
- moutdwm(ast, 0x1E6E0070, 0x00000000);
-
- moutdwm(ast, 0x1E6E0074, CBR_SIZE2);
- data = cbr_scan2(ast);
- if (data != 0) {
- mask = 0x00010001;
- for (cnt = 0; cnt < 16; cnt++) {
- if (data & mask) {
- if (dllmin[cnt] > dlli) {
- dllmin[cnt] = dlli;
- }
- if (dllmax[cnt] < dlli) {
- dllmax[cnt] = dlli;
- }
- }
- mask <<= 1;
- }
- passcnt++;
- } else if (passcnt >= CBR_THRESHOLD2) {
- break;
- }
+ tag[0][dlli] = 0x0;
+ tag[1][dlli] = 0x0;
}
- gold_sadj[0] = 0x0;
- gold_sadj[1] = 0xFF;
- for (cnt = 0; cnt < 8; cnt++) {
- if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
- if (gold_sadj[0] < dllmin[cnt]) {
- gold_sadj[0] = dllmin[cnt];
- }
- if (gold_sadj[1] > dllmax[cnt]) {
- gold_sadj[1] = dllmax[cnt];
- }
- }
+ for (dqidly = 0; dqidly < 32; dqidly++) {
+ pass[dqidly][0][0] = 0xff;
+ pass[dqidly][0][1] = 0x0;
+ pass[dqidly][1][0] = 0xff;
+ pass[dqidly][1][1] = 0x0;
}
- gold_sadj[0] = (gold_sadj[1] + gold_sadj[0]) >> 1;
- gold_sadj[1] = mindwm(ast, 0x1E6E0080);
-
- data = 0;
- for (cnt = 0; cnt < 8; cnt++) {
- data >>= 3;
- data2 = gold_sadj[1] & 0x7;
- gold_sadj[1] >>= 3;
- if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
- dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
- if (gold_sadj[0] >= dlli) {
- dlli = (gold_sadj[0] - dlli) >> 1;
- if (dlli > 0) {
- dlli = 1;
- }
- if (data2 != 3) {
- data2 = (data2 + dlli) & 0x7;
- }
- } else {
- dlli = (dlli - gold_sadj[0]) >> 1;
- if (dlli > 0) {
- dlli = 1;
- }
- if (data2 != 4) {
- data2 = (data2 - dlli) & 0x7;
+ for (dqidly = 0; dqidly < 32; dqidly++) {
+ passcnt[0] = passcnt[1] = 0;
+ for (dqsip = 0; dqsip < 2; dqsip++) {
+ ast_moutdwm(ast, 0x1E6E000C, 0);
+ ast_moutdwm(ast, 0x1E6E0018, reg_mcr18 | (dqidly << 16) | (dqsip << 23));
+ ast_moutdwm(ast, 0x1E6E000C, reg_mcr0c);
+ for (dlli = 0; dlli < 76; dlli++) {
+ ast_moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, 0x1E6E0070, 0);
+ ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE0);
+ if (cbr_scan3(ast)) {
+ if (dlli == 0)
+ break;
+ passcnt[dqsip]++;
+ tag[dqsip][dlli] = 'P';
+ if (dlli < pass[dqidly][dqsip][0])
+ pass[dqidly][dqsip][0] = (u16) dlli;
+ if (dlli > pass[dqidly][dqsip][1])
+ pass[dqidly][dqsip][1] = (u16) dlli;
+ } else if (passcnt[dqsip] >= 5)
+ break;
+ else {
+ pass[dqidly][dqsip][0] = 0xff;
+ pass[dqidly][dqsip][1] = 0x0;
}
}
}
- data |= data2 << 21;
- }
- moutdwm(ast, 0x1E6E0080, data);
-
- gold_sadj[0] = 0x0;
- gold_sadj[1] = 0xFF;
- for (cnt = 8; cnt < 16; cnt++) {
- if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
- if (gold_sadj[0] < dllmin[cnt]) {
- gold_sadj[0] = dllmin[cnt];
- }
- if (gold_sadj[1] > dllmax[cnt]) {
- gold_sadj[1] = dllmax[cnt];
- }
- }
+ if (passcnt[0] == 0 && passcnt[1] == 0)
+ dqidly++;
}
- gold_sadj[0] = (gold_sadj[1] + gold_sadj[0]) >> 1;
- gold_sadj[1] = mindwm(ast, 0x1E6E0084);
-
- data = 0;
- for (cnt = 8; cnt < 16; cnt++) {
- data >>= 3;
- data2 = gold_sadj[1] & 0x7;
- gold_sadj[1] >>= 3;
- if ((dllmax[cnt] > dllmin[cnt]) && ((dllmax[cnt] - dllmin[cnt]) >= CBR_THRESHOLD2)) {
- dlli = (dllmin[cnt] + dllmax[cnt]) >> 1;
- if (gold_sadj[0] >= dlli) {
- dlli = (gold_sadj[0] - dlli) >> 1;
- if (dlli > 0) {
- dlli = 1;
- }
- if (data2 != 3) {
- data2 = (data2 + dlli) & 0x7;
- }
- } else {
- dlli = (dlli - gold_sadj[0]) >> 1;
- if (dlli > 0) {
- dlli = 1;
- }
- if (data2 != 4) {
- data2 = (data2 - dlli) & 0x7;
- }
+ /* Search margin */
+ g_dqidly = g_dqsip = g_margin = g_side = 0;
+
+ for (dqidly = 0; dqidly < 32; dqidly++) {
+ for (dqsip = 0; dqsip < 2; dqsip++) {
+ if (pass[dqidly][dqsip][0] > pass[dqidly][dqsip][1])
+ continue;
+ diff = pass[dqidly][dqsip][1] - pass[dqidly][dqsip][0];
+ if ((diff+2) < g_margin)
+ continue;
+ passcnt[0] = passcnt[1] = 0;
+ for (dlli = pass[dqidly][dqsip][0]; dlli > 0 && tag[dqsip][dlli] != 0; dlli--, passcnt[0]++);
+ for (dlli = pass[dqidly][dqsip][1]; dlli < 76 && tag[dqsip][dlli] != 0; dlli++, passcnt[1]++);
+ if (passcnt[0] > passcnt[1])
+ passcnt[0] = passcnt[1];
+ passcnt[1] = 0;
+ if (passcnt[0] > g_side)
+ passcnt[1] = passcnt[0] - g_side;
+ if (diff > (g_margin+1) && (passcnt[1] > 0 || passcnt[0] > 8)) {
+ g_margin = diff;
+ g_dqidly = dqidly;
+ g_dqsip = dqsip;
+ g_side = passcnt[0];
+ } else if (passcnt[1] > 1 && g_side < 8) {
+ if (diff > g_margin)
+ g_margin = diff;
+ g_dqidly = dqidly;
+ g_dqsip = dqsip;
+ g_side = passcnt[0];
}
}
- data |= data2 << 21;
}
- moutdwm(ast, 0x1E6E0084, data);
-
-} /* finetuneDQI_L2 */
+ reg_mcr18 = reg_mcr18 | (g_dqidly << 16) | (g_dqsip << 23);
+ ast_moutdwm(ast, 0x1E6E0018, reg_mcr18);
-static void cbr_dll2(struct ast_private *ast, struct ast2300_dram_param *param)
+}
+static bool cbr_dll2(struct ast_private *ast, struct ast2300_dram_param *param)
{
- u32 dllmin[2], dllmax[2], dlli, data, data2, passcnt;
+ u32 dllmin[2], dllmax[2], dlli, data, passcnt, retry = 0;
+ bool status = false;
-
- finetuneDQI_L(ast, param);
- finetuneDQI_L2(ast, param);
+ finetuneDQSI(ast);
+ if (finetuneDQI_L(ast, param) == false)
+ return status;
CBR_START2:
dllmin[0] = dllmin[1] = 0xff;
dllmax[0] = dllmax[1] = 0x0;
passcnt = 0;
for (dlli = 0; dlli < 76; dlli++) {
- moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24));
- /* Wait DQSI latch phase calibration */
- moutdwm(ast, 0x1E6E0074, 0x00000010);
- moutdwm(ast, 0x1E6E0070, 0x00000003);
- do {
- data = mindwm(ast, 0x1E6E0070);
- } while (!(data & 0x00001000));
- moutdwm(ast, 0x1E6E0070, 0x00000000);
-
- moutdwm(ast, 0x1E6E0074, CBR_SIZE2);
+ ast_moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE2);
data = cbr_scan(ast);
if (data != 0) {
if (data & 0x1) {
@@ -957,44 +844,31 @@ CBR_START2:
break;
}
}
+ if (retry++ > 10)
+ goto CBR_DONE2;
if (dllmax[0] == 0 || (dllmax[0]-dllmin[0]) < CBR_THRESHOLD) {
goto CBR_START2;
}
if (dllmax[1] == 0 || (dllmax[1]-dllmin[1]) < CBR_THRESHOLD) {
goto CBR_START2;
}
+ status = true;
+CBR_DONE2:
dlli = (dllmin[1] + dllmax[1]) >> 1;
dlli <<= 8;
dlli += (dllmin[0] + dllmax[0]) >> 1;
- moutdwm(ast, 0x1E6E0068, (mindwm(ast, 0x1E6E0068) & 0xFFFF) | (dlli << 16));
-
- data = (mindwm(ast, 0x1E6E0080) >> 24) & 0x1F;
- data2 = (mindwm(ast, 0x1E6E0018) & 0xff80ffff) | (data << 16);
- moutdwm(ast, 0x1E6E0018, data2);
- moutdwm(ast, 0x1E6E0024, 0x8001 | (data << 1) | (param->dll2_finetune_step << 8));
-
- /* Wait DQSI latch phase calibration */
- moutdwm(ast, 0x1E6E0074, 0x00000010);
- moutdwm(ast, 0x1E6E0070, 0x00000003);
- do {
- data = mindwm(ast, 0x1E6E0070);
- } while (!(data & 0x00001000));
- moutdwm(ast, 0x1E6E0070, 0x00000000);
- moutdwm(ast, 0x1E6E0070, 0x00000003);
- do {
- data = mindwm(ast, 0x1E6E0070);
- } while (!(data & 0x00001000));
- moutdwm(ast, 0x1E6E0070, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0068, ast_mindwm(ast, 0x1E720058) | (dlli << 16));
+ return status;
} /* CBRDLL2 */
static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *param)
{
u32 trap, trap_AC2, trap_MRS;
- moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
+ ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
/* Ger trap info */
- trap = (mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
+ trap = (ast_mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
trap_AC2 = 0x00020000 + (trap << 16);
trap_AC2 |= 0x00300000 + ((trap & 0x2) << 19);
trap_MRS = 0x00000010 + (trap << 4);
@@ -1008,22 +882,35 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
switch (param->dram_freq) {
case 336:
- moutdwm(ast, 0x1E6E2020, 0x0190);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0190);
param->wodt = 0;
param->reg_AC1 = 0x22202725;
param->reg_AC2 = 0xAA007613 | trap_AC2;
param->reg_DQSIC = 0x000000BA;
param->reg_MRS = 0x04001400 | trap_MRS;
param->reg_EMRS = 0x00000000;
- param->reg_IOZ = 0x00000034;
+ param->reg_IOZ = 0x00000023;
param->reg_DQIDLY = 0x00000074;
param->reg_FREQ = 0x00004DC0;
param->madj_max = 96;
param->dll2_finetune_step = 3;
+ switch (param->dram_chipid) {
+ default:
+ case AST_DRAM_512Mx16:
+ case AST_DRAM_1Gx16:
+ param->reg_AC2 = 0xAA007613 | trap_AC2;
+ break;
+ case AST_DRAM_2Gx16:
+ param->reg_AC2 = 0xAA00761C | trap_AC2;
+ break;
+ case AST_DRAM_4Gx16:
+ param->reg_AC2 = 0xAA007636 | trap_AC2;
+ break;
+ }
break;
default:
case 396:
- moutdwm(ast, 0x1E6E2020, 0x03F1);
+ ast_moutdwm(ast, 0x1E6E2020, 0x03F1);
param->wodt = 1;
param->reg_AC1 = 0x33302825;
param->reg_AC2 = 0xCC009617 | trap_AC2;
@@ -1033,7 +920,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->reg_IOZ = 0x00000034;
param->reg_DRV = 0x000000FA;
param->reg_DQIDLY = 0x00000089;
- param->reg_FREQ = 0x000050C0;
+ param->reg_FREQ = 0x00005040;
param->madj_max = 96;
param->dll2_finetune_step = 4;
@@ -1053,14 +940,14 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
break;
case 408:
- moutdwm(ast, 0x1E6E2020, 0x01F0);
+ ast_moutdwm(ast, 0x1E6E2020, 0x01F0);
param->wodt = 1;
param->reg_AC1 = 0x33302825;
param->reg_AC2 = 0xCC009617 | trap_AC2;
param->reg_DQSIC = 0x000000E2;
param->reg_MRS = 0x04001600 | trap_MRS;
param->reg_EMRS = 0x00000000;
- param->reg_IOZ = 0x00000034;
+ param->reg_IOZ = 0x00000023;
param->reg_DRV = 0x000000FA;
param->reg_DQIDLY = 0x00000089;
param->reg_FREQ = 0x000050C0;
@@ -1083,7 +970,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
break;
case 456:
- moutdwm(ast, 0x1E6E2020, 0x0230);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0230);
param->wodt = 0;
param->reg_AC1 = 0x33302926;
param->reg_AC2 = 0xCD44961A;
@@ -1097,7 +984,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->dll2_finetune_step = 4;
break;
case 504:
- moutdwm(ast, 0x1E6E2020, 0x0270);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0270);
param->wodt = 1;
param->reg_AC1 = 0x33302926;
param->reg_AC2 = 0xDE44A61D;
@@ -1111,7 +998,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->dll2_finetune_step = 4;
break;
case 528:
- moutdwm(ast, 0x1E6E2020, 0x0290);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0290);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x33302926;
@@ -1127,7 +1014,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->dll2_finetune_step = 3;
break;
case 576:
- moutdwm(ast, 0x1E6E2020, 0x0140);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0140);
param->reg_MADJ = 0x00136868;
param->reg_SADJ = 0x00004534;
param->wodt = 1;
@@ -1145,7 +1032,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->dll2_finetune_step = 3;
break;
case 600:
- moutdwm(ast, 0x1E6E2020, 0x02E1);
+ ast_moutdwm(ast, 0x1E6E2020, 0x02E1);
param->reg_MADJ = 0x00136868;
param->reg_SADJ = 0x00004534;
param->wodt = 1;
@@ -1163,7 +1050,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->dll2_finetune_step = 3;
break;
case 624:
- moutdwm(ast, 0x1E6E2020, 0x0160);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0160);
param->reg_MADJ = 0x00136868;
param->reg_SADJ = 0x00004534;
param->wodt = 1;
@@ -1196,7 +1083,7 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
case AST_DRAM_4Gx16:
param->dram_config = 0x133;
break;
- }; /* switch size */
+ } /* switch size */
switch (param->vram_size) {
default:
@@ -1218,106 +1105,98 @@ static void get_ddr3_info(struct ast_private *ast, struct ast2300_dram_param *pa
static void ddr3_init(struct ast_private *ast, struct ast2300_dram_param *param)
{
- u32 data, data2;
+ u32 data, data2, retry = 0;
- moutdwm(ast, 0x1E6E0000, 0xFC600309);
- moutdwm(ast, 0x1E6E0018, 0x00000100);
- moutdwm(ast, 0x1E6E0024, 0x00000000);
- moutdwm(ast, 0x1E6E0034, 0x00000000);
+ddr3_init_start:
+ ast_moutdwm(ast, 0x1E6E0000, 0xFC600309);
+ ast_moutdwm(ast, 0x1E6E0018, 0x00000100);
+ ast_moutdwm(ast, 0x1E6E0024, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0034, 0x00000000);
udelay(10);
- moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
- moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
+ ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
+ ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
udelay(10);
- moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
+ ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
udelay(10);
- moutdwm(ast, 0x1E6E0004, param->dram_config);
- moutdwm(ast, 0x1E6E0008, 0x90040f);
- moutdwm(ast, 0x1E6E0010, param->reg_AC1);
- moutdwm(ast, 0x1E6E0014, param->reg_AC2);
- moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
- moutdwm(ast, 0x1E6E0080, 0x00000000);
- moutdwm(ast, 0x1E6E0084, 0x00000000);
- moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
- moutdwm(ast, 0x1E6E0018, 0x4040A170);
- moutdwm(ast, 0x1E6E0018, 0x20402370);
- moutdwm(ast, 0x1E6E0038, 0x00000000);
- moutdwm(ast, 0x1E6E0040, 0xFF444444);
- moutdwm(ast, 0x1E6E0044, 0x22222222);
- moutdwm(ast, 0x1E6E0048, 0x22222222);
- moutdwm(ast, 0x1E6E004C, 0x00000002);
- moutdwm(ast, 0x1E6E0050, 0x80000000);
- moutdwm(ast, 0x1E6E0050, 0x00000000);
- moutdwm(ast, 0x1E6E0054, 0);
- moutdwm(ast, 0x1E6E0060, param->reg_DRV);
- moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
- moutdwm(ast, 0x1E6E0070, 0x00000000);
- moutdwm(ast, 0x1E6E0074, 0x00000000);
- moutdwm(ast, 0x1E6E0078, 0x00000000);
- moutdwm(ast, 0x1E6E007C, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0004, param->dram_config);
+ ast_moutdwm(ast, 0x1E6E0008, 0x90040f);
+ ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1);
+ ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2);
+ ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
+ ast_moutdwm(ast, 0x1E6E0080, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0084, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
+ ast_moutdwm(ast, 0x1E6E0018, 0x4000A170);
+ ast_moutdwm(ast, 0x1E6E0018, 0x00002370);
+ ast_moutdwm(ast, 0x1E6E0038, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0040, 0xFF444444);
+ ast_moutdwm(ast, 0x1E6E0044, 0x22222222);
+ ast_moutdwm(ast, 0x1E6E0048, 0x22222222);
+ ast_moutdwm(ast, 0x1E6E004C, 0x00000002);
+ ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
+ ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0054, 0);
+ ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV);
+ ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
+ ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0074, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0078, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
/* Wait MCLK2X lock to MCLK */
do {
- data = mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, 0x1E6E001C);
} while (!(data & 0x08000000));
- moutdwm(ast, 0x1E6E0034, 0x00000001);
- moutdwm(ast, 0x1E6E000C, 0x00005C04);
- udelay(10);
- moutdwm(ast, 0x1E6E000C, 0x00000000);
- moutdwm(ast, 0x1E6E0034, 0x00000000);
- data = mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, 0x1E6E001C);
data = (data >> 8) & 0xff;
while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
- data2 = (mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
+ data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
if ((data2 & 0xff) > param->madj_max) {
break;
}
- moutdwm(ast, 0x1E6E0064, data2);
+ ast_moutdwm(ast, 0x1E6E0064, data2);
if (data2 & 0x00100000) {
data2 = ((data2 & 0xff) >> 3) + 3;
} else {
data2 = ((data2 & 0xff) >> 2) + 5;
}
- data = mindwm(ast, 0x1E6E0068) & 0xffff00ff;
+ data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff;
data2 += data & 0xff;
data = data | (data2 << 8);
- moutdwm(ast, 0x1E6E0068, data);
+ ast_moutdwm(ast, 0x1E6E0068, data);
udelay(10);
- moutdwm(ast, 0x1E6E0064, mindwm(ast, 0x1E6E0064) | 0xC0000);
+ ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000);
udelay(10);
- data = mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
- moutdwm(ast, 0x1E6E0018, data);
+ data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
+ ast_moutdwm(ast, 0x1E6E0018, data);
data = data | 0x200;
- moutdwm(ast, 0x1E6E0018, data);
+ ast_moutdwm(ast, 0x1E6E0018, data);
do {
- data = mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, 0x1E6E001C);
} while (!(data & 0x08000000));
- moutdwm(ast, 0x1E6E0034, 0x00000001);
- moutdwm(ast, 0x1E6E000C, 0x00005C04);
- udelay(10);
- moutdwm(ast, 0x1E6E000C, 0x00000000);
- moutdwm(ast, 0x1E6E0034, 0x00000000);
- data = mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, 0x1E6E001C);
data = (data >> 8) & 0xff;
}
- data = mindwm(ast, 0x1E6E0018) | 0xC00;
- moutdwm(ast, 0x1E6E0018, data);
+ ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0068) & 0xffff);
+ data = ast_mindwm(ast, 0x1E6E0018) | 0xC00;
+ ast_moutdwm(ast, 0x1E6E0018, data);
- moutdwm(ast, 0x1E6E0034, 0x00000001);
- moutdwm(ast, 0x1E6E000C, 0x00000040);
+ ast_moutdwm(ast, 0x1E6E0034, 0x00000001);
+ ast_moutdwm(ast, 0x1E6E000C, 0x00000040);
udelay(50);
/* Mode Register Setting */
- moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
- moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
- moutdwm(ast, 0x1E6E0028, 0x00000005);
- moutdwm(ast, 0x1E6E0028, 0x00000007);
- moutdwm(ast, 0x1E6E0028, 0x00000003);
- moutdwm(ast, 0x1E6E0028, 0x00000001);
- moutdwm(ast, 0x1E6E002C, param->reg_MRS);
- moutdwm(ast, 0x1E6E000C, 0x00005C08);
- moutdwm(ast, 0x1E6E0028, 0x00000001);
-
- moutdwm(ast, 0x1E6E000C, 0x7FFF5C01);
+ ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
+ ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000005);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000007);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
+ ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS);
+ ast_moutdwm(ast, 0x1E6E000C, 0x00005C08);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
+
+ ast_moutdwm(ast, 0x1E6E000C, 0x00005C01);
data = 0;
if (param->wodt) {
data = 0x300;
@@ -1325,30 +1204,23 @@ static void ddr3_init(struct ast_private *ast, struct ast2300_dram_param *param)
if (param->rodt) {
data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
}
- moutdwm(ast, 0x1E6E0034, data | 0x3);
+ ast_moutdwm(ast, 0x1E6E0034, data | 0x3);
- /* Wait DQI delay lock */
- do {
- data = mindwm(ast, 0x1E6E0080);
- } while (!(data & 0x40000000));
- /* Wait DQSI delay lock */
- do {
- data = mindwm(ast, 0x1E6E0020);
- } while (!(data & 0x00000800));
/* Calibrate the DQSI delay */
- cbr_dll2(ast, param);
+ if ((cbr_dll2(ast, param) == false) && (retry++ < 10))
+ goto ddr3_init_start;
- moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
+ ast_moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
/* ECC Memory Initialization */
#ifdef ECC
- moutdwm(ast, 0x1E6E007C, 0x00000000);
- moutdwm(ast, 0x1E6E0070, 0x221);
+ ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0070, 0x221);
do {
- data = mindwm(ast, 0x1E6E0070);
+ data = ast_mindwm(ast, 0x1E6E0070);
} while (!(data & 0x00001000));
- moutdwm(ast, 0x1E6E0070, 0x00000000);
- moutdwm(ast, 0x1E6E0050, 0x80000000);
- moutdwm(ast, 0x1E6E0050, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
+ ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
#endif
@@ -1358,10 +1230,10 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
{
u32 trap, trap_AC2, trap_MRS;
- moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
+ ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
/* Ger trap info */
- trap = (mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
+ trap = (ast_mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
trap_AC2 = (trap << 20) | (trap << 16);
trap_AC2 += 0x00110000;
trap_MRS = 0x00000040 | (trap << 4);
@@ -1375,7 +1247,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
switch (param->dram_freq) {
case 264:
- moutdwm(ast, 0x1E6E2020, 0x0130);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0130);
param->wodt = 0;
param->reg_AC1 = 0x11101513;
param->reg_AC2 = 0x78117011;
@@ -1390,7 +1262,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->dll2_finetune_step = 3;
break;
case 336:
- moutdwm(ast, 0x1E6E2020, 0x0190);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0190);
param->wodt = 1;
param->reg_AC1 = 0x22202613;
param->reg_AC2 = 0xAA009016 | trap_AC2;
@@ -1403,10 +1275,25 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->reg_FREQ = 0x00004DC0;
param->madj_max = 96;
param->dll2_finetune_step = 3;
+ switch (param->dram_chipid) {
+ default:
+ case AST_DRAM_512Mx16:
+ param->reg_AC2 = 0xAA009012 | trap_AC2;
+ break;
+ case AST_DRAM_1Gx16:
+ param->reg_AC2 = 0xAA009016 | trap_AC2;
+ break;
+ case AST_DRAM_2Gx16:
+ param->reg_AC2 = 0xAA009023 | trap_AC2;
+ break;
+ case AST_DRAM_4Gx16:
+ param->reg_AC2 = 0xAA00903B | trap_AC2;
+ break;
+ }
break;
default:
case 396:
- moutdwm(ast, 0x1E6E2020, 0x03F1);
+ ast_moutdwm(ast, 0x1E6E2020, 0x03F1);
param->wodt = 1;
param->rodt = 0;
param->reg_AC1 = 0x33302714;
@@ -1417,7 +1304,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->reg_DRV = 0x000000FA;
param->reg_IOZ = 0x00000034;
param->reg_DQIDLY = 0x00000089;
- param->reg_FREQ = 0x000050C0;
+ param->reg_FREQ = 0x00005040;
param->madj_max = 96;
param->dll2_finetune_step = 4;
@@ -1440,7 +1327,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
break;
case 408:
- moutdwm(ast, 0x1E6E2020, 0x01F0);
+ ast_moutdwm(ast, 0x1E6E2020, 0x01F0);
param->wodt = 1;
param->rodt = 0;
param->reg_AC1 = 0x33302714;
@@ -1473,7 +1360,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
break;
case 456:
- moutdwm(ast, 0x1E6E2020, 0x0230);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0230);
param->wodt = 0;
param->reg_AC1 = 0x33302815;
param->reg_AC2 = 0xCD44B01E;
@@ -1488,7 +1375,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->dll2_finetune_step = 3;
break;
case 504:
- moutdwm(ast, 0x1E6E2020, 0x0261);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0261);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x33302815;
@@ -1504,7 +1391,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->dll2_finetune_step = 3;
break;
case 528:
- moutdwm(ast, 0x1E6E2020, 0x0120);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0120);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x33302815;
@@ -1520,7 +1407,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->dll2_finetune_step = 3;
break;
case 552:
- moutdwm(ast, 0x1E6E2020, 0x02A1);
+ ast_moutdwm(ast, 0x1E6E2020, 0x02A1);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x43402915;
@@ -1536,7 +1423,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
param->dll2_finetune_step = 3;
break;
case 576:
- moutdwm(ast, 0x1E6E2020, 0x0140);
+ ast_moutdwm(ast, 0x1E6E2020, 0x0140);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x43402915;
@@ -1567,7 +1454,7 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
case AST_DRAM_4Gx16:
param->dram_config = 0x123;
break;
- }; /* switch size */
+ } /* switch size */
switch (param->vram_size) {
default:
@@ -1588,110 +1475,102 @@ static void get_ddr2_info(struct ast_private *ast, struct ast2300_dram_param *pa
static void ddr2_init(struct ast_private *ast, struct ast2300_dram_param *param)
{
- u32 data, data2;
-
- moutdwm(ast, 0x1E6E0000, 0xFC600309);
- moutdwm(ast, 0x1E6E0018, 0x00000100);
- moutdwm(ast, 0x1E6E0024, 0x00000000);
- moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
- moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
+ u32 data, data2, retry = 0;
+
+ddr2_init_start:
+ ast_moutdwm(ast, 0x1E6E0000, 0xFC600309);
+ ast_moutdwm(ast, 0x1E6E0018, 0x00000100);
+ ast_moutdwm(ast, 0x1E6E0024, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
+ ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
udelay(10);
- moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
+ ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
udelay(10);
- moutdwm(ast, 0x1E6E0004, param->dram_config);
- moutdwm(ast, 0x1E6E0008, 0x90040f);
- moutdwm(ast, 0x1E6E0010, param->reg_AC1);
- moutdwm(ast, 0x1E6E0014, param->reg_AC2);
- moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
- moutdwm(ast, 0x1E6E0080, 0x00000000);
- moutdwm(ast, 0x1E6E0084, 0x00000000);
- moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
- moutdwm(ast, 0x1E6E0018, 0x4040A130);
- moutdwm(ast, 0x1E6E0018, 0x20402330);
- moutdwm(ast, 0x1E6E0038, 0x00000000);
- moutdwm(ast, 0x1E6E0040, 0xFF808000);
- moutdwm(ast, 0x1E6E0044, 0x88848466);
- moutdwm(ast, 0x1E6E0048, 0x44440008);
- moutdwm(ast, 0x1E6E004C, 0x00000000);
- moutdwm(ast, 0x1E6E0050, 0x80000000);
- moutdwm(ast, 0x1E6E0050, 0x00000000);
- moutdwm(ast, 0x1E6E0054, 0);
- moutdwm(ast, 0x1E6E0060, param->reg_DRV);
- moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
- moutdwm(ast, 0x1E6E0070, 0x00000000);
- moutdwm(ast, 0x1E6E0074, 0x00000000);
- moutdwm(ast, 0x1E6E0078, 0x00000000);
- moutdwm(ast, 0x1E6E007C, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0004, param->dram_config);
+ ast_moutdwm(ast, 0x1E6E0008, 0x90040f);
+ ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1);
+ ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2);
+ ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
+ ast_moutdwm(ast, 0x1E6E0080, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0084, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
+ ast_moutdwm(ast, 0x1E6E0018, 0x4000A130);
+ ast_moutdwm(ast, 0x1E6E0018, 0x00002330);
+ ast_moutdwm(ast, 0x1E6E0038, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0040, 0xFF808000);
+ ast_moutdwm(ast, 0x1E6E0044, 0x88848466);
+ ast_moutdwm(ast, 0x1E6E0048, 0x44440008);
+ ast_moutdwm(ast, 0x1E6E004C, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
+ ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0054, 0);
+ ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV);
+ ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
+ ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0074, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0078, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
/* Wait MCLK2X lock to MCLK */
do {
- data = mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, 0x1E6E001C);
} while (!(data & 0x08000000));
- moutdwm(ast, 0x1E6E0034, 0x00000001);
- moutdwm(ast, 0x1E6E000C, 0x00005C04);
- udelay(10);
- moutdwm(ast, 0x1E6E000C, 0x00000000);
- moutdwm(ast, 0x1E6E0034, 0x00000000);
- data = mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, 0x1E6E001C);
data = (data >> 8) & 0xff;
while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
- data2 = (mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
+ data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
if ((data2 & 0xff) > param->madj_max) {
break;
}
- moutdwm(ast, 0x1E6E0064, data2);
+ ast_moutdwm(ast, 0x1E6E0064, data2);
if (data2 & 0x00100000) {
data2 = ((data2 & 0xff) >> 3) + 3;
} else {
data2 = ((data2 & 0xff) >> 2) + 5;
}
- data = mindwm(ast, 0x1E6E0068) & 0xffff00ff;
+ data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff;
data2 += data & 0xff;
data = data | (data2 << 8);
- moutdwm(ast, 0x1E6E0068, data);
+ ast_moutdwm(ast, 0x1E6E0068, data);
udelay(10);
- moutdwm(ast, 0x1E6E0064, mindwm(ast, 0x1E6E0064) | 0xC0000);
+ ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000);
udelay(10);
- data = mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
- moutdwm(ast, 0x1E6E0018, data);
+ data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
+ ast_moutdwm(ast, 0x1E6E0018, data);
data = data | 0x200;
- moutdwm(ast, 0x1E6E0018, data);
+ ast_moutdwm(ast, 0x1E6E0018, data);
do {
- data = mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, 0x1E6E001C);
} while (!(data & 0x08000000));
- moutdwm(ast, 0x1E6E0034, 0x00000001);
- moutdwm(ast, 0x1E6E000C, 0x00005C04);
- udelay(10);
- moutdwm(ast, 0x1E6E000C, 0x00000000);
- moutdwm(ast, 0x1E6E0034, 0x00000000);
- data = mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, 0x1E6E001C);
data = (data >> 8) & 0xff;
}
- data = mindwm(ast, 0x1E6E0018) | 0xC00;
- moutdwm(ast, 0x1E6E0018, data);
+ ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0008) & 0xffff);
+ data = ast_mindwm(ast, 0x1E6E0018) | 0xC00;
+ ast_moutdwm(ast, 0x1E6E0018, data);
- moutdwm(ast, 0x1E6E0034, 0x00000001);
- moutdwm(ast, 0x1E6E000C, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0034, 0x00000001);
+ ast_moutdwm(ast, 0x1E6E000C, 0x00000000);
udelay(50);
/* Mode Register Setting */
- moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
- moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
- moutdwm(ast, 0x1E6E0028, 0x00000005);
- moutdwm(ast, 0x1E6E0028, 0x00000007);
- moutdwm(ast, 0x1E6E0028, 0x00000003);
- moutdwm(ast, 0x1E6E0028, 0x00000001);
-
- moutdwm(ast, 0x1E6E000C, 0x00005C08);
- moutdwm(ast, 0x1E6E002C, param->reg_MRS);
- moutdwm(ast, 0x1E6E0028, 0x00000001);
- moutdwm(ast, 0x1E6E0030, param->reg_EMRS | 0x380);
- moutdwm(ast, 0x1E6E0028, 0x00000003);
- moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
- moutdwm(ast, 0x1E6E0028, 0x00000003);
-
- moutdwm(ast, 0x1E6E000C, 0x7FFF5C01);
+ ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
+ ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000005);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000007);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
+
+ ast_moutdwm(ast, 0x1E6E000C, 0x00005C08);
+ ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
+ ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS | 0x380);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
+ ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
+ ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
+
+ ast_moutdwm(ast, 0x1E6E000C, 0x7FFF5C01);
data = 0;
if (param->wodt) {
data = 0x500;
@@ -1699,30 +1578,23 @@ static void ddr2_init(struct ast_private *ast, struct ast2300_dram_param *param)
if (param->rodt) {
data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
}
- moutdwm(ast, 0x1E6E0034, data | 0x3);
- moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
+ ast_moutdwm(ast, 0x1E6E0034, data | 0x3);
+ ast_moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
- /* Wait DQI delay lock */
- do {
- data = mindwm(ast, 0x1E6E0080);
- } while (!(data & 0x40000000));
- /* Wait DQSI delay lock */
- do {
- data = mindwm(ast, 0x1E6E0020);
- } while (!(data & 0x00000800));
/* Calibrate the DQSI delay */
- cbr_dll2(ast, param);
+ if ((cbr_dll2(ast, param) == false) && (retry++ < 10))
+ goto ddr2_init_start;
/* ECC Memory Initialization */
#ifdef ECC
- moutdwm(ast, 0x1E6E007C, 0x00000000);
- moutdwm(ast, 0x1E6E0070, 0x221);
+ ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0070, 0x221);
do {
- data = mindwm(ast, 0x1E6E0070);
+ data = ast_mindwm(ast, 0x1E6E0070);
} while (!(data & 0x00001000));
- moutdwm(ast, 0x1E6E0070, 0x00000000);
- moutdwm(ast, 0x1E6E0050, 0x80000000);
- moutdwm(ast, 0x1E6E0050, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
+ ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
+ ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
#endif
}
@@ -1768,8 +1640,8 @@ static void ast_init_dram_2300(struct drm_device *dev)
ddr2_init(ast, &param);
}
- temp = mindwm(ast, 0x1e6e2040);
- moutdwm(ast, 0x1e6e2040, temp | 0x40);
+ temp = ast_mindwm(ast, 0x1e6e2040);
+ ast_moutdwm(ast, 0x1e6e2040, temp | 0x40);
}
/* wait ready */
diff --git a/drivers/gpu/drm/ast/ast_tables.h b/drivers/gpu/drm/ast/ast_tables.h
index 95fa6aba26bc..4c761dcea972 100644
--- a/drivers/gpu/drm/ast/ast_tables.h
+++ b/drivers/gpu/drm/ast/ast_tables.h
@@ -42,7 +42,7 @@
#define HBorder 0x00000020
#define VBorder 0x00000010
#define WideScreenMode 0x00000100
-
+#define NewModeInfo 0x00000200
/* DCLK Index */
#define VCLK25_175 0x00
@@ -67,6 +67,11 @@
#define VCLK106_5 0x12
#define VCLK146_25 0x13
#define VCLK148_5 0x14
+#define VCLK71 0x15
+#define VCLK88_75 0x16
+#define VCLK119 0x17
+#define VCLK85_5 0x18
+#define VCLK97_75 0x19
static struct ast_vbios_dclk_info dclk_table[] = {
{0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */
@@ -90,6 +95,10 @@ static struct ast_vbios_dclk_info dclk_table[] = {
{0x28, 0x49, 0x80}, /* 12: VCLK106.5 */
{0x37, 0x49, 0x80}, /* 13: VCLK146.25 */
{0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */
+ {0x47, 0x6c, 0x80}, /* 15: VCLK71 */
+ {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */
+ {0x77, 0x58, 0x80}, /* 17: VCLK119 */
+ {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */
};
static struct ast_vbios_stdtable vbios_stdtable[] = {
@@ -225,41 +234,63 @@ static struct ast_vbios_enhtable res_1600x1200[] = {
(SyncPP | Charx8Dot), 0xFF, 1, 0x33 },
};
-static struct ast_vbios_enhtable res_1920x1200[] = {
- {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
- (SyncNP | Charx8Dot), 60, 1, 0x34 },
- {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
- (SyncNP | Charx8Dot), 0xFF, 1, 0x34 },
+/* 16:9 */
+static struct ast_vbios_enhtable res_1360x768[] = {
+ {1792, 1360, 64,112, 795, 768, 3, 6, VCLK85_5, /* 60Hz */
+ (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x39 },
+ {1792, 1360, 64,112, 795, 768, 3, 6, VCLK85_5, /* end */
+ (SyncPP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x39 },
+};
+
+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 }
};
+static struct ast_vbios_enhtable res_1920x1080[] = {
+ {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */
+ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x38 },
+ {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */
+ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x38 },
+};
+
+
/* 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 },
{1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */
- (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x35 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x35 },
{1680, 1280, 72,128, 831, 800, 3, 6, VCLK83_5, /* 60Hz */
- (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x35 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x35 },
};
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), 60, 1, 0x36 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x36 },
{1904, 1440, 80,152, 934, 900, 3, 6, VCLK106_5, /* 60Hz */
- (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x36 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 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), 60, 1, 0x37 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x37 },
{2240, 1680,104,176, 1089, 1050, 3, 6, VCLK146_25, /* 60Hz */
- (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x37 },
+ (SyncPN | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x37 },
};
-/* HDTV */
-static struct ast_vbios_enhtable res_1920x1080[] = {
- {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */
- (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode), 60, 1, 0x38 },
- {2200, 1920, 88, 44, 1125, 1080, 4, 5, VCLK148_5, /* 60Hz */
- (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode), 0xFF, 1, 0x38 },
+static struct ast_vbios_enhtable res_1920x1200[] = {
+ {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
+ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 60, 1, 0x34 },
+ {2080, 1920, 48, 32, 1235, 1200, 3, 6, VCLK154, /* 60Hz */
+ (SyncNP | Charx8Dot | LineCompareOff | WideScreenMode | NewModeInfo), 0xFF, 1, 0x34 },
};
+
#endif
diff --git a/drivers/gpu/drm/bochs/bochs_mm.c b/drivers/gpu/drm/bochs/bochs_mm.c
index f488be55d650..b9a695d92792 100644
--- a/drivers/gpu/drm/bochs/bochs_mm.c
+++ b/drivers/gpu/drm/bochs/bochs_mm.c
@@ -434,17 +434,13 @@ static void bochs_bo_unref(struct bochs_bo **bo)
tbo = &((*bo)->bo);
ttm_bo_unref(&tbo);
- if (tbo == NULL)
- *bo = NULL;
-
+ *bo = NULL;
}
void bochs_gem_free_object(struct drm_gem_object *obj)
{
struct bochs_bo *bochs_bo = gem_to_bochs_bo(obj);
- if (!bochs_bo)
- return;
bochs_bo_unref(&bochs_bo);
}
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
index b171901a3553..98fd17ae4916 100644
--- a/drivers/gpu/drm/bridge/ptn3460.c
+++ b/drivers/gpu/drm/bridge/ptn3460.c
@@ -225,12 +225,6 @@ out:
return num_modes;
}
-static int ptn3460_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- return MODE_OK;
-}
-
struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
{
struct ptn3460_bridge *ptn_bridge;
@@ -242,7 +236,6 @@ struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
.get_modes = ptn3460_get_modes,
- .mode_valid = ptn3460_mode_valid,
.best_encoder = ptn3460_best_encoder,
};
diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c
index 4b0170cf53fd..99c1983f99d2 100644
--- a/drivers/gpu/drm/cirrus/cirrus_main.c
+++ b/drivers/gpu/drm/cirrus/cirrus_main.c
@@ -264,17 +264,13 @@ static void cirrus_bo_unref(struct cirrus_bo **bo)
tbo = &((*bo)->bo);
ttm_bo_unref(&tbo);
- if (tbo == NULL)
- *bo = NULL;
-
+ *bo = NULL;
}
void cirrus_gem_free_object(struct drm_gem_object *obj)
{
struct cirrus_bo *cirrus_bo = gem_to_cirrus_bo(obj);
- if (!cirrus_bo)
- return;
cirrus_bo_unref(&cirrus_bo);
}
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
index f59433b7610c..49332c5fe35b 100644
--- a/drivers/gpu/drm/cirrus/cirrus_mode.c
+++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
@@ -505,13 +505,6 @@ static int cirrus_vga_get_modes(struct drm_connector *connector)
return count;
}
-static int cirrus_vga_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- /* Any mode we've added is valid */
- return MODE_OK;
-}
-
static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector
*connector)
{
@@ -546,7 +539,6 @@ static void cirrus_connector_destroy(struct drm_connector *connector)
struct drm_connector_helper_funcs cirrus_vga_connector_helper_funcs = {
.get_modes = cirrus_vga_get_modes,
- .mode_valid = cirrus_vga_mode_valid,
.best_encoder = cirrus_connector_best_encoder,
};
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index edec31fe3fed..68175b54504b 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -363,7 +363,7 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset,
list->master = dev->primary->master;
*maplist = list;
return 0;
- }
+}
int drm_addmap(struct drm_device * dev, resource_size_t offset,
unsigned int size, enum drm_map_type type,
@@ -656,13 +656,13 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request)
DRM_DEBUG("zone invalid\n");
return -EINVAL;
}
- spin_lock(&dev->count_lock);
+ spin_lock(&dev->buf_lock);
if (dev->buf_use) {
- spin_unlock(&dev->count_lock);
+ spin_unlock(&dev->buf_lock);
return -EBUSY;
}
atomic_inc(&dev->buf_alloc);
- spin_unlock(&dev->count_lock);
+ spin_unlock(&dev->buf_lock);
mutex_lock(&dev->struct_mutex);
entry = &dma->bufs[order];
@@ -805,13 +805,13 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request)
page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
total = PAGE_SIZE << page_order;
- spin_lock(&dev->count_lock);
+ spin_lock(&dev->buf_lock);
if (dev->buf_use) {
- spin_unlock(&dev->count_lock);
+ spin_unlock(&dev->buf_lock);
return -EBUSY;
}
atomic_inc(&dev->buf_alloc);
- spin_unlock(&dev->count_lock);
+ spin_unlock(&dev->buf_lock);
mutex_lock(&dev->struct_mutex);
entry = &dma->bufs[order];
@@ -1015,13 +1015,13 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request
if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER)
return -EINVAL;
- spin_lock(&dev->count_lock);
+ spin_lock(&dev->buf_lock);
if (dev->buf_use) {
- spin_unlock(&dev->count_lock);
+ spin_unlock(&dev->buf_lock);
return -EBUSY;
}
atomic_inc(&dev->buf_alloc);
- spin_unlock(&dev->count_lock);
+ spin_unlock(&dev->buf_lock);
mutex_lock(&dev->struct_mutex);
entry = &dma->bufs[order];
@@ -1175,7 +1175,7 @@ int drm_addbufs(struct drm_device *dev, void *data,
* \param arg pointer to a drm_buf_info structure.
* \return zero on success or a negative number on failure.
*
- * Increments drm_device::buf_use while holding the drm_device::count_lock
+ * Increments drm_device::buf_use while holding the drm_device::buf_lock
* lock, preventing of allocating more buffers after this call. Information
* about each requested buffer is then copied into user space.
*/
@@ -1196,13 +1196,13 @@ int drm_infobufs(struct drm_device *dev, void *data,
if (!dma)
return -EINVAL;
- spin_lock(&dev->count_lock);
+ spin_lock(&dev->buf_lock);
if (atomic_read(&dev->buf_alloc)) {
- spin_unlock(&dev->count_lock);
+ spin_unlock(&dev->buf_lock);
return -EBUSY;
}
++dev->buf_use; /* Can't allocate more after this call */
- spin_unlock(&dev->count_lock);
+ spin_unlock(&dev->buf_lock);
for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) {
if (dma->bufs[i].buf_count)
@@ -1381,13 +1381,13 @@ int drm_mapbufs(struct drm_device *dev, void *data,
if (!dma)
return -EINVAL;
- spin_lock(&dev->count_lock);
+ spin_lock(&dev->buf_lock);
if (atomic_read(&dev->buf_alloc)) {
- spin_unlock(&dev->count_lock);
+ spin_unlock(&dev->buf_lock);
return -EBUSY;
}
dev->buf_use++; /* Can't allocate more after this call */
- spin_unlock(&dev->count_lock);
+ spin_unlock(&dev->buf_lock);
if (request->count >= dma->buf_count) {
if ((dev->agp && (dma->flags & _DRM_DMA_USE_AGP))
diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c
index 534cb89b160d..a6b690626a6b 100644
--- a/drivers/gpu/drm/drm_cache.c
+++ b/drivers/gpu/drm/drm_cache.c
@@ -131,14 +131,14 @@ drm_clflush_sg(struct sg_table *st)
EXPORT_SYMBOL(drm_clflush_sg);
void
-drm_clflush_virt_range(char *addr, unsigned long length)
+drm_clflush_virt_range(void *addr, unsigned long length)
{
#if defined(CONFIG_X86)
if (cpu_has_clflush) {
- char *end = addr + length;
+ void *end = addr + length;
mb();
for (; addr < end; addr += boot_cpu_data.x86_clflush_size)
- clflush(addr);
+ clflushopt(addr);
clflushopt(end - 1);
mb();
return;
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index d8b7099abece..fe94cc10cd35 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -37,6 +37,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_modeset_lock.h>
#include "drm_crtc_internal.h"
@@ -50,12 +51,42 @@
*/
void drm_modeset_lock_all(struct drm_device *dev)
{
- struct drm_crtc *crtc;
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_modeset_acquire_ctx *ctx;
+ int ret;
- mutex_lock(&dev->mode_config.mutex);
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (WARN_ON(!ctx))
+ return;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
- mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
+ 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);
@@ -67,10 +98,17 @@ EXPORT_SYMBOL(drm_modeset_lock_all);
*/
void drm_modeset_unlock_all(struct drm_device *dev)
{
- struct drm_crtc *crtc;
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
- mutex_unlock(&crtc->mutex);
+ 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);
}
@@ -91,8 +129,9 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
return;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
- WARN_ON(!mutex_is_locked(&crtc->mutex));
+ 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);
@@ -227,6 +266,7 @@ static const struct drm_prop_enum_list drm_encoder_enum_list[] =
{ DRM_MODE_ENCODER_TVDAC, "TV" },
{ DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
{ DRM_MODE_ENCODER_DSI, "DSI" },
+ { DRM_MODE_ENCODER_DPMST, "DP MST" },
};
static const struct drm_prop_enum_list drm_subpixel_enum_list[] =
@@ -256,46 +296,6 @@ void drm_connector_ida_destroy(void)
}
/**
- * drm_get_encoder_name - return a string for encoder
- * @encoder: encoder to compute name of
- *
- * Note that the buffer used by this function is globally shared and owned by
- * the function itself.
- *
- * FIXME: This isn't really multithreading safe.
- */
-const char *drm_get_encoder_name(const struct drm_encoder *encoder)
-{
- static char buf[32];
-
- snprintf(buf, 32, "%s-%d",
- drm_encoder_enum_list[encoder->encoder_type].name,
- encoder->base.id);
- return buf;
-}
-EXPORT_SYMBOL(drm_get_encoder_name);
-
-/**
- * drm_get_connector_name - return a string for connector
- * @connector: connector to compute name of
- *
- * Note that the buffer used by this function is globally shared and owned by
- * the function itself.
- *
- * FIXME: This isn't really multithreading safe.
- */
-const char *drm_get_connector_name(const struct drm_connector *connector)
-{
- static char buf[32];
-
- snprintf(buf, 32, "%s-%d",
- drm_connector_enum_list[connector->connector_type].name,
- connector->connector_type_id);
- return buf;
-}
-EXPORT_SYMBOL(drm_get_connector_name);
-
-/**
* drm_get_connector_status_name - return a string for connector status
* @status: connector status to compute name of
*
@@ -409,6 +409,21 @@ void drm_mode_object_put(struct drm_device *dev,
mutex_unlock(&dev->mode_config.idr_mutex);
}
+static struct drm_mode_object *_object_find(struct drm_device *dev,
+ uint32_t id, uint32_t type)
+{
+ struct drm_mode_object *obj = NULL;
+
+ 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))
+ obj = NULL;
+ mutex_unlock(&dev->mode_config.idr_mutex);
+
+ return obj;
+}
+
/**
* drm_mode_object_find - look up a drm object with static lifetime
* @dev: drm device
@@ -416,7 +431,9 @@ void drm_mode_object_put(struct drm_device *dev,
* @type: type of the mode object
*
* Note that framebuffers cannot be looked up with this functions - since those
- * are reference counted, they need special treatment.
+ * are reference counted, they need special treatment. Even with
+ * DRM_MODE_OBJECT_ANY (although that will simply return NULL
+ * rather than WARN_ON()).
*/
struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
uint32_t id, uint32_t type)
@@ -426,13 +443,10 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
/* Framebuffers are reference counted and need their own lookup
* function.*/
WARN_ON(type == DRM_MODE_OBJECT_FB);
-
- mutex_lock(&dev->mode_config.idr_mutex);
- obj = idr_find(&dev->mode_config.crtc_idr, id);
- if (!obj || (obj->type != type) || (obj->id != id))
+ obj = _object_find(dev, id, type);
+ /* 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);
-
return obj;
}
EXPORT_SYMBOL(drm_mode_object_find);
@@ -538,7 +552,7 @@ EXPORT_SYMBOL(drm_framebuffer_lookup);
*/
void drm_framebuffer_unreference(struct drm_framebuffer *fb)
{
- DRM_DEBUG("FB ID: %d\n", fb->base.id);
+ DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
kref_put(&fb->refcount, drm_framebuffer_free);
}
EXPORT_SYMBOL(drm_framebuffer_unreference);
@@ -551,7 +565,7 @@ EXPORT_SYMBOL(drm_framebuffer_unreference);
*/
void drm_framebuffer_reference(struct drm_framebuffer *fb)
{
- DRM_DEBUG("FB ID: %d\n", fb->base.id);
+ DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
kref_get(&fb->refcount);
}
EXPORT_SYMBOL(drm_framebuffer_reference);
@@ -563,7 +577,7 @@ static void drm_framebuffer_free_bug(struct kref *kref)
static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
{
- DRM_DEBUG("FB ID: %d\n", fb->base.id);
+ DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
kref_put(&fb->refcount, drm_framebuffer_free_bug);
}
@@ -691,6 +705,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
}
EXPORT_SYMBOL(drm_framebuffer_remove);
+DEFINE_WW_CLASS(crtc_ww_class);
+
/**
* drm_crtc_init_with_planes - Initialise a new CRTC object with
* specified primary and cursor planes.
@@ -710,6 +726,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
void *cursor,
const struct drm_crtc_funcs *funcs)
{
+ struct drm_mode_config *config = &dev->mode_config;
int ret;
crtc->dev = dev;
@@ -717,8 +734,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
crtc->invert_dimensions = false;
drm_modeset_lock_all(dev);
- mutex_init(&crtc->mutex);
- mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
+ 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)
@@ -726,8 +744,8 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
crtc->base.properties = &crtc->properties;
- list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
- dev->mode_config.num_crtc++;
+ list_add_tail(&crtc->head, &config->crtc_list);
+ config->num_crtc++;
crtc->primary = primary;
if (primary)
@@ -755,6 +773,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
kfree(crtc->gamma_store);
crtc->gamma_store = NULL;
+ drm_modeset_lock_fini(&crtc->mutex);
+
drm_mode_object_put(dev, &crtc->base);
list_del(&crtc->head);
dev->mode_config.num_crtc--;
@@ -824,7 +844,7 @@ int drm_connector_init(struct drm_device *dev,
ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
if (ret)
- goto out;
+ goto out_unlock;
connector->base.properties = &connector->properties;
connector->dev = dev;
@@ -834,9 +854,17 @@ int drm_connector_init(struct drm_device *dev,
ida_simple_get(connector_ida, 1, 0, GFP_KERNEL);
if (connector->connector_type_id < 0) {
ret = connector->connector_type_id;
- drm_mode_object_put(dev, &connector->base);
- goto out;
+ goto out_put;
+ }
+ connector->name =
+ kasprintf(GFP_KERNEL, "%s-%d",
+ drm_connector_enum_list[connector_type].name,
+ connector->connector_type_id);
+ if (!connector->name) {
+ ret = -ENOMEM;
+ goto out_put;
}
+
INIT_LIST_HEAD(&connector->probed_modes);
INIT_LIST_HEAD(&connector->modes);
connector->edid_blob_ptr = NULL;
@@ -853,7 +881,11 @@ int drm_connector_init(struct drm_device *dev,
drm_object_attach_property(&connector->base,
dev->mode_config.dpms_property, 0);
- out:
+out_put:
+ if (ret)
+ drm_mode_object_put(dev, &connector->base);
+
+out_unlock:
drm_modeset_unlock_all(dev);
return ret;
@@ -881,6 +913,8 @@ void drm_connector_cleanup(struct drm_connector *connector)
connector->connector_type_id);
drm_mode_object_put(dev, &connector->base);
+ kfree(connector->name);
+ connector->name = NULL;
list_del(&connector->head);
dev->mode_config.num_connector--;
}
@@ -982,16 +1016,27 @@ int drm_encoder_init(struct drm_device *dev,
ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
if (ret)
- goto out;
+ goto out_unlock;
encoder->dev = dev;
encoder->encoder_type = encoder_type;
encoder->funcs = funcs;
+ encoder->name = kasprintf(GFP_KERNEL, "%s-%d",
+ drm_encoder_enum_list[encoder_type].name,
+ encoder->base.id);
+ if (!encoder->name) {
+ ret = -ENOMEM;
+ goto out_put;
+ }
list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
dev->mode_config.num_encoder++;
- out:
+out_put:
+ if (ret)
+ drm_mode_object_put(dev, &encoder->base);
+
+out_unlock:
drm_modeset_unlock_all(dev);
return ret;
@@ -1009,6 +1054,8 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
struct drm_device *dev = encoder->dev;
drm_modeset_lock_all(dev);
drm_mode_object_put(dev, &encoder->base);
+ kfree(encoder->name);
+ encoder->name = NULL;
list_del(&encoder->head);
dev->mode_config.num_encoder--;
drm_modeset_unlock_all(dev);
@@ -1145,16 +1192,19 @@ EXPORT_SYMBOL(drm_plane_cleanup);
*/
void drm_plane_force_disable(struct drm_plane *plane)
{
+ struct drm_framebuffer *old_fb = plane->fb;
int ret;
- if (!plane->fb)
+ if (!old_fb)
return;
ret = plane->funcs->disable_plane(plane);
- if (ret)
+ if (ret) {
DRM_ERROR("failed to disable plane with busy fb\n");
+ return;
+ }
/* disconnect the plane from the fb and crtc: */
- __drm_framebuffer_unreference(plane->fb);
+ __drm_framebuffer_unreference(old_fb);
plane->fb = NULL;
plane->crtc = NULL;
}
@@ -1378,6 +1428,12 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr
return 0;
}
+void drm_mode_group_destroy(struct drm_mode_group *group)
+{
+ kfree(group->id_list);
+ group->id_list = NULL;
+}
+
/*
* NOTE: Driver's shouldn't ever call drm_mode_group_init_legacy_group - it is
* the drm core's responsibility to set up mode control groups.
@@ -1614,7 +1670,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
&dev->mode_config.encoder_list,
head) {
DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id,
- drm_get_encoder_name(encoder));
+ encoder->name);
if (put_user(encoder->base.id, encoder_id +
copied)) {
ret = -EFAULT;
@@ -1646,7 +1702,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
head) {
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id,
- drm_get_connector_name(connector));
+ connector->name);
if (put_user(connector->base.id,
connector_id + copied)) {
ret = -EFAULT;
@@ -1695,7 +1751,6 @@ int drm_mode_getcrtc(struct drm_device *dev,
{
struct drm_mode_crtc *crtc_resp = data;
struct drm_crtc *crtc;
- struct drm_mode_object *obj;
int ret = 0;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
@@ -1703,13 +1758,11 @@ int drm_mode_getcrtc(struct drm_device *dev,
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
- DRM_MODE_OBJECT_CRTC);
- if (!obj) {
+ crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
+ if (!crtc) {
ret = -ENOENT;
goto out;
}
- crtc = obj_to_crtc(obj);
crtc_resp->x = crtc->x;
crtc_resp->y = crtc->y;
@@ -1763,7 +1816,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_mode_get_connector *out_resp = data;
- struct drm_mode_object *obj;
struct drm_connector *connector;
struct drm_display_mode *mode;
int mode_count = 0;
@@ -1787,13 +1839,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
mutex_lock(&dev->mode_config.mutex);
- obj = drm_mode_object_find(dev, out_resp->connector_id,
- DRM_MODE_OBJECT_CONNECTOR);
- if (!obj) {
+ connector = drm_connector_find(dev, out_resp->connector_id);
+ if (!connector) {
ret = -ENOENT;
goto out;
}
- connector = obj_to_connector(obj);
props_count = connector->properties.count;
@@ -1821,10 +1871,12 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
out_resp->mm_height = connector->display_info.height_mm;
out_resp->subpixel = connector->display_info.subpixel_order;
out_resp->connection = connector->status;
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
if (connector->encoder)
out_resp->encoder_id = connector->encoder->base.id;
else
out_resp->encoder_id = 0;
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
/*
* This ioctl is called twice, once to determine how much space is
@@ -1908,7 +1960,6 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_mode_get_encoder *enc_resp = data;
- struct drm_mode_object *obj;
struct drm_encoder *encoder;
int ret = 0;
@@ -1916,13 +1967,11 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, enc_resp->encoder_id,
- DRM_MODE_OBJECT_ENCODER);
- if (!obj) {
+ encoder = drm_encoder_find(dev, enc_resp->encoder_id);
+ if (!encoder) {
ret = -ENOENT;
goto out;
}
- encoder = obj_to_encoder(obj);
if (encoder->crtc)
enc_resp->crtc_id = encoder->crtc->base.id;
@@ -2020,7 +2069,6 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_mode_get_plane *plane_resp = data;
- struct drm_mode_object *obj;
struct drm_plane *plane;
uint32_t __user *format_ptr;
int ret = 0;
@@ -2029,13 +2077,11 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, plane_resp->plane_id,
- DRM_MODE_OBJECT_PLANE);
- if (!obj) {
+ plane = drm_plane_find(dev, plane_resp->plane_id);
+ if (!plane) {
ret = -ENOENT;
goto out;
}
- plane = obj_to_plane(obj);
if (plane->crtc)
plane_resp->crtc_id = plane->crtc->base.id;
@@ -2088,7 +2134,6 @@ 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;
struct drm_framebuffer *fb = NULL, *old_fb = NULL;
@@ -2103,35 +2148,42 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
* 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) {
+ 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;
}
- plane = obj_to_plane(obj);
/* No fb means shut it down */
if (!plane_req->fb_id) {
drm_modeset_lock_all(dev);
old_fb = plane->fb;
- plane->funcs->disable_plane(plane);
- plane->crtc = NULL;
- plane->fb = NULL;
+ ret = plane->funcs->disable_plane(plane);
+ if (!ret) {
+ plane->crtc = NULL;
+ plane->fb = NULL;
+ } else {
+ old_fb = NULL;
+ }
drm_modeset_unlock_all(dev);
goto out;
}
- obj = drm_mode_object_find(dev, plane_req->crtc_id,
- DRM_MODE_OBJECT_CRTC);
- if (!obj) {
+ 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;
}
- crtc = obj_to_crtc(obj);
+
+ /* Check whether this plane is usable on this CRTC */
+ if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) {
+ DRM_DEBUG_KMS("Invalid crtc for plane\n");
+ ret = -EINVAL;
+ goto out;
+ }
fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
if (!fb) {
@@ -2187,16 +2239,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
}
drm_modeset_lock_all(dev);
+ 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);
if (!ret) {
- old_fb = plane->fb;
plane->crtc = crtc;
plane->fb = fb;
fb = NULL;
+ } else {
+ old_fb = NULL;
}
drm_modeset_unlock_all(dev);
@@ -2239,9 +2293,7 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
ret = crtc->funcs->set_config(set);
if (ret == 0) {
crtc->primary->crtc = crtc;
-
- /* crtc->fb must be updated by ->set_config, enforces this. */
- WARN_ON(fb != crtc->primary->fb);
+ crtc->primary->fb = fb;
}
list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
@@ -2318,7 +2370,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
{
struct drm_mode_config *config = &dev->mode_config;
struct drm_mode_crtc *crtc_req = data;
- struct drm_mode_object *obj;
struct drm_crtc *crtc;
struct drm_connector **connector_set = NULL, *connector;
struct drm_framebuffer *fb = NULL;
@@ -2336,14 +2387,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
return -ERANGE;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, crtc_req->crtc_id,
- DRM_MODE_OBJECT_CRTC);
- if (!obj) {
+ crtc = drm_crtc_find(dev, crtc_req->crtc_id);
+ if (!crtc) {
DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
ret = -ENOENT;
goto out;
}
- crtc = obj_to_crtc(obj);
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
if (crtc_req->mode_valid) {
@@ -2426,18 +2475,16 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
goto out;
}
- obj = drm_mode_object_find(dev, out_id,
- DRM_MODE_OBJECT_CONNECTOR);
- if (!obj) {
+ connector = drm_connector_find(dev, out_id);
+ if (!connector) {
DRM_DEBUG_KMS("Connector id %d unknown\n",
out_id);
ret = -ENOENT;
goto out;
}
- connector = obj_to_connector(obj);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id,
- drm_get_connector_name(connector));
+ connector->name);
connector_set[i] = connector;
}
@@ -2466,7 +2513,6 @@ static int drm_mode_cursor_common(struct drm_device *dev,
struct drm_mode_cursor2 *req,
struct drm_file *file_priv)
{
- struct drm_mode_object *obj;
struct drm_crtc *crtc;
int ret = 0;
@@ -2476,14 +2522,13 @@ static int drm_mode_cursor_common(struct drm_device *dev,
if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
return -EINVAL;
- obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
- if (!obj) {
+ crtc = drm_crtc_find(dev, req->crtc_id);
+ if (!crtc) {
DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
return -ENOENT;
}
- crtc = obj_to_crtc(obj);
- mutex_lock(&crtc->mutex);
+ drm_modeset_lock(&crtc->mutex, NULL);
if (req->flags & DRM_MODE_CURSOR_BO) {
if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
ret = -ENXIO;
@@ -2507,7 +2552,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
}
}
out:
- mutex_unlock(&crtc->mutex);
+ drm_modeset_unlock(&crtc->mutex);
return ret;
@@ -3097,6 +3142,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
if (!property)
return NULL;
+ property->dev = dev;
+
if (num_values) {
property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
if (!property->values)
@@ -3117,6 +3164,9 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
}
list_add_tail(&property->head, &dev->mode_config.property_list);
+
+ WARN_ON(!drm_property_type_valid(property));
+
return property;
fail:
kfree(property->values);
@@ -3217,6 +3267,22 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_property_create_bitmask);
+static struct drm_property *property_create_range(struct drm_device *dev,
+ int flags, const char *name,
+ uint64_t min, uint64_t max)
+{
+ struct drm_property *property;
+
+ property = drm_property_create(dev, flags, name, 2);
+ if (!property)
+ return NULL;
+
+ property->values[0] = min;
+ property->values[1] = max;
+
+ return property;
+}
+
/**
* drm_property_create - create a new ranged property type
* @dev: drm device
@@ -3239,20 +3305,36 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
const char *name,
uint64_t min, uint64_t max)
{
+ return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
+ name, min, max);
+}
+EXPORT_SYMBOL(drm_property_create_range);
+
+struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
+ int flags, const char *name,
+ int64_t min, int64_t max)
+{
+ return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
+ name, I642U64(min), I642U64(max));
+}
+EXPORT_SYMBOL(drm_property_create_signed_range);
+
+struct drm_property *drm_property_create_object(struct drm_device *dev,
+ int flags, const char *name, uint32_t type)
+{
struct drm_property *property;
- flags |= DRM_MODE_PROP_RANGE;
+ flags |= DRM_MODE_PROP_OBJECT;
- property = drm_property_create(dev, flags, name, 2);
+ property = drm_property_create(dev, flags, name, 1);
if (!property)
return NULL;
- property->values[0] = min;
- property->values[1] = max;
+ property->values[0] = type;
return property;
}
-EXPORT_SYMBOL(drm_property_create_range);
+EXPORT_SYMBOL(drm_property_create_object);
/**
* drm_property_add_enum - add a possible value to an enumeration property
@@ -3274,14 +3356,16 @@ int drm_property_add_enum(struct drm_property *property, int index,
{
struct drm_property_enum *prop_enum;
- if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
+ if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+ drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
return -EINVAL;
/*
* Bitmask enum properties have the additional constraint of values
* from 0 to 63
*/
- if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63))
+ if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
+ (value > 63))
return -EINVAL;
if (!list_empty(&property->enum_blob_list)) {
@@ -3438,7 +3522,6 @@ EXPORT_SYMBOL(drm_object_property_get_value);
int drm_mode_getproperty_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
- struct drm_mode_object *obj;
struct drm_mode_get_property *out_resp = data;
struct drm_property *property;
int enum_count = 0;
@@ -3457,17 +3540,17 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
- if (!obj) {
+ property = drm_property_find(dev, out_resp->prop_id);
+ if (!property) {
ret = -ENOENT;
goto done;
}
- property = obj_to_property(obj);
- if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
+ if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+ drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
list_for_each_entry(prop_enum, &property->enum_blob_list, head)
enum_count++;
- } else if (property->flags & DRM_MODE_PROP_BLOB) {
+ } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
list_for_each_entry(prop_blob, &property->enum_blob_list, head)
blob_count++;
}
@@ -3489,7 +3572,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
}
out_resp->count_values = value_count;
- if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
+ if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+ drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
copied = 0;
enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3511,7 +3595,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
out_resp->count_enum_blobs = enum_count;
}
- if (property->flags & DRM_MODE_PROP_BLOB) {
+ if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
copied = 0;
blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3590,7 +3674,6 @@ static void drm_property_destroy_blob(struct drm_device *dev,
int drm_mode_getblob_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
- struct drm_mode_object *obj;
struct drm_mode_get_blob *out_resp = data;
struct drm_property_blob *blob;
int ret = 0;
@@ -3600,12 +3683,11 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
- if (!obj) {
+ blob = drm_property_blob_find(dev, out_resp->blob_id);
+ if (!blob) {
ret = -ENOENT;
goto done;
}
- blob = obj_to_blob(obj);
if (out_resp->length == blob->length) {
blob_ptr = (void __user *)(unsigned long)out_resp->data;
@@ -3667,19 +3749,40 @@ static bool drm_property_change_is_valid(struct drm_property *property,
{
if (property->flags & DRM_MODE_PROP_IMMUTABLE)
return false;
- if (property->flags & DRM_MODE_PROP_RANGE) {
+
+ if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
if (value < property->values[0] || value > property->values[1])
return false;
return true;
- } else if (property->flags & DRM_MODE_PROP_BITMASK) {
+ } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
+ int64_t svalue = U642I64(value);
+ if (svalue < U642I64(property->values[0]) ||
+ svalue > U642I64(property->values[1]))
+ return false;
+ return true;
+ } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
int i;
uint64_t valid_mask = 0;
for (i = 0; i < property->num_values; i++)
valid_mask |= (1ULL << property->values[i]);
return !(value & ~valid_mask);
- } else if (property->flags & DRM_MODE_PROP_BLOB) {
+ } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
/* Only the driver knows */
return true;
+ } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
+ struct drm_mode_object *obj;
+ /* a zero value for an object property translates to null: */
+ if (value == 0)
+ return true;
+ /*
+ * NOTE: use _object_find() directly to bypass restriction on
+ * looking up refcnt'd objects (ie. fb's). For a refcnt'd
+ * object this could race against object finalization, so it
+ * simply tells us that the object *was* valid. Which is good
+ * enough.
+ */
+ obj = _object_find(property->dev, value, property->values[0]);
+ return obj != NULL;
} else {
int i;
for (i = 0; i < property->num_values; i++)
@@ -3987,7 +4090,6 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
struct drm_mode_crtc_lut *crtc_lut = data;
- struct drm_mode_object *obj;
struct drm_crtc *crtc;
void *r_base, *g_base, *b_base;
int size;
@@ -3997,12 +4099,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
- if (!obj) {
+ crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+ if (!crtc) {
ret = -ENOENT;
goto out;
}
- crtc = obj_to_crtc(obj);
if (crtc->funcs->gamma_set == NULL) {
ret = -ENOSYS;
@@ -4061,7 +4162,6 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
struct drm_mode_crtc_lut *crtc_lut = data;
- struct drm_mode_object *obj;
struct drm_crtc *crtc;
void *r_base, *g_base, *b_base;
int size;
@@ -4071,12 +4171,11 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
return -EINVAL;
drm_modeset_lock_all(dev);
- obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
- if (!obj) {
+ crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+ if (!crtc) {
ret = -ENOENT;
goto out;
}
- crtc = obj_to_crtc(obj);
/* memcpy into gamma store */
if (crtc_lut->gamma_size != crtc->gamma_size) {
@@ -4129,7 +4228,6 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
struct drm_mode_crtc_page_flip *page_flip = data;
- struct drm_mode_object *obj;
struct drm_crtc *crtc;
struct drm_framebuffer *fb = NULL, *old_fb = NULL;
struct drm_pending_vblank_event *e = NULL;
@@ -4143,12 +4241,11 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
return -EINVAL;
- obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
- if (!obj)
+ crtc = drm_crtc_find(dev, page_flip->crtc_id);
+ if (!crtc)
return -ENOENT;
- crtc = obj_to_crtc(obj);
- mutex_lock(&crtc->mutex);
+ drm_modeset_lock(&crtc->mutex, NULL);
if (crtc->primary->fb == NULL) {
/* The framebuffer is currently unbound, presumably
* due to a hotplug event, that userspace has not
@@ -4232,7 +4329,7 @@ out:
drm_framebuffer_unreference(fb);
if (old_fb)
drm_framebuffer_unreference(old_fb);
- mutex_unlock(&crtc->mutex);
+ drm_modeset_unlock(&crtc->mutex);
return ret;
}
@@ -4597,6 +4694,7 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
void drm_mode_config_init(struct drm_device *dev)
{
mutex_init(&dev->mode_config.mutex);
+ drm_modeset_lock_init(&dev->mode_config.connection_mutex);
mutex_init(&dev->mode_config.idr_mutex);
mutex_init(&dev->mode_config.fb_lock);
INIT_LIST_HEAD(&dev->mode_config.fb_list);
@@ -4696,5 +4794,6 @@ void drm_mode_config_cleanup(struct drm_device *dev)
}
idr_destroy(&dev->mode_config.crtc_idr);
+ drm_modeset_lock_fini(&dev->mode_config.connection_mutex);
}
EXPORT_SYMBOL(drm_mode_config_cleanup);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 872ba11c4533..78b37f3febd3 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -93,8 +93,10 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
* We can expect this mutex to be locked if we are not panicking.
* Locking is currently fubar in the panic handler.
*/
- if (!oops_in_progress)
+ if (!oops_in_progress) {
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)
@@ -153,20 +155,14 @@ drm_encoder_disable(struct drm_encoder *encoder)
static void __drm_helper_disable_unused_functions(struct drm_device *dev)
{
struct drm_encoder *encoder;
- struct drm_connector *connector;
struct drm_crtc *crtc;
drm_warn_on_modeset_not_all_locked(dev);
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (!connector->encoder)
- continue;
- }
-
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (!drm_helper_encoder_in_use(encoder)) {
drm_encoder_disable(encoder);
- /* disconnector encoder from any connector */
+ /* disconnect encoder from any connector */
encoder->crtc = NULL;
}
}
@@ -349,7 +345,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
continue;
DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n",
- encoder->base.id, drm_get_encoder_name(encoder),
+ encoder->base.id, encoder->name,
mode->base.id, mode->name);
encoder_funcs = encoder->helper_private;
encoder_funcs->mode_set(encoder, mode, adjusted_mode);
@@ -400,8 +396,7 @@ done:
}
EXPORT_SYMBOL(drm_crtc_helper_set_mode);
-
-static int
+static void
drm_crtc_helper_disable(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
@@ -430,7 +425,6 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
}
__drm_helper_disable_unused_functions(dev);
- return 0;
}
/**
@@ -481,7 +475,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
(int)set->num_connectors, set->x, set->y);
} else {
DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id);
- return drm_crtc_helper_disable(set->crtc);
+ drm_crtc_helper_disable(set->crtc);
+ return 0;
}
dev = set->crtc->dev;
@@ -620,11 +615,11 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
}
if (new_crtc) {
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
- connector->base.id, drm_get_connector_name(connector),
+ connector->base.id, connector->name,
new_crtc->base.id);
} else {
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
- connector->base.id, drm_get_connector_name(connector));
+ connector->base.id, connector->name);
}
}
@@ -650,7 +645,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
for (i = 0; i < set->num_connectors; i++) {
DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
- drm_get_connector_name(set->connectors[i]));
+ set->connectors[i]->name);
set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
}
}
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 4b6e6f3ba0a1..08e33b8b13a4 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -206,13 +206,17 @@ i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
* i2c_dp_aux_add_bus() - register an i2c adapter using the aux ch helper
* @adapter: i2c adapter to register
*
- * This registers an i2c adapater that uses dp aux channel as it's underlaying
+ * This registers an i2c adapter that uses dp aux channel as it's underlaying
* transport. The driver needs to fill out the &i2c_algo_dp_aux_data structure
* and store it in the algo_data member of the @adapter argument. This will be
* used by the i2c over dp aux algorithm to drive the hardware.
*
* RETURNS:
* 0 on success, -ERRNO on failure.
+ *
+ * IMPORTANT:
+ * This interface is deprecated, please switch to the new dp aux helpers and
+ * drm_dp_aux_register().
*/
int
i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
@@ -378,7 +382,10 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
* transactions.
*/
for (retry = 0; retry < 7; retry++) {
+
+ mutex_lock(&aux->hw_mutex);
err = aux->transfer(aux, &msg);
+ mutex_unlock(&aux->hw_mutex);
if (err < 0) {
if (err == -EBUSY)
continue;
@@ -592,7 +599,9 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
* before giving up the AUX transaction.
*/
for (retry = 0; retry < 7; retry++) {
+ mutex_lock(&aux->hw_mutex);
err = aux->transfer(aux, msg);
+ mutex_unlock(&aux->hw_mutex);
if (err < 0) {
if (err == -EBUSY)
continue;
@@ -725,13 +734,15 @@ static const struct i2c_algorithm drm_dp_i2c_algo = {
};
/**
- * drm_dp_aux_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
+ * drm_dp_aux_register() - initialise and register aux channel
* @aux: DisplayPort AUX channel
*
* Returns 0 on success or a negative error code on failure.
*/
-int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux)
+int drm_dp_aux_register(struct drm_dp_aux *aux)
{
+ mutex_init(&aux->hw_mutex);
+
aux->ddc.algo = &drm_dp_i2c_algo;
aux->ddc.algo_data = aux;
aux->ddc.retries = 3;
@@ -746,14 +757,14 @@ int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux)
return i2c_add_adapter(&aux->ddc);
}
-EXPORT_SYMBOL(drm_dp_aux_register_i2c_bus);
+EXPORT_SYMBOL(drm_dp_aux_register);
/**
- * drm_dp_aux_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
+ * drm_dp_aux_unregister() - unregister an AUX adapter
* @aux: DisplayPort AUX channel
*/
-void drm_dp_aux_unregister_i2c_bus(struct drm_dp_aux *aux)
+void drm_dp_aux_unregister(struct drm_dp_aux *aux)
{
i2c_del_adapter(&aux->ddc);
}
-EXPORT_SYMBOL(drm_dp_aux_unregister_i2c_bus);
+EXPORT_SYMBOL(drm_dp_aux_unregister);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index d4e3f9d9370f..dfa9769b26b5 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -70,6 +70,8 @@
#define EDID_QUIRK_FORCE_REDUCED_BLANKING (1 << 7)
/* Force 8bpc */
#define EDID_QUIRK_FORCE_8BPC (1 << 8)
+/* Force 12bpc */
+#define EDID_QUIRK_FORCE_12BPC (1 << 9)
struct detailed_mode_closure {
struct drm_connector *connector;
@@ -125,6 +127,9 @@ static struct edid_quirk {
{ "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 },
{ "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 },
+ /* Sony PVM-2541A does up to 12 bpc, but only reports max 8 bpc */
+ { "SNY", 0x2541, EDID_QUIRK_FORCE_12BPC },
+
/* ViewSonic VA2026w */
{ "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING },
@@ -984,9 +989,13 @@ static const u8 edid_header[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
};
- /*
- * Sanity check the header of the base EDID block. Return 8 if the header
- * is perfect, down to 0 if it's totally wrong.
+/**
+ * drm_edid_header_is_valid - sanity check the header of the base EDID block
+ * @raw_edid: pointer to raw base EDID block
+ *
+ * Sanity check the header of the base EDID block.
+ *
+ * Return: 8 if the header is perfect, down to 0 if it's totally wrong.
*/
int drm_edid_header_is_valid(const u8 *raw_edid)
{
@@ -1005,9 +1014,16 @@ module_param_named(edid_fixup, edid_fixup, int, 0400);
MODULE_PARM_DESC(edid_fixup,
"Minimum number of valid EDID header bytes (0-8, default 6)");
-/*
- * Sanity check the EDID block (base or extension). Return 0 if the block
- * doesn't check out, or 1 if it's valid.
+/**
+ * drm_edid_block_valid - Sanity check the EDID block (base or extension)
+ * @raw_edid: pointer to raw EDID block
+ * @block: type of block to validate (0 for base, extension otherwise)
+ * @print_bad_edid: if true, dump bad EDID blocks to the console
+ *
+ * Validate a base or extension EDID block and optionally dump bad blocks to
+ * the console.
+ *
+ * Return: True if the block is valid, false otherwise.
*/
bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid)
{
@@ -1077,6 +1093,8 @@ EXPORT_SYMBOL(drm_edid_block_valid);
* @edid: EDID data
*
* Sanity-check an entire EDID record (including extensions)
+ *
+ * Return: True if the EDID data is valid, false otherwise.
*/
bool drm_edid_is_valid(struct edid *edid)
{
@@ -1096,18 +1114,15 @@ EXPORT_SYMBOL(drm_edid_is_valid);
#define DDC_SEGMENT_ADDR 0x30
/**
- * Get EDID information via I2C.
- *
- * @adapter : i2c device adaptor
+ * drm_do_probe_ddc_edid() - get EDID information via I2C
+ * @adapter: I2C device adaptor
* @buf: EDID data buffer to be filled
* @block: 128 byte EDID block to start fetching from
* @len: EDID data buffer length to fetch
*
- * Returns:
- *
- * 0 on success or -1 on failure.
+ * Try to fetch EDID information by calling I2C driver functions.
*
- * Try to fetch EDID information by calling i2c driver function.
+ * Return: 0 on success or -1 on failure.
*/
static int
drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
@@ -1118,7 +1133,8 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
unsigned char xfers = segment ? 3 : 2;
int ret, retries = 5;
- /* The core i2c driver will automatically retry the transfer if the
+ /*
+ * The core I2C driver will automatically retry the transfer if the
* adapter reports EAGAIN. However, we find that bit-banging transfers
* are susceptible to errors under a heavily loaded machine and
* generate spurious NAKs and timeouts. Retrying the transfer
@@ -1144,10 +1160,10 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
}
};
- /*
- * Avoid sending the segment addr to not upset non-compliant ddc
- * monitors.
- */
+ /*
+ * Avoid sending the segment addr to not upset non-compliant
+ * DDC monitors.
+ */
ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
if (ret == -ENXIO) {
@@ -1216,7 +1232,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
if (i == 4 && print_bad_edid) {
dev_warn(connector->dev->dev,
"%s: Ignoring invalid EDID block %d.\n",
- drm_get_connector_name(connector), j);
+ connector->name, j);
connector->bad_edid_counter++;
}
@@ -1236,7 +1252,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
carp:
if (print_bad_edid) {
dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n",
- drm_get_connector_name(connector), j);
+ connector->name, j);
}
connector->bad_edid_counter++;
@@ -1246,12 +1262,10 @@ out:
}
/**
- * Probe DDC presence.
- * @adapter: i2c adapter to probe
+ * drm_probe_ddc() - probe DDC presence
+ * @adapter: I2C adapter to probe
*
- * Returns:
- *
- * 1 on success
+ * Return: True on success, false on failure.
*/
bool
drm_probe_ddc(struct i2c_adapter *adapter)
@@ -1265,12 +1279,12 @@ EXPORT_SYMBOL(drm_probe_ddc);
/**
* drm_get_edid - get EDID data, if available
* @connector: connector we're probing
- * @adapter: i2c adapter to use for DDC
+ * @adapter: I2C adapter to use for DDC
*
- * Poke the given i2c channel to grab EDID data if possible. If found,
+ * Poke the given I2C channel to grab EDID data if possible. If found,
* attach it to the connector.
*
- * Return edid data or NULL if we couldn't find any.
+ * Return: Pointer to valid EDID or NULL if we couldn't find any.
*/
struct edid *drm_get_edid(struct drm_connector *connector,
struct i2c_adapter *adapter)
@@ -1288,7 +1302,7 @@ EXPORT_SYMBOL(drm_get_edid);
* drm_edid_duplicate - duplicate an EDID and the extensions
* @edid: EDID to duplicate
*
- * Return duplicate edid or NULL on allocation failure.
+ * Return: Pointer to duplicated EDID or NULL on allocation failure.
*/
struct edid *drm_edid_duplicate(const struct edid *edid)
{
@@ -1411,7 +1425,8 @@ mode_is_rb(const struct drm_display_mode *mode)
* @rb: Mode reduced-blanking-ness
*
* Walk the DMT mode list looking for a match for the given parameters.
- * Return a newly allocated copy of the mode, or NULL if not found.
+ *
+ * Return: A newly allocated copy of the mode, or NULL if not found.
*/
struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
int hsize, int vsize, int fresh,
@@ -1595,14 +1610,13 @@ bad_std_timing(u8 a, u8 b)
* @connector: connector of for the EDID block
* @edid: EDID block to scan
* @t: standard timing params
- * @revision: standard timing level
*
* Take the standard timing params (in this case width, aspect, and refresh)
* and convert them into a real mode using CVT/GTF/DMT.
*/
static struct drm_display_mode *
drm_mode_std(struct drm_connector *connector, struct edid *edid,
- struct std_timing *t, int revision)
+ struct std_timing *t)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *m, *mode = NULL;
@@ -1623,7 +1637,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid,
vrefresh_rate = vfreq + 60;
/* the vdisplay is calculated based on the aspect ratio */
if (aspect_ratio == 0) {
- if (revision < 3)
+ if (edid->revision < 3)
vsize = hsize;
else
vsize = (hsize * 10) / 16;
@@ -2140,7 +2154,7 @@ do_established_modes(struct detailed_timing *timing, void *c)
/**
* add_established_modes - get est. modes from EDID and add them
- * @connector: connector of for the EDID block
+ * @connector: connector to add mode(s) to
* @edid: EDID block to scan
*
* Each EDID block contains a bitmap of the supported "established modes" list
@@ -2191,8 +2205,7 @@ do_standard_modes(struct detailed_timing *timing, void *c)
struct drm_display_mode *newmode;
std = &data->data.timings[i];
- newmode = drm_mode_std(connector, edid, std,
- edid->revision);
+ newmode = drm_mode_std(connector, edid, std);
if (newmode) {
drm_mode_probed_add(connector, newmode);
closure->modes++;
@@ -2203,7 +2216,7 @@ do_standard_modes(struct detailed_timing *timing, void *c)
/**
* add_standard_modes - get std. modes from EDID and add them
- * @connector: connector of for the EDID block
+ * @connector: connector to add mode(s) to
* @edid: EDID block to scan
*
* Standard modes can be calculated using the appropriate standard (DMT,
@@ -2221,8 +2234,7 @@ add_standard_modes(struct drm_connector *connector, struct edid *edid)
struct drm_display_mode *newmode;
newmode = drm_mode_std(connector, edid,
- &edid->standard_timings[i],
- edid->revision);
+ &edid->standard_timings[i]);
if (newmode) {
drm_mode_probed_add(connector, newmode);
modes++;
@@ -2425,7 +2437,7 @@ cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)
* drm_match_cea_mode - look for a CEA mode matching given mode
* @to_match: display mode
*
- * Returns the CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861
+ * Return: The CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861
* mode.
*/
u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
@@ -2452,6 +2464,22 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)
}
EXPORT_SYMBOL(drm_match_cea_mode);
+/**
+ * drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to
+ * the input VIC from the CEA mode list
+ * @video_code: ID given to each of the CEA modes
+ *
+ * Returns picture aspect ratio
+ */
+enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code)
+{
+ /* return picture aspect ratio for video_code - 1 to access the
+ * right array element
+ */
+ return edid_cea_modes[video_code-1].picture_aspect_ratio;
+}
+EXPORT_SYMBOL(drm_get_cea_aspect_ratio);
+
/*
* Calculate the alternate clock for HDMI modes (those from the HDMI vendor
* specific block).
@@ -3023,11 +3051,9 @@ monitor_name(struct detailed_timing *t, void *data)
* @connector: connector corresponding to the HDMI/DP sink
* @edid: EDID to parse
*
- * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver.
- * Some ELD fields are left to the graphics driver caller:
- * - Conn_Type
- * - HDCP
- * - Port_ID
+ * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
+ * Conn_Type, HDCP and Port_ID ELD fields are left for the graphics driver to
+ * fill in.
*/
void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
{
@@ -3111,9 +3137,10 @@ EXPORT_SYMBOL(drm_edid_to_eld);
* @sads: pointer that will be set to the extracted SADs
*
* Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it.
- * Note: returned pointer needs to be kfreed
*
- * Return number of found SADs or negative number on error.
+ * Note: The returned pointer needs to be freed using kfree().
+ *
+ * Return: The number of found SADs or negative number on error.
*/
int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads)
{
@@ -3170,9 +3197,11 @@ EXPORT_SYMBOL(drm_edid_to_sad);
* @sadb: pointer to the speaker block
*
* Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it.
- * Note: returned pointer needs to be kfreed
*
- * Return number of found Speaker Allocation Blocks or negative number on error.
+ * Note: The returned pointer needs to be freed using kfree().
+ *
+ * Return: The number of found Speaker Allocation Blocks or negative number on
+ * error.
*/
int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
{
@@ -3204,10 +3233,9 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
/* Speaker Allocation Data Block */
if (dbl == 3) {
- *sadb = kmalloc(dbl, GFP_KERNEL);
+ *sadb = kmemdup(&db[1], dbl, GFP_KERNEL);
if (!*sadb)
return -ENOMEM;
- memcpy(*sadb, &db[1], dbl);
count = dbl;
break;
}
@@ -3219,9 +3247,12 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
/**
- * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond
+ * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay
* @connector: connector associated with the HDMI/DP sink
* @mode: the display mode
+ *
+ * Return: The HDMI/DP sink's audio-video sync delay in milliseconds or 0 if
+ * the sink doesn't support audio or video.
*/
int drm_av_sync_delay(struct drm_connector *connector,
struct drm_display_mode *mode)
@@ -3263,6 +3294,9 @@ EXPORT_SYMBOL(drm_av_sync_delay);
*
* It's possible for one encoder to be associated with multiple HDMI/DP sinks.
* The policy is now hard coded to simply use the first HDMI/DP sink's ELD.
+ *
+ * Return: The connector associated with the first HDMI/DP sink that has ELD
+ * attached to it.
*/
struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
struct drm_display_mode *mode)
@@ -3270,6 +3304,8 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
struct drm_connector *connector;
struct drm_device *dev = encoder->dev;
+ WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder == encoder && connector->eld[0])
return connector;
@@ -3279,11 +3315,12 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
EXPORT_SYMBOL(drm_select_eld);
/**
- * drm_detect_hdmi_monitor - detect whether monitor is hdmi.
+ * drm_detect_hdmi_monitor - detect whether monitor is HDMI
* @edid: monitor EDID information
*
* Parse the CEA extension according to CEA-861-B.
- * Return true if HDMI, false if not or unknown.
+ *
+ * Return: True if the monitor is HDMI, false if not or unknown.
*/
bool drm_detect_hdmi_monitor(struct edid *edid)
{
@@ -3321,6 +3358,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor);
* audio format, assume at least 'basic audio' support, even if 'basic
* audio' is not defined in EDID.
*
+ * Return: True if the monitor supports audio, false otherwise.
*/
bool drm_detect_monitor_audio(struct edid *edid)
{
@@ -3364,6 +3402,8 @@ EXPORT_SYMBOL(drm_detect_monitor_audio);
* Check whether the monitor reports the RGB quantization range selection
* as supported. The AVI infoframe can then be used to inform the monitor
* which quantization range (full or limited) is used.
+ *
+ * Return: True if the RGB quantization range is selectable, false otherwise.
*/
bool drm_rgb_quant_range_selectable(struct edid *edid)
{
@@ -3390,16 +3430,119 @@ bool drm_rgb_quant_range_selectable(struct edid *edid)
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.
+ *
+ * Parse the CEA extension according to CEA-861-B.
+ * Return true if HDMI deep color supported, false if not or unknown.
+ */
+static bool drm_assign_hdmi_deep_color_info(struct edid *edid,
+ struct drm_display_info *info,
+ struct drm_connector *connector)
+{
+ u8 *edid_ext, *hdmi;
+ int i;
+ int start_offset, end_offset;
+ unsigned int dc_bpc = 0;
+
+ edid_ext = drm_find_cea_extension(edid);
+ if (!edid_ext)
+ return false;
+
+ if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
+ return false;
+
+ /*
+ * Because HDMI identifier is in Vendor Specific Block,
+ * search it from all data blocks of CEA extension.
+ */
+ for_each_cea_db(edid_ext, i, start_offset, end_offset) {
+ if (cea_db_is_hdmi_vsdb(&edid_ext[i])) {
+ /* HDMI supports at least 8 bpc */
+ info->bpc = 8;
+
+ hdmi = &edid_ext[i];
+ if (cea_db_payload_len(hdmi) < 6)
+ return false;
+
+ if (hdmi[6] & DRM_EDID_HDMI_DC_30) {
+ dc_bpc = 10;
+ info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30;
+ DRM_DEBUG("%s: HDMI sink does deep color 30.\n",
+ connector->name);
+ }
+
+ if (hdmi[6] & DRM_EDID_HDMI_DC_36) {
+ dc_bpc = 12;
+ info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36;
+ DRM_DEBUG("%s: HDMI sink does deep color 36.\n",
+ connector->name);
+ }
+
+ if (hdmi[6] & DRM_EDID_HDMI_DC_48) {
+ dc_bpc = 16;
+ info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48;
+ DRM_DEBUG("%s: HDMI sink does deep color 48.\n",
+ connector->name);
+ }
+
+ if (dc_bpc > 0) {
+ DRM_DEBUG("%s: Assigning HDMI sink color depth as %d bpc.\n",
+ connector->name, dc_bpc);
+ info->bpc = dc_bpc;
+
+ /*
+ * Deep color support mandates RGB444 support for all video
+ * modes and forbids YCRCB422 support for all video modes per
+ * HDMI 1.3 spec.
+ */
+ info->color_formats = DRM_COLOR_FORMAT_RGB444;
+
+ /* YCRCB444 is optional according to spec. */
+ if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
+ info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+ DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n",
+ connector->name);
+ }
+
+ /*
+ * Spec says that if any deep color mode is supported at all,
+ * then deep color 36 bit must be supported.
+ */
+ if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) {
+ DRM_DEBUG("%s: HDMI sink should do DC_36, but does not!\n",
+ connector->name);
+ }
+
+ return true;
+ }
+ else {
+ DRM_DEBUG("%s: No deep color support on this HDMI sink.\n",
+ connector->name);
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
* drm_add_display_info - pull display info out if present
* @edid: EDID data
* @info: display info (attached to connector)
+ * @connector: connector whose edid is used to build display info
*
* Grab any available display info and stuff it into the drm_display_info
* structure that's part of the connector. Useful for tracking bpp and
* color spaces.
*/
static void drm_add_display_info(struct edid *edid,
- struct drm_display_info *info)
+ struct drm_display_info *info,
+ struct drm_connector *connector)
{
u8 *edid_ext;
@@ -3429,6 +3572,9 @@ static void drm_add_display_info(struct edid *edid,
info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
}
+ /* HDMI deep color modes supported? Assign to info, if so */
+ drm_assign_hdmi_deep_color_info(edid, info, connector);
+
/* Only defined for 1.4 with digital displays */
if (edid->revision < 4)
return;
@@ -3458,6 +3604,9 @@ static void drm_add_display_info(struct edid *edid,
break;
}
+ DRM_DEBUG("%s: Assigning EDID-1.4 digital sink color depth as %d bpc.\n",
+ connector->name, info->bpc);
+
info->color_formats |= DRM_COLOR_FORMAT_RGB444;
if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
@@ -3468,11 +3617,11 @@ static void drm_add_display_info(struct edid *edid,
/**
* drm_add_edid_modes - add modes from EDID data, if available
* @connector: connector we're probing
- * @edid: edid data
+ * @edid: EDID data
*
* Add the specified modes to the connector's mode list.
*
- * Return number of modes added or 0 if we couldn't find any.
+ * Return: The number of modes added or 0 if we couldn't find any.
*/
int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
{
@@ -3484,7 +3633,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
}
if (!drm_edid_is_valid(edid)) {
dev_warn(connector->dev->dev, "%s: EDID invalid.\n",
- drm_get_connector_name(connector));
+ connector->name);
return 0;
}
@@ -3516,11 +3665,14 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
edid_fixup_preferred(connector, quirks);
- drm_add_display_info(edid, &connector->display_info);
+ drm_add_display_info(edid, &connector->display_info, connector);
if (quirks & EDID_QUIRK_FORCE_8BPC)
connector->display_info.bpc = 8;
+ if (quirks & EDID_QUIRK_FORCE_12BPC)
+ connector->display_info.bpc = 12;
+
return num_modes;
}
EXPORT_SYMBOL(drm_add_edid_modes);
@@ -3534,7 +3686,7 @@ EXPORT_SYMBOL(drm_add_edid_modes);
* Add the specified modes to the connector's mode list. Only when the
* hdisplay/vdisplay is not beyond the given limit, it will be added.
*
- * Return number of modes added or 0 if we couldn't find any.
+ * Return: The number of modes added or 0 if we couldn't find any.
*/
int drm_add_modes_noedid(struct drm_connector *connector,
int hdisplay, int vdisplay)
@@ -3573,13 +3725,22 @@ int drm_add_modes_noedid(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_add_modes_noedid);
+/**
+ * drm_set_preferred_mode - Sets the preferred mode of a connector
+ * @connector: connector whose mode list should be processed
+ * @hpref: horizontal resolution of preferred mode
+ * @vpref: vertical resolution of preferred mode
+ *
+ * Marks a mode as preferred if it matches the resolution specified by @hpref
+ * and @vpref.
+ */
void drm_set_preferred_mode(struct drm_connector *connector,
int hpref, int vpref)
{
struct drm_display_mode *mode;
list_for_each_entry(mode, &connector->probed_modes, head) {
- if (mode->hdisplay == hpref &&
+ if (mode->hdisplay == hpref &&
mode->vdisplay == vpref)
mode->type |= DRM_MODE_TYPE_PREFERRED;
}
@@ -3592,7 +3753,7 @@ EXPORT_SYMBOL(drm_set_preferred_mode);
* @frame: HDMI AVI infoframe
* @mode: DRM display mode
*
- * Returns 0 on success or a negative error code on failure.
+ * Return: 0 on success or a negative error code on failure.
*/
int
drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
@@ -3613,6 +3774,12 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
frame->video_code = drm_match_cea_mode(mode);
frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
+
+ /* Populate picture aspect ratio from CEA mode list */
+ if (frame->video_code > 0)
+ frame->picture_aspect = drm_get_cea_aspect_ratio(
+ frame->video_code);
+
frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;
@@ -3657,7 +3824,7 @@ s3d_structure_from_display_mode(const struct drm_display_mode *mode)
* 4k or stereoscopic 3D mode. So when giving any other mode as input this
* function will return -EINVAL, error that can be safely ignored.
*
- * Returns 0 on success or a negative error code on failure.
+ * Return: 0 on success or a negative error code on failure.
*/
int
drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c
index 1b4c7a5442c5..0a235fe61c9b 100644
--- a/drivers/gpu/drm/drm_edid_load.c
+++ b/drivers/gpu/drm/drm_edid_load.c
@@ -31,8 +31,9 @@ module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
"from built-in data or /lib/firmware instead. ");
-#define GENERIC_EDIDS 5
+#define GENERIC_EDIDS 6
static const char *generic_edid_name[GENERIC_EDIDS] = {
+ "edid/800x600.bin",
"edid/1024x768.bin",
"edid/1280x1024.bin",
"edid/1600x1200.bin",
@@ -44,6 +45,24 @@ static const u8 generic_edid[GENERIC_EDIDS][128] = {
{
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x16, 0x01, 0x03, 0x6d, 0x1b, 0x14, 0x78,
+ 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+ 0x20, 0x50, 0x54, 0x01, 0x00, 0x00, 0x45, 0x40,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xa0, 0x0f,
+ 0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80,
+ 0x14, 0x00, 0x15, 0xd0, 0x10, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+ 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+ 0x3d, 0x24, 0x26, 0x05, 0x00, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
+ 0x56, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xc2,
+ },
+ {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78,
0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
@@ -242,7 +261,7 @@ out:
int drm_load_edid_firmware(struct drm_connector *connector)
{
- const char *connector_name = drm_get_connector_name(connector);
+ const char *connector_name = connector->name;
char *edidname = edid_firmware, *last, *colon;
int ret;
struct edid *edid;
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index 61b5a47ad239..f27c883be391 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -429,13 +429,8 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini);
*/
void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma)
{
- if (fbdev_cma) {
- struct drm_device *dev = fbdev_cma->fb_helper.dev;
-
- drm_modeset_lock_all(dev);
- drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper);
- drm_modeset_unlock_all(dev);
- }
+ if (fbdev_cma)
+ drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev_cma->fb_helper);
}
EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 04d3fd3658f3..d5d8cea1a679 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -45,13 +45,13 @@ static LIST_HEAD(kernel_fb_helper_list);
* DOC: fbdev helpers
*
* The fb helper functions are useful to provide an fbdev on top of a drm kernel
- * mode setting driver. They can be used mostly independantely from the crtc
+ * mode setting driver. They can be used mostly independently from the crtc
* 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 beheviour can override the
+ * 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().
*
* At runtime drivers should restore the fbdev console by calling
@@ -59,7 +59,7 @@ static LIST_HEAD(kernel_fb_helper_list);
* should also notify the fb helper code from updates to the output
* configuration by calling drm_fb_helper_hotplug_event(). For easier
* integration with the output polling code in drm_crtc_helper.c the modeset
- * code proves a ->output_poll_changed callback.
+ * code provides a ->output_poll_changed callback.
*
* All other functions exported by the fb helper library can be used to
* implement the fbdev driver interface by the driver.
@@ -120,7 +120,7 @@ static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
mode = &fb_helper_conn->cmdline_mode;
/* do something on return - turn off connector maybe */
- if (fb_get_options(drm_get_connector_name(connector), &option))
+ if (fb_get_options(connector->name, &option))
continue;
if (drm_mode_parse_command_line_for_connector(option,
@@ -142,12 +142,12 @@ static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
}
DRM_INFO("forcing %s connector %s\n",
- drm_get_connector_name(connector), s);
+ connector->name, s);
connector->force = mode->force;
}
DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
- drm_get_connector_name(connector),
+ connector->name,
mode->xres, mode->yres,
mode->refresh_specified ? mode->refresh : 60,
mode->rb ? " reduced blanking" : "",
@@ -273,15 +273,7 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
}
EXPORT_SYMBOL(drm_fb_helper_debug_leave);
-/**
- * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
- * @fb_helper: fbcon to restore
- *
- * This should be called from driver's drm ->lastclose callback
- * when implementing an fbcon on top of kms using this helper. This ensures that
- * the user isn't greeted with a black screen when e.g. X dies.
- */
-bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
{
struct drm_device *dev = fb_helper->dev;
struct drm_plane *plane;
@@ -311,7 +303,40 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
}
return error;
}
-EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
+/**
+ * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
+ * @fb_helper: fbcon to restore
+ *
+ * This should be called from driver's drm ->lastclose callback
+ * when implementing an fbcon on top of kms using this helper. This ensures that
+ * the user isn't greeted with a black screen when e.g. X dies.
+ *
+ * Use this variant if you need to bypass locking (panic), or already
+ * hold all modeset locks. Otherwise use drm_fb_helper_restore_fbdev_mode_unlocked()
+ */
+static bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+{
+ return restore_fbdev_mode(fb_helper);
+}
+
+/**
+ * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
+ * @fb_helper: fbcon to restore
+ *
+ * This should be called from driver's drm ->lastclose callback
+ * when implementing an fbcon on top of kms using this helper. This ensures that
+ * the user isn't greeted with a black screen when e.g. X dies.
+ */
+bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+{
+ struct drm_device *dev = fb_helper->dev;
+ bool ret;
+ drm_modeset_lock_all(dev);
+ ret = restore_fbdev_mode(fb_helper);
+ drm_modeset_unlock_all(dev);
+ return ret;
+}
+EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
/*
* restore fbcon display for all kms driver's using this helper, used for sysrq
@@ -326,12 +351,25 @@ static bool drm_fb_helper_force_kernel_mode(void)
return false;
list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
- if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+ struct drm_device *dev = helper->dev;
+
+ 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:
+ */
+ if (!mutex_trylock(&dev->mode_config.mutex)) {
+ error = true;
continue;
+ }
ret = drm_fb_helper_restore_fbdev_mode(helper);
if (ret)
error = true;
+
+ mutex_unlock(&dev->mode_config.mutex);
}
return error;
}
@@ -811,7 +849,6 @@ EXPORT_SYMBOL(drm_fb_helper_check_var);
int drm_fb_helper_set_par(struct fb_info *info)
{
struct drm_fb_helper *fb_helper = info->par;
- struct drm_device *dev = fb_helper->dev;
struct fb_var_screeninfo *var = &info->var;
if (var->pixclock != 0) {
@@ -819,9 +856,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
return -EINVAL;
}
- drm_modeset_lock_all(dev);
- drm_fb_helper_restore_fbdev_mode(fb_helper);
- drm_modeset_unlock_all(dev);
+ drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
if (fb_helper->delayed_hotplug) {
fb_helper->delayed_hotplug = false;
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index e1eba0b7cd45..021fe5d11df5 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -43,8 +43,7 @@
DEFINE_MUTEX(drm_global_mutex);
EXPORT_SYMBOL(drm_global_mutex);
-static int drm_open_helper(struct inode *inode, struct file *filp,
- struct drm_minor *minor);
+static int drm_open_helper(struct file *filp, struct drm_minor *minor);
static int drm_setup(struct drm_device * dev)
{
@@ -95,7 +94,7 @@ int drm_open(struct inode *inode, struct file *filp)
/* share address_space across all char-devs of a single device */
filp->f_mapping = dev->anon_inode->i_mapping;
- retcode = drm_open_helper(inode, filp, minor);
+ retcode = drm_open_helper(filp, minor);
if (retcode)
goto err_undo;
if (need_setup) {
@@ -171,7 +170,6 @@ static int drm_cpu_valid(void)
/**
* Called whenever a process opens /dev/drm.
*
- * \param inode device inode.
* \param filp file pointer.
* \param minor acquired minor-object.
* \return zero on success or a negative number on failure.
@@ -179,8 +177,7 @@ static int drm_cpu_valid(void)
* Creates and initializes a drm_file structure for the file private data in \p
* filp and add it into the double linked list in \p dev.
*/
-static int drm_open_helper(struct inode *inode, struct file *filp,
- struct drm_minor *minor)
+static int drm_open_helper(struct file *filp, struct drm_minor *minor)
{
struct drm_device *dev = minor->dev;
struct drm_file *priv;
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 9909bef59800..f7d71190aad5 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -474,21 +474,10 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask)
goto fail;
pages[i] = p;
- /* There is a hypothetical issue w/ drivers that require
- * buffer memory in the low 4GB.. if the pages are un-
- * pinned, and swapped out, they can end up swapped back
- * in above 4GB. If pages are already in memory, then
- * shmem_read_mapping_page_gfp will ignore the gfpmask,
- * even if the already in-memory page disobeys the mask.
- *
- * It is only a theoretical issue today, because none of
- * the devices with this limitation can be populated with
- * enough memory to trigger the issue. But this BUG_ON()
- * is here as a reminder in case the problem with
- * shmem_read_mapping_page_gfp() isn't solved by the time
- * it does become a real issue.
- *
- * See this thread: http://lkml.org/lkml/2011/7/11/238
+ /* Make sure shmem keeps __GFP_DMA32 allocated pages in the
+ * correct region during swapin. Note that this requires
+ * __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) &&
(page_to_pfn(p) >= 0x00100000UL));
diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c
index 7473035dd28b..86feedd5e6f6 100644
--- a/drivers/gpu/drm/drm_info.c
+++ b/drivers/gpu/drm/drm_info.c
@@ -47,18 +47,16 @@ int drm_name_info(struct seq_file *m, void *data)
struct drm_minor *minor = node->minor;
struct drm_device *dev = minor->dev;
struct drm_master *master = minor->master;
- const char *bus_name;
if (!master)
return 0;
- bus_name = dev->driver->bus->get_name(dev);
if (master->unique) {
seq_printf(m, "%s %s %s\n",
- bus_name,
+ dev->driver->name,
dev_name(dev->dev), master->unique);
} else {
seq_printf(m, "%s %s\n",
- bus_name, dev_name(dev->dev));
+ dev->driver->name, dev_name(dev->dev));
}
return 0;
}
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 93a42040bedb..69c61f392e66 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -72,9 +72,6 @@ static void
drm_unset_busid(struct drm_device *dev,
struct drm_master *master)
{
- kfree(dev->devname);
- dev->devname = NULL;
-
kfree(master->unique);
master->unique = NULL;
master->unique_len = 0;
@@ -93,7 +90,8 @@ drm_unset_busid(struct drm_device *dev,
* Copies the bus id from userspace into drm_device::unique, and verifies that
* it matches the device this DRM is attached to (EINVAL otherwise). Deprecated
* in interface version 1.1 and will return EBUSY when setversion has requested
- * version 1.1 or greater.
+ * 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,
struct drm_file *file_priv)
@@ -108,10 +106,13 @@ int drm_setunique(struct drm_device *dev, void *data,
if (!u->unique_len || u->unique_len > 1024)
return -EINVAL;
- if (!dev->driver->bus->set_unique)
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
+
+ if (WARN_ON(!dev->pdev))
return -EINVAL;
- ret = dev->driver->bus->set_unique(dev, master, u);
+ ret = drm_pci_set_unique(dev, master, u);
if (ret)
goto err;
@@ -130,13 +131,25 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
if (master->unique != NULL)
drm_unset_busid(dev, master);
- ret = dev->driver->bus->set_busid(dev, master);
- if (ret)
- goto err;
+ if (dev->driver->bus && dev->driver->bus->set_busid) {
+ ret = dev->driver->bus->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 "
+ "%ps. Use drm_dev_set_unique() to set the unique "
+ "name explicitly.", dev->driver))
+ return -EINVAL;
+
+ master->unique = kstrdup(dev->unique, GFP_KERNEL);
+ if (master->unique)
+ master->unique_len = strlen(dev->unique);
+ }
+
return 0;
-err:
- drm_unset_busid(dev, master);
- return ret;
}
/**
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index ec5c3f4cdd01..0de123afdb34 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -1,6 +1,5 @@
-/**
- * \file drm_irq.c
- * IRQ support
+/*
+ * drm_irq.c IRQ and vblank support
*
* \author Rickard E. (Rik) Faith <faith@valinux.com>
* \author Gareth Hughes <gareth@valinux.com>
@@ -56,33 +55,6 @@
*/
#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000
-/**
- * Get interrupt from bus id.
- *
- * \param inode device inode.
- * \param file_priv DRM file private.
- * \param cmd command.
- * \param arg user argument, pointing to a drm_irq_busid structure.
- * \return zero on success or a negative number on failure.
- *
- * Finds the PCI device with the specified bus id and gets its IRQ number.
- * This IOCTL is deprecated, and will now return EINVAL for any busid not equal
- * to that of the device that this DRM instance attached to.
- */
-int drm_irq_by_busid(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- struct drm_irq_busid *p = data;
-
- if (!dev->driver->bus->irq_by_busid)
- return -EINVAL;
-
- if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
- return -EINVAL;
-
- return dev->driver->bus->irq_by_busid(dev, p);
-}
-
/*
* Clear vblank timestamp buffer for a crtc.
*/
@@ -167,33 +139,40 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc)
static void vblank_disable_fn(unsigned long arg)
{
- struct drm_device *dev = (struct drm_device *)arg;
+ struct drm_vblank_crtc *vblank = (void *)arg;
+ struct drm_device *dev = vblank->dev;
unsigned long irqflags;
- int i;
+ int crtc = vblank->crtc;
if (!dev->vblank_disable_allowed)
return;
- for (i = 0; i < dev->num_crtcs; i++) {
- spin_lock_irqsave(&dev->vbl_lock, irqflags);
- if (atomic_read(&dev->vblank[i].refcount) == 0 &&
- dev->vblank[i].enabled) {
- DRM_DEBUG("disabling vblank on crtc %d\n", i);
- vblank_disable_and_save(dev, i);
- }
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
+ if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) {
+ DRM_DEBUG("disabling vblank on crtc %d\n", crtc);
+ vblank_disable_and_save(dev, crtc);
}
+ spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
}
+/**
+ * drm_vblank_cleanup - cleanup vblank support
+ * @dev: DRM device
+ *
+ * This function cleans up any resources allocated in drm_vblank_init.
+ */
void drm_vblank_cleanup(struct drm_device *dev)
{
+ int crtc;
+
/* Bail if the driver didn't call drm_vblank_init() */
if (dev->num_crtcs == 0)
return;
- del_timer_sync(&dev->vblank_disable_timer);
-
- vblank_disable_fn((unsigned long)dev);
+ for (crtc = 0; crtc < dev->num_crtcs; crtc++) {
+ del_timer_sync(&dev->vblank[crtc].disable_timer);
+ vblank_disable_fn((unsigned long)&dev->vblank[crtc]);
+ }
kfree(dev->vblank);
@@ -201,12 +180,20 @@ void drm_vblank_cleanup(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_vblank_cleanup);
+/**
+ * drm_vblank_init - initialize vblank support
+ * @dev: drm_device
+ * @num_crtcs: number of crtcs supported by @dev
+ *
+ * This function initializes vblank support for @num_crtcs display pipelines.
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
+ */
int drm_vblank_init(struct drm_device *dev, int num_crtcs)
{
int i, ret = -ENOMEM;
- setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,
- (unsigned long)dev);
spin_lock_init(&dev->vbl_lock);
spin_lock_init(&dev->vblank_time_lock);
@@ -216,8 +203,13 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
if (!dev->vblank)
goto err;
- for (i = 0; i < num_crtcs; i++)
+ 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]);
+ }
DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
@@ -261,42 +253,42 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state)
}
/**
- * Install IRQ handler.
- *
- * \param dev DRM device.
+ * drm_irq_install - install IRQ handler
+ * @dev: DRM device
+ * @irq: IRQ number to install the handler for
*
* Initializes the IRQ related data. Installs the handler, calling the driver
- * \c irq_preinstall() and \c irq_postinstall() functions
- * before and after the installation.
+ * irq_preinstall() and irq_postinstall() functions before and after the
+ * installation.
+ *
+ * This is the simplified helper interface provided for drivers with no special
+ * needs. Drivers which need to install interrupt handlers for multiple
+ * interrupts must instead set drm_device->irq_enabled to signal the DRM core
+ * that vblank interrupts are available.
+ *
+ * Returns:
+ * Zero on success or a negative error code on failure.
*/
-int drm_irq_install(struct drm_device *dev)
+int drm_irq_install(struct drm_device *dev, int irq)
{
int ret;
unsigned long sh_flags = 0;
- char *irqname;
if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
return -EINVAL;
- if (drm_dev_to_irq(dev) == 0)
+ if (irq == 0)
return -EINVAL;
- mutex_lock(&dev->struct_mutex);
-
/* Driver must have been initialized */
- if (!dev->dev_private) {
- mutex_unlock(&dev->struct_mutex);
+ if (!dev->dev_private)
return -EINVAL;
- }
- if (dev->irq_enabled) {
- mutex_unlock(&dev->struct_mutex);
+ if (dev->irq_enabled)
return -EBUSY;
- }
dev->irq_enabled = true;
- mutex_unlock(&dev->struct_mutex);
- DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev));
+ DRM_DEBUG("irq=%d\n", irq);
/* Before installing handler */
if (dev->driver->irq_preinstall)
@@ -306,18 +298,11 @@ int drm_irq_install(struct drm_device *dev)
if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))
sh_flags = IRQF_SHARED;
- if (dev->devname)
- irqname = dev->devname;
- else
- irqname = dev->driver->name;
-
- ret = request_irq(drm_dev_to_irq(dev), dev->driver->irq_handler,
- sh_flags, irqname, dev);
+ ret = request_irq(irq, dev->driver->irq_handler,
+ sh_flags, dev->driver->name, dev);
if (ret < 0) {
- mutex_lock(&dev->struct_mutex);
dev->irq_enabled = false;
- mutex_unlock(&dev->struct_mutex);
return ret;
}
@@ -329,12 +314,12 @@ int drm_irq_install(struct drm_device *dev)
ret = dev->driver->irq_postinstall(dev);
if (ret < 0) {
- mutex_lock(&dev->struct_mutex);
dev->irq_enabled = false;
- mutex_unlock(&dev->struct_mutex);
if (!drm_core_check_feature(dev, DRIVER_MODESET))
vga_client_register(dev->pdev, NULL, NULL, NULL);
- free_irq(drm_dev_to_irq(dev), dev);
+ free_irq(irq, dev);
+ } else {
+ dev->irq = irq;
}
return ret;
@@ -342,11 +327,20 @@ int drm_irq_install(struct drm_device *dev)
EXPORT_SYMBOL(drm_irq_install);
/**
- * Uninstall the IRQ handler.
+ * drm_irq_uninstall - uninstall the IRQ handler
+ * @dev: DRM device
+ *
+ * Calls the driver's irq_uninstall() function and unregisters the IRQ handler.
+ * This should only be called by drivers which used drm_irq_install() to set up
+ * their interrupt handler. Other drivers must only reset
+ * drm_device->irq_enabled to false.
*
- * \param dev DRM device.
+ * Note that for kernel modesetting drivers it is a bug if this function fails.
+ * The sanity checks are only to catch buggy user modesetting drivers which call
+ * the same function through an ioctl.
*
- * Calls the driver's \c irq_uninstall() function, and stops the irq.
+ * Returns:
+ * Zero on success or a negative error code on failure.
*/
int drm_irq_uninstall(struct drm_device *dev)
{
@@ -357,10 +351,8 @@ int drm_irq_uninstall(struct drm_device *dev)
if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
return -EINVAL;
- mutex_lock(&dev->struct_mutex);
irq_enabled = dev->irq_enabled;
dev->irq_enabled = false;
- mutex_unlock(&dev->struct_mutex);
/*
* Wake up any waiters so they don't hang.
@@ -379,7 +371,7 @@ int drm_irq_uninstall(struct drm_device *dev)
if (!irq_enabled)
return -EINVAL;
- DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev));
+ DRM_DEBUG("irq=%d\n", dev->irq);
if (!drm_core_check_feature(dev, DRIVER_MODESET))
vga_client_register(dev->pdev, NULL, NULL, NULL);
@@ -387,13 +379,13 @@ int drm_irq_uninstall(struct drm_device *dev)
if (dev->driver->irq_uninstall)
dev->driver->irq_uninstall(dev);
- free_irq(drm_dev_to_irq(dev), dev);
+ free_irq(dev->irq, dev);
return 0;
}
EXPORT_SYMBOL(drm_irq_uninstall);
-/**
+/*
* IRQ control ioctl.
*
* \param inode device inode.
@@ -408,43 +400,52 @@ int drm_control(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_control *ctl = data;
+ int ret = 0, irq;
/* if we haven't irq we fallback for compatibility reasons -
* this used to be a separate function in drm_dma.h
*/
+ if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
+ return 0;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return 0;
+ /* UMS was only ever support on pci devices. */
+ if (WARN_ON(!dev->pdev))
+ return -EINVAL;
switch (ctl->func) {
case DRM_INST_HANDLER:
- if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
- return 0;
- if (drm_core_check_feature(dev, DRIVER_MODESET))
- return 0;
+ irq = dev->pdev->irq;
+
if (dev->if_version < DRM_IF_VERSION(1, 2) &&
- ctl->irq != drm_dev_to_irq(dev))
+ ctl->irq != irq)
return -EINVAL;
- return drm_irq_install(dev);
+ mutex_lock(&dev->struct_mutex);
+ ret = drm_irq_install(dev, irq);
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
case DRM_UNINST_HANDLER:
- if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
- return 0;
- if (drm_core_check_feature(dev, DRIVER_MODESET))
- return 0;
- return drm_irq_uninstall(dev);
+ mutex_lock(&dev->struct_mutex);
+ ret = drm_irq_uninstall(dev);
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
default:
return -EINVAL;
}
}
/**
- * drm_calc_timestamping_constants - Calculate vblank timestamp constants
- *
- * @crtc drm_crtc whose timestamp constants should be updated.
- * @mode display mode containing the scanout timings
+ * drm_calc_timestamping_constants - calculate vblank timestamp constants
+ * @crtc: drm_crtc whose timestamp constants should be updated.
+ * @mode: display mode containing the scanout timings
*
* Calculate and store various constants which are later
* needed by vblank and swap-completion timestamping, e.g,
* by drm_calc_vbltimestamp_from_scanoutpos(). They are
- * derived from crtc's true scanout timing, so they take
+ * derived from CRTC's true scanout timing, so they take
* things like panel scaling or other adjustments into account.
*/
void drm_calc_timestamping_constants(struct drm_crtc *crtc,
@@ -489,11 +490,22 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,
EXPORT_SYMBOL(drm_calc_timestamping_constants);
/**
- * drm_calc_vbltimestamp_from_scanoutpos - helper routine for kms
- * drivers. Implements calculation of exact vblank timestamps from
- * given drm_display_mode timings and current video scanout position
- * of a crtc. This can be called from within get_vblank_timestamp()
- * implementation of a kms driver to implement the actual timestamping.
+ * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
+ * @dev: DRM device
+ * @crtc: Which CRTC's vblank timestamp to retrieve
+ * @max_error: Desired maximum allowable error in timestamps (nanosecs)
+ * On return contains true maximum error of timestamp
+ * @vblank_time: Pointer to struct timeval which should receive the timestamp
+ * @flags: Flags to pass to driver:
+ * 0 = Default,
+ * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler
+ * @refcrtc: CRTC which defines scanout timing
+ * @mode: mode which defines the scanout timings
+ *
+ * Implements calculation of exact vblank timestamps from given drm_display_mode
+ * timings and current video scanout position of a CRTC. This can be called from
+ * within get_vblank_timestamp() implementation of a kms driver to implement the
+ * actual timestamping.
*
* Should return timestamps conforming to the OML_sync_control OpenML
* extension specification. The timestamp corresponds to the end of
@@ -508,21 +520,11 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
* returns as no operation if a doublescan or interlaced video mode is
* active. Higher level code is expected to handle this.
*
- * @dev: DRM device.
- * @crtc: Which crtc's vblank timestamp to retrieve.
- * @max_error: Desired maximum allowable error in timestamps (nanosecs).
- * On return contains true maximum error of timestamp.
- * @vblank_time: Pointer to struct timeval which should receive the timestamp.
- * @flags: Flags to pass to driver:
- * 0 = Default.
- * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler.
- * @refcrtc: drm_crtc* of crtc which defines scanout timing.
- * @mode: mode which defines the scanout timings
- *
- * Returns negative value on error, failure or if not supported in current
+ * Returns:
+ * Negative value on error, failure or if not supported in current
* video mode:
*
- * -EINVAL - Invalid crtc.
+ * -EINVAL - Invalid CRTC.
* -EAGAIN - Temporary unavailable, e.g., called before initial modeset.
* -ENOTSUPP - Function not supported in current display mode.
* -EIO - Failed, e.g., due to failed scanout position query.
@@ -671,23 +673,23 @@ static struct timeval get_drm_timestamp(void)
/**
* drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
- * vblank interval.
- *
+ * vblank interval
* @dev: DRM device
- * @crtc: which crtc's vblank timestamp to retrieve
+ * @crtc: which CRTC's vblank timestamp to retrieve
* @tvblank: Pointer to target struct timeval which should receive the timestamp
* @flags: Flags to pass to driver:
- * 0 = Default.
- * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler.
+ * 0 = Default,
+ * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler
*
* Fetches the system timestamp corresponding to the time of the most recent
- * vblank interval on specified crtc. May call into kms-driver to
+ * vblank interval on specified CRTC. May call into kms-driver to
* compute the timestamp with a high-precision GPU specific method.
*
* Returns zero if timestamp originates from uncorrected do_gettimeofday()
* call, i.e., it isn't very precisely locked to the true vblank.
*
- * Returns non-zero if timestamp is considered to be very precise.
+ * Returns:
+ * Non-zero if timestamp is considered to be very precise, zero otherwise.
*/
u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
struct timeval *tvblank, unsigned flags)
@@ -722,6 +724,9 @@ EXPORT_SYMBOL(drm_get_last_vbltimestamp);
* Fetches the "cooked" vblank count value that represents the number of
* vblank events since the system was booted, including lost events due to
* modesetting activity.
+ *
+ * Returns:
+ * The software vblank counter.
*/
u32 drm_vblank_count(struct drm_device *dev, int crtc)
{
@@ -740,8 +745,7 @@ EXPORT_SYMBOL(drm_vblank_count);
* Fetches the "cooked" vblank count value that represents the number of
* vblank events since the system was booted, including lost events due to
* modesetting activity. Returns corresponding system timestamp of the time
- * of the vblank interval that corresponds to the current value vblank counter
- * value.
+ * of the vblank interval that corresponds to the current vblank counter value.
*/
u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc,
struct timeval *vblanktime)
@@ -870,6 +874,42 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
}
/**
+ * 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)
+{
+ int ret = 0;
+
+ assert_spin_locked(&dev->vbl_lock);
+
+ spin_lock(&dev->vblank_time_lock);
+
+ if (!dev->vblank[crtc].enabled) {
+ /*
+ * Enable vblank irqs under vblank_time_lock protection.
+ * All vblank count & timestamp updates are held off
+ * until we are done reinitializing master counter and
+ * timestamps. Filtercode in drm_handle_vblank() will
+ * prevent double-accounting of same vblank interval.
+ */
+ 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);
+ else {
+ dev->vblank[crtc].enabled = true;
+ drm_update_vblank_count(dev, crtc);
+ }
+ }
+
+ spin_unlock(&dev->vblank_time_lock);
+
+ return ret;
+}
+
+/**
* drm_vblank_get - get a reference count on vblank events
* @dev: DRM device
* @crtc: which CRTC to own
@@ -877,36 +917,20 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc)
* Acquire a reference count on vblank events to avoid having them disabled
* while in use.
*
- * RETURNS
+ * This is the legacy version of drm_crtc_vblank_get().
+ *
+ * Returns:
* Zero on success, nonzero on failure.
*/
int drm_vblank_get(struct drm_device *dev, int crtc)
{
- unsigned long irqflags, irqflags2;
+ unsigned long irqflags;
int ret = 0;
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) {
- spin_lock_irqsave(&dev->vblank_time_lock, irqflags2);
- if (!dev->vblank[crtc].enabled) {
- /* Enable vblank irqs under vblank_time_lock protection.
- * All vblank count & timestamp updates are held off
- * until we are done reinitializing master counter and
- * timestamps. Filtercode in drm_handle_vblank() will
- * prevent double-accounting of same vblank interval.
- */
- 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);
- else {
- dev->vblank[crtc].enabled = true;
- drm_update_vblank_count(dev, crtc);
- }
- }
- spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags2);
+ ret = drm_vblank_enable(dev, crtc);
} else {
if (!dev->vblank[crtc].enabled) {
atomic_dec(&dev->vblank[crtc].refcount);
@@ -920,12 +944,32 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
EXPORT_SYMBOL(drm_vblank_get);
/**
+ * drm_crtc_vblank_get - get a reference count on vblank events
+ * @crtc: which CRTC to own
+ *
+ * Acquire a reference count on vblank events to avoid having them disabled
+ * while in use.
+ *
+ * This is the native kms version of drm_vblank_off().
+ *
+ * Returns:
+ * Zero on success, nonzero on failure.
+ */
+int drm_crtc_vblank_get(struct drm_crtc *crtc)
+{
+ return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_get);
+
+/**
* drm_vblank_put - give up ownership of vblank events
* @dev: DRM device
* @crtc: which counter to give up
*
* Release ownership of a given vblank counter, turning off interrupts
* if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
+ *
+ * This is the legacy version of drm_crtc_vblank_put().
*/
void drm_vblank_put(struct drm_device *dev, int crtc)
{
@@ -934,17 +978,39 @@ void drm_vblank_put(struct drm_device *dev, int crtc)
/* Last user schedules interrupt disable */
if (atomic_dec_and_test(&dev->vblank[crtc].refcount) &&
(drm_vblank_offdelay > 0))
- mod_timer(&dev->vblank_disable_timer,
+ mod_timer(&dev->vblank[crtc].disable_timer,
jiffies + ((drm_vblank_offdelay * HZ)/1000));
}
EXPORT_SYMBOL(drm_vblank_put);
/**
+ * drm_crtc_vblank_put - give up ownership of vblank events
+ * @crtc: which counter to give up
+ *
+ * Release ownership of a given vblank counter, turning off interrupts
+ * if possible. Disable interrupts after drm_vblank_offdelay milliseconds.
+ *
+ * This is the native kms version of drm_vblank_put().
+ */
+void drm_crtc_vblank_put(struct drm_crtc *crtc)
+{
+ drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_put);
+
+/**
* drm_vblank_off - disable vblank events on a CRTC
* @dev: DRM device
* @crtc: CRTC in question
*
- * Caller must hold event lock.
+ * Drivers can use this function to shut down the vblank interrupt handling when
+ * disabling a crtc. This function ensures that the latest vblank frame count is
+ * stored so that drm_vblank_on() can restore it again.
+ *
+ * Drivers must use this function when the hardware vblank counter can get
+ * reset, e.g. when suspending.
+ *
+ * This is the legacy version of drm_crtc_vblank_off().
*/
void drm_vblank_off(struct drm_device *dev, int crtc)
{
@@ -978,12 +1044,87 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
EXPORT_SYMBOL(drm_vblank_off);
/**
+ * drm_crtc_vblank_off - disable vblank events on a CRTC
+ * @crtc: CRTC in question
+ *
+ * Drivers can use this function to shut down the vblank interrupt handling when
+ * disabling a crtc. This function ensures that the latest vblank frame count is
+ * stored so that drm_vblank_on can restore it again.
+ *
+ * Drivers must use this function when the hardware vblank counter can get
+ * reset, e.g. when suspending.
+ *
+ * This is the native kms version of drm_vblank_off().
+ */
+void drm_crtc_vblank_off(struct drm_crtc *crtc)
+{
+ drm_vblank_off(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_off);
+
+/**
+ * drm_vblank_on - enable vblank events on a CRTC
+ * @dev: DRM device
+ * @crtc: CRTC in question
+ *
+ * This functions restores the vblank interrupt state captured with
+ * drm_vblank_off() again. Note that calls to drm_vblank_on() and
+ * drm_vblank_off() can be unbalanced and so can also be unconditionaly called
+ * in driver load code to reflect the current hardware state of the crtc.
+ *
+ * This is the legacy version of drm_crtc_vblank_on().
+ */
+void drm_vblank_on(struct drm_device *dev, int crtc)
+{
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
+ /* re-enable interrupts if there's are users left */
+ if (atomic_read(&dev->vblank[crtc].refcount) != 0)
+ WARN_ON(drm_vblank_enable(dev, crtc));
+ spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+}
+EXPORT_SYMBOL(drm_vblank_on);
+
+/**
+ * drm_crtc_vblank_on - enable vblank events on a CRTC
+ * @crtc: CRTC in question
+ *
+ * This functions restores the vblank interrupt state captured with
+ * drm_vblank_off() again. Note that calls to drm_vblank_on() and
+ * drm_vblank_off() can be unbalanced and so can also be unconditionaly called
+ * in driver load code to reflect the current hardware state of the crtc.
+ *
+ * This is the native kms version of drm_vblank_on().
+ */
+void drm_crtc_vblank_on(struct drm_crtc *crtc)
+{
+ drm_vblank_on(crtc->dev, drm_crtc_index(crtc));
+}
+EXPORT_SYMBOL(drm_crtc_vblank_on);
+
+/**
* drm_vblank_pre_modeset - account for vblanks across mode sets
* @dev: DRM device
* @crtc: CRTC in question
*
* Account for vblank events across mode setting events, which will likely
* reset the hardware frame counter.
+ *
+ * This is done by grabbing a temporary vblank reference to ensure that the
+ * vblank interrupt keeps running across the modeset sequence. With this the
+ * software-side vblank frame counting will ensure that there are no jumps or
+ * discontinuities.
+ *
+ * Unfortunately this approach is racy and also doesn't work when the vblank
+ * interrupt stops running, e.g. across system suspend resume. It is therefore
+ * highly recommended that drivers use the newer drm_vblank_off() and
+ * drm_vblank_on() instead. drm_vblank_pre_modeset() only works correctly when
+ * using "cooked" software vblank frame counters and not relying on any hardware
+ * counters.
+ *
+ * Drivers must call drm_vblank_post_modeset() when re-enabling the same crtc
+ * again.
*/
void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
{
@@ -1005,6 +1146,14 @@ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
}
EXPORT_SYMBOL(drm_vblank_pre_modeset);
+/**
+ * drm_vblank_post_modeset - undo drm_vblank_pre_modeset changes
+ * @dev: DRM device
+ * @crtc: CRTC in question
+ *
+ * This function again drops the temporary vblank reference acquired in
+ * drm_vblank_pre_modeset.
+ */
void drm_vblank_post_modeset(struct drm_device *dev, int crtc)
{
unsigned long irqflags;
@@ -1026,7 +1175,7 @@ void drm_vblank_post_modeset(struct drm_device *dev, int crtc)
}
EXPORT_SYMBOL(drm_vblank_post_modeset);
-/**
+/*
* drm_modeset_ctl - handle vblank event counter changes across mode switch
* @DRM_IOCTL_ARGS: standard ioctl arguments
*
@@ -1139,7 +1288,7 @@ err_put:
return ret;
}
-/**
+/*
* Wait for VBLANK.
*
* \param inode device inode.
@@ -1150,7 +1299,7 @@ err_put:
*
* This function enables the vblank interrupt on the pipe requested, then
* sleeps waiting for the requested sequence number to occur, and drops
- * the vblank interrupt refcount afterwards. (vblank irq disable follows that
+ * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
* after a timeout with no further vblank waits scheduled).
*/
int drm_wait_vblank(struct drm_device *dev, void *data,
@@ -1160,9 +1309,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
int ret;
unsigned int flags, seq, crtc, high_crtc;
- if (drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
- if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled))
- return -EINVAL;
+ if (!dev->irq_enabled)
+ return -EINVAL;
if (vblwait->request.type & _DRM_VBLANK_SIGNAL)
return -EINVAL;
@@ -1222,6 +1370,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
DRM_WAIT_ON(ret, dev->vblank[crtc].queue, 3 * HZ,
(((drm_vblank_count(dev, crtc) -
vblwait->request.sequence) <= (1 << 23)) ||
+ !dev->vblank[crtc].enabled ||
!dev->irq_enabled));
if (ret != -EINTR) {
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 09821f46d768..e633df2f68d8 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -282,6 +282,14 @@ static int mipi_dsi_drv_remove(struct device *dev)
return drv->remove(dsi);
}
+static void mipi_dsi_drv_shutdown(struct device *dev)
+{
+ struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
+
+ drv->shutdown(dsi);
+}
+
/**
* mipi_dsi_driver_register - register a driver for DSI devices
* @drv: DSI driver structure
@@ -293,6 +301,8 @@ int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
drv->driver.probe = mipi_dsi_drv_probe;
if (drv->remove)
drv->driver.remove = mipi_dsi_drv_remove;
+ if (drv->shutdown)
+ drv->driver.shutdown = mipi_dsi_drv_shutdown;
return driver_register(&drv->driver);
}
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 8b410576fce4..bedf1894e17e 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1013,6 +1013,7 @@ EXPORT_SYMBOL(drm_mode_sort);
/**
* drm_mode_connector_list_update - update the mode list for the connector
* @connector: the connector to update
+ * @merge_type_bits: whether to merge or overright type bits.
*
* This moves the modes from the @connector probed_modes list
* to the actual mode list. It compares the probed mode against the current
@@ -1021,7 +1022,8 @@ EXPORT_SYMBOL(drm_mode_sort);
* This is just a helper functions doesn't validate any modes itself and also
* doesn't prune any invalid modes. Callers need to do that themselves.
*/
-void drm_mode_connector_list_update(struct drm_connector *connector)
+void drm_mode_connector_list_update(struct drm_connector *connector,
+ bool merge_type_bits)
{
struct drm_display_mode *mode;
struct drm_display_mode *pmode, *pt;
@@ -1039,7 +1041,10 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
/* if equal delete the probed mode */
mode->status = pmode->status;
/* Merge type bits together */
- mode->type |= pmode->type;
+ if (merge_type_bits)
+ mode->type |= pmode->type;
+ else
+ mode->type = pmode->type;
list_del(&pmode->head);
drm_mode_destroy(connector->dev, pmode);
break;
diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
new file mode 100644
index 000000000000..7c2497dea1e9
--- /dev/null
+++ b/drivers/gpu/drm/drm_modeset_lock.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@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.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_modeset_lock.h>
+
+/**
+ * DOC: kms locking
+ *
+ * As KMS moves toward more fine grained locking, and atomic ioctl where
+ * userspace can indirectly control locking order, it becomes necessary
+ * to use ww_mutex and acquire-contexts to avoid deadlocks. But because
+ * the locking is more distributed around the driver code, we want a bit
+ * 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
+ *
+ * The basic usage pattern is to:
+ *
+ * drm_modeset_acquire_init(&ctx)
+ * retry:
+ * foreach (lock in random_ordered_set_of_locks) {
+ * ret = drm_modeset_lock(lock, &ctx)
+ * if (ret == -EDEADLK) {
+ * drm_modeset_backoff(&ctx);
+ * goto retry;
+ * }
+ * }
+ *
+ * ... do stuff ...
+ *
+ * drm_modeset_drop_locks(&ctx);
+ * drm_modeset_acquire_fini(&ctx);
+ */
+
+
+/**
+ * drm_modeset_acquire_init - initialize acquire context
+ * @ctx: the acquire context
+ * @flags: for future
+ */
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+ uint32_t flags)
+{
+ ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
+ INIT_LIST_HEAD(&ctx->locked);
+}
+EXPORT_SYMBOL(drm_modeset_acquire_init);
+
+/**
+ * drm_modeset_acquire_fini - cleanup acquire context
+ * @ctx: the acquire context
+ */
+void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
+{
+ ww_acquire_fini(&ctx->ww_ctx);
+}
+EXPORT_SYMBOL(drm_modeset_acquire_fini);
+
+/**
+ * drm_modeset_drop_locks - drop all locks
+ * @ctx: the acquire context
+ *
+ * Drop all locks currently held against this acquire context.
+ */
+void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
+{
+ WARN_ON(ctx->contended);
+ while (!list_empty(&ctx->locked)) {
+ struct drm_modeset_lock *lock;
+
+ lock = list_first_entry(&ctx->locked,
+ struct drm_modeset_lock, head);
+
+ drm_modeset_unlock(lock);
+ }
+}
+EXPORT_SYMBOL(drm_modeset_drop_locks);
+
+static inline int modeset_lock(struct drm_modeset_lock *lock,
+ struct drm_modeset_acquire_ctx *ctx,
+ bool interruptible, bool slow)
+{
+ int ret;
+
+ WARN_ON(ctx->contended);
+
+ 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);
+ } else if (slow) {
+ ww_mutex_lock_slow(&lock->mutex, &ctx->ww_ctx);
+ ret = 0;
+ } else {
+ ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
+ }
+ if (!ret) {
+ WARN_ON(!list_empty(&lock->head));
+ list_add(&lock->head, &ctx->locked);
+ } else if (ret == -EALREADY) {
+ /* we already hold the lock.. this is fine. For atomic
+ * we will need to be able to drm_modeset_lock() things
+ * without having to keep track of what is already locked
+ * or not.
+ */
+ ret = 0;
+ } else if (ret == -EDEADLK) {
+ ctx->contended = lock;
+ }
+
+ return ret;
+}
+
+static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
+ bool interruptible)
+{
+ struct drm_modeset_lock *contended = ctx->contended;
+
+ ctx->contended = NULL;
+
+ if (WARN_ON(!contended))
+ return 0;
+
+ drm_modeset_drop_locks(ctx);
+
+ return modeset_lock(contended, ctx, interruptible, true);
+}
+
+/**
+ * drm_modeset_backoff - deadlock avoidance backoff
+ * @ctx: the acquire context
+ *
+ * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
+ * you must call this function to drop all currently held locks and
+ * block until the contended lock becomes available.
+ */
+void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
+{
+ modeset_backoff(ctx, false);
+}
+EXPORT_SYMBOL(drm_modeset_backoff);
+
+/**
+ * drm_modeset_backoff_interruptible - deadlock avoidance backoff
+ * @ctx: the acquire context
+ *
+ * Interruptible version of drm_modeset_backoff()
+ */
+int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
+{
+ return modeset_backoff(ctx, true);
+}
+EXPORT_SYMBOL(drm_modeset_backoff_interruptible);
+
+/**
+ * drm_modeset_lock - take modeset lock
+ * @lock: lock to take
+ * @ctx: acquire ctx
+ *
+ * If ctx is not NULL, then its ww acquire context is used and the
+ * lock will be tracked by the context and can be released by calling
+ * drm_modeset_drop_locks(). If -EDEADLK is returned, this means a
+ * deadlock scenario has been detected and it is an error to attempt
+ * to take any more locks without first calling drm_modeset_backoff().
+ */
+int drm_modeset_lock(struct drm_modeset_lock *lock,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ if (ctx)
+ return modeset_lock(lock, ctx, false, false);
+
+ ww_mutex_lock(&lock->mutex, NULL);
+ return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock);
+
+/**
+ * drm_modeset_lock_interruptible - take modeset lock
+ * @lock: lock to take
+ * @ctx: acquire ctx
+ *
+ * Interruptible version of drm_modeset_lock()
+ */
+int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ if (ctx)
+ return modeset_lock(lock, ctx, true, false);
+
+ return ww_mutex_lock_interruptible(&lock->mutex, NULL);
+}
+EXPORT_SYMBOL(drm_modeset_lock_interruptible);
+
+/**
+ * drm_modeset_unlock - drop modeset lock
+ * @lock: lock to release
+ */
+void drm_modeset_unlock(struct drm_modeset_lock *lock)
+{
+ list_del_init(&lock->head);
+ ww_mutex_unlock(&lock->mutex);
+}
+EXPORT_SYMBOL(drm_modeset_unlock);
+
+/* Temporary.. until we have sufficiently fine grained locking, there
+ * are a couple scenarios where it is convenient to grab all crtc locks.
+ * It is planned to remove this:
+ */
+int drm_modeset_lock_all_crtcs(struct drm_device *dev,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_crtc *crtc;
+ int ret = 0;
+
+ list_for_each_entry(crtc, &config->crtc_list, head) {
+ ret = drm_modeset_lock(&crtc->mutex, ctx);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
index 9c696a5ad74d..020cfd934854 100644
--- a/drivers/gpu/drm/drm_pci.c
+++ b/drivers/gpu/drm/drm_pci.c
@@ -1,17 +1,3 @@
-/* drm_pci.h -- PCI DMA memory management wrappers for DRM -*- linux-c -*- */
-/**
- * \file drm_pci.c
- * \brief Functions and ioctls to manage PCI memory
- *
- * \warning These interfaces aren't stable yet.
- *
- * \todo Implement the remaining ioctl's for the PCI pools.
- * \todo The wrappers here are so thin that they would be better off inlined..
- *
- * \author José Fonseca <jrfonseca@tungstengraphics.com>
- * \author Leif Delgass <ldelgass@retinalburn.net>
- */
-
/*
* Copyright 2003 José Fonseca.
* Copyright 2003 Leif Delgass.
@@ -42,12 +28,14 @@
#include <linux/export.h>
#include <drm/drmP.h>
-/**********************************************************************/
-/** \name PCI memory */
-/*@{*/
-
/**
- * \brief Allocate a PCI consistent memory block, for DMA.
+ * drm_pci_alloc - Allocate a PCI consistent memory block, for DMA.
+ * @dev: DRM device
+ * @size: size of block to allocate
+ * @align: alignment of block
+ *
+ * Return: A handle to the allocated memory block on success or NULL on
+ * failure.
*/
drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t align)
{
@@ -88,8 +76,8 @@ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t ali
EXPORT_SYMBOL(drm_pci_alloc);
-/**
- * \brief Free a PCI consistent memory block without freeing its descriptor.
+/*
+ * Free a PCI consistent memory block without freeing its descriptor.
*
* This function is for internal use in the Linux-specific DRM core code.
*/
@@ -111,7 +99,9 @@ void __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
}
/**
- * \brief Free a PCI consistent memory block
+ * drm_pci_free - Free a PCI consistent memory block
+ * @dev: DRM device
+ * @dmah: handle to memory block
*/
void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
{
@@ -137,21 +127,9 @@ static int drm_get_pci_domain(struct drm_device *dev)
return pci_domain_nr(dev->pdev->bus);
}
-static int drm_pci_get_irq(struct drm_device *dev)
-{
- return dev->pdev->irq;
-}
-
-static const char *drm_pci_get_name(struct drm_device *dev)
-{
- struct pci_driver *pdriver = dev->driver->kdriver.pci;
- return pdriver->name;
-}
-
static int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
{
int len, ret;
- struct pci_driver *pdriver = dev->driver->kdriver.pci;
master->unique_len = 40;
master->unique_size = master->unique_len;
master->unique = kmalloc(master->unique_size, GFP_KERNEL);
@@ -173,29 +151,16 @@ static int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master)
} else
master->unique_len = len;
- dev->devname =
- kmalloc(strlen(pdriver->name) +
- master->unique_len + 2, GFP_KERNEL);
-
- if (dev->devname == NULL) {
- ret = -ENOMEM;
- goto err;
- }
-
- sprintf(dev->devname, "%s@%s", pdriver->name,
- master->unique);
-
return 0;
err:
return ret;
}
-static int drm_pci_set_unique(struct drm_device *dev,
- struct drm_master *master,
- struct drm_unique *u)
+int drm_pci_set_unique(struct drm_device *dev,
+ struct drm_master *master,
+ struct drm_unique *u)
{
int domain, bus, slot, func, ret;
- const char *bus_name;
master->unique_len = u->unique_len;
master->unique_size = u->unique_len + 1;
@@ -212,17 +177,6 @@ static int drm_pci_set_unique(struct drm_device *dev,
master->unique[master->unique_len] = '\0';
- bus_name = dev->driver->bus->get_name(dev);
- dev->devname = kmalloc(strlen(bus_name) +
- strlen(master->unique) + 2, GFP_KERNEL);
- if (!dev->devname) {
- ret = -ENOMEM;
- goto err;
- }
-
- sprintf(dev->devname, "%s@%s", bus_name,
- master->unique);
-
/* Return error if the busid submitted doesn't match the device's actual
* busid.
*/
@@ -247,7 +201,6 @@ err:
return ret;
}
-
static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p)
{
if ((p->busnum >> 8) != drm_get_pci_domain(dev) ||
@@ -262,6 +215,36 @@ static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p)
return 0;
}
+/**
+ * drm_irq_by_busid - Get interrupt from bus ID
+ * @dev: DRM device
+ * @data: IOCTL parameter pointing to a drm_irq_busid structure
+ * @file_priv: DRM file private.
+ *
+ * Finds the PCI device with the specified bus id and gets its IRQ number.
+ * This IOCTL is deprecated, and will now return EINVAL for any busid not equal
+ * to that of the device that this DRM instance attached to.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_irq_by_busid(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct drm_irq_busid *p = data;
+
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return -EINVAL;
+
+ /* UMS was only ever support on PCI devices. */
+ if (WARN_ON(!dev->pdev))
+ return -EINVAL;
+
+ if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
+ return -EINVAL;
+
+ return drm_pci_irq_by_busid(dev, p);
+}
+
static void drm_pci_agp_init(struct drm_device *dev)
{
if (drm_core_check_feature(dev, DRIVER_USE_AGP)) {
@@ -287,24 +270,20 @@ void drm_pci_agp_destroy(struct drm_device *dev)
}
static struct drm_bus drm_pci_bus = {
- .bus_type = DRIVER_BUS_PCI,
- .get_irq = drm_pci_get_irq,
- .get_name = drm_pci_get_name,
.set_busid = drm_pci_set_busid,
- .set_unique = drm_pci_set_unique,
- .irq_by_busid = drm_pci_irq_by_busid,
};
/**
- * Register.
- *
- * \param pdev - PCI device structure
- * \param ent entry from the PCI ID table with device type flags
- * \return zero on success or a negative number on failure.
+ * drm_get_pci_dev - Register a PCI device with the DRM subsystem
+ * @pdev: PCI device
+ * @ent: entry from the PCI ID table that matches @pdev
+ * @driver: DRM device driver
*
* Attempt to gets inter module "drm" information. If we are first
* then register the character device and inter module information.
* Try and register, if we fail to register, backout previous work.
+ *
+ * Return: 0 on success or a negative error code on failure.
*/
int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
struct drm_driver *driver)
@@ -357,15 +336,14 @@ err_free:
EXPORT_SYMBOL(drm_get_pci_dev);
/**
- * PCI device initialization. Called direct from modules at load time.
- *
- * \return zero on success or a negative number on failure.
+ * drm_pci_init - Register matching PCI devices with the DRM subsystem
+ * @driver: DRM device driver
+ * @pdriver: PCI device driver
*
- * Initializes a drm_device structures,registering the
- * stubs and initializing the AGP device.
+ * Initializes a drm_device structures, registering the stubs and initializing
+ * the AGP device.
*
- * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and
- * after the initialization for driver customization.
+ * Return: 0 on success or a negative error code on failure.
*/
int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
{
@@ -375,7 +353,6 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
DRM_DEBUG("\n");
- driver->kdriver.pci = pdriver;
driver->bus = &drm_pci_bus;
if (driver->driver_features & DRIVER_MODESET)
@@ -453,11 +430,31 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
}
void drm_pci_agp_destroy(struct drm_device *dev) {}
+
+int drm_irq_by_busid(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ return -EINVAL;
+}
+
+int drm_pci_set_unique(struct drm_device *dev,
+ struct drm_master *master,
+ struct drm_unique *u)
+{
+ return -EINVAL;
+}
#endif
EXPORT_SYMBOL(drm_pci_init);
-/*@}*/
+/**
+ * drm_pci_exit - Unregister matching PCI devices from the DRM subsystem
+ * @driver: DRM device driver
+ * @pdriver: PCI device driver
+ *
+ * Unregisters one or more devices matched by a PCI driver from the DRM
+ * subsystem.
+ */
void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver)
{
struct drm_device *dev, *tmp;
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index d2b1c03b3d71..6d133149cc74 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -25,7 +25,9 @@
#include <linux/list.h>
#include <drm/drmP.h>
+#include <drm/drm_plane_helper.h>
#include <drm/drm_rect.h>
+#include <drm/drm_plane_helper.h>
#define SUBPIXEL_MASK 0xffff
@@ -36,9 +38,9 @@
* creating the primary plane. However drivers that still call
* drm_plane_init() will use this minimal format list as the default.
*/
-const static uint32_t safe_modeset_formats[] = {
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_ARGB8888,
+static const uint32_t safe_modeset_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
};
/*
@@ -54,6 +56,13 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
struct drm_connector *connector;
int count = 0;
+ /*
+ * Note: Once we change the plane hooks to more fine-grained locking we
+ * need to grab the connection_mutex here to be able to make these
+ * checks.
+ */
+ 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 && connector->encoder->crtc == crtc) {
if (connector_list != NULL && count < num_connectors)
@@ -66,6 +75,79 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
}
/**
+ * drm_plane_helper_check_update() - Check plane update for validity
+ * @plane: plane object to update
+ * @crtc: owning CRTC of owning plane
+ * @fb: framebuffer to flip onto plane
+ * @src: source coordinates in 16.16 fixed point
+ * @dest: integer destination coordinates
+ * @clip: integer clipping coordinates
+ * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point
+ * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point
+ * @can_position: is it legal to position the plane such that it
+ * doesn't cover the entire crtc? This will generally
+ * only be false for primary planes.
+ * @can_update_disabled: can the plane be updated while the crtc
+ * is disabled?
+ * @visible: output parameter indicating whether plane is still visible after
+ * clipping
+ *
+ * Checks that a desired plane update is valid. Drivers that provide
+ * their own plane handling rather than helper-provided implementations may
+ * still wish to call this function to avoid duplication of error checking
+ * code.
+ *
+ * RETURNS:
+ * Zero if update appears valid, error code on failure
+ */
+int drm_plane_helper_check_update(struct drm_plane *plane,
+ struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_rect *src,
+ struct drm_rect *dest,
+ const struct drm_rect *clip,
+ int min_scale,
+ int max_scale,
+ bool can_position,
+ bool can_update_disabled,
+ bool *visible)
+{
+ int hscale, vscale;
+
+ if (!crtc->enabled && !can_update_disabled) {
+ DRM_DEBUG_KMS("Cannot update plane of a disabled CRTC.\n");
+ return -EINVAL;
+ }
+
+ /* Check scaling */
+ hscale = drm_rect_calc_hscale(src, dest, min_scale, max_scale);
+ vscale = drm_rect_calc_vscale(src, dest, min_scale, max_scale);
+ if (hscale < 0 || vscale < 0) {
+ DRM_DEBUG_KMS("Invalid scaling of plane\n");
+ return -ERANGE;
+ }
+
+ *visible = drm_rect_clip_scaled(src, dest, clip, hscale, vscale);
+ if (!*visible)
+ /*
+ * Plane isn't visible; some drivers can handle this
+ * so we just return success here. Drivers that can't
+ * (including those that use the primary plane helper's
+ * update function) will return an error from their
+ * update_plane handler.
+ */
+ return 0;
+
+ if (!can_position && !drm_rect_equals(dest, clip)) {
+ DRM_DEBUG_KMS("Plane must cover entire CRTC\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_plane_helper_check_update);
+
+/**
* drm_primary_helper_update() - Helper for primary plane update
* @plane: plane object to update
* @crtc: owning CRTC of owning plane
@@ -113,56 +195,42 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
.x = src_x >> 16,
.y = src_y >> 16,
};
+ struct drm_rect src = {
+ .x1 = src_x,
+ .y1 = src_y,
+ .x2 = src_x + src_w,
+ .y2 = src_y + src_h,
+ };
struct drm_rect dest = {
.x1 = crtc_x,
.y1 = crtc_y,
.x2 = crtc_x + crtc_w,
.y2 = crtc_y + crtc_h,
};
- struct drm_rect clip = {
+ const struct drm_rect clip = {
.x2 = crtc->mode.hdisplay,
.y2 = crtc->mode.vdisplay,
};
struct drm_connector **connector_list;
- struct drm_framebuffer *tmpfb;
int num_connectors, ret;
+ bool visible;
- if (!crtc->enabled) {
- DRM_DEBUG_KMS("Cannot update primary plane of a disabled CRTC.\n");
- return -EINVAL;
- }
-
- /* Disallow subpixel positioning */
- if ((src_x | src_y | src_w | src_h) & SUBPIXEL_MASK) {
- DRM_DEBUG_KMS("Primary plane does not support subpixel positioning\n");
- return -EINVAL;
- }
-
- /* Primary planes are locked to their owning CRTC */
- if (plane->possible_crtcs != drm_crtc_mask(crtc)) {
- DRM_DEBUG_KMS("Cannot change primary plane CRTC\n");
- return -EINVAL;
- }
-
- /* Disallow scaling */
- if (crtc_w != src_w || crtc_h != src_h) {
- DRM_DEBUG_KMS("Can't scale primary plane\n");
- return -EINVAL;
- }
-
- /* Make sure primary plane covers entire CRTC */
- drm_rect_intersect(&dest, &clip);
- if (dest.x1 != 0 || dest.y1 != 0 ||
- dest.x2 != crtc->mode.hdisplay || dest.y2 != crtc->mode.vdisplay) {
- DRM_DEBUG_KMS("Primary plane must cover entire CRTC\n");
- return -EINVAL;
- }
-
- /* Framebuffer must be big enough to cover entire plane */
- ret = drm_crtc_check_viewport(crtc, crtc_x, crtc_y, &crtc->mode, fb);
+ ret = drm_plane_helper_check_update(plane, crtc, fb,
+ &src, &dest, &clip,
+ DRM_PLANE_HELPER_NO_SCALING,
+ DRM_PLANE_HELPER_NO_SCALING,
+ false, false, &visible);
if (ret)
return ret;
+ if (!visible)
+ /*
+ * Primary plane isn't visible. Note that unless a driver
+ * provides their own disable function, this will just
+ * wind up returning -EINVAL to userspace.
+ */
+ return plane->funcs->disable_plane(plane);
+
/* Find current connectors for CRTC */
num_connectors = get_connectors_for_crtc(crtc, NULL, 0);
BUG_ON(num_connectors == 0);
@@ -176,21 +244,14 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
set.num_connectors = num_connectors;
/*
- * set_config() adjusts crtc->primary->fb; however the DRM setplane
- * code that called us expects to handle the framebuffer update and
- * reference counting; save and restore the current fb before
- * calling it.
- *
- * N.B., we call set_config() directly here rather than using
+ * We call set_config() directly here rather than using
* drm_mode_set_config_internal. We're reprogramming the same
* connectors that were already in use, so we shouldn't need the extra
* cross-CRTC fb refcounting to accomodate stealing connectors.
* drm_mode_setplane() already handles the basic refcounting for the
* framebuffers involved in this operation.
*/
- tmpfb = plane->fb;
ret = crtc->funcs->set_config(&set);
- plane->fb = tmpfb;
kfree(connector_list);
return ret;
@@ -232,7 +293,6 @@ EXPORT_SYMBOL(drm_primary_helper_disable);
*/
void drm_primary_helper_destroy(struct drm_plane *plane)
{
- plane->funcs->disable_plane(plane);
drm_plane_cleanup(plane);
kfree(plane);
}
diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c
index 319ff5385601..d5b76f148c12 100644
--- a/drivers/gpu/drm/drm_platform.c
+++ b/drivers/gpu/drm/drm_platform.c
@@ -68,16 +68,6 @@ err_free:
return ret;
}
-static int drm_platform_get_irq(struct drm_device *dev)
-{
- return platform_get_irq(dev->platformdev, 0);
-}
-
-static const char *drm_platform_get_name(struct drm_device *dev)
-{
- return dev->platformdev->name;
-}
-
static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master)
{
int len, ret, id;
@@ -106,46 +96,30 @@ static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *mas
goto err;
}
- dev->devname =
- kmalloc(strlen(dev->platformdev->name) +
- master->unique_len + 2, GFP_KERNEL);
-
- if (dev->devname == NULL) {
- ret = -ENOMEM;
- goto err;
- }
-
- sprintf(dev->devname, "%s@%s", dev->platformdev->name,
- master->unique);
return 0;
err:
return ret;
}
static struct drm_bus drm_platform_bus = {
- .bus_type = DRIVER_BUS_PLATFORM,
- .get_irq = drm_platform_get_irq,
- .get_name = drm_platform_get_name,
.set_busid = drm_platform_set_busid,
};
/**
- * Platform device initialization. Called direct from modules.
+ * drm_platform_init - Register a platform device with the DRM subsystem
+ * @driver: DRM device driver
+ * @platform_device: platform device to register
*
- * \return zero on success or a negative number on failure.
- *
- * Initializes a drm_device structures,registering the
- * stubs
+ * Registers the specified DRM device driver and platform device with the DRM
+ * subsystem, initializing a drm_device structure and calling the driver's
+ * .load() function.
*
- * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and
- * after the initialization for driver customization.
+ * Return: 0 on success or a negative error code on failure.
*/
-
int drm_platform_init(struct drm_driver *driver, struct platform_device *platform_device)
{
DRM_DEBUG("\n");
- driver->kdriver.platform_device = platform_device;
driver->bus = &drm_platform_bus;
return drm_get_platform_dev(platform_device, driver);
}
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index e70f54d4a581..d22676b89cbb 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -82,26 +82,8 @@ static void drm_mode_validate_flag(struct drm_connector *connector,
return;
}
-/**
- * drm_helper_probe_single_connector_modes - get complete set of display modes
- * @connector: connector to probe
- * @maxX: max width for modes
- * @maxY: max height for modes
- *
- * Based on the helper callbacks implemented by @connector try to detect all
- * valid modes. Modes will first be added to the connector's probed_modes list,
- * then culled (based on validity and the @maxX, @maxY parameters) and put into
- * the normal modes list.
- *
- * Intended to be use as a generic implementation of the ->fill_modes()
- * @connector vfunc for drivers that use the crtc helpers for output mode
- * filtering and detection.
- *
- * Returns:
- * The number of modes found on @connector.
- */
-int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
- uint32_t maxX, uint32_t maxY)
+static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connector *connector,
+ uint32_t maxX, uint32_t maxY, bool merge_type_bits)
{
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode;
@@ -114,7 +96,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
- drm_get_connector_name(connector));
+ connector->name);
/* set all modes to the unverified state */
list_for_each_entry(mode, &connector->modes, head)
mode->status = MODE_UNVERIFIED;
@@ -138,7 +120,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
if (connector->status == connector_status_disconnected) {
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n",
- connector->base.id, drm_get_connector_name(connector));
+ connector->base.id, connector->name);
drm_mode_connector_update_edid_property(connector, NULL);
verbose_prune = false;
goto prune;
@@ -155,7 +137,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
if (count == 0)
goto prune;
- drm_mode_connector_list_update(connector);
+ drm_mode_connector_list_update(connector, merge_type_bits);
if (maxX && maxY)
drm_mode_validate_size(dev, &connector->modes, maxX, maxY);
@@ -169,7 +151,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
drm_mode_validate_flag(connector, mode_flags);
list_for_each_entry(mode, &connector->modes, head) {
- if (mode->status == MODE_OK)
+ if (mode->status == MODE_OK && connector_funcs->mode_valid)
mode->status = connector_funcs->mode_valid(connector,
mode);
}
@@ -186,7 +168,7 @@ prune:
drm_mode_sort(&connector->modes);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id,
- drm_get_connector_name(connector));
+ connector->name);
list_for_each_entry(mode, &connector->modes, head) {
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
drm_mode_debug_printmodeline(mode);
@@ -194,9 +176,49 @@ prune:
return count;
}
+
+/**
+ * drm_helper_probe_single_connector_modes - get complete set of display modes
+ * @connector: connector to probe
+ * @maxX: max width for modes
+ * @maxY: max height for modes
+ *
+ * Based on the helper callbacks implemented by @connector try to detect all
+ * valid modes. Modes will first be added to the connector's probed_modes list,
+ * then culled (based on validity and the @maxX, @maxY parameters) and put into
+ * the normal modes list.
+ *
+ * Intended to be use as a generic implementation of the ->fill_modes()
+ * @connector vfunc for drivers that use the crtc helpers for output mode
+ * filtering and detection.
+ *
+ * Returns:
+ * The number of modes found on @connector.
+ */
+int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
+ uint32_t maxX, uint32_t maxY)
+{
+ return drm_helper_probe_single_connector_modes_merge_bits(connector, maxX, maxY, true);
+}
EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
/**
+ * drm_helper_probe_single_connector_modes_nomerge - get complete set of display modes
+ * @connector: connector to probe
+ * @maxX: max width for modes
+ * @maxY: max height for modes
+ *
+ * This operates like drm_hehlper_probe_single_connector_modes except it
+ * replaces the mode bits instead of merging them for preferred modes.
+ */
+int drm_helper_probe_single_connector_modes_nomerge(struct drm_connector *connector,
+ uint32_t maxX, uint32_t maxY)
+{
+ return drm_helper_probe_single_connector_modes_merge_bits(connector, maxX, maxY, false);
+}
+EXPORT_SYMBOL(drm_helper_probe_single_connector_modes_nomerge);
+
+/**
* drm_kms_helper_hotplug_event - fire off KMS hotplug events
* @dev: drm_device whose connector state changed
*
@@ -264,7 +286,7 @@ static void output_poll_execute(struct work_struct *work)
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] "
"status updated from %s to %s\n",
connector->base.id,
- drm_get_connector_name(connector),
+ connector->name,
old, new);
changed = true;
@@ -409,7 +431,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev)
connector->status = connector->funcs->detect(connector, false);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
connector->base.id,
- drm_get_connector_name(connector),
+ connector->name,
drm_get_connector_status_name(old_status),
drm_get_connector_status_name(connector->status));
if (old_status != connector->status)
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index 4c24c3ac1efa..14d16464000a 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -1,16 +1,11 @@
-/**
- * \file drm_stub.h
- * Stub support
- *
- * \author Rickard E. (Rik) Faith <faith@valinux.com>
- */
-
/*
* 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
@@ -128,7 +123,10 @@ struct drm_master *drm_master_create(struct drm_minor *minor)
kref_init(&master->refcount);
spin_lock_init(&master->lock.spinlock);
init_waitqueue_head(&master->lock.lock_queue);
- drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER);
+ if (drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER)) {
+ kfree(master);
+ return NULL;
+ }
INIT_LIST_HEAD(&master->magicfree);
master->minor = minor;
@@ -166,9 +164,6 @@ static void drm_master_destroy(struct kref *kref)
master->unique_len = 0;
}
- kfree(dev->devname);
- dev->devname = NULL;
-
list_for_each_entry_safe(pt, next, &master->magicfree, head) {
list_del(&pt->head);
drm_ht_remove_item(&master->magiclist, &pt->hash_item);
@@ -294,6 +289,7 @@ static void drm_minor_free(struct drm_device *dev, unsigned int type)
slot = drm_minor_get_slot(dev, type);
if (*slot) {
+ drm_mode_group_destroy(&(*slot)->mode_group);
kfree(*slot);
*slot = NULL;
}
@@ -424,11 +420,15 @@ void drm_minor_release(struct drm_minor *minor)
}
/**
- * Called via drm_exit() at module unload time or when pci device is
- * unplugged.
+ * drm_put_dev - Unregister and release a DRM device
+ * @dev: DRM device
*
- * Cleans up all DRM device, calling drm_lastclose().
+ * 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)
{
@@ -535,7 +535,7 @@ static void drm_fs_inode_free(struct inode *inode)
}
/**
- * drm_dev_alloc - Allocate new drm device
+ * drm_dev_alloc - Allocate new DRM device
* @driver: DRM driver to allocate device for
* @parent: Parent device object
*
@@ -569,7 +569,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
INIT_LIST_HEAD(&dev->maplist);
INIT_LIST_HEAD(&dev->vblank_event_list);
- spin_lock_init(&dev->count_lock);
+ spin_lock_init(&dev->buf_lock);
spin_lock_init(&dev->event_lock);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->ctxlist_mutex);
@@ -648,9 +648,8 @@ static void drm_dev_release(struct kref *ref)
drm_minor_free(dev, DRM_MINOR_RENDER);
drm_minor_free(dev, DRM_MINOR_CONTROL);
- kfree(dev->devname);
-
mutex_destroy(&dev->master_mutex);
+ kfree(dev->unique);
kfree(dev);
}
@@ -690,6 +689,7 @@ 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()
@@ -778,3 +778,28 @@ void drm_dev_unregister(struct drm_device *dev)
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 c22c3097c3e8..369b26278e76 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -380,9 +380,9 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
connector->kdev = device_create(drm_class, dev->primary->kdev,
0, connector, "card%d-%s",
- dev->primary->index, drm_get_connector_name(connector));
+ dev->primary->index, connector->name);
DRM_DEBUG("adding \"%s\" to sysfs\n",
- drm_get_connector_name(connector));
+ connector->name);
if (IS_ERR(connector->kdev)) {
DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev));
@@ -460,7 +460,7 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
if (!connector->kdev)
return;
DRM_DEBUG("removing \"%s\" from sysfs\n",
- drm_get_connector_name(connector));
+ connector->name);
for (i = 0; i < ARRAY_SIZE(connector_attrs); i++)
device_remove_file(connector->kdev, &connector_attrs[i]);
diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c
index c3406aad2944..f2fe94aab901 100644
--- a/drivers/gpu/drm/drm_usb.c
+++ b/drivers/gpu/drm/drm_usb.c
@@ -36,16 +36,6 @@ err_free:
}
EXPORT_SYMBOL(drm_get_usb_dev);
-static int drm_usb_get_irq(struct drm_device *dev)
-{
- return 0;
-}
-
-static const char *drm_usb_get_name(struct drm_device *dev)
-{
- return "USB";
-}
-
static int drm_usb_set_busid(struct drm_device *dev,
struct drm_master *master)
{
@@ -53,18 +43,24 @@ static int drm_usb_set_busid(struct drm_device *dev,
}
static struct drm_bus drm_usb_bus = {
- .bus_type = DRIVER_BUS_USB,
- .get_irq = drm_usb_get_irq,
- .get_name = drm_usb_get_name,
.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->kdriver.usb = udriver;
driver->bus = &drm_usb_bus;
res = usb_register(udriver);
@@ -72,6 +68,14 @@ int drm_usb_init(struct drm_driver *driver, struct usb_driver *udriver)
}
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)
{
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 5bf5bca94f56..178d2a9672a8 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -26,14 +26,14 @@ config DRM_EXYNOS_DMABUF
config DRM_EXYNOS_FIMD
bool "Exynos DRM FIMD"
- depends on DRM_EXYNOS && !FB_S3C && !ARCH_MULTIPLATFORM
+ depends on DRM_EXYNOS && !FB_S3C
select FB_MODE_HELPERS
help
Choose this option if you want to use Exynos FIMD for DRM.
config DRM_EXYNOS_DPI
bool "EXYNOS DRM parallel output support"
- depends on DRM_EXYNOS
+ depends on DRM_EXYNOS_FIMD
select DRM_PANEL
default n
help
@@ -41,7 +41,7 @@ config DRM_EXYNOS_DPI
config DRM_EXYNOS_DSI
bool "EXYNOS DRM MIPI-DSI driver support"
- depends on DRM_EXYNOS
+ depends on DRM_EXYNOS_FIMD
select DRM_MIPI_DSI
select DRM_PANEL
default n
@@ -50,7 +50,7 @@ config DRM_EXYNOS_DSI
config DRM_EXYNOS_DP
bool "EXYNOS DRM DP driver support"
- depends on DRM_EXYNOS && ARCH_EXYNOS
+ depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
default DRM_EXYNOS
help
This enables support for DP device.
diff --git a/drivers/gpu/drm/exynos/exynos_ddc.c b/drivers/gpu/drm/exynos/exynos_ddc.c
deleted file mode 100644
index 6a8c84e7c839..000000000000
--- a/drivers/gpu/drm/exynos/exynos_ddc.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- * Authors:
- * Seung-Woo Kim <sw0312.kim@samsung.com>
- * Inki Dae <inki.dae@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#include <drm/drmP.h>
-
-#include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/of.h>
-
-#include "exynos_drm_drv.h"
-#include "exynos_hdmi.h"
-
-static int s5p_ddc_probe(struct i2c_client *client,
- const struct i2c_device_id *dev_id)
-{
- hdmi_attach_ddc_client(client);
-
- dev_info(&client->adapter->dev,
- "attached %s into i2c adapter successfully\n",
- client->name);
-
- return 0;
-}
-
-static int s5p_ddc_remove(struct i2c_client *client)
-{
- dev_info(&client->adapter->dev,
- "detached %s from i2c adapter successfully\n",
- client->name);
-
- return 0;
-}
-
-static struct of_device_id hdmiddc_match_types[] = {
- {
- .compatible = "samsung,exynos5-hdmiddc",
- }, {
- .compatible = "samsung,exynos4210-hdmiddc",
- }, {
- /* end node */
- }
-};
-
-struct i2c_driver ddc_driver = {
- .driver = {
- .name = "exynos-hdmiddc",
- .owner = THIS_MODULE,
- .of_match_table = hdmiddc_match_types,
- },
- .probe = s5p_ddc_probe,
- .remove = s5p_ddc_remove,
- .command = NULL,
-};
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index aed533bbfd31..a8ffc8c1477b 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -18,6 +18,9 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/component.h>
#include <linux/phy/phy.h>
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
@@ -141,15 +144,15 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
return -EIO;
}
- exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_TEST_REQUEST,
+ exynos_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST,
&test_vector);
- if (test_vector & DPCD_TEST_EDID_READ) {
+ if (test_vector & DP_TEST_LINK_EDID_READ) {
exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TEST_EDID_CHECKSUM,
+ DP_TEST_EDID_CHECKSUM,
edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TEST_RESPONSE,
- DPCD_TEST_EDID_CHECKSUM_WRITE);
+ DP_TEST_RESPONSE,
+ DP_TEST_EDID_CHECKSUM_WRITE);
}
} else {
dev_info(dp->dev, "EDID data does not include any extensions.\n");
@@ -171,15 +174,15 @@ static int exynos_dp_read_edid(struct exynos_dp_device *dp)
}
exynos_dp_read_byte_from_dpcd(dp,
- DPCD_ADDR_TEST_REQUEST,
+ DP_TEST_REQUEST,
&test_vector);
- if (test_vector & DPCD_TEST_EDID_READ) {
+ if (test_vector & DP_TEST_LINK_EDID_READ) {
exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TEST_EDID_CHECKSUM,
+ DP_TEST_EDID_CHECKSUM,
edid[EDID_CHECKSUM]);
exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TEST_RESPONSE,
- DPCD_TEST_EDID_CHECKSUM_WRITE);
+ DP_TEST_RESPONSE,
+ DP_TEST_EDID_CHECKSUM_WRITE);
}
}
@@ -193,8 +196,8 @@ static int exynos_dp_handle_edid(struct exynos_dp_device *dp)
int i;
int retval;
- /* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */
- retval = exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_DPCD_REV,
+ /* Read DPCD DP_DPCD_REV~RECEIVE_PORT1_CAP_1 */
+ retval = exynos_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV,
12, buf);
if (retval)
return retval;
@@ -214,14 +217,14 @@ static void exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp_device *dp,
{
u8 data;
- exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, &data);
+ exynos_dp_read_byte_from_dpcd(dp, DP_LANE_COUNT_SET, &data);
if (enable)
- exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET,
- DPCD_ENHANCED_FRAME_EN |
+ exynos_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
+ DP_LANE_COUNT_ENHANCED_FRAME_EN |
DPCD_LANE_COUNT_SET(data));
else
- exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET,
+ exynos_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
DPCD_LANE_COUNT_SET(data));
}
@@ -230,7 +233,7 @@ static int exynos_dp_is_enhanced_mode_available(struct exynos_dp_device *dp)
u8 data;
int retval;
- exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data);
+ exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
retval = DPCD_ENHANCED_FRAME_CAP(data);
return retval;
@@ -250,8 +253,8 @@ static void exynos_dp_training_pattern_dis(struct exynos_dp_device *dp)
exynos_dp_set_training_pattern(dp, DP_NONE);
exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TRAINING_PATTERN_SET,
- DPCD_TRAINING_PATTERN_DISABLED);
+ DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
}
static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp,
@@ -295,7 +298,7 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
/* Setup RX configuration */
buf[0] = dp->link_train.link_rate;
buf[1] = dp->link_train.lane_count;
- retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET,
+ retval = exynos_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET,
2, buf);
if (retval)
return retval;
@@ -322,16 +325,16 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp)
/* Set RX training pattern */
retval = exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TRAINING_PATTERN_SET,
- DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1);
+ DP_TRAINING_PATTERN_SET,
+ DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1);
if (retval)
return retval;
for (lane = 0; lane < lane_count; lane++)
- buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 |
- DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0;
+ buf[lane] = DP_TRAIN_PRE_EMPHASIS_0 |
+ DP_TRAIN_VOLTAGE_SWING_400;
- retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET,
+ retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
lane_count, buf);
return retval;
@@ -352,7 +355,7 @@ static int exynos_dp_clock_recovery_ok(u8 link_status[2], int lane_count)
for (lane = 0; lane < lane_count; lane++) {
lane_status = exynos_dp_get_lane_status(link_status, lane);
- if ((lane_status & DPCD_LANE_CR_DONE) == 0)
+ if ((lane_status & DP_LANE_CR_DONE) == 0)
return -EINVAL;
}
return 0;
@@ -364,13 +367,13 @@ static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align,
int lane;
u8 lane_status;
- if ((link_align & DPCD_INTERLANE_ALIGN_DONE) == 0)
+ if ((link_align & DP_INTERLANE_ALIGN_DONE) == 0)
return -EINVAL;
for (lane = 0; lane < lane_count; lane++) {
lane_status = exynos_dp_get_lane_status(link_status, lane);
- lane_status &= DPCD_CHANNEL_EQ_BITS;
- if (lane_status != DPCD_CHANNEL_EQ_BITS)
+ lane_status &= DP_CHANNEL_EQ_BITS;
+ if (lane_status != DP_CHANNEL_EQ_BITS)
return -EINVAL;
}
@@ -468,9 +471,9 @@ static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp,
DPCD_PRE_EMPHASIS_SET(pre_emphasis);
if (voltage_swing == VOLTAGE_LEVEL_3)
- training_lane |= DPCD_MAX_SWING_REACHED;
+ training_lane |= DP_TRAIN_MAX_SWING_REACHED;
if (pre_emphasis == PRE_EMPHASIS_LEVEL_3)
- training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED;
+ training_lane |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
dp->link_train.training_lane[lane] = training_lane;
}
@@ -487,12 +490,12 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
lane_count = dp->link_train.lane_count;
retval = exynos_dp_read_bytes_from_dpcd(dp,
- DPCD_ADDR_LANE0_1_STATUS, 2, link_status);
+ DP_LANE0_1_STATUS, 2, link_status);
if (retval)
return retval;
retval = exynos_dp_read_bytes_from_dpcd(dp,
- DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
+ DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
if (retval)
return retval;
@@ -501,9 +504,9 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
exynos_dp_set_training_pattern(dp, TRAINING_PTN2);
retval = exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TRAINING_PATTERN_SET,
- DPCD_SCRAMBLING_DISABLED |
- DPCD_TRAINING_PATTERN_2);
+ DP_TRAINING_PATTERN_SET,
+ DP_LINK_SCRAMBLING_DISABLE |
+ DP_TRAINING_PATTERN_2);
if (retval)
return retval;
@@ -543,7 +546,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
dp->link_train.training_lane[lane], lane);
retval = exynos_dp_write_bytes_to_dpcd(dp,
- DPCD_ADDR_TRAINING_LANE0_SET, lane_count,
+ DP_TRAINING_LANE0_SET, lane_count,
dp->link_train.training_lane);
if (retval)
return retval;
@@ -562,7 +565,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
lane_count = dp->link_train.lane_count;
retval = exynos_dp_read_bytes_from_dpcd(dp,
- DPCD_ADDR_LANE0_1_STATUS, 2, link_status);
+ DP_LANE0_1_STATUS, 2, link_status);
if (retval)
return retval;
@@ -572,12 +575,12 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
}
retval = exynos_dp_read_bytes_from_dpcd(dp,
- DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
+ DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
if (retval)
return retval;
retval = exynos_dp_read_byte_from_dpcd(dp,
- DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED, &link_align);
+ DP_LANE_ALIGN_STATUS_UPDATED, &link_align);
if (retval)
return retval;
@@ -619,7 +622,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
exynos_dp_set_lane_link_training(dp,
dp->link_train.training_lane[lane], lane);
- retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET,
+ retval = exynos_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
lane_count, dp->link_train.training_lane);
return retval;
@@ -634,7 +637,7 @@ static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp,
* For DP rev.1.1, Maximum link rate of Main Link lanes
* 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
*/
- exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LINK_RATE, &data);
+ exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LINK_RATE, &data);
*bandwidth = data;
}
@@ -647,7 +650,7 @@ static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp,
* For DP rev.1.1, Maximum number of Main Link lanes
* 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
*/
- exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data);
+ exynos_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
*lane_count = DPCD_MAX_LANE_COUNT(data);
}
@@ -819,20 +822,20 @@ static void exynos_dp_enable_scramble(struct exynos_dp_device *dp, bool enable)
exynos_dp_enable_scrambling(dp);
exynos_dp_read_byte_from_dpcd(dp,
- DPCD_ADDR_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_SET,
&data);
exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TRAINING_PATTERN_SET,
- (u8)(data & ~DPCD_SCRAMBLING_DISABLED));
+ DP_TRAINING_PATTERN_SET,
+ (u8)(data & ~DP_LINK_SCRAMBLING_DISABLE));
} else {
exynos_dp_disable_scrambling(dp);
exynos_dp_read_byte_from_dpcd(dp,
- DPCD_ADDR_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_SET,
&data);
exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TRAINING_PATTERN_SET,
- (u8)(data | DPCD_SCRAMBLING_DISABLED));
+ DP_TRAINING_PATTERN_SET,
+ (u8)(data | DP_LINK_SCRAMBLING_DISABLE));
}
}
@@ -949,12 +952,6 @@ static int exynos_dp_get_modes(struct drm_connector *connector)
return 1;
}
-static int exynos_dp_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- return MODE_OK;
-}
-
static struct drm_encoder *exynos_dp_best_encoder(
struct drm_connector *connector)
{
@@ -965,20 +962,9 @@ static struct drm_encoder *exynos_dp_best_encoder(
static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = {
.get_modes = exynos_dp_get_modes,
- .mode_valid = exynos_dp_mode_valid,
.best_encoder = exynos_dp_best_encoder,
};
-static int exynos_dp_initialize(struct exynos_drm_display *display,
- struct drm_device *drm_dev)
-{
- struct exynos_dp_device *dp = display->ctx;
-
- dp->drm_dev = drm_dev;
-
- return 0;
-}
-
static bool find_bridge(const char *compat, struct bridge_init *bridge)
{
bridge->client = NULL;
@@ -1101,12 +1087,11 @@ static void exynos_dp_dpms(struct exynos_drm_display *display, int mode)
break;
default:
break;
- };
+ }
dp->dpms_mode = mode;
}
static struct exynos_drm_display_ops exynos_dp_display_ops = {
- .initialize = exynos_dp_initialize,
.create_connector = exynos_dp_create_connector,
.dpms = exynos_dp_dpms,
};
@@ -1123,10 +1108,8 @@ static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
dp_video_config = devm_kzalloc(dev,
sizeof(*dp_video_config), GFP_KERNEL);
- if (!dp_video_config) {
- dev_err(dev, "memory allocation for video config failed\n");
+ if (!dp_video_config)
return ERR_PTR(-ENOMEM);
- }
dp_video_config->h_sync_polarity =
of_property_read_bool(dp_node, "hsync-active-high");
@@ -1185,10 +1168,7 @@ static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy");
if (!dp_phy_node) {
dp->phy = devm_phy_get(dp->dev, "dp");
- if (IS_ERR(dp->phy))
- return PTR_ERR(dp->phy);
- else
- return 0;
+ return PTR_ERR_OR_ZERO(dp->phy);
}
if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) {
@@ -1230,19 +1210,20 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
return 0;
}
-static int exynos_dp_probe(struct platform_device *pdev)
+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;
+ unsigned int irq_flags;
int ret = 0;
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
GFP_KERNEL);
- if (!dp) {
- dev_err(&pdev->dev, "no memory for device data\n");
+ if (!dp)
return -ENOMEM;
- }
dp->dev = &pdev->dev;
dp->dpms_mode = DRM_MODE_DPMS_OFF;
@@ -1273,7 +1254,30 @@ static int exynos_dp_probe(struct platform_device *pdev)
if (IS_ERR(dp->reg_base))
return PTR_ERR(dp->reg_base);
- dp->irq = platform_get_irq(pdev, 0);
+ dp->hpd_gpio = of_get_named_gpio(dev->of_node, "samsung,hpd-gpio", 0);
+
+ if (gpio_is_valid(dp->hpd_gpio)) {
+ /*
+ * Set up the hotplug GPIO from the device tree as an interrupt.
+ * Simply specifying a different interrupt in the device tree
+ * doesn't work since we handle hotplug rather differently when
+ * using a GPIO. We also need the actual GPIO specifier so
+ * that we can get the current state of the GPIO.
+ */
+ ret = devm_gpio_request_one(&pdev->dev, dp->hpd_gpio, GPIOF_IN,
+ "hpd_gpio");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get hpd gpio\n");
+ return ret;
+ }
+ dp->irq = gpio_to_irq(dp->hpd_gpio);
+ irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+ } else {
+ dp->hpd_gpio = -ENODEV;
+ dp->irq = platform_get_irq(pdev, 0);
+ irq_flags = 0;
+ }
+
if (dp->irq == -ENXIO) {
dev_err(&pdev->dev, "failed to get irq\n");
return -ENODEV;
@@ -1285,28 +1289,61 @@ static int exynos_dp_probe(struct platform_device *pdev)
exynos_dp_init_dp(dp);
- ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0,
- "exynos-dp", dp);
+ ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler,
+ irq_flags, "exynos-dp", dp);
if (ret) {
dev_err(&pdev->dev, "failed to request irq\n");
return ret;
}
disable_irq(dp->irq);
+ dp->drm_dev = drm_dev;
exynos_dp_display.ctx = dp;
platform_set_drvdata(pdev, &exynos_dp_display);
- exynos_drm_display_register(&exynos_dp_display);
- return 0;
+ return exynos_drm_create_enc_conn(drm_dev, &exynos_dp_display);
}
-static int exynos_dp_remove(struct platform_device *pdev)
+static void exynos_dp_unbind(struct device *dev, struct device *master,
+ void *data)
{
- struct exynos_drm_display *display = platform_get_drvdata(pdev);
+ 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);
- exynos_drm_display_unregister(&exynos_dp_display);
+
+ encoder->funcs->destroy(encoder);
+ drm_connector_cleanup(&dp->connector);
+}
+
+static const struct component_ops exynos_dp_ops = {
+ .bind = exynos_dp_bind,
+ .unbind = exynos_dp_unbind,
+};
+
+static int exynos_dp_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
+ exynos_dp_display.type);
+ if (ret)
+ return ret;
+
+ ret = component_add(&pdev->dev, &exynos_dp_ops);
+ if (ret)
+ exynos_drm_component_del(&pdev->dev,
+ EXYNOS_DEVICE_TYPE_CONNECTOR);
+
+ return ret;
+}
+
+static int exynos_dp_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &exynos_dp_ops);
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h
index d6a900d4ee40..02cc4f9ab903 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.h
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.h
@@ -14,6 +14,7 @@
#define _EXYNOS_DP_CORE_H
#include <drm/drm_crtc.h>
+#include <drm/drm_dp_helper.h>
#include <drm/exynos_drm.h>
#define DP_TIMEOUT_LOOP_COUNT 100
@@ -159,6 +160,7 @@ struct exynos_dp_device {
struct work_struct hotplug_work;
struct phy *phy;
int dpms_mode;
+ int hpd_gpio;
struct exynos_drm_panel_info panel;
};
@@ -261,69 +263,17 @@ void exynos_dp_disable_scrambling(struct exynos_dp_device *dp);
#define EDID_EXTENSION_FLAG 0x7e
#define EDID_CHECKSUM 0x7f
-/* Definition for DPCD Register */
-#define DPCD_ADDR_DPCD_REV 0x0000
-#define DPCD_ADDR_MAX_LINK_RATE 0x0001
-#define DPCD_ADDR_MAX_LANE_COUNT 0x0002
-#define DPCD_ADDR_LINK_BW_SET 0x0100
-#define DPCD_ADDR_LANE_COUNT_SET 0x0101
-#define DPCD_ADDR_TRAINING_PATTERN_SET 0x0102
-#define DPCD_ADDR_TRAINING_LANE0_SET 0x0103
-#define DPCD_ADDR_LANE0_1_STATUS 0x0202
-#define DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED 0x0204
-#define DPCD_ADDR_ADJUST_REQUEST_LANE0_1 0x0206
-#define DPCD_ADDR_ADJUST_REQUEST_LANE2_3 0x0207
-#define DPCD_ADDR_TEST_REQUEST 0x0218
-#define DPCD_ADDR_TEST_RESPONSE 0x0260
-#define DPCD_ADDR_TEST_EDID_CHECKSUM 0x0261
-#define DPCD_ADDR_SINK_POWER_STATE 0x0600
-
-/* DPCD_ADDR_MAX_LANE_COUNT */
+/* DP_MAX_LANE_COUNT */
#define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1)
#define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f)
-/* DPCD_ADDR_LANE_COUNT_SET */
-#define DPCD_ENHANCED_FRAME_EN (0x1 << 7)
+/* DP_LANE_COUNT_SET */
#define DPCD_LANE_COUNT_SET(x) ((x) & 0x1f)
-/* DPCD_ADDR_TRAINING_PATTERN_SET */
-#define DPCD_SCRAMBLING_DISABLED (0x1 << 5)
-#define DPCD_SCRAMBLING_ENABLED (0x0 << 5)
-#define DPCD_TRAINING_PATTERN_2 (0x2 << 0)
-#define DPCD_TRAINING_PATTERN_1 (0x1 << 0)
-#define DPCD_TRAINING_PATTERN_DISABLED (0x0 << 0)
-
-/* DPCD_ADDR_TRAINING_LANE0_SET */
-#define DPCD_MAX_PRE_EMPHASIS_REACHED (0x1 << 5)
+/* DP_TRAINING_LANE0_SET */
#define DPCD_PRE_EMPHASIS_SET(x) (((x) & 0x3) << 3)
#define DPCD_PRE_EMPHASIS_GET(x) (((x) >> 3) & 0x3)
-#define DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 (0x0 << 3)
-#define DPCD_MAX_SWING_REACHED (0x1 << 2)
#define DPCD_VOLTAGE_SWING_SET(x) (((x) & 0x3) << 0)
#define DPCD_VOLTAGE_SWING_GET(x) (((x) >> 0) & 0x3)
-#define DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0 (0x0 << 0)
-
-/* DPCD_ADDR_LANE0_1_STATUS */
-#define DPCD_LANE_SYMBOL_LOCKED (0x1 << 2)
-#define DPCD_LANE_CHANNEL_EQ_DONE (0x1 << 1)
-#define DPCD_LANE_CR_DONE (0x1 << 0)
-#define DPCD_CHANNEL_EQ_BITS (DPCD_LANE_CR_DONE| \
- DPCD_LANE_CHANNEL_EQ_DONE|\
- DPCD_LANE_SYMBOL_LOCKED)
-
-/* DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED */
-#define DPCD_LINK_STATUS_UPDATED (0x1 << 7)
-#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED (0x1 << 6)
-#define DPCD_INTERLANE_ALIGN_DONE (0x1 << 0)
-
-/* DPCD_ADDR_TEST_REQUEST */
-#define DPCD_TEST_EDID_READ (0x1 << 2)
-
-/* DPCD_ADDR_TEST_RESPONSE */
-#define DPCD_TEST_EDID_CHECKSUM_WRITE (0x1 << 2)
-
-/* DPCD_ADDR_SINK_POWER_STATE */
-#define DPCD_SET_POWER_STATE_D0 (0x1 << 0)
-#define DPCD_SET_POWER_STATE_D4 (0x2 << 0)
#endif /* _EXYNOS_DP_CORE_H */
diff --git a/drivers/gpu/drm/exynos/exynos_dp_reg.c b/drivers/gpu/drm/exynos/exynos_dp_reg.c
index b70da5052ff0..c1f87a2a9284 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_reg.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_reg.c
@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/gpio.h>
#include "exynos_dp_core.h"
#include "exynos_dp_reg.h"
@@ -326,6 +327,9 @@ void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp)
{
u32 reg;
+ if (gpio_is_valid(dp->hpd_gpio))
+ return;
+
reg = HOTPLUG_CHG | HPD_LOST | PLUG;
writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
@@ -337,6 +341,9 @@ void exynos_dp_init_hpd(struct exynos_dp_device *dp)
{
u32 reg;
+ if (gpio_is_valid(dp->hpd_gpio))
+ return;
+
exynos_dp_clear_hotplug_interrupts(dp);
reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
@@ -348,19 +355,27 @@ enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
{
u32 reg;
- /* Parse hotplug interrupt status register */
- reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
+ if (gpio_is_valid(dp->hpd_gpio)) {
+ reg = gpio_get_value(dp->hpd_gpio);
+ if (reg)
+ return DP_IRQ_TYPE_HP_CABLE_IN;
+ else
+ return DP_IRQ_TYPE_HP_CABLE_OUT;
+ } else {
+ /* Parse hotplug interrupt status register */
+ reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
- if (reg & PLUG)
- return DP_IRQ_TYPE_HP_CABLE_IN;
+ if (reg & PLUG)
+ return DP_IRQ_TYPE_HP_CABLE_IN;
- if (reg & HPD_LOST)
- return DP_IRQ_TYPE_HP_CABLE_OUT;
+ if (reg & HPD_LOST)
+ return DP_IRQ_TYPE_HP_CABLE_OUT;
- if (reg & HOTPLUG_CHG)
- return DP_IRQ_TYPE_HP_CHANGE;
+ if (reg & HOTPLUG_CHG)
+ return DP_IRQ_TYPE_HP_CHANGE;
- return DP_IRQ_TYPE_UNKNOWN;
+ return DP_IRQ_TYPE_UNKNOWN;
+ }
}
void exynos_dp_reset_aux(struct exynos_dp_device *dp)
@@ -386,7 +401,7 @@ void exynos_dp_init_aux(struct exynos_dp_device *dp)
/* Disable AUX transaction H/W retry */
reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
- writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL) ;
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL);
/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
reg = DEFER_CTRL_EN | DEFER_COUNT(1);
@@ -402,9 +417,14 @@ int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp)
{
u32 reg;
- reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
- if (reg & HPD_STATUS)
- return 0;
+ if (gpio_is_valid(dp->hpd_gpio)) {
+ if (gpio_get_value(dp->hpd_gpio))
+ return 0;
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
+ if (reg & HPD_STATUS)
+ return 0;
+ }
return -EINVAL;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index 0e9e06ce36b8..4c9f972eaa07 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -19,21 +19,19 @@
#include "exynos_drm_fbdev.h"
static LIST_HEAD(exynos_drm_subdrv_list);
-static LIST_HEAD(exynos_drm_manager_list);
-static LIST_HEAD(exynos_drm_display_list);
-static int exynos_drm_create_enc_conn(struct drm_device *dev,
+int exynos_drm_create_enc_conn(struct drm_device *dev,
struct exynos_drm_display *display)
{
struct drm_encoder *encoder;
- struct exynos_drm_manager *manager;
int ret;
unsigned long possible_crtcs = 0;
- /* Find possible crtcs for this display */
- list_for_each_entry(manager, &exynos_drm_manager_list, list)
- if (manager->type == display->type)
- possible_crtcs |= 1 << manager->pipe;
+ ret = exynos_drm_crtc_get_pipe_from_type(dev, display->type);
+ if (ret < 0)
+ return ret;
+
+ possible_crtcs |= 1 << ret;
/* create and initialize a encoder for this sub driver. */
encoder = exynos_drm_encoder_create(dev, display, possible_crtcs);
@@ -57,127 +55,29 @@ err_destroy_encoder:
return ret;
}
-static int exynos_drm_subdrv_probe(struct drm_device *dev,
- struct exynos_drm_subdrv *subdrv)
-{
- if (subdrv->probe) {
- int ret;
-
- subdrv->drm_dev = dev;
-
- /*
- * this probe callback would be called by sub driver
- * after setting of all resources to this sub driver,
- * such as clock, irq and register map are done or by load()
- * of exynos drm driver.
- *
- * P.S. note that this driver is considered for modularization.
- */
- ret = subdrv->probe(dev, subdrv->dev);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static void exynos_drm_subdrv_remove(struct drm_device *dev,
- struct exynos_drm_subdrv *subdrv)
-{
- if (subdrv->remove)
- subdrv->remove(dev, subdrv->dev);
-}
-
-int exynos_drm_initialize_managers(struct drm_device *dev)
+int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
{
- struct exynos_drm_manager *manager, *n;
- int ret, pipe = 0;
-
- list_for_each_entry(manager, &exynos_drm_manager_list, list) {
- if (manager->ops->initialize) {
- ret = manager->ops->initialize(manager, dev, pipe);
- if (ret) {
- DRM_ERROR("Mgr init [%d] failed with %d\n",
- manager->type, ret);
- goto err;
- }
- }
+ if (!subdrv)
+ return -EINVAL;
- manager->drm_dev = dev;
- manager->pipe = pipe++;
+ list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
- ret = exynos_drm_crtc_create(manager);
- if (ret) {
- DRM_ERROR("CRTC create [%d] failed with %d\n",
- manager->type, ret);
- goto err;
- }
- }
return 0;
-
-err:
- list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list) {
- if (pipe-- > 0)
- exynos_drm_manager_unregister(manager);
- else
- list_del(&manager->list);
- }
- return ret;
-}
-
-void exynos_drm_remove_managers(struct drm_device *dev)
-{
- struct exynos_drm_manager *manager, *n;
-
- list_for_each_entry_safe(manager, n, &exynos_drm_manager_list, list)
- exynos_drm_manager_unregister(manager);
}
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
-int exynos_drm_initialize_displays(struct drm_device *dev)
+int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
{
- struct exynos_drm_display *display, *n;
- int ret, initialized = 0;
-
- list_for_each_entry(display, &exynos_drm_display_list, list) {
- if (display->ops->initialize) {
- ret = display->ops->initialize(display, dev);
- if (ret) {
- DRM_ERROR("Display init [%d] failed with %d\n",
- display->type, ret);
- goto err;
- }
- }
+ if (!subdrv)
+ return -EINVAL;
- initialized++;
+ list_del(&subdrv->list);
- ret = exynos_drm_create_enc_conn(dev, display);
- if (ret) {
- DRM_ERROR("Encoder create [%d] failed with %d\n",
- display->type, ret);
- goto err;
- }
- }
return 0;
-
-err:
- list_for_each_entry_safe(display, n, &exynos_drm_display_list, list) {
- if (initialized-- > 0)
- exynos_drm_display_unregister(display);
- else
- list_del(&display->list);
- }
- return ret;
-}
-
-void exynos_drm_remove_displays(struct drm_device *dev)
-{
- struct exynos_drm_display *display, *n;
-
- list_for_each_entry_safe(display, n, &exynos_drm_display_list, list)
- exynos_drm_display_unregister(display);
}
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
-int exynos_drm_device_register(struct drm_device *dev)
+int exynos_drm_device_subdrv_probe(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv, *n;
int err;
@@ -186,19 +86,28 @@ int exynos_drm_device_register(struct drm_device *dev)
return -EINVAL;
list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
- err = exynos_drm_subdrv_probe(dev, subdrv);
- if (err) {
- DRM_DEBUG("exynos drm subdrv probe failed.\n");
- list_del(&subdrv->list);
- continue;
+ if (subdrv->probe) {
+ subdrv->drm_dev = dev;
+
+ /*
+ * this probe callback would be called by sub driver
+ * after setting of all resources to this sub driver,
+ * such as clock, irq and register map are done.
+ */
+ err = subdrv->probe(dev, subdrv->dev);
+ if (err) {
+ DRM_DEBUG("exynos drm subdrv probe failed.\n");
+ list_del(&subdrv->list);
+ continue;
+ }
}
}
return 0;
}
-EXPORT_SYMBOL_GPL(exynos_drm_device_register);
+EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_probe);
-int exynos_drm_device_unregister(struct drm_device *dev)
+int exynos_drm_device_subdrv_remove(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv;
@@ -208,66 +117,13 @@ int exynos_drm_device_unregister(struct drm_device *dev)
}
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
- exynos_drm_subdrv_remove(dev, subdrv);
+ if (subdrv->remove)
+ subdrv->remove(dev, subdrv->dev);
}
return 0;
}
-EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
-
-int exynos_drm_manager_register(struct exynos_drm_manager *manager)
-{
- BUG_ON(!manager->ops);
- list_add_tail(&manager->list, &exynos_drm_manager_list);
- return 0;
-}
-
-int exynos_drm_manager_unregister(struct exynos_drm_manager *manager)
-{
- if (manager->ops->remove)
- manager->ops->remove(manager);
-
- list_del(&manager->list);
- return 0;
-}
-
-int exynos_drm_display_register(struct exynos_drm_display *display)
-{
- BUG_ON(!display->ops);
- list_add_tail(&display->list, &exynos_drm_display_list);
- return 0;
-}
-
-int exynos_drm_display_unregister(struct exynos_drm_display *display)
-{
- if (display->ops->remove)
- display->ops->remove(display);
-
- list_del(&display->list);
- return 0;
-}
-
-int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
-{
- if (!subdrv)
- return -EINVAL;
-
- list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
-
-int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
-{
- if (!subdrv)
- return -EINVAL;
-
- list_del(&subdrv->list);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
+EXPORT_SYMBOL_GPL(exynos_drm_device_subdrv_remove);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
{
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 1ef5ab9c9d51..95c9435d0266 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -368,6 +368,7 @@ int exynos_drm_crtc_create(struct exynos_drm_manager *manager)
return -ENOMEM;
}
+ manager->crtc = &exynos_crtc->drm_crtc;
crtc = &exynos_crtc->drm_crtc;
private->crtc[manager->pipe] = crtc;
@@ -491,3 +492,19 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
manager->ops->wait_for_vblank(manager);
}
}
+
+int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
+ unsigned int out_type)
+{
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) {
+ struct exynos_drm_crtc *exynos_crtc;
+
+ exynos_crtc = to_exynos_crtc(crtc);
+ if (exynos_crtc->manager->type == out_type)
+ return exynos_crtc->manager->pipe;
+ }
+
+ return -EPERM;
+}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
index c27b66cc5d24..9f74b10a8a01 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h
@@ -32,4 +32,8 @@ void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos);
void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos);
void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
+/* This function gets pipe value to crtc device matched with out_type. */
+int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
+ unsigned int out_type);
+
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
index 2b09c7c0bfcc..482127f633c5 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -40,20 +40,10 @@ exynos_dpi_detect(struct drm_connector *connector, bool force)
{
struct exynos_dpi *ctx = connector_to_dpi(connector);
- /* panels supported only by boot-loader are always connected */
- if (!ctx->panel_node)
- return connector_status_connected;
-
- if (!ctx->panel) {
- ctx->panel = of_drm_find_panel(ctx->panel_node);
- if (ctx->panel)
- drm_panel_attach(ctx->panel, &ctx->connector);
- }
-
- if (ctx->panel)
- return connector_status_connected;
+ if (!ctx->panel->connector)
+ drm_panel_attach(ctx->panel, &ctx->connector);
- return connector_status_disconnected;
+ return connector_status_connected;
}
static void exynos_dpi_connector_destroy(struct drm_connector *connector)
@@ -94,12 +84,6 @@ static int exynos_dpi_get_modes(struct drm_connector *connector)
return 0;
}
-static int exynos_dpi_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- return MODE_OK;
-}
-
static struct drm_encoder *
exynos_dpi_best_encoder(struct drm_connector *connector)
{
@@ -110,7 +94,6 @@ exynos_dpi_best_encoder(struct drm_connector *connector)
static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
.get_modes = exynos_dpi_get_modes,
- .mode_valid = exynos_dpi_mode_valid,
.best_encoder = exynos_dpi_best_encoder,
};
@@ -123,10 +106,7 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
ctx->encoder = encoder;
- if (ctx->panel_node)
- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
- else
- connector->polled = DRM_CONNECTOR_POLL_HPD;
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
ret = drm_connector_init(encoder->dev, connector,
&exynos_dpi_connector_funcs,
@@ -172,7 +152,7 @@ static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode)
break;
default:
break;
- };
+ }
ctx->dpms_mode = mode;
}
@@ -294,8 +274,10 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
return -ENOMEM;
ret = of_get_videomode(dn, vm, 0);
- if (ret < 0)
+ if (ret < 0) {
+ devm_kfree(dev, vm);
return ret;
+ }
ctx->vm = vm;
@@ -308,32 +290,58 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
return 0;
}
-int exynos_dpi_probe(struct device *dev)
+struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
{
struct exynos_dpi *ctx;
int ret;
+ ret = exynos_drm_component_add(dev,
+ EXYNOS_DEVICE_TYPE_CONNECTOR,
+ exynos_dpi_display.type);
+ if (ret)
+ return ERR_PTR(ret);
+
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
- return -ENOMEM;
+ goto err_del_component;
ctx->dev = dev;
exynos_dpi_display.ctx = ctx;
ctx->dpms_mode = DRM_MODE_DPMS_OFF;
ret = exynos_dpi_parse_dt(ctx);
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ devm_kfree(dev, ctx);
+ goto err_del_component;
+ }
- exynos_drm_display_register(&exynos_dpi_display);
+ if (ctx->panel_node) {
+ ctx->panel = of_drm_find_panel(ctx->panel_node);
+ if (!ctx->panel) {
+ exynos_drm_component_del(dev,
+ EXYNOS_DEVICE_TYPE_CONNECTOR);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+ }
- return 0;
+ return &exynos_dpi_display;
+
+err_del_component:
+ exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
+
+ return NULL;
}
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);
- exynos_drm_display_unregister(&exynos_dpi_display);
+ encoder->funcs->destroy(encoder);
+ drm_connector_cleanup(&ctx->connector);
+
+ exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 2d27ba23a6a8..d91f27777537 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -16,6 +16,7 @@
#include <drm/drm_crtc_helper.h>
#include <linux/anon_inodes.h>
+#include <linux/component.h>
#include <drm/exynos_drm.h>
@@ -40,9 +41,19 @@
#define VBLANK_OFF_DELAY 50000
-/* platform device pointer for eynos drm device. */
static struct platform_device *exynos_drm_pdev;
+static DEFINE_MUTEX(drm_component_lock);
+static LIST_HEAD(drm_component_list);
+
+struct component_dev {
+ struct list_head list;
+ struct device *crtc_dev;
+ struct device *conn_dev;
+ enum exynos_drm_output_type out_type;
+ unsigned int dev_type_flag;
+};
+
static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
{
struct exynos_drm_private *private;
@@ -73,38 +84,21 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
exynos_drm_mode_config_init(dev);
- ret = exynos_drm_initialize_managers(dev);
- if (ret)
- goto err_mode_config_cleanup;
-
for (nr = 0; nr < MAX_PLANE; nr++) {
struct drm_plane *plane;
unsigned long possible_crtcs = (1 << MAX_CRTC) - 1;
plane = exynos_plane_init(dev, possible_crtcs, false);
if (!plane)
- goto err_manager_cleanup;
+ goto err_mode_config_cleanup;
}
- ret = exynos_drm_initialize_displays(dev);
- if (ret)
- goto err_manager_cleanup;
-
/* init kms poll for handling hpd */
drm_kms_helper_poll_init(dev);
ret = drm_vblank_init(dev, MAX_CRTC);
if (ret)
- goto err_display_cleanup;
-
- /*
- * probe sub drivers such as display controller and hdmi driver,
- * that were registered at probe() of platform driver
- * to the sub driver and create encoder and connector for them.
- */
- ret = exynos_drm_device_register(dev);
- if (ret)
- goto err_vblank;
+ goto err_mode_config_cleanup;
/* setup possible_clones. */
exynos_drm_encoder_setup(dev);
@@ -113,17 +107,25 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
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;
+
+ /* Probe non kms sub drivers and virtual display driver. */
+ ret = exynos_drm_device_subdrv_probe(dev);
+ if (ret)
+ goto err_unbind_all;
+
/* force connectors detection */
drm_helper_hpd_irq_event(dev);
return 0;
-err_vblank:
+err_unbind_all:
+ component_unbind_all(dev->dev, dev);
+err_cleanup_vblank:
drm_vblank_cleanup(dev);
-err_display_cleanup:
- exynos_drm_remove_displays(dev);
-err_manager_cleanup:
- exynos_drm_remove_managers(dev);
err_mode_config_cleanup:
drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev);
@@ -135,17 +137,17 @@ err_free_private:
static int exynos_drm_unload(struct drm_device *dev)
{
+ exynos_drm_device_subdrv_remove(dev);
+
exynos_drm_fbdev_fini(dev);
- exynos_drm_device_unregister(dev);
drm_vblank_cleanup(dev);
drm_kms_helper_poll_fini(dev);
- exynos_drm_remove_displays(dev);
- exynos_drm_remove_managers(dev);
drm_mode_config_cleanup(dev);
drm_release_iommu_mapping(dev);
kfree(dev->dev_private);
+ component_unbind_all(dev->dev, dev);
dev->dev_private = NULL;
return 0;
@@ -183,9 +185,9 @@ static int exynos_drm_resume(struct drm_device *dev)
if (connector->funcs->dpms)
connector->funcs->dpms(connector, connector->dpms);
}
+ drm_modeset_unlock_all(dev);
drm_helper_resume_force_mode(dev);
- drm_modeset_unlock_all(dev);
return 0;
}
@@ -323,8 +325,7 @@ static const struct file_operations exynos_drm_driver_fops = {
};
static struct drm_driver exynos_drm_driver = {
- .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
- DRIVER_GEM | DRIVER_PRIME,
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
.load = exynos_drm_load,
.unload = exynos_drm_unload,
.suspend = exynos_drm_suspend,
@@ -355,27 +356,6 @@ static struct drm_driver exynos_drm_driver = {
.minor = DRIVER_MINOR,
};
-static int exynos_drm_platform_probe(struct platform_device *pdev)
-{
- int ret;
-
- ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- pm_runtime_enable(&pdev->dev);
- pm_runtime_get_sync(&pdev->dev);
-
- return drm_platform_init(&exynos_drm_driver, pdev);
-}
-
-static int exynos_drm_platform_remove(struct platform_device *pdev)
-{
- drm_put_dev(platform_get_drvdata(pdev));
-
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int exynos_drm_sys_suspend(struct device *dev)
{
@@ -400,196 +380,319 @@ static int exynos_drm_sys_resume(struct device *dev)
}
#endif
-#ifdef CONFIG_PM_RUNTIME
-static int exynos_drm_runtime_suspend(struct device *dev)
+static const struct dev_pm_ops exynos_drm_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
+};
+
+int exynos_drm_component_add(struct device *dev,
+ enum exynos_drm_device_type dev_type,
+ enum exynos_drm_output_type out_type)
{
- struct drm_device *drm_dev = dev_get_drvdata(dev);
- pm_message_t message;
+ struct component_dev *cdev;
- if (pm_runtime_suspended(dev))
- return 0;
+ if (dev_type != EXYNOS_DEVICE_TYPE_CRTC &&
+ dev_type != EXYNOS_DEVICE_TYPE_CONNECTOR) {
+ DRM_ERROR("invalid device type.\n");
+ return -EINVAL;
+ }
- message.event = PM_EVENT_SUSPEND;
- return exynos_drm_suspend(drm_dev, message);
+ mutex_lock(&drm_component_lock);
+
+ /*
+ * Make sure to check if there is a component which has two device
+ * objects, for connector and for encoder/connector.
+ * It should make sure that crtc and encoder/connector drivers are
+ * ready before exynos drm core binds them.
+ */
+ list_for_each_entry(cdev, &drm_component_list, list) {
+ if (cdev->out_type == out_type) {
+ /*
+ * If crtc and encoder/connector device objects are
+ * added already just return.
+ */
+ if (cdev->dev_type_flag == (EXYNOS_DEVICE_TYPE_CRTC |
+ EXYNOS_DEVICE_TYPE_CONNECTOR)) {
+ mutex_unlock(&drm_component_lock);
+ return 0;
+ }
+
+ if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
+ cdev->crtc_dev = dev;
+ cdev->dev_type_flag |= dev_type;
+ }
+
+ if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
+ cdev->conn_dev = dev;
+ cdev->dev_type_flag |= dev_type;
+ }
+
+ mutex_unlock(&drm_component_lock);
+ return 0;
+ }
+ }
+
+ mutex_unlock(&drm_component_lock);
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
+
+ if (dev_type == EXYNOS_DEVICE_TYPE_CRTC)
+ cdev->crtc_dev = dev;
+ if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR)
+ cdev->conn_dev = dev;
+
+ cdev->out_type = out_type;
+ cdev->dev_type_flag = dev_type;
+
+ mutex_lock(&drm_component_lock);
+ list_add_tail(&cdev->list, &drm_component_list);
+ mutex_unlock(&drm_component_lock);
+
+ return 0;
}
-static int exynos_drm_runtime_resume(struct device *dev)
+void exynos_drm_component_del(struct device *dev,
+ enum exynos_drm_device_type dev_type)
{
- struct drm_device *drm_dev = dev_get_drvdata(dev);
+ struct component_dev *cdev, *next;
- if (!pm_runtime_suspended(dev))
- return 0;
+ mutex_lock(&drm_component_lock);
- return exynos_drm_resume(drm_dev);
+ list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
+ if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
+ if (cdev->crtc_dev == dev) {
+ cdev->crtc_dev = NULL;
+ cdev->dev_type_flag &= ~dev_type;
+ }
+ }
+
+ if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
+ if (cdev->conn_dev == dev) {
+ cdev->conn_dev = NULL;
+ cdev->dev_type_flag &= ~dev_type;
+ }
+ }
+
+ /*
+ * Release cdev object only in case that both of crtc and
+ * encoder/connector device objects are NULL.
+ */
+ if (!cdev->crtc_dev && !cdev->conn_dev) {
+ list_del(&cdev->list);
+ kfree(cdev);
+ }
+
+ break;
+ }
+
+ mutex_unlock(&drm_component_lock);
}
-#endif
-static const struct dev_pm_ops exynos_drm_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
- SET_RUNTIME_PM_OPS(exynos_drm_runtime_suspend,
- exynos_drm_runtime_resume, NULL)
-};
+static int compare_of(struct device *dev, void *data)
+{
+ return dev == (struct device *)data;
+}
-static struct platform_driver exynos_drm_platform_driver = {
- .probe = exynos_drm_platform_probe,
- .remove = exynos_drm_platform_remove,
- .driver = {
- .owner = THIS_MODULE,
- .name = "exynos-drm",
- .pm = &exynos_drm_pm_ops,
- },
+static int exynos_drm_add_components(struct device *dev, struct master *m)
+{
+ 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;
+
+ /*
+ * Add components to master only in case that crtc and
+ * encoder/connector device objects exist.
+ */
+ if (!cdev->crtc_dev || !cdev->conn_dev)
+ continue;
+
+ attach_cnt++;
+
+ mutex_unlock(&drm_component_lock);
+
+ /*
+ * 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;
+
+ goto out_lock;
+ }
+
+ /*
+ * Do not chage below call order.
+ * crtc device first should be added to master because
+ * 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;
+
+out_lock:
+ mutex_lock(&drm_component_lock);
+ }
+
+ mutex_unlock(&drm_component_lock);
+
+ return attach_cnt ? 0 : -ENODEV;
+}
+
+static int exynos_drm_bind(struct device *dev)
+{
+ return drm_platform_init(&exynos_drm_driver, to_platform_device(dev));
+}
+
+static void exynos_drm_unbind(struct device *dev)
+{
+ drm_put_dev(dev_get_drvdata(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 __init exynos_drm_init(void)
+static int exynos_drm_platform_probe(struct platform_device *pdev)
{
int ret;
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls);
+
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+ ret = platform_driver_register(&fimd_driver);
+ if (ret < 0)
+ return ret;
+#endif
+
#ifdef CONFIG_DRM_EXYNOS_DP
ret = platform_driver_register(&dp_driver);
if (ret < 0)
- goto out_dp;
+ goto err_unregister_fimd_drv;
#endif
#ifdef CONFIG_DRM_EXYNOS_DSI
ret = platform_driver_register(&dsi_driver);
if (ret < 0)
- goto out_dsi;
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_FIMD
- ret = platform_driver_register(&fimd_driver);
- if (ret < 0)
- goto out_fimd;
+ goto err_unregister_dp_drv;
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
- ret = platform_driver_register(&hdmi_driver);
- if (ret < 0)
- goto out_hdmi;
ret = platform_driver_register(&mixer_driver);
if (ret < 0)
- goto out_mixer;
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_VIDI
- ret = platform_driver_register(&vidi_driver);
+ goto err_unregister_dsi_drv;
+ ret = platform_driver_register(&hdmi_driver);
if (ret < 0)
- goto out_vidi;
+ goto err_unregister_mixer_drv;
#endif
#ifdef CONFIG_DRM_EXYNOS_G2D
ret = platform_driver_register(&g2d_driver);
if (ret < 0)
- goto out_g2d;
+ goto err_unregister_hdmi_drv;
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMC
ret = platform_driver_register(&fimc_driver);
if (ret < 0)
- goto out_fimc;
+ goto err_unregister_g2d_drv;
#endif
#ifdef CONFIG_DRM_EXYNOS_ROTATOR
ret = platform_driver_register(&rotator_driver);
if (ret < 0)
- goto out_rotator;
+ goto err_unregister_fimc_drv;
#endif
#ifdef CONFIG_DRM_EXYNOS_GSC
ret = platform_driver_register(&gsc_driver);
if (ret < 0)
- goto out_gsc;
+ goto err_unregister_rotator_drv;
#endif
#ifdef CONFIG_DRM_EXYNOS_IPP
ret = platform_driver_register(&ipp_driver);
if (ret < 0)
- goto out_ipp;
+ goto err_unregister_gsc_drv;
ret = exynos_platform_device_ipp_register();
if (ret < 0)
- goto out_ipp_dev;
+ goto err_unregister_ipp_drv;
#endif
- ret = platform_driver_register(&exynos_drm_platform_driver);
+ ret = component_master_add(&pdev->dev, &exynos_drm_ops);
if (ret < 0)
- goto out_drm;
-
- exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
- NULL, 0);
- if (IS_ERR(exynos_drm_pdev)) {
- ret = PTR_ERR(exynos_drm_pdev);
- goto out;
- }
+ DRM_DEBUG_KMS("re-tried by last sub driver probed later.\n");
return 0;
-out:
- platform_driver_unregister(&exynos_drm_platform_driver);
-
-out_drm:
#ifdef CONFIG_DRM_EXYNOS_IPP
- exynos_platform_device_ipp_unregister();
-out_ipp_dev:
+err_unregister_ipp_drv:
platform_driver_unregister(&ipp_driver);
-out_ipp:
+err_unregister_gsc_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_GSC
platform_driver_unregister(&gsc_driver);
-out_gsc:
+err_unregister_rotator_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_ROTATOR
platform_driver_unregister(&rotator_driver);
-out_rotator:
+err_unregister_fimc_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMC
platform_driver_unregister(&fimc_driver);
-out_fimc:
+err_unregister_g2d_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_G2D
platform_driver_unregister(&g2d_driver);
-out_g2d:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_VIDI
- platform_driver_unregister(&vidi_driver);
-out_vidi:
+err_unregister_hdmi_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
- platform_driver_unregister(&mixer_driver);
-out_mixer:
platform_driver_unregister(&hdmi_driver);
-out_hdmi:
-#endif
-
-#ifdef CONFIG_DRM_EXYNOS_FIMD
- platform_driver_unregister(&fimd_driver);
-out_fimd:
+err_unregister_mixer_drv:
+ platform_driver_unregister(&mixer_driver);
+err_unregister_dsi_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_DSI
platform_driver_unregister(&dsi_driver);
-out_dsi:
+err_unregister_dp_drv:
#endif
#ifdef CONFIG_DRM_EXYNOS_DP
platform_driver_unregister(&dp_driver);
-out_dp:
+err_unregister_fimd_drv:
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+ platform_driver_unregister(&fimd_driver);
#endif
return ret;
}
-static void __exit exynos_drm_exit(void)
+static int exynos_drm_platform_remove(struct platform_device *pdev)
{
- platform_device_unregister(exynos_drm_pdev);
-
- platform_driver_unregister(&exynos_drm_platform_driver);
-
#ifdef CONFIG_DRM_EXYNOS_IPP
exynos_platform_device_ipp_unregister();
platform_driver_unregister(&ipp_driver);
@@ -616,10 +719,6 @@ static void __exit exynos_drm_exit(void)
platform_driver_unregister(&hdmi_driver);
#endif
-#ifdef CONFIG_DRM_EXYNOS_VIDI
- platform_driver_unregister(&vidi_driver);
-#endif
-
#ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver);
#endif
@@ -631,6 +730,59 @@ static void __exit exynos_drm_exit(void)
#ifdef CONFIG_DRM_EXYNOS_DP
platform_driver_unregister(&dp_driver);
#endif
+ component_master_del(&pdev->dev, &exynos_drm_ops);
+ return 0;
+}
+
+static struct platform_driver exynos_drm_platform_driver = {
+ .probe = exynos_drm_platform_probe,
+ .remove = exynos_drm_platform_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "exynos-drm",
+ .pm = &exynos_drm_pm_ops,
+ },
+};
+
+static int exynos_drm_init(void)
+{
+ int ret;
+
+ exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
+ NULL, 0);
+ if (IS_ERR(exynos_drm_pdev))
+ return PTR_ERR(exynos_drm_pdev);
+
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ ret = exynos_drm_probe_vidi();
+ if (ret < 0)
+ goto err_unregister_pd;
+#endif
+
+ ret = platform_driver_register(&exynos_drm_platform_driver);
+ if (ret)
+ goto err_remove_vidi;
+
+ return 0;
+
+err_unregister_pd:
+ platform_device_unregister(exynos_drm_pdev);
+
+err_remove_vidi:
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ exynos_drm_remove_vidi();
+#endif
+
+ return ret;
+}
+
+static void exynos_drm_exit(void)
+{
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ exynos_drm_remove_vidi();
+#endif
+ platform_device_unregister(exynos_drm_pdev);
+ platform_driver_unregister(&exynos_drm_platform_driver);
}
module_init(exynos_drm_init);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index ce3e6a30deaa..36535f398848 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -42,6 +42,13 @@ struct drm_connector;
extern unsigned int drm_vblank_offdelay;
+/* This enumerates device type. */
+enum exynos_drm_device_type {
+ EXYNOS_DEVICE_TYPE_NONE,
+ EXYNOS_DEVICE_TYPE_CRTC,
+ EXYNOS_DEVICE_TYPE_CONNECTOR,
+};
+
/* this enumerates display type. */
enum exynos_drm_output_type {
EXYNOS_DISPLAY_TYPE_NONE,
@@ -122,7 +129,6 @@ struct exynos_drm_overlay {
* Exynos DRM Display Structure.
* - this structure is common to analog tv, digital tv and lcd panel.
*
- * @initialize: initializes the display with drm_dev
* @remove: cleans up the display for removal
* @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and
@@ -133,8 +139,6 @@ struct exynos_drm_overlay {
*/
struct exynos_drm_display;
struct exynos_drm_display_ops {
- int (*initialize)(struct exynos_drm_display *display,
- struct drm_device *drm_dev);
int (*create_connector)(struct exynos_drm_display *display,
struct drm_encoder *encoder);
void (*remove)(struct exynos_drm_display *display);
@@ -172,8 +176,6 @@ struct exynos_drm_display {
/*
* Exynos drm manager ops
*
- * @initialize: initializes the manager with drm_dev
- * @remove: cleans up the manager for removal
* @dpms: control device power.
* @mode_fixup: fix mode data before applying it
* @mode_set: set the given mode to the manager
@@ -189,9 +191,6 @@ struct exynos_drm_display {
*/
struct exynos_drm_manager;
struct exynos_drm_manager_ops {
- int (*initialize)(struct exynos_drm_manager *mgr,
- struct drm_device *drm_dev, int pipe);
- void (*remove)(struct exynos_drm_manager *mgr);
void (*dpms)(struct exynos_drm_manager *mgr, int mode);
bool (*mode_fixup)(struct exynos_drm_manager *mgr,
const struct drm_display_mode *mode,
@@ -215,6 +214,7 @@ struct exynos_drm_manager_ops {
* @list: the list entry for this manager
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @drm_dev: pointer to the drm device
+ * @crtc: crtc object.
* @pipe: the pipe number for this crtc/manager
* @ops: pointer to callbacks for exynos drm specific functionality
* @ctx: A pointer to the manager's implementation specific context
@@ -223,6 +223,7 @@ struct exynos_drm_manager {
struct list_head list;
enum exynos_drm_output_type type;
struct drm_device *drm_dev;
+ struct drm_crtc *crtc;
int pipe;
struct exynos_drm_manager_ops *ops;
void *ctx;
@@ -254,6 +255,7 @@ struct drm_exynos_file_private {
* otherwise default one.
* @da_space_size: size of device address space.
* if 0 then default value is used for it.
+ * @pipe: the pipe number for this crtc/manager.
*/
struct exynos_drm_private {
struct drm_fb_helper *fb_helper;
@@ -271,6 +273,8 @@ struct exynos_drm_private {
unsigned long da_start;
unsigned long da_space_size;
+
+ unsigned int pipe;
};
/*
@@ -281,11 +285,11 @@ struct exynos_drm_private {
* @drm_dev: pointer to drm_device and this pointer would be set
* when sub driver calls exynos_drm_subdrv_register().
* @manager: subdrv has its own manager to control a hardware appropriately
- * and we can access a hardware drawing on this manager.
+ * and we can access a hardware drawing on this manager.
* @probe: this callback would be called by exynos drm driver after
- * subdrv is registered to it.
+ * subdrv is registered to it.
* @remove: this callback is used to release resources created
- * by probe callback.
+ * by probe callback.
* @open: this would be called with drm device file open.
* @close: this would be called with drm device file close.
*/
@@ -302,39 +306,14 @@ struct exynos_drm_subdrv {
struct drm_file *file);
};
-/*
- * this function calls a probe callback registered to sub driver list and
- * create its own encoder and connector and then set drm_device object
- * to global one.
- */
-int exynos_drm_device_register(struct drm_device *dev);
-/*
- * this function calls a remove callback registered to sub driver list and
- * destroy its own encoder and connetor.
- */
-int exynos_drm_device_unregister(struct drm_device *dev);
-
-int exynos_drm_initialize_managers(struct drm_device *dev);
-void exynos_drm_remove_managers(struct drm_device *dev);
-int exynos_drm_initialize_displays(struct drm_device *dev);
-void exynos_drm_remove_displays(struct drm_device *dev);
-
-int exynos_drm_manager_register(struct exynos_drm_manager *manager);
-int exynos_drm_manager_unregister(struct exynos_drm_manager *manager);
-int exynos_drm_display_register(struct exynos_drm_display *display);
-int exynos_drm_display_unregister(struct exynos_drm_display *display);
-
-/*
- * this function would be called by sub drivers such as display controller
- * or hdmi driver to register this sub driver object to exynos drm driver
- * and when a sub driver is registered to exynos drm driver a probe callback
- * of the sub driver is called and creates its own encoder and connector.
- */
+ /* This function would be called by non kms drivers such as g2d and ipp. */
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
/* this function removes subdrv list from exynos drm driver */
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
+int exynos_drm_device_subdrv_probe(struct drm_device *dev);
+int exynos_drm_device_subdrv_remove(struct drm_device *dev);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
@@ -360,18 +339,40 @@ int exynos_platform_device_ipp_register(void);
void exynos_platform_device_ipp_unregister(void);
#ifdef CONFIG_DRM_EXYNOS_DPI
-int exynos_dpi_probe(struct device *dev);
+struct exynos_drm_display * exynos_dpi_probe(struct device *dev);
int exynos_dpi_remove(struct device *dev);
#else
-static inline int exynos_dpi_probe(struct device *dev) { return 0; }
+static inline struct exynos_drm_display *
+exynos_dpi_probe(struct device *dev) { return 0; }
static inline int exynos_dpi_remove(struct device *dev) { return 0; }
#endif
+/*
+ * this function registers exynos drm vidi platform device/driver.
+ */
+int exynos_drm_probe_vidi(void);
+
+/*
+ * this function unregister exynos drm vidi platform device/driver.
+ */
+void exynos_drm_remove_vidi(void);
+
+/* This function creates a encoder and a connector, and initializes them. */
+int exynos_drm_create_enc_conn(struct drm_device *dev,
+ struct exynos_drm_display *display);
+
+int exynos_drm_component_add(struct device *dev,
+ enum exynos_drm_device_type dev_type,
+ enum exynos_drm_output_type out_type);
+
+void exynos_drm_component_del(struct device *dev,
+ enum exynos_drm_device_type dev_type);
+
+extern struct platform_driver fimd_driver;
extern struct platform_driver dp_driver;
extern struct platform_driver dsi_driver;
-extern struct platform_driver fimd_driver;
-extern struct platform_driver hdmi_driver;
extern struct platform_driver mixer_driver;
+extern struct platform_driver hdmi_driver;
extern struct platform_driver exynos_drm_common_hdmi_driver;
extern struct platform_driver vidi_driver;
extern struct platform_driver g2d_driver;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 4ac438187568..6302aa64f6c1 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -19,6 +19,7 @@
#include <linux/irq.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
+#include <linux/component.h>
#include <video/mipi_display.h>
#include <video/videomode.h>
@@ -1378,16 +1379,60 @@ end:
return ret;
}
+static int exynos_dsi_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct drm_device *drm_dev = data;
+ struct exynos_dsi *dsi;
+ int ret;
+
+ ret = exynos_drm_create_enc_conn(drm_dev, &exynos_dsi_display);
+ if (ret) {
+ DRM_ERROR("Encoder create [%d] failed with %d\n",
+ exynos_dsi_display.type, ret);
+ return ret;
+ }
+
+ dsi = exynos_dsi_display.ctx;
+
+ return mipi_dsi_host_register(&dsi->dsi_host);
+}
+
+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 = {
+ .bind = exynos_dsi_bind,
+ .unbind = exynos_dsi_unbind,
+};
+
static int exynos_dsi_probe(struct platform_device *pdev)
{
struct resource *res;
struct exynos_dsi *dsi;
int ret;
+ ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
+ exynos_dsi_display.type);
+ if (ret)
+ return ret;
+
dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi) {
dev_err(&pdev->dev, "failed to allocate dsi object.\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_del_component;
}
init_completion(&dsi->completed);
@@ -1401,7 +1446,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
ret = exynos_dsi_parse_dt(dsi);
if (ret)
- return ret;
+ goto err_del_component;
dsi->supplies[0].supply = "vddcore";
dsi->supplies[1].supply = "vddio";
@@ -1415,32 +1460,37 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk");
if (IS_ERR(dsi->pll_clk)) {
dev_info(&pdev->dev, "failed to get dsi pll input clock\n");
- return -EPROBE_DEFER;
+ ret = PTR_ERR(dsi->pll_clk);
+ goto err_del_component;
}
dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
if (IS_ERR(dsi->bus_clk)) {
dev_info(&pdev->dev, "failed to get dsi bus clock\n");
- return -EPROBE_DEFER;
+ ret = PTR_ERR(dsi->bus_clk);
+ goto err_del_component;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dsi->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dsi->reg_base)) {
dev_err(&pdev->dev, "failed to remap io region\n");
- return PTR_ERR(dsi->reg_base);
+ ret = PTR_ERR(dsi->reg_base);
+ goto err_del_component;
}
dsi->phy = devm_phy_get(&pdev->dev, "dsim");
if (IS_ERR(dsi->phy)) {
dev_info(&pdev->dev, "failed to get dsim phy\n");
- return -EPROBE_DEFER;
+ ret = PTR_ERR(dsi->phy);
+ goto err_del_component;
}
dsi->irq = platform_get_irq(pdev, 0);
if (dsi->irq < 0) {
dev_err(&pdev->dev, "failed to request dsi irq resource\n");
- return dsi->irq;
+ ret = dsi->irq;
+ goto err_del_component;
}
irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
@@ -1449,58 +1499,31 @@ static int exynos_dsi_probe(struct platform_device *pdev)
dev_name(&pdev->dev), dsi);
if (ret) {
dev_err(&pdev->dev, "failed to request dsi irq\n");
- return ret;
+ goto err_del_component;
}
exynos_dsi_display.ctx = dsi;
platform_set_drvdata(pdev, &exynos_dsi_display);
- exynos_drm_display_register(&exynos_dsi_display);
-
- return mipi_dsi_host_register(&dsi->dsi_host);
-}
-
-static int exynos_dsi_remove(struct platform_device *pdev)
-{
- struct exynos_dsi *dsi = exynos_dsi_display.ctx;
-
- exynos_dsi_dpms(&exynos_dsi_display, DRM_MODE_DPMS_OFF);
-
- exynos_drm_display_unregister(&exynos_dsi_display);
- mipi_dsi_host_unregister(&dsi->dsi_host);
-
- return 0;
-}
-#if CONFIG_PM_SLEEP
-static int exynos_dsi_resume(struct device *dev)
-{
- struct exynos_dsi *dsi = exynos_dsi_display.ctx;
+ ret = component_add(&pdev->dev, &exynos_dsi_component_ops);
+ if (ret)
+ goto err_del_component;
- if (dsi->state & DSIM_STATE_ENABLED) {
- dsi->state &= ~DSIM_STATE_ENABLED;
- exynos_dsi_enable(dsi);
- }
+ return ret;
- return 0;
+err_del_component:
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
+ return ret;
}
-static int exynos_dsi_suspend(struct device *dev)
+static int exynos_dsi_remove(struct platform_device *pdev)
{
- struct exynos_dsi *dsi = exynos_dsi_display.ctx;
-
- if (dsi->state & DSIM_STATE_ENABLED) {
- exynos_dsi_disable(dsi);
- dsi->state |= DSIM_STATE_ENABLED;
- }
+ component_del(&pdev->dev, &exynos_dsi_component_ops);
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
-#endif
-
-static const struct dev_pm_ops exynos_dsi_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume)
-};
static struct of_device_id exynos_dsi_of_match[] = {
{ .compatible = "samsung,exynos4210-mipi-dsi" },
@@ -1513,7 +1536,6 @@ struct platform_driver dsi_driver = {
.driver = {
.name = "exynos-dsi",
.owner = THIS_MODULE,
- .pm = &exynos_dsi_pm_ops,
.of_match_table = exynos_dsi_of_match,
},
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index addbf7536da4..d771b467cf0c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -121,16 +121,8 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
offset += fbi->var.yoffset * fb->pitches[0];
- dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
fbi->screen_base = buffer->kvaddr + offset;
- if (is_drm_iommu_supported(dev))
- fbi->fix.smem_start = (unsigned long)
- (page_to_phys(sg_page(buffer->sgt->sgl)) + offset);
- else
- fbi->fix.smem_start = (unsigned long)buffer->dma_addr;
-
fbi->screen_size = size;
- fbi->fix.smem_len = size;
return 0;
}
@@ -237,7 +229,7 @@ static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
.fb_probe = exynos_drm_fbdev_create,
};
-bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
+static bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
{
struct drm_connector *connector;
bool ret = false;
@@ -375,7 +367,5 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
if (!private || !private->fb_helper)
return;
- drm_modeset_lock_all(dev);
- drm_fb_helper_restore_fbdev_mode(private->fb_helper);
- drm_modeset_unlock_all(dev);
+ drm_fb_helper_restore_fbdev_mode_unlocked(private->fb_helper);
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c
index 30d76b2ff9c2..831dde9034c6 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c
@@ -18,6 +18,7 @@
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
+#include <linux/spinlock.h>
#include <drm/drmP.h>
#include <drm/exynos_drm.h>
@@ -57,7 +58,6 @@
#define FIMC_SHFACTOR 10
#define FIMC_BUF_STOP 1
#define FIMC_BUF_START 2
-#define FIMC_REG_SZ 32
#define FIMC_WIDTH_ITU_709 1280
#define FIMC_REFRESH_MAX 60
#define FIMC_REFRESH_MIN 12
@@ -69,9 +69,6 @@
#define get_fimc_context(dev) platform_get_drvdata(to_platform_device(dev))
#define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\
struct fimc_context, ippdrv);
-#define fimc_read(offset) readl(ctx->regs + (offset))
-#define fimc_write(cfg, offset) writel(cfg, ctx->regs + (offset))
-
enum fimc_wb {
FIMC_WB_NONE,
FIMC_WB_A,
@@ -161,7 +158,7 @@ struct fimc_context {
struct exynos_drm_ippdrv ippdrv;
struct resource *regs_res;
void __iomem *regs;
- struct mutex lock;
+ spinlock_t lock;
struct clk *clocks[FIMC_CLKS_MAX];
u32 clk_frequency;
struct regmap *sysreg;
@@ -172,39 +169,53 @@ struct fimc_context {
bool suspended;
};
+static u32 fimc_read(struct fimc_context *ctx, u32 reg)
+{
+ return readl(ctx->regs + reg);
+}
+
+static void fimc_write(struct fimc_context *ctx, u32 val, u32 reg)
+{
+ writel(val, ctx->regs + reg);
+}
+
+static void fimc_set_bits(struct fimc_context *ctx, u32 reg, u32 bits)
+{
+ void __iomem *r = ctx->regs + reg;
+
+ writel(readl(r) | bits, r);
+}
+
+static void fimc_clear_bits(struct fimc_context *ctx, u32 reg, u32 bits)
+{
+ void __iomem *r = ctx->regs + reg;
+
+ writel(readl(r) & ~bits, r);
+}
+
static void fimc_sw_reset(struct fimc_context *ctx)
{
u32 cfg;
/* stop dma operation */
- cfg = fimc_read(EXYNOS_CISTATUS);
- if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg)) {
- cfg = fimc_read(EXYNOS_MSCTRL);
- cfg &= ~EXYNOS_MSCTRL_ENVID;
- fimc_write(cfg, EXYNOS_MSCTRL);
- }
+ cfg = fimc_read(ctx, EXYNOS_CISTATUS);
+ if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg))
+ fimc_clear_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
- cfg = fimc_read(EXYNOS_CISRCFMT);
- cfg |= EXYNOS_CISRCFMT_ITU601_8BIT;
- fimc_write(cfg, EXYNOS_CISRCFMT);
+ fimc_set_bits(ctx, EXYNOS_CISRCFMT, EXYNOS_CISRCFMT_ITU601_8BIT);
/* disable image capture */
- cfg = fimc_read(EXYNOS_CIIMGCPT);
- cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
- fimc_write(cfg, EXYNOS_CIIMGCPT);
+ fimc_clear_bits(ctx, EXYNOS_CIIMGCPT,
+ EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
/* s/w reset */
- cfg = fimc_read(EXYNOS_CIGCTRL);
- cfg |= (EXYNOS_CIGCTRL_SWRST);
- fimc_write(cfg, EXYNOS_CIGCTRL);
+ fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_SWRST);
/* s/w reset complete */
- cfg = fimc_read(EXYNOS_CIGCTRL);
- cfg &= ~EXYNOS_CIGCTRL_SWRST;
- fimc_write(cfg, EXYNOS_CIGCTRL);
+ fimc_clear_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_SWRST);
/* reset sequence */
- fimc_write(0x0, EXYNOS_CIFCNTSEQ);
+ fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ);
}
static int fimc_set_camblk_fimd0_wb(struct fimc_context *ctx)
@@ -220,7 +231,7 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb)
DRM_DEBUG_KMS("wb[%d]\n", wb);
- cfg = fimc_read(EXYNOS_CIGCTRL);
+ cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK |
EXYNOS_CIGCTRL_SELCAM_ITU_MASK |
EXYNOS_CIGCTRL_SELCAM_MIPI_MASK |
@@ -246,7 +257,7 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb)
break;
}
- fimc_write(cfg, EXYNOS_CIGCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
}
static void fimc_set_polarity(struct fimc_context *ctx,
@@ -259,7 +270,7 @@ static void fimc_set_polarity(struct fimc_context *ctx,
DRM_DEBUG_KMS("inv_href[%d]inv_hsync[%d]\n",
pol->inv_href, pol->inv_hsync);
- cfg = fimc_read(EXYNOS_CIGCTRL);
+ cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC |
EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC);
@@ -272,7 +283,7 @@ static void fimc_set_polarity(struct fimc_context *ctx,
if (pol->inv_hsync)
cfg |= EXYNOS_CIGCTRL_INVPOLHSYNC;
- fimc_write(cfg, EXYNOS_CIGCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
}
static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable)
@@ -281,70 +292,54 @@ static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable)
DRM_DEBUG_KMS("enable[%d]\n", enable);
- cfg = fimc_read(EXYNOS_CIGCTRL);
+ cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
if (enable)
cfg |= EXYNOS_CIGCTRL_CAM_JPEG;
else
cfg &= ~EXYNOS_CIGCTRL_CAM_JPEG;
- fimc_write(cfg, EXYNOS_CIGCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
}
-static void fimc_handle_irq(struct fimc_context *ctx, bool enable,
- bool overflow, bool level)
+static void fimc_mask_irq(struct fimc_context *ctx, bool enable)
{
u32 cfg;
- DRM_DEBUG_KMS("enable[%d]overflow[%d]level[%d]\n",
- enable, overflow, level);
+ DRM_DEBUG_KMS("enable[%d]\n", enable);
- cfg = fimc_read(EXYNOS_CIGCTRL);
+ cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
if (enable) {
- cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_LEVEL);
- cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE;
- if (overflow)
- cfg |= EXYNOS_CIGCTRL_IRQ_OVFEN;
- if (level)
- cfg |= EXYNOS_CIGCTRL_IRQ_LEVEL;
+ cfg &= ~EXYNOS_CIGCTRL_IRQ_OVFEN;
+ cfg |= EXYNOS_CIGCTRL_IRQ_ENABLE | EXYNOS_CIGCTRL_IRQ_LEVEL;
} else
- cfg &= ~(EXYNOS_CIGCTRL_IRQ_OVFEN | EXYNOS_CIGCTRL_IRQ_ENABLE);
-
- fimc_write(cfg, EXYNOS_CIGCTRL);
+ cfg &= ~EXYNOS_CIGCTRL_IRQ_ENABLE;
+ fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
}
static void fimc_clear_irq(struct fimc_context *ctx)
{
- u32 cfg;
-
- cfg = fimc_read(EXYNOS_CIGCTRL);
- cfg |= EXYNOS_CIGCTRL_IRQ_CLR;
- fimc_write(cfg, EXYNOS_CIGCTRL);
+ fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_CLR);
}
static bool fimc_check_ovf(struct fimc_context *ctx)
{
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
- u32 cfg, status, flag;
+ u32 status, flag;
- status = fimc_read(EXYNOS_CISTATUS);
+ status = fimc_read(ctx, EXYNOS_CISTATUS);
flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB |
EXYNOS_CISTATUS_OVFICR;
DRM_DEBUG_KMS("flag[0x%x]\n", flag);
if (status & flag) {
- cfg = fimc_read(EXYNOS_CIWDOFST);
- cfg |= (EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
+ fimc_set_bits(ctx, EXYNOS_CIWDOFST,
+ EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
EXYNOS_CIWDOFST_CLROVFICR);
-
- fimc_write(cfg, EXYNOS_CIWDOFST);
-
- cfg = fimc_read(EXYNOS_CIWDOFST);
- cfg &= ~(EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
+ fimc_clear_bits(ctx, EXYNOS_CIWDOFST,
+ EXYNOS_CIWDOFST_CLROVFIY | EXYNOS_CIWDOFST_CLROVFICB |
EXYNOS_CIWDOFST_CLROVFICR);
- fimc_write(cfg, EXYNOS_CIWDOFST);
-
dev_err(ippdrv->dev, "occurred overflow at %d, status 0x%x.\n",
ctx->id, status);
return true;
@@ -357,7 +352,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx)
{
u32 cfg;
- cfg = fimc_read(EXYNOS_CISTATUS);
+ cfg = fimc_read(ctx, EXYNOS_CISTATUS);
DRM_DEBUG_KMS("cfg[0x%x]\n", cfg);
@@ -365,7 +360,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx)
return false;
cfg &= ~(EXYNOS_CISTATUS_FRAMEEND);
- fimc_write(cfg, EXYNOS_CISTATUS);
+ fimc_write(ctx, cfg, EXYNOS_CISTATUS);
return true;
}
@@ -375,7 +370,7 @@ static int fimc_get_buf_id(struct fimc_context *ctx)
u32 cfg;
int frame_cnt, buf_id;
- cfg = fimc_read(EXYNOS_CISTATUS2);
+ cfg = fimc_read(ctx, EXYNOS_CISTATUS2);
frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg);
if (frame_cnt == 0)
@@ -402,13 +397,13 @@ static void fimc_handle_lastend(struct fimc_context *ctx, bool enable)
DRM_DEBUG_KMS("enable[%d]\n", enable);
- cfg = fimc_read(EXYNOS_CIOCTRL);
+ cfg = fimc_read(ctx, EXYNOS_CIOCTRL);
if (enable)
cfg |= EXYNOS_CIOCTRL_LASTENDEN;
else
cfg &= ~EXYNOS_CIOCTRL_LASTENDEN;
- fimc_write(cfg, EXYNOS_CIOCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CIOCTRL);
}
@@ -420,18 +415,18 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
/* RGB */
- cfg = fimc_read(EXYNOS_CISCCTRL);
+ cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
cfg &= ~EXYNOS_CISCCTRL_INRGB_FMT_RGB_MASK;
switch (fmt) {
case DRM_FORMAT_RGB565:
cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB565;
- fimc_write(cfg, EXYNOS_CISCCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
return 0;
case DRM_FORMAT_RGB888:
case DRM_FORMAT_XRGB8888:
cfg |= EXYNOS_CISCCTRL_INRGB_FMT_RGB888;
- fimc_write(cfg, EXYNOS_CISCCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
return 0;
default:
/* bypass */
@@ -439,7 +434,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
}
/* YUV */
- cfg = fimc_read(EXYNOS_MSCTRL);
+ cfg = fimc_read(ctx, EXYNOS_MSCTRL);
cfg &= ~(EXYNOS_MSCTRL_ORDER2P_SHIFT_MASK |
EXYNOS_MSCTRL_C_INT_IN_2PLANE |
EXYNOS_MSCTRL_ORDER422_YCBYCR);
@@ -479,7 +474,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt)
return -EINVAL;
}
- fimc_write(cfg, EXYNOS_MSCTRL);
+ fimc_write(ctx, cfg, EXYNOS_MSCTRL);
return 0;
}
@@ -492,7 +487,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
- cfg = fimc_read(EXYNOS_MSCTRL);
+ cfg = fimc_read(ctx, EXYNOS_MSCTRL);
cfg &= ~EXYNOS_MSCTRL_INFORMAT_RGB;
switch (fmt) {
@@ -527,9 +522,9 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
return -EINVAL;
}
- fimc_write(cfg, EXYNOS_MSCTRL);
+ fimc_write(ctx, cfg, EXYNOS_MSCTRL);
- cfg = fimc_read(EXYNOS_CIDMAPARAM);
+ cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM);
cfg &= ~EXYNOS_CIDMAPARAM_R_MODE_MASK;
if (fmt == DRM_FORMAT_NV12MT)
@@ -537,7 +532,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt)
else
cfg |= EXYNOS_CIDMAPARAM_R_MODE_LINEAR;
- fimc_write(cfg, EXYNOS_CIDMAPARAM);
+ fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM);
return fimc_src_set_fmt_order(ctx, fmt);
}
@@ -552,11 +547,11 @@ static int fimc_src_set_transf(struct device *dev,
DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
- cfg1 = fimc_read(EXYNOS_MSCTRL);
+ cfg1 = fimc_read(ctx, EXYNOS_MSCTRL);
cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR |
EXYNOS_MSCTRL_FLIP_Y_MIRROR);
- cfg2 = fimc_read(EXYNOS_CITRGFMT);
+ cfg2 = fimc_read(ctx, EXYNOS_CITRGFMT);
cfg2 &= ~EXYNOS_CITRGFMT_INROT90_CLOCKWISE;
switch (degree) {
@@ -595,8 +590,8 @@ static int fimc_src_set_transf(struct device *dev,
return -EINVAL;
}
- fimc_write(cfg1, EXYNOS_MSCTRL);
- fimc_write(cfg2, EXYNOS_CITRGFMT);
+ fimc_write(ctx, cfg1, EXYNOS_MSCTRL);
+ fimc_write(ctx, cfg2, EXYNOS_CITRGFMT);
*swap = (cfg2 & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) ? 1 : 0;
return 0;
@@ -621,17 +616,17 @@ static int fimc_set_window(struct fimc_context *ctx,
* set window offset 1, 2 size
* check figure 43-21 in user manual
*/
- cfg = fimc_read(EXYNOS_CIWDOFST);
+ cfg = fimc_read(ctx, EXYNOS_CIWDOFST);
cfg &= ~(EXYNOS_CIWDOFST_WINHOROFST_MASK |
EXYNOS_CIWDOFST_WINVEROFST_MASK);
cfg |= (EXYNOS_CIWDOFST_WINHOROFST(h1) |
EXYNOS_CIWDOFST_WINVEROFST(v1));
cfg |= EXYNOS_CIWDOFST_WINOFSEN;
- fimc_write(cfg, EXYNOS_CIWDOFST);
+ fimc_write(ctx, cfg, EXYNOS_CIWDOFST);
cfg = (EXYNOS_CIWDOFST2_WINHOROFST2(h2) |
EXYNOS_CIWDOFST2_WINVEROFST2(v2));
- fimc_write(cfg, EXYNOS_CIWDOFST2);
+ fimc_write(ctx, cfg, EXYNOS_CIWDOFST2);
return 0;
}
@@ -651,7 +646,7 @@ static int fimc_src_set_size(struct device *dev, int swap,
cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) |
EXYNOS_ORGISIZE_VERTICAL(img_sz.vsize));
- fimc_write(cfg, EXYNOS_ORGISIZE);
+ fimc_write(ctx, cfg, EXYNOS_ORGISIZE);
DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h);
@@ -663,12 +658,12 @@ static int fimc_src_set_size(struct device *dev, int swap,
}
/* set input DMA image size */
- cfg = fimc_read(EXYNOS_CIREAL_ISIZE);
+ cfg = fimc_read(ctx, EXYNOS_CIREAL_ISIZE);
cfg &= ~(EXYNOS_CIREAL_ISIZE_HEIGHT_MASK |
EXYNOS_CIREAL_ISIZE_WIDTH_MASK);
cfg |= (EXYNOS_CIREAL_ISIZE_WIDTH(img_pos.w) |
EXYNOS_CIREAL_ISIZE_HEIGHT(img_pos.h));
- fimc_write(cfg, EXYNOS_CIREAL_ISIZE);
+ fimc_write(ctx, cfg, EXYNOS_CIREAL_ISIZE);
/*
* set input FIFO image size
@@ -677,18 +672,18 @@ static int fimc_src_set_size(struct device *dev, int swap,
cfg = (EXYNOS_CISRCFMT_ITU601_8BIT |
EXYNOS_CISRCFMT_SOURCEHSIZE(img_sz.hsize) |
EXYNOS_CISRCFMT_SOURCEVSIZE(img_sz.vsize));
- fimc_write(cfg, EXYNOS_CISRCFMT);
+ fimc_write(ctx, cfg, EXYNOS_CISRCFMT);
/* offset Y(RGB), Cb, Cr */
cfg = (EXYNOS_CIIYOFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIIYOFF_VERTICAL(img_pos.y));
- fimc_write(cfg, EXYNOS_CIIYOFF);
+ fimc_write(ctx, cfg, EXYNOS_CIIYOFF);
cfg = (EXYNOS_CIICBOFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIICBOFF_VERTICAL(img_pos.y));
- fimc_write(cfg, EXYNOS_CIICBOFF);
+ fimc_write(ctx, cfg, EXYNOS_CIICBOFF);
cfg = (EXYNOS_CIICROFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIICROFF_VERTICAL(img_pos.y));
- fimc_write(cfg, EXYNOS_CIICROFF);
+ fimc_write(ctx, cfg, EXYNOS_CIICROFF);
return fimc_set_window(ctx, &img_pos, &img_sz);
}
@@ -722,25 +717,25 @@ static int fimc_src_set_addr(struct device *dev,
switch (buf_type) {
case IPP_BUF_ENQUEUE:
config = &property->config[EXYNOS_DRM_OPS_SRC];
- fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y],
+ fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y],
EXYNOS_CIIYSA(buf_id));
if (config->fmt == DRM_FORMAT_YVU420) {
- fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR],
+ fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
EXYNOS_CIICBSA(buf_id));
- fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
+ fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
EXYNOS_CIICRSA(buf_id));
} else {
- fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
+ fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
EXYNOS_CIICBSA(buf_id));
- fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR],
+ fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
EXYNOS_CIICRSA(buf_id));
}
break;
case IPP_BUF_DEQUEUE:
- fimc_write(0x0, EXYNOS_CIIYSA(buf_id));
- fimc_write(0x0, EXYNOS_CIICBSA(buf_id));
- fimc_write(0x0, EXYNOS_CIICRSA(buf_id));
+ fimc_write(ctx, 0x0, EXYNOS_CIIYSA(buf_id));
+ fimc_write(ctx, 0x0, EXYNOS_CIICBSA(buf_id));
+ fimc_write(ctx, 0x0, EXYNOS_CIICRSA(buf_id));
break;
default:
/* bypass */
@@ -765,22 +760,22 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
/* RGB */
- cfg = fimc_read(EXYNOS_CISCCTRL);
+ cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
cfg &= ~EXYNOS_CISCCTRL_OUTRGB_FMT_RGB_MASK;
switch (fmt) {
case DRM_FORMAT_RGB565:
cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB565;
- fimc_write(cfg, EXYNOS_CISCCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
return 0;
case DRM_FORMAT_RGB888:
cfg |= EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888;
- fimc_write(cfg, EXYNOS_CISCCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
return 0;
case DRM_FORMAT_XRGB8888:
cfg |= (EXYNOS_CISCCTRL_OUTRGB_FMT_RGB888 |
EXYNOS_CISCCTRL_EXTRGB_EXTENSION);
- fimc_write(cfg, EXYNOS_CISCCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
break;
default:
/* bypass */
@@ -788,7 +783,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
}
/* YUV */
- cfg = fimc_read(EXYNOS_CIOCTRL);
+ cfg = fimc_read(ctx, EXYNOS_CIOCTRL);
cfg &= ~(EXYNOS_CIOCTRL_ORDER2P_MASK |
EXYNOS_CIOCTRL_ORDER422_MASK |
EXYNOS_CIOCTRL_YCBCR_PLANE_MASK);
@@ -830,7 +825,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt)
return -EINVAL;
}
- fimc_write(cfg, EXYNOS_CIOCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CIOCTRL);
return 0;
}
@@ -843,16 +838,16 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
DRM_DEBUG_KMS("fmt[0x%x]\n", fmt);
- cfg = fimc_read(EXYNOS_CIEXTEN);
+ cfg = fimc_read(ctx, EXYNOS_CIEXTEN);
if (fmt == DRM_FORMAT_AYUV) {
cfg |= EXYNOS_CIEXTEN_YUV444_OUT;
- fimc_write(cfg, EXYNOS_CIEXTEN);
+ fimc_write(ctx, cfg, EXYNOS_CIEXTEN);
} else {
cfg &= ~EXYNOS_CIEXTEN_YUV444_OUT;
- fimc_write(cfg, EXYNOS_CIEXTEN);
+ fimc_write(ctx, cfg, EXYNOS_CIEXTEN);
- cfg = fimc_read(EXYNOS_CITRGFMT);
+ cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
cfg &= ~EXYNOS_CITRGFMT_OUTFORMAT_MASK;
switch (fmt) {
@@ -885,10 +880,10 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
return -EINVAL;
}
- fimc_write(cfg, EXYNOS_CITRGFMT);
+ fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
}
- cfg = fimc_read(EXYNOS_CIDMAPARAM);
+ cfg = fimc_read(ctx, EXYNOS_CIDMAPARAM);
cfg &= ~EXYNOS_CIDMAPARAM_W_MODE_MASK;
if (fmt == DRM_FORMAT_NV12MT)
@@ -896,7 +891,7 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt)
else
cfg |= EXYNOS_CIDMAPARAM_W_MODE_LINEAR;
- fimc_write(cfg, EXYNOS_CIDMAPARAM);
+ fimc_write(ctx, cfg, EXYNOS_CIDMAPARAM);
return fimc_dst_set_fmt_order(ctx, fmt);
}
@@ -911,7 +906,7 @@ static int fimc_dst_set_transf(struct device *dev,
DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip);
- cfg = fimc_read(EXYNOS_CITRGFMT);
+ cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK;
cfg &= ~EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE;
@@ -951,53 +946,23 @@ static int fimc_dst_set_transf(struct device *dev,
return -EINVAL;
}
- fimc_write(cfg, EXYNOS_CITRGFMT);
+ fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
*swap = (cfg & EXYNOS_CITRGFMT_OUTROT90_CLOCKWISE) ? 1 : 0;
return 0;
}
-static int fimc_get_ratio_shift(u32 src, u32 dst, u32 *ratio, u32 *shift)
-{
- DRM_DEBUG_KMS("src[%d]dst[%d]\n", src, dst);
-
- if (src >= dst * 64) {
- DRM_ERROR("failed to make ratio and shift.\n");
- return -EINVAL;
- } else if (src >= dst * 32) {
- *ratio = 32;
- *shift = 5;
- } else if (src >= dst * 16) {
- *ratio = 16;
- *shift = 4;
- } else if (src >= dst * 8) {
- *ratio = 8;
- *shift = 3;
- } else if (src >= dst * 4) {
- *ratio = 4;
- *shift = 2;
- } else if (src >= dst * 2) {
- *ratio = 2;
- *shift = 1;
- } else {
- *ratio = 1;
- *shift = 0;
- }
-
- return 0;
-}
-
static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
struct drm_exynos_pos *src, struct drm_exynos_pos *dst)
{
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
u32 cfg, cfg_ext, shfactor;
u32 pre_dst_width, pre_dst_height;
- u32 pre_hratio, hfactor, pre_vratio, vfactor;
+ u32 hfactor, vfactor;
int ret = 0;
u32 src_w, src_h, dst_w, dst_h;
- cfg_ext = fimc_read(EXYNOS_CITRGFMT);
+ cfg_ext = fimc_read(ctx, EXYNOS_CITRGFMT);
if (cfg_ext & EXYNOS_CITRGFMT_INROT90_CLOCKWISE) {
src_w = src->h;
src_h = src->w;
@@ -1014,24 +979,24 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
dst_h = dst->h;
}
- ret = fimc_get_ratio_shift(src_w, dst_w, &pre_hratio, &hfactor);
- if (ret) {
+ /* fimc_ippdrv_check_property assures that dividers are not null */
+ hfactor = fls(src_w / dst_w / 2);
+ if (hfactor > FIMC_SHFACTOR / 2) {
dev_err(ippdrv->dev, "failed to get ratio horizontal.\n");
- return ret;
+ return -EINVAL;
}
- ret = fimc_get_ratio_shift(src_h, dst_h, &pre_vratio, &vfactor);
- if (ret) {
+ vfactor = fls(src_h / dst_h / 2);
+ if (vfactor > FIMC_SHFACTOR / 2) {
dev_err(ippdrv->dev, "failed to get ratio vertical.\n");
- return ret;
+ return -EINVAL;
}
- pre_dst_width = src_w / pre_hratio;
- pre_dst_height = src_h / pre_vratio;
+ pre_dst_width = src_w >> hfactor;
+ pre_dst_height = src_h >> vfactor;
DRM_DEBUG_KMS("pre_dst_width[%d]pre_dst_height[%d]\n",
pre_dst_width, pre_dst_height);
- DRM_DEBUG_KMS("pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n",
- pre_hratio, hfactor, pre_vratio, vfactor);
+ DRM_DEBUG_KMS("hfactor[%d]vfactor[%d]\n", hfactor, vfactor);
sc->hratio = (src_w << 14) / (dst_w << hfactor);
sc->vratio = (src_h << 14) / (dst_h << vfactor);
@@ -1044,13 +1009,13 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc,
DRM_DEBUG_KMS("shfactor[%d]\n", shfactor);
cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) |
- EXYNOS_CISCPRERATIO_PREHORRATIO(pre_hratio) |
- EXYNOS_CISCPRERATIO_PREVERRATIO(pre_vratio));
- fimc_write(cfg, EXYNOS_CISCPRERATIO);
+ EXYNOS_CISCPRERATIO_PREHORRATIO(1 << hfactor) |
+ EXYNOS_CISCPRERATIO_PREVERRATIO(1 << vfactor));
+ fimc_write(ctx, cfg, EXYNOS_CISCPRERATIO);
cfg = (EXYNOS_CISCPREDST_PREDSTWIDTH(pre_dst_width) |
EXYNOS_CISCPREDST_PREDSTHEIGHT(pre_dst_height));
- fimc_write(cfg, EXYNOS_CISCPREDST);
+ fimc_write(ctx, cfg, EXYNOS_CISCPREDST);
return ret;
}
@@ -1064,7 +1029,7 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc)
DRM_DEBUG_KMS("hratio[%d]vratio[%d]\n",
sc->hratio, sc->vratio);
- cfg = fimc_read(EXYNOS_CISCCTRL);
+ cfg = fimc_read(ctx, EXYNOS_CISCCTRL);
cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS |
EXYNOS_CISCCTRL_SCALEUP_H | EXYNOS_CISCCTRL_SCALEUP_V |
EXYNOS_CISCCTRL_MAIN_V_RATIO_MASK |
@@ -1084,14 +1049,14 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc)
cfg |= (EXYNOS_CISCCTRL_MAINHORRATIO((sc->hratio >> 6)) |
EXYNOS_CISCCTRL_MAINVERRATIO((sc->vratio >> 6)));
- fimc_write(cfg, EXYNOS_CISCCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CISCCTRL);
- cfg_ext = fimc_read(EXYNOS_CIEXTEN);
+ cfg_ext = fimc_read(ctx, EXYNOS_CIEXTEN);
cfg_ext &= ~EXYNOS_CIEXTEN_MAINHORRATIO_EXT_MASK;
cfg_ext &= ~EXYNOS_CIEXTEN_MAINVERRATIO_EXT_MASK;
cfg_ext |= (EXYNOS_CIEXTEN_MAINHORRATIO_EXT(sc->hratio) |
EXYNOS_CIEXTEN_MAINVERRATIO_EXT(sc->vratio));
- fimc_write(cfg_ext, EXYNOS_CIEXTEN);
+ fimc_write(ctx, cfg_ext, EXYNOS_CIEXTEN);
}
static int fimc_dst_set_size(struct device *dev, int swap,
@@ -1109,12 +1074,12 @@ static int fimc_dst_set_size(struct device *dev, int swap,
cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) |
EXYNOS_ORGOSIZE_VERTICAL(img_sz.vsize));
- fimc_write(cfg, EXYNOS_ORGOSIZE);
+ fimc_write(ctx, cfg, EXYNOS_ORGOSIZE);
DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h);
/* CSC ITU */
- cfg = fimc_read(EXYNOS_CIGCTRL);
+ cfg = fimc_read(ctx, EXYNOS_CIGCTRL);
cfg &= ~EXYNOS_CIGCTRL_CSC_MASK;
if (sz->hsize >= FIMC_WIDTH_ITU_709)
@@ -1122,7 +1087,7 @@ static int fimc_dst_set_size(struct device *dev, int swap,
else
cfg |= EXYNOS_CIGCTRL_CSC_ITU601;
- fimc_write(cfg, EXYNOS_CIGCTRL);
+ fimc_write(ctx, cfg, EXYNOS_CIGCTRL);
if (swap) {
img_pos.w = pos->h;
@@ -1132,41 +1097,38 @@ static int fimc_dst_set_size(struct device *dev, int swap,
}
/* target image size */
- cfg = fimc_read(EXYNOS_CITRGFMT);
+ cfg = fimc_read(ctx, EXYNOS_CITRGFMT);
cfg &= ~(EXYNOS_CITRGFMT_TARGETH_MASK |
EXYNOS_CITRGFMT_TARGETV_MASK);
cfg |= (EXYNOS_CITRGFMT_TARGETHSIZE(img_pos.w) |
EXYNOS_CITRGFMT_TARGETVSIZE(img_pos.h));
- fimc_write(cfg, EXYNOS_CITRGFMT);
+ fimc_write(ctx, cfg, EXYNOS_CITRGFMT);
/* target area */
cfg = EXYNOS_CITAREA_TARGET_AREA(img_pos.w * img_pos.h);
- fimc_write(cfg, EXYNOS_CITAREA);
+ fimc_write(ctx, cfg, EXYNOS_CITAREA);
/* offset Y(RGB), Cb, Cr */
cfg = (EXYNOS_CIOYOFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIOYOFF_VERTICAL(img_pos.y));
- fimc_write(cfg, EXYNOS_CIOYOFF);
+ fimc_write(ctx, cfg, EXYNOS_CIOYOFF);
cfg = (EXYNOS_CIOCBOFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIOCBOFF_VERTICAL(img_pos.y));
- fimc_write(cfg, EXYNOS_CIOCBOFF);
+ fimc_write(ctx, cfg, EXYNOS_CIOCBOFF);
cfg = (EXYNOS_CIOCROFF_HORIZONTAL(img_pos.x) |
EXYNOS_CIOCROFF_VERTICAL(img_pos.y));
- fimc_write(cfg, EXYNOS_CIOCROFF);
+ fimc_write(ctx, cfg, EXYNOS_CIOCROFF);
return 0;
}
-static int fimc_dst_get_buf_seq(struct fimc_context *ctx)
+static int fimc_dst_get_buf_count(struct fimc_context *ctx)
{
- u32 cfg, i, buf_num = 0;
- u32 mask = 0x00000001;
+ u32 cfg, buf_num;
- cfg = fimc_read(EXYNOS_CIFCNTSEQ);
+ cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ);
- for (i = 0; i < FIMC_REG_SZ; i++)
- if (cfg & (mask << i))
- buf_num++;
+ buf_num = hweight32(cfg);
DRM_DEBUG_KMS("buf_num[%d]\n", buf_num);
@@ -1181,13 +1143,14 @@ static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
u32 cfg;
u32 mask = 0x00000001 << buf_id;
int ret = 0;
+ unsigned long flags;
DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type);
- mutex_lock(&ctx->lock);
+ spin_lock_irqsave(&ctx->lock, flags);
/* mask register set */
- cfg = fimc_read(EXYNOS_CIFCNTSEQ);
+ cfg = fimc_read(ctx, EXYNOS_CIFCNTSEQ);
switch (buf_type) {
case IPP_BUF_ENQUEUE:
@@ -1205,20 +1168,20 @@ static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id,
/* sequence id */
cfg &= ~mask;
cfg |= (enable << buf_id);
- fimc_write(cfg, EXYNOS_CIFCNTSEQ);
+ fimc_write(ctx, cfg, EXYNOS_CIFCNTSEQ);
/* interrupt enable */
if (buf_type == IPP_BUF_ENQUEUE &&
- fimc_dst_get_buf_seq(ctx) >= FIMC_BUF_START)
- fimc_handle_irq(ctx, true, false, true);
+ fimc_dst_get_buf_count(ctx) >= FIMC_BUF_START)
+ fimc_mask_irq(ctx, true);
/* interrupt disable */
if (buf_type == IPP_BUF_DEQUEUE &&
- fimc_dst_get_buf_seq(ctx) <= FIMC_BUF_STOP)
- fimc_handle_irq(ctx, false, false, true);
+ fimc_dst_get_buf_count(ctx) <= FIMC_BUF_STOP)
+ fimc_mask_irq(ctx, false);
err_unlock:
- mutex_unlock(&ctx->lock);
+ spin_unlock_irqrestore(&ctx->lock, flags);
return ret;
}
@@ -1252,25 +1215,25 @@ static int fimc_dst_set_addr(struct device *dev,
case IPP_BUF_ENQUEUE:
config = &property->config[EXYNOS_DRM_OPS_DST];
- fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_Y],
+ fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_Y],
EXYNOS_CIOYSA(buf_id));
if (config->fmt == DRM_FORMAT_YVU420) {
- fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR],
+ fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
EXYNOS_CIOCBSA(buf_id));
- fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
+ fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
EXYNOS_CIOCRSA(buf_id));
} else {
- fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CB],
+ fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CB],
EXYNOS_CIOCBSA(buf_id));
- fimc_write(buf_info->base[EXYNOS_DRM_PLANAR_CR],
+ fimc_write(ctx, buf_info->base[EXYNOS_DRM_PLANAR_CR],
EXYNOS_CIOCRSA(buf_id));
}
break;
case IPP_BUF_DEQUEUE:
- fimc_write(0x0, EXYNOS_CIOYSA(buf_id));
- fimc_write(0x0, EXYNOS_CIOCBSA(buf_id));
- fimc_write(0x0, EXYNOS_CIOCRSA(buf_id));
+ fimc_write(ctx, 0x0, EXYNOS_CIOYSA(buf_id));
+ fimc_write(ctx, 0x0, EXYNOS_CIOCBSA(buf_id));
+ fimc_write(ctx, 0x0, EXYNOS_CIOCRSA(buf_id));
break;
default:
/* bypass */
@@ -1342,11 +1305,7 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id)
static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
{
- struct drm_exynos_ipp_prop_list *prop_list;
-
- prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
- if (!prop_list)
- return -ENOMEM;
+ struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
prop_list->version = 1;
prop_list->writeback = 1;
@@ -1371,8 +1330,6 @@ static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
prop_list->scale_min.hsize = FIMC_SCALE_MIN;
prop_list->scale_min.vsize = FIMC_SCALE_MIN;
- ippdrv->prop_list = prop_list;
-
return 0;
}
@@ -1395,7 +1352,7 @@ static int fimc_ippdrv_check_property(struct device *dev,
{
struct fimc_context *ctx = get_fimc_context(dev);
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
- struct drm_exynos_ipp_prop_list *pp = ippdrv->prop_list;
+ struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list;
struct drm_exynos_ipp_config *config;
struct drm_exynos_pos *pos;
struct drm_exynos_sz *sz;
@@ -1508,15 +1465,15 @@ static void fimc_clear_addr(struct fimc_context *ctx)
int i;
for (i = 0; i < FIMC_MAX_SRC; i++) {
- fimc_write(0, EXYNOS_CIIYSA(i));
- fimc_write(0, EXYNOS_CIICBSA(i));
- fimc_write(0, EXYNOS_CIICRSA(i));
+ fimc_write(ctx, 0, EXYNOS_CIIYSA(i));
+ fimc_write(ctx, 0, EXYNOS_CIICBSA(i));
+ fimc_write(ctx, 0, EXYNOS_CIICRSA(i));
}
for (i = 0; i < FIMC_MAX_DST; i++) {
- fimc_write(0, EXYNOS_CIOYSA(i));
- fimc_write(0, EXYNOS_CIOCBSA(i));
- fimc_write(0, EXYNOS_CIOCRSA(i));
+ fimc_write(ctx, 0, EXYNOS_CIOYSA(i));
+ fimc_write(ctx, 0, EXYNOS_CIOCBSA(i));
+ fimc_write(ctx, 0, EXYNOS_CIOCRSA(i));
}
}
@@ -1556,7 +1513,7 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
property = &c_node->property;
- fimc_handle_irq(ctx, true, false, true);
+ fimc_mask_irq(ctx, true);
for_each_ipp_ops(i) {
config = &property->config[i];
@@ -1582,10 +1539,10 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
fimc_handle_lastend(ctx, false);
/* setup dma */
- cfg0 = fimc_read(EXYNOS_MSCTRL);
+ cfg0 = fimc_read(ctx, EXYNOS_MSCTRL);
cfg0 &= ~EXYNOS_MSCTRL_INPUT_MASK;
cfg0 |= EXYNOS_MSCTRL_INPUT_MEMORY;
- fimc_write(cfg0, EXYNOS_MSCTRL);
+ fimc_write(ctx, cfg0, EXYNOS_MSCTRL);
break;
case IPP_CMD_WB:
fimc_set_type_ctrl(ctx, FIMC_WB_A);
@@ -1610,41 +1567,33 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
}
/* Reset status */
- fimc_write(0x0, EXYNOS_CISTATUS);
+ fimc_write(ctx, 0x0, EXYNOS_CISTATUS);
- cfg0 = fimc_read(EXYNOS_CIIMGCPT);
+ cfg0 = fimc_read(ctx, EXYNOS_CIIMGCPT);
cfg0 &= ~EXYNOS_CIIMGCPT_IMGCPTEN_SC;
cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN_SC;
/* Scaler */
- cfg1 = fimc_read(EXYNOS_CISCCTRL);
+ cfg1 = fimc_read(ctx, EXYNOS_CISCCTRL);
cfg1 &= ~EXYNOS_CISCCTRL_SCAN_MASK;
cfg1 |= (EXYNOS_CISCCTRL_PROGRESSIVE |
EXYNOS_CISCCTRL_SCALERSTART);
- fimc_write(cfg1, EXYNOS_CISCCTRL);
+ fimc_write(ctx, cfg1, EXYNOS_CISCCTRL);
/* Enable image capture*/
cfg0 |= EXYNOS_CIIMGCPT_IMGCPTEN;
- fimc_write(cfg0, EXYNOS_CIIMGCPT);
+ fimc_write(ctx, cfg0, EXYNOS_CIIMGCPT);
/* Disable frame end irq */
- cfg0 = fimc_read(EXYNOS_CIGCTRL);
- cfg0 &= ~EXYNOS_CIGCTRL_IRQ_END_DISABLE;
- fimc_write(cfg0, EXYNOS_CIGCTRL);
+ fimc_clear_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE);
- cfg0 = fimc_read(EXYNOS_CIOCTRL);
- cfg0 &= ~EXYNOS_CIOCTRL_WEAVE_MASK;
- fimc_write(cfg0, EXYNOS_CIOCTRL);
+ fimc_clear_bits(ctx, EXYNOS_CIOCTRL, EXYNOS_CIOCTRL_WEAVE_MASK);
if (cmd == IPP_CMD_M2M) {
- cfg0 = fimc_read(EXYNOS_MSCTRL);
- cfg0 |= EXYNOS_MSCTRL_ENVID;
- fimc_write(cfg0, EXYNOS_MSCTRL);
+ fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
- cfg0 = fimc_read(EXYNOS_MSCTRL);
- cfg0 |= EXYNOS_MSCTRL_ENVID;
- fimc_write(cfg0, EXYNOS_MSCTRL);
+ fimc_set_bits(ctx, EXYNOS_MSCTRL, EXYNOS_MSCTRL_ENVID);
}
return 0;
@@ -1661,10 +1610,10 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
switch (cmd) {
case IPP_CMD_M2M:
/* Source clear */
- cfg = fimc_read(EXYNOS_MSCTRL);
+ cfg = fimc_read(ctx, EXYNOS_MSCTRL);
cfg &= ~EXYNOS_MSCTRL_INPUT_MASK;
cfg &= ~EXYNOS_MSCTRL_ENVID;
- fimc_write(cfg, EXYNOS_MSCTRL);
+ fimc_write(ctx, cfg, EXYNOS_MSCTRL);
break;
case IPP_CMD_WB:
exynos_drm_ippnb_send_event(IPP_SET_WRITEBACK, (void *)&set_wb);
@@ -1675,25 +1624,20 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd)
break;
}
- fimc_handle_irq(ctx, false, false, true);
+ fimc_mask_irq(ctx, false);
/* reset sequence */
- fimc_write(0x0, EXYNOS_CIFCNTSEQ);
+ fimc_write(ctx, 0x0, EXYNOS_CIFCNTSEQ);
/* Scaler disable */
- cfg = fimc_read(EXYNOS_CISCCTRL);
- cfg &= ~EXYNOS_CISCCTRL_SCALERSTART;
- fimc_write(cfg, EXYNOS_CISCCTRL);
+ fimc_clear_bits(ctx, EXYNOS_CISCCTRL, EXYNOS_CISCCTRL_SCALERSTART);
/* Disable image capture */
- cfg = fimc_read(EXYNOS_CIIMGCPT);
- cfg &= ~(EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
- fimc_write(cfg, EXYNOS_CIIMGCPT);
+ fimc_clear_bits(ctx, EXYNOS_CIIMGCPT,
+ EXYNOS_CIIMGCPT_IMGCPTEN_SC | EXYNOS_CIIMGCPT_IMGCPTEN);
/* Enable frame end irq */
- cfg = fimc_read(EXYNOS_CIGCTRL);
- cfg |= EXYNOS_CIGCTRL_IRQ_END_DISABLE;
- fimc_write(cfg, EXYNOS_CIGCTRL);
+ fimc_set_bits(ctx, EXYNOS_CIGCTRL, EXYNOS_CIGCTRL_IRQ_END_DISABLE);
}
static void fimc_put_clocks(struct fimc_context *ctx)
@@ -1848,7 +1792,7 @@ static int fimc_probe(struct platform_device *pdev)
DRM_DEBUG_KMS("id[%d]ippdrv[0x%x]\n", ctx->id, (int)ippdrv);
- mutex_init(&ctx->lock);
+ spin_lock_init(&ctx->lock);
platform_set_drvdata(pdev, ctx);
pm_runtime_set_active(dev);
@@ -1879,7 +1823,6 @@ static int fimc_remove(struct platform_device *pdev)
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
exynos_drm_ippdrv_unregister(ippdrv);
- mutex_destroy(&ctx->lock);
fimc_put_clocks(ctx);
pm_runtime_set_suspended(dev);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 40fd6ccfcd6f..bb45ab2e7384 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -19,6 +19,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
+#include <linux/component.h>
#include <video/of_display_timing.h>
#include <video/of_videomode.h>
@@ -38,6 +39,7 @@
*/
#define FIMD_DEFAULT_FRAMERATE 60
+#define MIN_FB_WIDTH_FOR_16WORD_BURST 128
/* position control register for hardware window 0, 2 ~ 4.*/
#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
@@ -122,6 +124,7 @@ struct fimd_context {
struct exynos_drm_panel_info panel;
struct fimd_driver_data *driver_data;
+ struct exynos_drm_display *display;
};
static const struct of_device_id fimd_driver_dt_match[] = {
@@ -143,13 +146,57 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data(
return (struct fimd_driver_data *)of_id->data;
}
+static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
+{
+ struct fimd_context *ctx = mgr->ctx;
+
+ if (ctx->suspended)
+ return;
+
+ atomic_set(&ctx->wait_vsync_event, 1);
+
+ /*
+ * wait for FIMD to signal VSYNC interrupt or return after
+ * timeout which is set to 50ms (refresh rate of 20).
+ */
+ if (!wait_event_timeout(ctx->wait_vsync_queue,
+ !atomic_read(&ctx->wait_vsync_event),
+ HZ/20))
+ DRM_DEBUG_KMS("vblank wait timed out.\n");
+}
+
+
+static void fimd_clear_channel(struct exynos_drm_manager *mgr)
+{
+ struct fimd_context *ctx = mgr->ctx;
+ int win, ch_enabled = 0;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* 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);
+ ch_enabled = 1;
+ }
+ }
+
+ /* Wait for vsync, as disable channel takes effect at next vsync */
+ if (ch_enabled)
+ fimd_wait_for_vblank(mgr);
+}
+
static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
- struct drm_device *drm_dev, int pipe)
+ struct drm_device *drm_dev)
{
struct fimd_context *ctx = mgr->ctx;
+ struct exynos_drm_private *priv;
+ priv = drm_dev->dev_private;
- ctx->drm_dev = drm_dev;
- ctx->pipe = pipe;
+ mgr->drm_dev = ctx->drm_dev = drm_dev;
+ mgr->pipe = ctx->pipe = priv->pipe++;
/*
* enable drm irq mode.
@@ -169,8 +216,14 @@ static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
drm_dev->vblank_disable_allowed = true;
/* attach this sub driver to iommu mapping if supported. */
- if (is_drm_iommu_supported(ctx->drm_dev))
+ if (is_drm_iommu_supported(ctx->drm_dev)) {
+ /*
+ * If any channel is already active, iommu will throw
+ * a PAGE FAULT when enabled. So clear any channel if enabled.
+ */
+ fimd_clear_channel(mgr);
drm_iommu_attach_device(ctx->drm_dev, ctx->dev);
+ }
return 0;
}
@@ -324,25 +377,6 @@ static void fimd_disable_vblank(struct exynos_drm_manager *mgr)
}
}
-static void fimd_wait_for_vblank(struct exynos_drm_manager *mgr)
-{
- struct fimd_context *ctx = mgr->ctx;
-
- if (ctx->suspended)
- return;
-
- atomic_set(&ctx->wait_vsync_event, 1);
-
- /*
- * wait for FIMD to signal VSYNC interrupt or return after
- * timeout which is set to 50ms (refresh rate of 20).
- */
- if (!wait_event_timeout(ctx->wait_vsync_queue,
- !atomic_read(&ctx->wait_vsync_event),
- HZ/20))
- DRM_DEBUG_KMS("vblank wait timed out.\n");
-}
-
static void fimd_win_mode_set(struct exynos_drm_manager *mgr,
struct exynos_drm_overlay *overlay)
{
@@ -446,6 +480,19 @@ static void fimd_win_set_pixfmt(struct fimd_context *ctx, unsigned int win)
DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
+ /*
+ * In case of exynos, setting dma-burst to 16Word causes permanent
+ * tearing for very small buffers, e.g. cursor buffer. Burst Mode
+ * switching which is based on overlay size is not recommended as
+ * overlay size varies alot towards the end of the screen and rapid
+ * movement causes unstable DMA which results into iommu crash/tear.
+ */
+
+ if (win_data->fb_width < MIN_FB_WIDTH_FOR_16WORD_BURST) {
+ val &= ~WINCONx_BURSTLEN_MASK;
+ val |= WINCONx_BURSTLEN_4WORD;
+ }
+
writel(val, ctx->regs + WINCON(win));
}
@@ -656,19 +703,6 @@ static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos)
win_data->enabled = false;
}
-static void fimd_clear_win(struct fimd_context *ctx, int win)
-{
- writel(0, ctx->regs + WINCON(win));
- writel(0, ctx->regs + VIDOSD_A(win));
- writel(0, ctx->regs + VIDOSD_B(win));
- writel(0, ctx->regs + VIDOSD_C(win));
-
- if (win == 1 || win == 2)
- writel(0, ctx->regs + VIDOSD_D(win));
-
- fimd_shadow_protect_win(ctx, win, false);
-}
-
static void fimd_window_suspend(struct exynos_drm_manager *mgr)
{
struct fimd_context *ctx = mgr->ctx;
@@ -803,8 +837,6 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode)
}
static struct exynos_drm_manager_ops fimd_manager_ops = {
- .initialize = fimd_mgr_initialize,
- .remove = fimd_mgr_remove,
.dpms = fimd_dpms,
.mode_fixup = fimd_mode_fixup,
.mode_set = fimd_mode_set,
@@ -849,20 +881,64 @@ out:
return IRQ_HANDLED;
}
+static int fimd_bind(struct device *dev, struct device *master, void *data)
+{
+ struct fimd_context *ctx = fimd_manager.ctx;
+ struct drm_device *drm_dev = data;
+
+ fimd_mgr_initialize(&fimd_manager, drm_dev);
+ exynos_drm_crtc_create(&fimd_manager);
+ if (ctx->display)
+ exynos_drm_create_enc_conn(drm_dev, ctx->display);
+
+ return 0;
+
+}
+
+static void fimd_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ 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);
+
+ if (ctx->display)
+ exynos_dpi_remove(dev);
+
+ fimd_mgr_remove(mgr);
+
+ crtc->funcs->destroy(crtc);
+}
+
+static const struct component_ops fimd_component_ops = {
+ .bind = fimd_bind,
+ .unbind = fimd_unbind,
+};
+
static int fimd_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fimd_context *ctx;
struct resource *res;
- int win;
int ret = -EINVAL;
- if (!dev->of_node)
- return -ENODEV;
+ ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
+ fimd_manager.type);
+ if (ret)
+ return ret;
+
+ if (!dev->of_node) {
+ ret = -ENODEV;
+ goto err_del_component;
+ }
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
+ if (!ctx) {
+ ret = -ENOMEM;
+ goto err_del_component;
+ }
ctx->dev = dev;
ctx->suspended = true;
@@ -875,32 +951,37 @@ static int fimd_probe(struct platform_device *pdev)
ctx->bus_clk = devm_clk_get(dev, "fimd");
if (IS_ERR(ctx->bus_clk)) {
dev_err(dev, "failed to get bus clock\n");
- return PTR_ERR(ctx->bus_clk);
+ ret = PTR_ERR(ctx->bus_clk);
+ goto err_del_component;
}
ctx->lcd_clk = devm_clk_get(dev, "sclk_fimd");
if (IS_ERR(ctx->lcd_clk)) {
dev_err(dev, "failed to get lcd clock\n");
- return PTR_ERR(ctx->lcd_clk);
+ ret = PTR_ERR(ctx->lcd_clk);
+ goto err_del_component;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctx->regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(ctx->regs))
- return PTR_ERR(ctx->regs);
+ if (IS_ERR(ctx->regs)) {
+ ret = PTR_ERR(ctx->regs);
+ goto err_del_component;
+ }
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync");
if (!res) {
dev_err(dev, "irq request failed.\n");
- return -ENXIO;
+ ret = -ENXIO;
+ goto err_del_component;
}
ret = devm_request_irq(dev, res->start, fimd_irq_handler,
0, "drm_fimd", ctx);
if (ret) {
dev_err(dev, "irq request failed.\n");
- return ret;
+ goto err_del_component;
}
ctx->driver_data = drm_fimd_get_driver_data(pdev);
@@ -910,30 +991,34 @@ static int fimd_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &fimd_manager);
fimd_manager.ctx = ctx;
- exynos_drm_manager_register(&fimd_manager);
- exynos_dpi_probe(ctx->dev);
+ ctx->display = exynos_dpi_probe(dev);
+ if (IS_ERR(ctx->display))
+ return PTR_ERR(ctx->display);
- pm_runtime_enable(dev);
+ pm_runtime_enable(&pdev->dev);
- for (win = 0; win < WINDOWS_NR; win++)
- fimd_clear_win(ctx, win);
+ ret = component_add(&pdev->dev, &fimd_component_ops);
+ if (ret)
+ goto err_disable_pm_runtime;
- return 0;
+ return ret;
+
+err_disable_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+
+err_del_component:
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
+ return ret;
}
static int fimd_remove(struct platform_device *pdev)
{
- struct exynos_drm_manager *mgr = platform_get_drvdata(pdev);
-
- exynos_dpi_remove(&pdev->dev);
-
- exynos_drm_manager_unregister(&fimd_manager);
-
- fimd_dpms(mgr, DRM_MODE_DPMS_OFF);
-
pm_runtime_disable(&pdev->dev);
+ component_del(&pdev->dev, &fimd_component_ops);
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
+
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index 42d2904d88c7..163a054922cb 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -612,22 +612,20 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
args->pitch = args->width * ((args->bpp + 7) / 8);
args->size = args->pitch * args->height;
- exynos_gem_obj = exynos_drm_gem_create(dev, EXYNOS_BO_CONTIG |
- EXYNOS_BO_WC, args->size);
- /*
- * If physically contiguous memory allocation fails and if IOMMU is
- * supported then try to get buffer from non physically contiguous
- * memory area.
- */
- if (IS_ERR(exynos_gem_obj) && is_drm_iommu_supported(dev)) {
- dev_warn(dev->dev, "contiguous FB allocation failed, falling back to non-contiguous\n");
+ if (is_drm_iommu_supported(dev)) {
+ exynos_gem_obj = exynos_drm_gem_create(dev,
+ EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
+ args->size);
+ } else {
exynos_gem_obj = exynos_drm_gem_create(dev,
- EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
- args->size);
+ EXYNOS_BO_CONTIG | EXYNOS_BO_WC,
+ args->size);
}
- if (IS_ERR(exynos_gem_obj))
+ if (IS_ERR(exynos_gem_obj)) {
+ dev_warn(dev->dev, "FB allocation failed.\n");
return PTR_ERR(exynos_gem_obj);
+ }
ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
&args->handle);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
index fa75059a6104..9e3ff1672965 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
@@ -1335,11 +1335,7 @@ static irqreturn_t gsc_irq_handler(int irq, void *dev_id)
static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
{
- struct drm_exynos_ipp_prop_list *prop_list;
-
- prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
- if (!prop_list)
- return -ENOMEM;
+ struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
prop_list->version = 1;
prop_list->writeback = 1;
@@ -1363,8 +1359,6 @@ static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
prop_list->scale_min.hsize = GSC_SCALE_MIN;
prop_list->scale_min.vsize = GSC_SCALE_MIN;
- ippdrv->prop_list = prop_list;
-
return 0;
}
@@ -1387,7 +1381,7 @@ static int gsc_ippdrv_check_property(struct device *dev,
{
struct gsc_context *ctx = get_gsc_context(dev);
struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv;
- struct drm_exynos_ipp_prop_list *pp = ippdrv->prop_list;
+ struct drm_exynos_ipp_prop_list *pp = &ippdrv->prop_list;
struct drm_exynos_ipp_config *config;
struct drm_exynos_pos *pos;
struct drm_exynos_sz *sz;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
index 3d78144387ac..a1888e128f1d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
@@ -167,6 +167,13 @@ static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj,
return 0;
}
+static void ipp_remove_id(struct idr *id_idr, struct mutex *lock, u32 id)
+{
+ mutex_lock(lock);
+ idr_remove(id_idr, id);
+ mutex_unlock(lock);
+}
+
static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id)
{
void *obj;
@@ -276,11 +283,6 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id)
DRM_DEBUG_KMS("prop_id[%d]\n", prop_id);
- if (list_empty(&exynos_drm_ippdrv_list)) {
- DRM_DEBUG_KMS("ippdrv_list is empty.\n");
- return ERR_PTR(-ENODEV);
- }
-
/*
* This case is search ipp driver by prop_id handle.
* sometimes, ipp subsystem find driver by prop_id.
@@ -289,11 +291,14 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id)
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", count++, (int)ippdrv);
- if (!list_empty(&ippdrv->cmd_list)) {
- list_for_each_entry(c_node, &ippdrv->cmd_list, list)
- if (c_node->property.prop_id == prop_id)
- return ippdrv;
+ mutex_lock(&ippdrv->cmd_lock);
+ list_for_each_entry(c_node, &ippdrv->cmd_list, list) {
+ if (c_node->property.prop_id == prop_id) {
+ mutex_unlock(&ippdrv->cmd_lock);
+ return ippdrv;
+ }
}
+ mutex_unlock(&ippdrv->cmd_lock);
}
return ERR_PTR(-ENODEV);
@@ -325,6 +330,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
if (!prop_list->ipp_id) {
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list)
count++;
+
/*
* Supports ippdrv list count for user application.
* First step user application getting ippdrv count.
@@ -346,7 +352,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data,
return PTR_ERR(ippdrv);
}
- prop_list = ippdrv->prop_list;
+ *prop_list = ippdrv->prop_list;
}
return 0;
@@ -386,9 +392,11 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property)
* 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);
@@ -396,6 +404,7 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property)
return 0;
}
}
+ mutex_unlock(&ippdrv->cmd_lock);
DRM_ERROR("failed to search property.\n");
@@ -499,7 +508,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
c_node->start_work = ipp_create_cmd_work();
if (IS_ERR(c_node->start_work)) {
DRM_ERROR("failed to create start work.\n");
- goto err_clear;
+ goto err_remove_id;
}
c_node->stop_work = ipp_create_cmd_work();
@@ -514,7 +523,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
goto err_free_stop;
}
- mutex_init(&c_node->cmd_lock);
+ mutex_init(&c_node->lock);
mutex_init(&c_node->mem_lock);
mutex_init(&c_node->event_lock);
@@ -526,7 +535,9 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data,
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);
/* make dedicated state without m2m */
if (!ipp_is_m2m_cmd(property->cmd))
@@ -538,18 +549,24 @@ err_free_stop:
kfree(c_node->stop_work);
err_free_start:
kfree(c_node->start_work);
+err_remove_id:
+ ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock, property->prop_id);
err_clear:
kfree(c_node);
return ret;
}
-static void ipp_clean_cmd_node(struct drm_exynos_ipp_cmd_node *c_node)
+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->cmd_lock);
+ mutex_destroy(&c_node->lock);
mutex_destroy(&c_node->mem_lock);
mutex_destroy(&c_node->event_lock);
@@ -567,17 +584,10 @@ static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node)
struct list_head *head;
int ret, i, count[EXYNOS_DRM_OPS_MAX] = { 0, };
- mutex_lock(&c_node->mem_lock);
-
for_each_ipp_ops(i) {
/* source/destination memory list */
head = &c_node->mem_list[i];
- if (list_empty(head)) {
- DRM_DEBUG_KMS("%s memory empty.\n", i ? "dst" : "src");
- continue;
- }
-
/* find memory node entry */
list_for_each_entry(m_node, head, list) {
DRM_DEBUG_KMS("%s,count[%d]m_node[0x%x]\n",
@@ -602,8 +612,6 @@ static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node)
ret = max(count[EXYNOS_DRM_OPS_SRC],
count[EXYNOS_DRM_OPS_DST]);
- mutex_unlock(&c_node->mem_lock);
-
return ret;
}
@@ -646,16 +654,13 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
return -EFAULT;
}
- mutex_lock(&c_node->mem_lock);
-
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");
- ret = -EFAULT;
- goto err_unlock;
+ return -EFAULT;
}
/* set address and enable irq */
@@ -664,12 +669,10 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv,
m_node->buf_id, IPP_BUF_ENQUEUE);
if (ret) {
DRM_ERROR("failed to set addr.\n");
- goto err_unlock;
+ return ret;
}
}
-err_unlock:
- mutex_unlock(&c_node->mem_lock);
return ret;
}
@@ -684,11 +687,9 @@ static struct drm_exynos_ipp_mem_node
void *addr;
int i;
- mutex_lock(&c_node->mem_lock);
-
m_node = kzalloc(sizeof(*m_node), GFP_KERNEL);
if (!m_node)
- goto err_unlock;
+ return ERR_PTR(-ENOMEM);
/* clear base address for error handling */
memset(&buf_info, 0x0, sizeof(buf_info));
@@ -722,15 +723,14 @@ static struct drm_exynos_ipp_mem_node
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);
-err_unlock:
- mutex_unlock(&c_node->mem_lock);
return ERR_PTR(-EFAULT);
}
@@ -747,13 +747,6 @@ static int ipp_put_mem_node(struct drm_device *drm_dev,
return -EFAULT;
}
- if (list_empty(&m_node->list)) {
- DRM_ERROR("empty memory node.\n");
- return -ENOMEM;
- }
-
- mutex_lock(&c_node->mem_lock);
-
DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id);
/* put gem buffer */
@@ -768,8 +761,6 @@ static int ipp_put_mem_node(struct drm_device *drm_dev,
list_del(&m_node->list);
kfree(m_node);
- mutex_unlock(&c_node->mem_lock);
-
return 0;
}
@@ -805,7 +796,9 @@ static int ipp_get_event(struct drm_device *drm_dev,
e->base.event = &e->event.base;
e->base.file_priv = file;
e->base.destroy = ipp_free_event;
+ mutex_lock(&c_node->event_lock);
list_add_tail(&e->base.link, &c_node->event_list);
+ mutex_unlock(&c_node->event_lock);
return 0;
}
@@ -816,11 +809,7 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node,
struct drm_exynos_ipp_send_event *e, *te;
int count = 0;
- if (list_empty(&c_node->event_list)) {
- DRM_DEBUG_KMS("event_list is empty.\n");
- return;
- }
-
+ mutex_lock(&c_node->event_lock);
list_for_each_entry_safe(e, te, &c_node->event_list, base.link) {
DRM_DEBUG_KMS("count[%d]e[0x%x]\n", count++, (int)e);
@@ -841,9 +830,13 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node,
/* delete list */
list_del(&e->base.link);
kfree(e);
- return;
+ goto out_unlock;
}
}
+
+out_unlock:
+ mutex_unlock(&c_node->event_lock);
+ return;
}
static void ipp_handle_cmd_work(struct device *dev,
@@ -887,7 +880,9 @@ static int ipp_queue_buf_with_run(struct device *dev,
return 0;
}
+ mutex_lock(&c_node->mem_lock);
if (!ipp_check_mem_list(c_node)) {
+ mutex_unlock(&c_node->mem_lock);
DRM_DEBUG_KMS("empty memory.\n");
return 0;
}
@@ -904,10 +899,12 @@ static int ipp_queue_buf_with_run(struct device *dev,
} else {
ret = ipp_set_mem_node(ippdrv, c_node, m_node);
if (ret) {
+ mutex_unlock(&c_node->mem_lock);
DRM_ERROR("failed to set m node.\n");
return ret;
}
}
+ mutex_unlock(&c_node->mem_lock);
return 0;
}
@@ -918,15 +915,15 @@ static void ipp_clean_queue_buf(struct drm_device *drm_dev,
{
struct drm_exynos_ipp_mem_node *m_node, *tm_node;
- if (!list_empty(&c_node->mem_list[qbuf->ops_id])) {
- /* delete list */
- list_for_each_entry_safe(m_node, tm_node,
- &c_node->mem_list[qbuf->ops_id], list) {
- if (m_node->buf_id == qbuf->buf_id &&
- m_node->ops_id == qbuf->ops_id)
- ipp_put_mem_node(drm_dev, c_node, m_node);
- }
+ /* delete list */
+ mutex_lock(&c_node->mem_lock);
+ list_for_each_entry_safe(m_node, tm_node,
+ &c_node->mem_list[qbuf->ops_id], list) {
+ if (m_node->buf_id == qbuf->buf_id &&
+ m_node->ops_id == qbuf->ops_id)
+ ipp_put_mem_node(drm_dev, c_node, m_node);
}
+ mutex_unlock(&c_node->mem_lock);
}
int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
@@ -998,7 +995,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
}
break;
case IPP_BUF_DEQUEUE:
- mutex_lock(&c_node->cmd_lock);
+ mutex_lock(&c_node->lock);
/* put event for destination buffer */
if (qbuf->ops_id == EXYNOS_DRM_OPS_DST)
@@ -1006,7 +1003,7 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data,
ipp_clean_queue_buf(drm_dev, c_node, qbuf);
- mutex_unlock(&c_node->cmd_lock);
+ mutex_unlock(&c_node->lock);
break;
default:
DRM_ERROR("invalid buffer control.\n");
@@ -1109,12 +1106,12 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
case IPP_CTRL_PLAY:
if (pm_runtime_suspended(ippdrv->dev))
pm_runtime_get_sync(ippdrv->dev);
+
c_node->state = IPP_STATE_START;
cmd_work = c_node->start_work;
cmd_work->ctrl = cmd_ctrl->ctrl;
ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node);
- c_node->state = IPP_STATE_START;
break;
case IPP_CTRL_STOP:
cmd_work = c_node->stop_work;
@@ -1129,10 +1126,12 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data,
c_node->state = IPP_STATE_STOP;
ippdrv->dedicated = false;
- ipp_clean_cmd_node(c_node);
+ mutex_lock(&ippdrv->cmd_lock);
+ ipp_clean_cmd_node(ctx, c_node);
if (list_empty(&ippdrv->cmd_list))
pm_runtime_put_sync(ippdrv->dev);
+ mutex_unlock(&ippdrv->cmd_lock);
break;
case IPP_CTRL_PAUSE:
cmd_work = c_node->stop_work;
@@ -1260,9 +1259,11 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
/* store command info in ippdrv */
ippdrv->c_node = c_node;
+ mutex_lock(&c_node->mem_lock);
if (!ipp_check_mem_list(c_node)) {
DRM_DEBUG_KMS("empty memory.\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_unlock;
}
/* set current property in ippdrv */
@@ -1270,7 +1271,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
if (ret) {
DRM_ERROR("failed to set property.\n");
ippdrv->c_node = NULL;
- return ret;
+ goto err_unlock;
}
/* check command */
@@ -1285,7 +1286,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
if (!m_node) {
DRM_ERROR("failed to get node.\n");
ret = -EFAULT;
- return ret;
+ goto err_unlock;
}
DRM_DEBUG_KMS("m_node[0x%x]\n", (int)m_node);
@@ -1293,7 +1294,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
ret = ipp_set_mem_node(ippdrv, c_node, m_node);
if (ret) {
DRM_ERROR("failed to set m node.\n");
- return ret;
+ goto err_unlock;
}
}
break;
@@ -1305,7 +1306,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
ret = ipp_set_mem_node(ippdrv, c_node, m_node);
if (ret) {
DRM_ERROR("failed to set m node.\n");
- return ret;
+ goto err_unlock;
}
}
break;
@@ -1317,14 +1318,16 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
ret = ipp_set_mem_node(ippdrv, c_node, m_node);
if (ret) {
DRM_ERROR("failed to set m node.\n");
- return ret;
+ goto err_unlock;
}
}
break;
default:
DRM_ERROR("invalid operations.\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_unlock;
}
+ mutex_unlock(&c_node->mem_lock);
DRM_DEBUG_KMS("cmd[%d]\n", property->cmd);
@@ -1333,11 +1336,17 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv,
ret = ippdrv->start(ippdrv->dev, property->cmd);
if (ret) {
DRM_ERROR("failed to start ops.\n");
+ ippdrv->c_node = NULL;
return ret;
}
}
return 0;
+
+err_unlock:
+ mutex_unlock(&c_node->mem_lock);
+ ippdrv->c_node = NULL;
+ return ret;
}
static int ipp_stop_property(struct drm_device *drm_dev,
@@ -1354,6 +1363,8 @@ static int ipp_stop_property(struct drm_device *drm_dev,
/* put event */
ipp_put_event(c_node, NULL);
+ mutex_lock(&c_node->mem_lock);
+
/* check command */
switch (property->cmd) {
case IPP_CMD_M2M:
@@ -1361,11 +1372,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
/* source/destination memory list */
head = &c_node->mem_list[i];
- if (list_empty(head)) {
- DRM_DEBUG_KMS("mem_list is empty.\n");
- break;
- }
-
list_for_each_entry_safe(m_node, tm_node,
head, list) {
ret = ipp_put_mem_node(drm_dev, c_node,
@@ -1381,11 +1387,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
/* destination memory list */
head = &c_node->mem_list[EXYNOS_DRM_OPS_DST];
- if (list_empty(head)) {
- DRM_DEBUG_KMS("mem_list is empty.\n");
- break;
- }
-
list_for_each_entry_safe(m_node, tm_node, head, list) {
ret = ipp_put_mem_node(drm_dev, c_node, m_node);
if (ret) {
@@ -1398,11 +1399,6 @@ static int ipp_stop_property(struct drm_device *drm_dev,
/* source memory list */
head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC];
- if (list_empty(head)) {
- DRM_DEBUG_KMS("mem_list is empty.\n");
- break;
- }
-
list_for_each_entry_safe(m_node, tm_node, head, list) {
ret = ipp_put_mem_node(drm_dev, c_node, m_node);
if (ret) {
@@ -1418,6 +1414,8 @@ static int ipp_stop_property(struct drm_device *drm_dev,
}
err_clear:
+ mutex_unlock(&c_node->mem_lock);
+
/* stop operations */
if (ippdrv->stop)
ippdrv->stop(ippdrv->dev, property->cmd);
@@ -1446,7 +1444,7 @@ void ipp_sched_cmd(struct work_struct *work)
return;
}
- mutex_lock(&c_node->cmd_lock);
+ mutex_lock(&c_node->lock);
property = &c_node->property;
@@ -1494,7 +1492,7 @@ void ipp_sched_cmd(struct work_struct *work)
DRM_DEBUG_KMS("ctrl[%d] done.\n", cmd_work->ctrl);
err_unlock:
- mutex_unlock(&c_node->cmd_lock);
+ mutex_unlock(&c_node->lock);
}
static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
@@ -1524,14 +1522,18 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
return -EINVAL;
}
+ mutex_lock(&c_node->event_lock);
if (list_empty(&c_node->event_list)) {
DRM_DEBUG_KMS("event list is empty.\n");
- return 0;
+ ret = 0;
+ goto err_event_unlock;
}
+ mutex_lock(&c_node->mem_lock);
if (!ipp_check_mem_list(c_node)) {
DRM_DEBUG_KMS("empty memory.\n");
- return 0;
+ ret = 0;
+ goto err_mem_unlock;
}
/* check command */
@@ -1545,7 +1547,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
struct drm_exynos_ipp_mem_node, list);
if (!m_node) {
DRM_ERROR("empty memory node.\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_mem_unlock;
}
tbuf_id[i] = m_node->buf_id;
@@ -1567,7 +1570,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
m_node = ipp_find_mem_node(c_node, &qbuf);
if (!m_node) {
DRM_ERROR("empty memory node.\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_mem_unlock;
}
tbuf_id[EXYNOS_DRM_OPS_DST] = m_node->buf_id;
@@ -1584,7 +1588,8 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
struct drm_exynos_ipp_mem_node, list);
if (!m_node) {
DRM_ERROR("empty memory node.\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_mem_unlock;
}
tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id;
@@ -1595,8 +1600,10 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
break;
default:
DRM_ERROR("invalid operations.\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_mem_unlock;
}
+ mutex_unlock(&c_node->mem_lock);
if (tbuf_id[EXYNOS_DRM_OPS_DST] != buf_id[EXYNOS_DRM_OPS_DST])
DRM_ERROR("failed to match buf_id[%d %d]prop_id[%d]\n",
@@ -1611,11 +1618,6 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
e = list_first_entry(&c_node->event_list,
struct drm_exynos_ipp_send_event, base.link);
- if (!e) {
- DRM_ERROR("empty event.\n");
- return -EINVAL;
- }
-
do_gettimeofday(&now);
DRM_DEBUG_KMS("tv_sec[%ld]tv_usec[%ld]\n", now.tv_sec, now.tv_usec);
e->event.tv_sec = now.tv_sec;
@@ -1630,11 +1632,18 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv,
list_move_tail(&e->base.link, &e->base.file_priv->event_list);
wake_up_interruptible(&e->base.file_priv->event_wait);
spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+ mutex_unlock(&c_node->event_lock);
DRM_DEBUG_KMS("done cmd[%d]prop_id[%d]buf_id[%d]\n",
property->cmd, property->prop_id, tbuf_id[EXYNOS_DRM_OPS_DST]);
return 0;
+
+err_mem_unlock:
+ mutex_unlock(&c_node->mem_lock);
+err_event_unlock:
+ mutex_unlock(&c_node->event_lock);
+ return ret;
}
void ipp_sched_event(struct work_struct *work)
@@ -1676,8 +1685,6 @@ void ipp_sched_event(struct work_struct *work)
goto err_completion;
}
- mutex_lock(&c_node->event_lock);
-
ret = ipp_send_event(ippdrv, c_node, event_work->buf_id);
if (ret) {
DRM_ERROR("failed to send event.\n");
@@ -1687,8 +1694,6 @@ void ipp_sched_event(struct work_struct *work)
err_completion:
if (ipp_is_m2m_cmd(c_node->property.cmd))
complete(&c_node->start_complete);
-
- mutex_unlock(&c_node->event_lock);
}
static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
@@ -1699,23 +1704,21 @@ 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,
- &ippdrv->ipp_id);
- if (ret) {
+ &ipp_id);
+ if (ret || ipp_id == 0) {
DRM_ERROR("failed to create id.\n");
- goto err_idr;
+ goto err;
}
DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]ipp_id[%d]\n",
- count++, (int)ippdrv, ippdrv->ipp_id);
+ count++, (int)ippdrv, ipp_id);
- if (ippdrv->ipp_id == 0) {
- DRM_ERROR("failed to get ipp_id[%d]\n",
- ippdrv->ipp_id);
- goto err_idr;
- }
+ ippdrv->prop_list.ipp_id = ipp_id;
/* store parent device for node */
ippdrv->parent_dev = dev;
@@ -1724,39 +1727,46 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
ippdrv->event_workq = ctx->event_workq;
ippdrv->sched_event = ipp_sched_event;
INIT_LIST_HEAD(&ippdrv->cmd_list);
+ mutex_init(&ippdrv->cmd_lock);
if (is_drm_iommu_supported(drm_dev)) {
ret = drm_iommu_attach_device(drm_dev, ippdrv->dev);
if (ret) {
DRM_ERROR("failed to activate iommu\n");
- goto err_iommu;
+ goto err;
}
}
}
return 0;
-err_iommu:
+err:
/* get ipp driver entry */
- list_for_each_entry_reverse(ippdrv, &exynos_drm_ippdrv_list, drv_list)
+ list_for_each_entry_continue_reverse(ippdrv, &exynos_drm_ippdrv_list,
+ drv_list) {
if (is_drm_iommu_supported(drm_dev))
drm_iommu_detach_device(drm_dev, ippdrv->dev);
-err_idr:
- idr_destroy(&ctx->ipp_idr);
- idr_destroy(&ctx->prop_idr);
+ ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,
+ ippdrv->prop_list.ipp_id);
+ }
+
return ret;
}
static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{
struct exynos_drm_ippdrv *ippdrv;
+ struct ipp_context *ctx = get_ipp_context(dev);
/* get ipp driver entry */
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
if (is_drm_iommu_supported(drm_dev))
drm_iommu_detach_device(drm_dev, ippdrv->dev);
+ ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock,
+ ippdrv->prop_list.ipp_id);
+
ippdrv->drm_dev = NULL;
exynos_drm_ippdrv_unregister(ippdrv);
}
@@ -1787,20 +1797,14 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
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);
- if (list_empty(&exynos_drm_ippdrv_list)) {
- DRM_DEBUG_KMS("ippdrv_list is empty.\n");
- goto err_clear;
- }
-
list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) {
- if (list_empty(&ippdrv->cmd_list))
- continue;
-
+ mutex_lock(&ippdrv->cmd_lock);
list_for_each_entry_safe(c_node, tc_node,
&ippdrv->cmd_list, list) {
DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n",
@@ -1820,14 +1824,14 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev,
}
ippdrv->dedicated = false;
- ipp_clean_cmd_node(c_node);
+ ipp_clean_cmd_node(ctx, c_node);
if (list_empty(&ippdrv->cmd_list))
pm_runtime_put_sync(ippdrv->dev);
}
}
+ mutex_unlock(&ippdrv->cmd_lock);
}
-err_clear:
kfree(priv);
return;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_ipp.h
index ab1634befc05..7aaeaae757c2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_ipp.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.h
@@ -52,7 +52,7 @@ struct drm_exynos_ipp_cmd_work {
* @list: list head to command queue information.
* @event_list: list head of event.
* @mem_list: list head to source,destination memory queue information.
- * @cmd_lock: lock for synchronization of access to ioctl.
+ * @lock: lock for synchronization of access to ioctl.
* @mem_lock: lock for synchronization of access to memory nodes.
* @event_lock: lock for synchronization of access to scheduled event.
* @start_complete: completion of start of command.
@@ -68,7 +68,7 @@ struct drm_exynos_ipp_cmd_node {
struct list_head list;
struct list_head event_list;
struct list_head mem_list[EXYNOS_DRM_OPS_MAX];
- struct mutex cmd_lock;
+ struct mutex lock;
struct mutex mem_lock;
struct mutex event_lock;
struct completion start_complete;
@@ -83,7 +83,7 @@ struct drm_exynos_ipp_cmd_node {
/*
* A structure of buffer information.
*
- * @gem_objs: Y, Cb, Cr each gem object.
+ * @handles: Y, Cb, Cr each gem object handle.
* @base: Y, Cb, Cr each planar address.
*/
struct drm_exynos_ipp_buf_info {
@@ -142,12 +142,12 @@ struct exynos_drm_ipp_ops {
* @parent_dev: parent device information.
* @dev: platform device.
* @drm_dev: drm device.
- * @ipp_id: id of ipp driver.
* @dedicated: dedicated ipp device.
* @ops: source, destination operations.
* @event_workq: event work queue.
* @c_node: current command information.
* @cmd_list: list head for command information.
+ * @cmd_lock: lock for synchronization of access to cmd_list.
* @prop_list: property informations of current ipp driver.
* @check_property: check property about format, size, buffer.
* @reset: reset ipp block.
@@ -160,13 +160,13 @@ struct exynos_drm_ippdrv {
struct device *parent_dev;
struct device *dev;
struct drm_device *drm_dev;
- u32 ipp_id;
bool dedicated;
struct exynos_drm_ipp_ops *ops[EXYNOS_DRM_OPS_MAX];
struct workqueue_struct *event_workq;
struct drm_exynos_ipp_cmd_node *c_node;
struct list_head cmd_list;
- struct drm_exynos_ipp_prop_list *prop_list;
+ struct mutex cmd_lock;
+ struct drm_exynos_ipp_prop_list prop_list;
int (*check_property)(struct device *dev,
struct drm_exynos_ipp_property *property);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
index 7b901688defa..f01fbb6dc1f0 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
@@ -158,8 +158,9 @@ static irqreturn_t rotator_irq_handler(int irq, void *arg)
rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
queue_work(ippdrv->event_workq,
(struct work_struct *)event_work);
- } else
+ } else {
DRM_ERROR("the SFR is set illegally\n");
+ }
return IRQ_HANDLED;
}
@@ -469,11 +470,7 @@ static struct exynos_drm_ipp_ops rot_dst_ops = {
static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
{
- struct drm_exynos_ipp_prop_list *prop_list;
-
- prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
- if (!prop_list)
- return -ENOMEM;
+ struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
prop_list->version = 1;
prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
@@ -486,8 +483,6 @@ static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
prop_list->crop = 0;
prop_list->scale = 0;
- ippdrv->prop_list = prop_list;
-
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index 852f2dadaebd..2fb8705d6461 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -51,6 +51,7 @@ struct vidi_context {
struct drm_crtc *crtc;
struct drm_encoder *encoder;
struct drm_connector connector;
+ struct exynos_drm_subdrv subdrv;
struct vidi_win_data win_data[WINDOWS_NR];
struct edid *raw_edid;
unsigned int clkdiv;
@@ -294,14 +295,13 @@ static void vidi_dpms(struct exynos_drm_manager *mgr, int mode)
}
static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
- struct drm_device *drm_dev, int pipe)
+ struct drm_device *drm_dev)
{
struct vidi_context *ctx = mgr->ctx;
+ struct exynos_drm_private *priv = drm_dev->dev_private;
- DRM_ERROR("vidi initialize ct=%p dev=%p pipe=%d\n", ctx, drm_dev, pipe);
-
- ctx->drm_dev = drm_dev;
- ctx->pipe = pipe;
+ mgr->drm_dev = ctx->drm_dev = drm_dev;
+ mgr->pipe = ctx->pipe = priv->pipe++;
/*
* enable drm irq mode.
@@ -324,7 +324,6 @@ static int vidi_mgr_initialize(struct exynos_drm_manager *mgr,
}
static struct exynos_drm_manager_ops vidi_manager_ops = {
- .initialize = vidi_mgr_initialize,
.dpms = vidi_dpms,
.commit = vidi_commit,
.enable_vblank = vidi_enable_vblank,
@@ -533,12 +532,6 @@ static int vidi_get_modes(struct drm_connector *connector)
return drm_add_edid_modes(connector, edid);
}
-static int vidi_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- return MODE_OK;
-}
-
static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector)
{
struct vidi_context *ctx = ctx_from_connector(connector);
@@ -548,7 +541,6 @@ static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector)
static struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
.get_modes = vidi_get_modes,
- .mode_valid = vidi_mode_valid,
.best_encoder = vidi_best_encoder,
};
@@ -586,13 +578,38 @@ static struct exynos_drm_display vidi_display = {
.ops = &vidi_display_ops,
};
+static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+{
+ struct exynos_drm_manager *mgr = get_vidi_mgr(dev);
+ struct vidi_context *ctx = mgr->ctx;
+ struct drm_crtc *crtc = ctx->crtc;
+ int ret;
+
+ vidi_mgr_initialize(mgr, drm_dev);
+
+ ret = exynos_drm_crtc_create(&vidi_manager);
+ if (ret) {
+ DRM_ERROR("failed to create crtc.\n");
+ return ret;
+ }
+
+ ret = exynos_drm_create_enc_conn(drm_dev, &vidi_display);
+ if (ret) {
+ crtc->funcs->destroy(crtc);
+ DRM_ERROR("failed to create encoder and connector.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static int vidi_probe(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
+ struct exynos_drm_subdrv *subdrv;
struct vidi_context *ctx;
int ret;
- ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
@@ -607,28 +624,43 @@ static int vidi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &vidi_manager);
- ret = device_create_file(dev, &dev_attr_connection);
- if (ret < 0)
- DRM_INFO("failed to create connection sysfs.\n");
+ subdrv = &ctx->subdrv;
+ subdrv->dev = &pdev->dev;
+ subdrv->probe = vidi_subdrv_probe;
+
+ ret = exynos_drm_subdrv_register(subdrv);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register drm vidi device\n");
+ return ret;
+ }
- exynos_drm_manager_register(&vidi_manager);
- exynos_drm_display_register(&vidi_display);
+ ret = device_create_file(&pdev->dev, &dev_attr_connection);
+ if (ret < 0) {
+ exynos_drm_subdrv_unregister(subdrv);
+ DRM_INFO("failed to create connection sysfs.\n");
+ }
return 0;
}
static int vidi_remove(struct platform_device *pdev)
{
- struct vidi_context *ctx = platform_get_drvdata(pdev);
-
- exynos_drm_display_unregister(&vidi_display);
- exynos_drm_manager_unregister(&vidi_manager);
+ 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);
ctx->raw_edid = NULL;
+
+ return -EINVAL;
}
+ crtc->funcs->destroy(crtc);
+ encoder->funcs->destroy(encoder);
+ drm_connector_cleanup(&ctx->connector);
+
return 0;
}
@@ -640,3 +672,31 @@ struct platform_driver vidi_driver = {
.owner = THIS_MODULE,
},
};
+
+int exynos_drm_probe_vidi(void)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = platform_device_register_simple("exynos-drm-vidi", -1, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ ret = platform_driver_register(&vidi_driver);
+ if (ret) {
+ platform_device_unregister(pdev);
+ return ret;
+ }
+
+ return ret;
+}
+
+void exynos_drm_remove_vidi(void)
+{
+ struct vidi_context *ctx = vidi_manager.ctx;
+ struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
+ struct platform_device *pdev = to_platform_device(subdrv->dev);
+
+ platform_driver_unregister(&vidi_driver);
+ platform_device_unregister(pdev);
+}
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 9a6d652a3ef2..c104d0c9b385 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -33,13 +33,17 @@
#include <linux/regulator/consumer.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <linux/i2c.h>
+#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/hdmi.h>
+#include <linux/component.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
+#include "exynos_drm_crtc.h"
#include "exynos_mixer.h"
#include <linux/gpio.h>
@@ -48,6 +52,8 @@
#define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev))
#define ctx_from_connector(c) container_of(c, struct hdmi_context, connector)
+#define HOTPLUG_DEBOUNCE_MS 1100
+
/* AVI header and aspect ratio */
#define HDMI_AVI_VERSION 0x02
#define HDMI_AVI_LENGTH 0x0D
@@ -66,6 +72,8 @@ enum hdmi_type {
struct hdmi_driver_data {
unsigned int type;
+ const struct hdmiphy_config *phy_confs;
+ unsigned int phy_conf_count;
unsigned int is_apb_phy:1;
};
@@ -74,7 +82,6 @@ struct hdmi_resources {
struct clk *sclk_hdmi;
struct clk *sclk_pixel;
struct clk *sclk_hdmiphy;
- struct clk *hdmiphy;
struct clk *mout_hdmi;
struct regulator_bulk_data *regul_bulk;
int regul_count;
@@ -185,17 +192,23 @@ struct hdmi_context {
void __iomem *regs;
int irq;
+ struct delayed_work hotplug_work;
struct i2c_adapter *ddc_adpt;
struct i2c_client *hdmiphy_port;
/* current hdmiphy conf regs */
+ struct drm_display_mode current_mode;
struct hdmi_conf_regs mode_conf;
struct hdmi_resources res;
int hpd_gpio;
+ void __iomem *regs_hdmiphy;
+ const struct hdmiphy_config *phy_confs;
+ unsigned int phy_conf_count;
+ struct regmap *pmureg;
enum hdmi_type type;
};
@@ -204,14 +217,6 @@ struct hdmiphy_config {
u8 conf[32];
};
-struct hdmi_driver_data exynos4212_hdmi_driver_data = {
- .type = HDMI_TYPE14,
-};
-
-struct hdmi_driver_data exynos5_hdmi_driver_data = {
- .type = HDMI_TYPE14,
-};
-
/* list of phy config settings */
static const struct hdmiphy_config hdmiphy_v13_configs[] = {
{
@@ -319,18 +324,18 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
{
.pixel_clock = 71000000,
.conf = {
- 0x01, 0x91, 0x1e, 0x15, 0x40, 0x3c, 0xce, 0x08,
- 0x04, 0x20, 0xb2, 0xd8, 0x45, 0xa0, 0xac, 0x80,
- 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
+ 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 73250000,
.conf = {
- 0x01, 0xd1, 0x1f, 0x15, 0x40, 0x18, 0xe9, 0x08,
- 0x02, 0xa0, 0xb7, 0xd8, 0x45, 0xa0, 0xac, 0x80,
- 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
+ 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
},
},
@@ -362,15 +367,6 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
},
},
{
- .pixel_clock = 88750000,
- .conf = {
- 0x01, 0x91, 0x25, 0x17, 0x40, 0x30, 0xfe, 0x08,
- 0x06, 0x20, 0xde, 0xd8, 0x45, 0xa0, 0xac, 0x80,
- 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
- 0x54, 0x8a, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
- },
- },
- {
.pixel_clock = 106500000,
.conf = {
0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
@@ -391,18 +387,18 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
{
.pixel_clock = 115500000,
.conf = {
- 0x01, 0xd1, 0x30, 0x1a, 0x40, 0x40, 0x10, 0x04,
- 0x04, 0xa0, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
- 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
+ 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
},
},
{
.pixel_clock = 119000000,
.conf = {
- 0x01, 0x91, 0x32, 0x14, 0x40, 0x60, 0xd8, 0x08,
- 0x06, 0x20, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
- 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
+ 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
},
},
@@ -426,6 +422,183 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
},
};
+static const struct hdmiphy_config hdmiphy_5420_configs[] = {
+ {
+ .pixel_clock = 25200000,
+ .conf = {
+ 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
+ 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 27000000,
+ .conf = {
+ 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
+ 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 27027000,
+ .conf = {
+ 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
+ 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+ 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 36000000,
+ .conf = {
+ 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
+ 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 40000000,
+ .conf = {
+ 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
+ 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 65000000,
+ .conf = {
+ 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
+ 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 71000000,
+ .conf = {
+ 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
+ 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 73250000,
+ .conf = {
+ 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
+ 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 74176000,
+ .conf = {
+ 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
+ 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 74250000,
+ .conf = {
+ 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
+ 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
+ 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 83500000,
+ .conf = {
+ 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
+ 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 88750000,
+ .conf = {
+ 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
+ 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 106500000,
+ .conf = {
+ 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
+ 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 108000000,
+ .conf = {
+ 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
+ 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 115500000,
+ .conf = {
+ 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
+ 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 146250000,
+ .conf = {
+ 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
+ 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
+ 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 148500000,
+ .conf = {
+ 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
+ 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
+ 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
+ },
+ },
+};
+
+static struct hdmi_driver_data exynos5420_hdmi_driver_data = {
+ .type = HDMI_TYPE14,
+ .phy_confs = hdmiphy_5420_configs,
+ .phy_conf_count = ARRAY_SIZE(hdmiphy_5420_configs),
+ .is_apb_phy = 1,
+};
+
+static struct hdmi_driver_data exynos4212_hdmi_driver_data = {
+ .type = HDMI_TYPE14,
+ .phy_confs = hdmiphy_v14_configs,
+ .phy_conf_count = ARRAY_SIZE(hdmiphy_v14_configs),
+ .is_apb_phy = 0,
+};
+
+static struct hdmi_driver_data exynos5_hdmi_driver_data = {
+ .type = HDMI_TYPE14,
+ .phy_confs = hdmiphy_v13_configs,
+ .phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
+ .is_apb_phy = 0,
+};
+
static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
{
return readl(hdata->regs + reg_id);
@@ -445,6 +618,48 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
writel(value, hdata->regs + reg_id);
}
+static int hdmiphy_reg_writeb(struct hdmi_context *hdata,
+ u32 reg_offset, u8 value)
+{
+ if (hdata->hdmiphy_port) {
+ u8 buffer[2];
+ int ret;
+
+ buffer[0] = reg_offset;
+ buffer[1] = value;
+
+ ret = i2c_master_send(hdata->hdmiphy_port, buffer, 2);
+ if (ret == 2)
+ return 0;
+ return ret;
+ } else {
+ writeb(value, hdata->regs_hdmiphy + (reg_offset<<2));
+ return 0;
+ }
+}
+
+static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
+ u32 reg_offset, const u8 *buf, u32 len)
+{
+ if ((reg_offset + len) > 32)
+ return -EINVAL;
+
+ if (hdata->hdmiphy_port) {
+ int ret;
+
+ ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
+ if (ret == len)
+ return 0;
+ return ret;
+ } else {
+ int i;
+ for (i = 0; i < len; i++)
+ writeb(buf[i], hdata->regs_hdmiphy +
+ ((reg_offset + i)<<2));
+ return 0;
+ }
+}
+
static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix)
{
#define DUMPREG(reg_id) \
@@ -809,6 +1024,8 @@ static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
{
struct hdmi_context *hdata = ctx_from_connector(connector);
+ hdata->hpd = gpio_get_value(hdata->hpd_gpio);
+
return hdata->hpd ? connector_status_connected :
connector_status_disconnected;
}
@@ -848,20 +1065,10 @@ static int hdmi_get_modes(struct drm_connector *connector)
static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
{
- const struct hdmiphy_config *confs;
- int count, i;
-
- if (hdata->type == HDMI_TYPE13) {
- confs = hdmiphy_v13_configs;
- count = ARRAY_SIZE(hdmiphy_v13_configs);
- } else if (hdata->type == HDMI_TYPE14) {
- confs = hdmiphy_v14_configs;
- count = ARRAY_SIZE(hdmiphy_v14_configs);
- } else
- return -EINVAL;
+ int i;
- for (i = 0; i < count; i++)
- if (confs[i].pixel_clock == pixel_clock)
+ for (i = 0; i < hdata->phy_conf_count; i++)
+ if (hdata->phy_confs[i].pixel_clock == pixel_clock)
return i;
DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
@@ -928,16 +1135,6 @@ static int hdmi_create_connector(struct exynos_drm_display *display,
return 0;
}
-static int hdmi_initialize(struct exynos_drm_display *display,
- struct drm_device *drm_dev)
-{
- struct hdmi_context *hdata = display->ctx;
-
- hdata->drm_dev = drm_dev;
-
- return 0;
-}
-
static void hdmi_mode_fixup(struct exynos_drm_display *display,
struct drm_connector *connector,
const struct drm_display_mode *mode,
@@ -1136,20 +1333,15 @@ static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
}
-static void hdmi_conf_reset(struct hdmi_context *hdata)
+static void hdmi_start(struct hdmi_context *hdata, bool start)
{
- u32 reg;
+ u32 val = start ? HDMI_TG_EN : 0;
- if (hdata->type == HDMI_TYPE13)
- reg = HDMI_V13_CORE_RSTOUT;
- else
- reg = HDMI_CORE_RSTOUT;
+ if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
+ val |= HDMI_FIELD_EN;
- /* resetting HDMI core */
- hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT);
- usleep_range(10000, 12000);
- hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT);
- usleep_range(10000, 12000);
+ hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
+ hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
}
static void hdmi_conf_init(struct hdmi_context *hdata)
@@ -1163,6 +1355,8 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
/* choose HDMI mode */
hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
+ /* Apply Video preable and Guard band in HDMI mode only */
+ hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
/* disable bluescreen */
hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
@@ -1286,12 +1480,7 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
clk_prepare_enable(hdata->res.sclk_hdmi);
/* enable HDMI and timing generator */
- hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
- if (core->int_pro_mode[0])
- hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
- HDMI_FIELD_EN);
- else
- hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
+ hdmi_start(hdata, true);
}
static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
@@ -1453,12 +1642,7 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
clk_prepare_enable(hdata->res.sclk_hdmi);
/* enable HDMI and timing generator */
- hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
- if (core->int_pro_mode[0])
- hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
- HDMI_FIELD_EN);
- else
- hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
+ hdmi_start(hdata, true);
}
static void hdmi_mode_apply(struct hdmi_context *hdata)
@@ -1499,32 +1683,51 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
static void hdmiphy_poweron(struct hdmi_context *hdata)
{
- if (hdata->type == HDMI_TYPE14)
- hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
- HDMI_PHY_POWER_OFF_EN);
+ if (hdata->type != HDMI_TYPE14)
+ return;
+
+ DRM_DEBUG_KMS("\n");
+
+ /* For PHY Mode Setting */
+ hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+ HDMI_PHY_ENABLE_MODE_SET);
+ /* Phy Power On */
+ hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
+ HDMI_PHY_POWER_ON);
+ /* For PHY Mode Setting */
+ hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+ HDMI_PHY_DISABLE_MODE_SET);
+ /* PHY SW Reset */
+ hdmiphy_conf_reset(hdata);
}
static void hdmiphy_poweroff(struct hdmi_context *hdata)
{
- if (hdata->type == HDMI_TYPE14)
- hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
- HDMI_PHY_POWER_OFF_EN);
+ if (hdata->type != HDMI_TYPE14)
+ return;
+
+ DRM_DEBUG_KMS("\n");
+
+ /* PHY SW Reset */
+ hdmiphy_conf_reset(hdata);
+ /* For PHY Mode Setting */
+ hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+ HDMI_PHY_ENABLE_MODE_SET);
+
+ /* PHY Power Off */
+ hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
+ HDMI_PHY_POWER_OFF);
+
+ /* For PHY Mode Setting */
+ hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+ HDMI_PHY_DISABLE_MODE_SET);
}
static void hdmiphy_conf_apply(struct hdmi_context *hdata)
{
- const u8 *hdmiphy_data;
- u8 buffer[32];
- u8 operation[2];
- u8 read_buffer[32] = {0, };
int ret;
int i;
- if (!hdata->hdmiphy_port) {
- DRM_ERROR("hdmiphy is not attached\n");
- return;
- }
-
/* pixel clock */
i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
if (i < 0) {
@@ -1532,39 +1735,21 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
return;
}
- if (hdata->type == HDMI_TYPE13)
- hdmiphy_data = hdmiphy_v13_configs[i].conf;
- else
- hdmiphy_data = hdmiphy_v14_configs[i].conf;
-
- memcpy(buffer, hdmiphy_data, 32);
- ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
- if (ret != 32) {
- DRM_ERROR("failed to configure HDMIPHY via I2C\n");
+ ret = hdmiphy_reg_write_buf(hdata, 0, hdata->phy_confs[i].conf, 32);
+ if (ret) {
+ DRM_ERROR("failed to configure hdmiphy\n");
return;
}
usleep_range(10000, 12000);
- /* operation mode */
- operation[0] = 0x1f;
- operation[1] = 0x80;
-
- ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
- if (ret != 2) {
+ ret = hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+ HDMI_PHY_DISABLE_MODE_SET);
+ if (ret) {
DRM_ERROR("failed to enable hdmiphy\n");
return;
}
- ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
- if (ret < 0) {
- DRM_ERROR("failed to read hdmiphy config\n");
- return;
- }
-
- for (i = 0; i < ret; i++)
- DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
- "recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
}
static void hdmi_conf_apply(struct hdmi_context *hdata)
@@ -1573,7 +1758,7 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
hdmiphy_conf_apply(hdata);
mutex_lock(&hdata->hdmi_mutex);
- hdmi_conf_reset(hdata);
+ hdmi_start(hdata, false);
hdmi_conf_init(hdata);
mutex_unlock(&hdata->hdmi_mutex);
@@ -1814,6 +1999,9 @@ static void hdmi_mode_set(struct exynos_drm_display *display,
m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
"INTERLACED" : "PROGERESSIVE");
+ /* preserve mode information for later use. */
+ drm_mode_copy(&hdata->current_mode, mode);
+
if (hdata->type == HDMI_TYPE13)
hdmi_v13_mode_set(hdata, mode);
else
@@ -1854,7 +2042,10 @@ static void hdmi_poweron(struct exynos_drm_display *display)
if (regulator_bulk_enable(res->regul_count, res->regul_bulk))
DRM_DEBUG_KMS("failed to enable regulator bulk\n");
- clk_prepare_enable(res->hdmiphy);
+ /* set pmu hdmiphy control bit to enable hdmiphy */
+ regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
+ PMU_HDMI_PHY_ENABLE_BIT, 1);
+
clk_prepare_enable(res->hdmi);
clk_prepare_enable(res->sclk_hdmi);
@@ -1872,16 +2063,20 @@ static void hdmi_poweroff(struct exynos_drm_display *display)
goto out;
mutex_unlock(&hdata->hdmi_mutex);
- /*
- * The TV power domain needs any condition of hdmiphy to turn off and
- * its reset state seems to meet the condition.
- */
- hdmiphy_conf_reset(hdata);
+ /* HDMI System Disable */
+ hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
+
hdmiphy_poweroff(hdata);
+ cancel_delayed_work(&hdata->hotplug_work);
+
clk_disable_unprepare(res->sclk_hdmi);
clk_disable_unprepare(res->hdmi);
- clk_disable_unprepare(res->hdmiphy);
+
+ /* reset pmu hdmiphy control bit to disable hdmiphy */
+ regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
+ PMU_HDMI_PHY_ENABLE_BIT, 0);
+
regulator_bulk_disable(res->regul_count, res->regul_bulk);
pm_runtime_put_sync(hdata->dev);
@@ -1913,7 +2108,6 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode)
}
static struct exynos_drm_display_ops hdmi_display_ops = {
- .initialize = hdmi_initialize,
.create_connector = hdmi_create_connector,
.mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set,
@@ -1926,9 +2120,11 @@ static struct exynos_drm_display hdmi_display = {
.ops = &hdmi_display_ops,
};
-static irqreturn_t hdmi_irq_thread(int irq, void *arg)
+static void hdmi_hotplug_work_func(struct work_struct *work)
{
- struct hdmi_context *hdata = arg;
+ struct hdmi_context *hdata;
+
+ hdata = container_of(work, struct hdmi_context, hotplug_work.work);
mutex_lock(&hdata->hdmi_mutex);
hdata->hpd = gpio_get_value(hdata->hpd_gpio);
@@ -1936,6 +2132,14 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
if (hdata->drm_dev)
drm_helper_hpd_irq_event(hdata->drm_dev);
+}
+
+static irqreturn_t hdmi_irq_thread(int irq, void *arg)
+{
+ struct hdmi_context *hdata = arg;
+
+ mod_delayed_work(system_wq, &hdata->hotplug_work,
+ msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
return IRQ_HANDLED;
}
@@ -1954,37 +2158,35 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
DRM_DEBUG_KMS("HDMI resource init\n");
- memset(res, 0, sizeof(*res));
-
/* get clocks, power */
res->hdmi = devm_clk_get(dev, "hdmi");
if (IS_ERR(res->hdmi)) {
DRM_ERROR("failed to get clock 'hdmi'\n");
+ ret = PTR_ERR(res->hdmi);
goto fail;
}
res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
if (IS_ERR(res->sclk_hdmi)) {
DRM_ERROR("failed to get clock 'sclk_hdmi'\n");
+ ret = PTR_ERR(res->sclk_hdmi);
goto fail;
}
res->sclk_pixel = devm_clk_get(dev, "sclk_pixel");
if (IS_ERR(res->sclk_pixel)) {
DRM_ERROR("failed to get clock 'sclk_pixel'\n");
+ ret = PTR_ERR(res->sclk_pixel);
goto fail;
}
res->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy");
if (IS_ERR(res->sclk_hdmiphy)) {
DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n");
- goto fail;
- }
- res->hdmiphy = devm_clk_get(dev, "hdmiphy");
- if (IS_ERR(res->hdmiphy)) {
- DRM_ERROR("failed to get clock 'hdmiphy'\n");
+ ret = PTR_ERR(res->sclk_hdmiphy);
goto fail;
}
res->mout_hdmi = devm_clk_get(dev, "mout_hdmi");
if (IS_ERR(res->mout_hdmi)) {
DRM_ERROR("failed to get clock 'mout_hdmi'\n");
+ ret = PTR_ERR(res->mout_hdmi);
goto fail;
}
@@ -1992,8 +2194,10 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
res->regul_bulk = devm_kzalloc(dev, ARRAY_SIZE(supply) *
sizeof(res->regul_bulk[0]), GFP_KERNEL);
- if (!res->regul_bulk)
+ if (!res->regul_bulk) {
+ ret = -ENOMEM;
goto fail;
+ }
for (i = 0; i < ARRAY_SIZE(supply); ++i) {
res->regul_bulk[i].supply = supply[i];
res->regul_bulk[i].consumer = NULL;
@@ -2001,14 +2205,14 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
if (ret) {
DRM_ERROR("failed to get regulators\n");
- goto fail;
+ return ret;
}
res->regul_count = ARRAY_SIZE(supply);
- return 0;
+ return ret;
fail:
DRM_ERROR("HDMI resource init - failed\n");
- return -ENODEV;
+ return ret;
}
static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
@@ -2043,42 +2247,105 @@ static struct of_device_id hdmi_match_types[] = {
.compatible = "samsung,exynos4212-hdmi",
.data = &exynos4212_hdmi_driver_data,
}, {
+ .compatible = "samsung,exynos5420-hdmi",
+ .data = &exynos5420_hdmi_driver_data,
+ }, {
/* end node */
}
};
+static int hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm_dev = data;
+ struct hdmi_context *hdata;
+
+ hdata = hdmi_display.ctx;
+ hdata->drm_dev = drm_dev;
+
+ return exynos_drm_create_enc_conn(drm_dev, &hdmi_display);
+}
+
+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 = {
+ .bind = hdmi_bind,
+ .unbind = hdmi_unbind,
+};
+
+static struct device_node *hdmi_legacy_ddc_dt_binding(struct device *dev)
+{
+ const char *compatible_str = "samsung,exynos4210-hdmiddc";
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, compatible_str);
+ if (np)
+ return of_get_next_parent(np);
+
+ return NULL;
+}
+
+static struct device_node *hdmi_legacy_phy_dt_binding(struct device *dev)
+{
+ const char *compatible_str = "samsung,exynos4212-hdmiphy";
+
+ return of_find_compatible_node(NULL, NULL, compatible_str);
+}
+
static int hdmi_probe(struct platform_device *pdev)
{
+ struct device_node *ddc_node, *phy_node;
+ struct s5p_hdmi_platform_data *pdata;
+ struct hdmi_driver_data *drv_data;
+ const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct hdmi_context *hdata;
- struct s5p_hdmi_platform_data *pdata;
struct resource *res;
- const struct of_device_id *match;
- struct device_node *ddc_node, *phy_node;
- struct hdmi_driver_data *drv_data;
int ret;
- if (!dev->of_node)
- return -ENODEV;
+ ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
+ hdmi_display.type);
+ if (ret)
+ return ret;
+
+ if (!dev->of_node) {
+ ret = -ENODEV;
+ goto err_del_component;
+ }
pdata = drm_hdmi_dt_parse_pdata(dev);
- if (!pdata)
- return -EINVAL;
+ if (!pdata) {
+ ret = -EINVAL;
+ goto err_del_component;
+ }
hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
- if (!hdata)
- return -ENOMEM;
+ if (!hdata) {
+ ret = -ENOMEM;
+ goto err_del_component;
+ }
mutex_init(&hdata->hdmi_mutex);
platform_set_drvdata(pdev, &hdmi_display);
match = of_match_node(hdmi_match_types, dev->of_node);
- if (!match)
- return -ENODEV;
+ if (!match) {
+ ret = -ENODEV;
+ goto err_del_component;
+ }
drv_data = (struct hdmi_driver_data *)match->data;
hdata->type = drv_data->type;
+ hdata->phy_confs = drv_data->phy_confs;
+ hdata->phy_conf_count = drv_data->phy_conf_count;
hdata->hpd_gpio = pdata->hpd_gpio;
hdata->dev = dev;
@@ -2086,35 +2353,44 @@ static int hdmi_probe(struct platform_device *pdev)
ret = hdmi_resources_init(hdata);
if (ret) {
DRM_ERROR("hdmi_resources_init failed\n");
- return -EINVAL;
+ return ret;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hdata->regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(hdata->regs))
- return PTR_ERR(hdata->regs);
+ if (IS_ERR(hdata->regs)) {
+ ret = PTR_ERR(hdata->regs);
+ goto err_del_component;
+ }
ret = devm_gpio_request(dev, hdata->hpd_gpio, "HPD");
if (ret) {
DRM_ERROR("failed to request HPD gpio\n");
- return ret;
+ goto err_del_component;
}
+ ddc_node = hdmi_legacy_ddc_dt_binding(dev);
+ if (ddc_node)
+ goto out_get_ddc_adpt;
+
/* DDC i2c driver */
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
if (!ddc_node) {
DRM_ERROR("Failed to find ddc node in device tree\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_del_component;
}
+
+out_get_ddc_adpt:
hdata->ddc_adpt = of_find_i2c_adapter_by_node(ddc_node);
if (!hdata->ddc_adpt) {
DRM_ERROR("Failed to get ddc i2c adapter by node\n");
- return -ENODEV;
+ return -EPROBE_DEFER;
}
- /* Not support APB PHY yet. */
- if (drv_data->is_apb_phy)
- return -EPERM;
+ phy_node = hdmi_legacy_phy_dt_binding(dev);
+ if (phy_node)
+ goto out_get_phy_port;
/* hdmiphy i2c driver */
phy_node = of_parse_phandle(dev->of_node, "phy", 0);
@@ -2123,11 +2399,22 @@ static int hdmi_probe(struct platform_device *pdev)
ret = -ENODEV;
goto err_ddc;
}
- hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
- if (!hdata->hdmiphy_port) {
- DRM_ERROR("Failed to get hdmi phy i2c client from node\n");
- ret = -ENODEV;
- goto err_ddc;
+
+out_get_phy_port:
+ if (drv_data->is_apb_phy) {
+ hdata->regs_hdmiphy = of_iomap(phy_node, 0);
+ if (!hdata->regs_hdmiphy) {
+ DRM_ERROR("failed to ioremap hdmi phy\n");
+ ret = -ENOMEM;
+ goto err_ddc;
+ }
+ } else {
+ hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
+ if (!hdata->hdmiphy_port) {
+ DRM_ERROR("Failed to get hdmi phy i2c client\n");
+ ret = -EPROBE_DEFER;
+ goto err_ddc;
+ }
}
hdata->irq = gpio_to_irq(hdata->hpd_gpio);
@@ -2139,6 +2426,8 @@ static int hdmi_probe(struct platform_device *pdev)
hdata->hpd = gpio_get_value(hdata->hpd_gpio);
+ INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
+
ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
hdmi_irq_thread, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
@@ -2148,30 +2437,51 @@ static int hdmi_probe(struct platform_device *pdev)
goto err_hdmiphy;
}
- pm_runtime_enable(dev);
+ hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,syscon-phandle");
+ if (IS_ERR(hdata->pmureg)) {
+ DRM_ERROR("syscon regmap lookup failed.\n");
+ ret = -EPROBE_DEFER;
+ goto err_hdmiphy;
+ }
+ pm_runtime_enable(dev);
hdmi_display.ctx = hdata;
- exynos_drm_display_register(&hdmi_display);
- return 0;
+ ret = component_add(&pdev->dev, &hdmi_component_ops);
+ if (ret)
+ goto err_disable_pm_runtime;
+
+ return ret;
+
+err_disable_pm_runtime:
+ pm_runtime_disable(dev);
err_hdmiphy:
- put_device(&hdata->hdmiphy_port->dev);
+ if (hdata->hdmiphy_port)
+ put_device(&hdata->hdmiphy_port->dev);
err_ddc:
put_device(&hdata->ddc_adpt->dev);
+
+err_del_component:
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
+
return ret;
}
static int hdmi_remove(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
- struct exynos_drm_display *display = get_hdmi_display(dev);
- struct hdmi_context *hdata = display->ctx;
+ struct hdmi_context *hdata = hdmi_display.ctx;
+
+ cancel_delayed_work_sync(&hdata->hotplug_work);
put_device(&hdata->hdmiphy_port->dev);
put_device(&hdata->ddc_adpt->dev);
+
pm_runtime_disable(&pdev->dev);
+ component_del(&pdev->dev, &hdmi_component_ops);
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h
deleted file mode 100644
index 0ddf3957de15..000000000000
--- a/drivers/gpu/drm/exynos/exynos_hdmi.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Authors:
- * Inki Dae <inki.dae@samsung.com>
- * Seung-Woo Kim <sw0312.kim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef _EXYNOS_HDMI_H_
-#define _EXYNOS_HDMI_H_
-
-void hdmi_attach_ddc_client(struct i2c_client *ddc);
-void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
-
-extern struct i2c_driver hdmiphy_driver;
-extern struct i2c_driver ddc_driver;
-
-#endif
diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c
deleted file mode 100644
index 59abb1494ceb..000000000000
--- a/drivers/gpu/drm/exynos/exynos_hdmiphy.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- * Authors:
- * Seung-Woo Kim <sw0312.kim@samsung.com>
- * Inki Dae <inki.dae@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
-
-#include <drm/drmP.h>
-
-#include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/of.h>
-
-#include "exynos_drm_drv.h"
-#include "exynos_hdmi.h"
-
-
-static int hdmiphy_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- hdmi_attach_hdmiphy_client(client);
-
- dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
- "into i2c adapter successfully\n");
-
- return 0;
-}
-
-static int hdmiphy_remove(struct i2c_client *client)
-{
- dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
- "from i2c adapter successfully\n");
-
- return 0;
-}
-
-static struct of_device_id hdmiphy_match_types[] = {
- {
- .compatible = "samsung,exynos5-hdmiphy",
- }, {
- .compatible = "samsung,exynos4210-hdmiphy",
- }, {
- .compatible = "samsung,exynos4212-hdmiphy",
- }, {
- /* end node */
- }
-};
-
-struct i2c_driver hdmiphy_driver = {
- .driver = {
- .name = "exynos-hdmiphy",
- .owner = THIS_MODULE,
- .of_match_table = hdmiphy_match_types,
- },
- .probe = hdmiphy_probe,
- .remove = hdmiphy_remove,
- .command = NULL,
-};
-EXPORT_SYMBOL(hdmiphy_driver);
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index ce288818d2c0..4c5aed7e54c8 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -31,6 +31,7 @@
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
+#include <linux/component.h>
#include <drm/exynos_drm.h>
@@ -830,13 +831,15 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
}
static int mixer_initialize(struct exynos_drm_manager *mgr,
- struct drm_device *drm_dev, int pipe)
+ struct drm_device *drm_dev)
{
int ret;
struct mixer_context *mixer_ctx = mgr->ctx;
+ struct exynos_drm_private *priv;
+ priv = drm_dev->dev_private;
- mixer_ctx->drm_dev = drm_dev;
- mixer_ctx->pipe = pipe;
+ mgr->drm_dev = mixer_ctx->drm_dev = drm_dev;
+ mgr->pipe = mixer_ctx->pipe = priv->pipe++;
/* acquire resources: regs, irqs, clocks */
ret = mixer_resources_init(mixer_ctx);
@@ -1142,8 +1145,6 @@ int mixer_check_mode(struct drm_display_mode *mode)
}
static struct exynos_drm_manager_ops mixer_manager_ops = {
- .initialize = mixer_initialize,
- .remove = mixer_mgr_remove,
.dpms = mixer_dpms,
.enable_vblank = mixer_enable_vblank,
.disable_vblank = mixer_disable_vblank,
@@ -1200,11 +1201,13 @@ static struct of_device_id mixer_match_types[] = {
}
};
-static int mixer_probe(struct platform_device *pdev)
+static int mixer_bind(struct device *dev, struct device *manager, void *data)
{
- struct device *dev = &pdev->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm_dev = data;
struct mixer_context *ctx;
struct mixer_drv_data *drv;
+ int ret;
dev_info(dev, "probe start\n");
@@ -1233,19 +1236,61 @@ static int mixer_probe(struct platform_device *pdev)
atomic_set(&ctx->wait_vsync_event, 0);
mixer_manager.ctx = ctx;
+ ret = mixer_initialize(&mixer_manager, drm_dev);
+ if (ret)
+ return ret;
+
platform_set_drvdata(pdev, &mixer_manager);
- exynos_drm_manager_register(&mixer_manager);
+ ret = exynos_drm_crtc_create(&mixer_manager);
+ if (ret) {
+ mixer_mgr_remove(&mixer_manager);
+ return ret;
+ }
pm_runtime_enable(dev);
return 0;
}
-static int mixer_remove(struct platform_device *pdev)
+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 = {
+ .bind = mixer_bind,
+ .unbind = mixer_unbind,
+};
+
+static int mixer_probe(struct platform_device *pdev)
{
- dev_info(&pdev->dev, "remove successful\n");
+ int ret;
- pm_runtime_disable(&pdev->dev);
+ ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC,
+ mixer_manager.type);
+ if (ret)
+ return ret;
+
+ ret = component_add(&pdev->dev, &mixer_component_ops);
+ if (ret)
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
+
+ return ret;
+}
+
+static int mixer_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &mixer_component_ops);
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h
index ef1b3eb3ba6e..3f35ac6d8a47 100644
--- a/drivers/gpu/drm/exynos/regs-hdmi.h
+++ b/drivers/gpu/drm/exynos/regs-hdmi.h
@@ -578,4 +578,20 @@
#define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074)
#define HDMI_TG_3D HDMI_TG_BASE(0x00F0)
+/* HDMI PHY Registers Offsets*/
+#define HDMIPHY_POWER (0x74 >> 2)
+#define HDMIPHY_MODE_SET_DONE (0x7c >> 2)
+
+/* HDMI PHY Values */
+#define HDMI_PHY_POWER_ON 0x80
+#define HDMI_PHY_POWER_OFF 0xff
+
+/* HDMI PHY Values */
+#define HDMI_PHY_DISABLE_MODE_SET 0x80
+#define HDMI_PHY_ENABLE_MODE_SET 0x00
+
+/* PMU Registers for PHY */
+#define PMU_HDMI_PHY_CONTROL 0x700
+#define PMU_HDMI_PHY_ENABLE_BIT BIT(0)
+
#endif /* SAMSUNG_REGS_HDMI_H */
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
index 489ffd2c66e5..87885d8c06e8 100644
--- a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
@@ -148,7 +148,7 @@ static int handle_dsi_error(struct mdfld_dsi_pkg_sender *sender, u32 mask)
break;
case BIT(14):
/*wait for all fifo empty*/
- /*wait_for_all_fifos_empty(sender)*/;
+ /*wait_for_all_fifos_empty(sender)*/
break;
case BIT(15):
dev_dbg(sender->dev->dev, "No Action required\n");
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index b686e56646eb..6e8fe9ec02b5 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -112,11 +112,9 @@ static void psb_driver_lastclose(struct drm_device *dev)
struct drm_psb_private *dev_priv = dev->dev_private;
struct psb_fbdev *fbdev = dev_priv->fbdev;
- drm_modeset_lock_all(dev);
- ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper);
+ ret = drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->psb_fb_helper);
if (ret)
DRM_DEBUG("failed to restore crtc mode\n");
- drm_modeset_unlock_all(dev);
return;
}
@@ -354,7 +352,7 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags)
PSB_WVDC32(0xFFFFFFFF, PSB_INT_MASK_R);
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
- drm_irq_install(dev);
+ drm_irq_install(dev, dev->pdev->irq);
dev->vblank_disable_allowed = true;
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
@@ -479,7 +477,7 @@ static struct drm_driver driver = {
.lastclose = psb_driver_lastclose,
.preclose = psb_driver_preclose,
- .num_ioctls = DRM_ARRAY_SIZE(psb_ioctls),
+ .num_ioctls = ARRAY_SIZE(psb_ioctls),
.device_is_agp = psb_driver_device_is_agp,
.irq_preinstall = psb_irq_preinstall,
.irq_postinstall = psb_irq_postinstall,
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 48af5cac1902..240c331405b9 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -568,11 +568,11 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes)
{
- uint8_t sum = 0;
+ int sum = 0;
while (bytes--)
- sum += *buf++;
- return (255 - sum) + 1;
+ sum -= *buf++;
+ return sum;
}
#define HB(x) (x)
diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c
index aeace37415aa..e88bac1d781f 100644
--- a/drivers/gpu/drm/i810/i810_dma.c
+++ b/drivers/gpu/drm/i810/i810_dma.c
@@ -1251,7 +1251,7 @@ const struct drm_ioctl_desc i810_ioctls[] = {
DRM_IOCTL_DEF_DRV(I810_FLIP, i810_flip_bufs, DRM_AUTH|DRM_UNLOCKED),
};
-int i810_max_ioctl = DRM_ARRAY_SIZE(i810_ioctls);
+int i810_max_ioctl = ARRAY_SIZE(i810_ioctls);
/**
* Determine if the device really is AGP or not.
diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig
index bea2d67196fb..437e1824d0bf 100644
--- a/drivers/gpu/drm/i915/Kconfig
+++ b/drivers/gpu/drm/i915/Kconfig
@@ -5,6 +5,7 @@ config DRM_I915
depends on (AGP || AGP=n)
select INTEL_GTT
select AGP_INTEL if AGP
+ select INTERVAL_TREE
# we need shmfs for the swappable backing store, and in particular
# the shmem_readpage() which depends upon tmpfs
select SHMEM
@@ -71,7 +72,7 @@ config DRM_I915_PRELIMINARY_HW_SUPPORT
config DRM_I915_UMS
bool "Enable userspace modesetting on Intel hardware (DEPRECATED)"
- depends on DRM_I915
+ depends on DRM_I915 && BROKEN
default n
help
Choose this option if you still need userspace modesetting.
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index b1445b73465b..cad1683d8bb5 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -18,6 +18,7 @@ i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o
# GEM code
i915-y += i915_cmd_parser.o \
i915_gem_context.o \
+ i915_gem_render_state.o \
i915_gem_debug.o \
i915_gem_dmabuf.o \
i915_gem_evict.o \
@@ -26,12 +27,18 @@ i915-y += i915_cmd_parser.o \
i915_gem.o \
i915_gem_stolen.o \
i915_gem_tiling.o \
+ i915_gem_userptr.o \
i915_gpu_error.o \
i915_irq.o \
i915_trace_points.o \
intel_ringbuffer.o \
intel_uncore.o
+# autogenerated null render state
+i915-y += intel_renderstate_gen6.o \
+ intel_renderstate_gen7.o \
+ intel_renderstate_gen8.o
+
# modesetting core code
i915-y += intel_bios.o \
intel_display.o \
@@ -55,6 +62,7 @@ i915-y += dvo_ch7017.o \
intel_dsi_cmd.o \
intel_dsi.o \
intel_dsi_pll.o \
+ intel_dsi_panel_vbt.o \
intel_dvo.o \
intel_hdmi.o \
intel_i2c.o \
diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c
index a0f5bdd69491..80449f475960 100644
--- a/drivers/gpu/drm/i915/dvo_ch7xxx.c
+++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c
@@ -160,7 +160,7 @@ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
- };
+ }
if (!ch7xxx->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c
index 0f1865d7d4d8..0f2587ff347c 100644
--- a/drivers/gpu/drm/i915/dvo_ivch.c
+++ b/drivers/gpu/drm/i915/dvo_ivch.c
@@ -195,7 +195,7 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
if (i2c_transfer(adapter, msgs, 3) == 3) {
*data = (in_buf[1] << 8) | in_buf[0];
return true;
- };
+ }
if (!priv->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from "
diff --git a/drivers/gpu/drm/i915/dvo_ns2501.c b/drivers/gpu/drm/i915/dvo_ns2501.c
index 8155ded79079..74f2af7c2d3e 100644
--- a/drivers/gpu/drm/i915/dvo_ns2501.c
+++ b/drivers/gpu/drm/i915/dvo_ns2501.c
@@ -121,7 +121,7 @@ static bool ns2501_readb(struct intel_dvo_device *dvo, int addr, uint8_t * ch)
if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
- };
+ }
if (!ns->quiet) {
DRM_DEBUG_KMS
@@ -233,9 +233,8 @@ static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo,
struct drm_display_mode *mode)
{
DRM_DEBUG_KMS
- ("%s: is mode valid (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d)\n",
- __FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay,
- mode->vtotal);
+ ("is mode valid (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d)\n",
+ mode->hdisplay, mode->htotal, mode->vdisplay, mode->vtotal);
/*
* Currently, these are all the modes I have data from.
@@ -261,9 +260,8 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo,
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
DRM_DEBUG_KMS
- ("%s: set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n",
- __FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay,
- mode->vtotal);
+ ("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???
@@ -277,8 +275,7 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo,
if (mode->hdisplay == 800 && mode->vdisplay == 600) {
/* mode 277 */
ns->reg_8_shadow &= ~NS2501_8_BPAS;
- DRM_DEBUG_KMS("%s: switching to 800x600\n",
- __FUNCTION__);
+ DRM_DEBUG_KMS("switching to 800x600\n");
/*
* No, I do not know where this data comes from.
@@ -341,8 +338,7 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo,
} else if (mode->hdisplay == 640 && mode->vdisplay == 480) {
/* mode 274 */
- DRM_DEBUG_KMS("%s: switching to 640x480\n",
- __FUNCTION__);
+ 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
@@ -406,8 +402,7 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo,
} else if (mode->hdisplay == 1024 && mode->vdisplay == 768) {
/* mode 280 */
- DRM_DEBUG_KMS("%s: switching to 1024x768\n",
- __FUNCTION__);
+ 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
@@ -458,8 +453,7 @@ static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable)
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
unsigned char ch;
- DRM_DEBUG_KMS("%s: Trying set the dpms of the DVO to %i\n",
- __FUNCTION__, enable);
+ DRM_DEBUG_KMS("Trying set the dpms of the DVO to %i\n", enable);
ch = ns->reg_8_shadow;
diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c
index 7b3e9e936200..fa0114967076 100644
--- a/drivers/gpu/drm/i915/dvo_sil164.c
+++ b/drivers/gpu/drm/i915/dvo_sil164.c
@@ -93,7 +93,7 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
- };
+ }
if (!sil->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c
index 12ea4b164692..7853719a0e81 100644
--- a/drivers/gpu/drm/i915/dvo_tfp410.c
+++ b/drivers/gpu/drm/i915/dvo_tfp410.c
@@ -118,7 +118,7 @@ static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
- };
+ }
if (!tfp->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c
index 4cf6d020d513..9d7954366bd2 100644
--- a/drivers/gpu/drm/i915/i915_cmd_parser.c
+++ b/drivers/gpu/drm/i915/i915_cmd_parser.c
@@ -28,7 +28,7 @@
#include "i915_drv.h"
/**
- * DOC: i915 batch buffer command parser
+ * DOC: batch buffer command parser
*
* Motivation:
* Certain OpenGL features (e.g. transform feedback, performance monitoring)
@@ -86,6 +86,367 @@
* general bitmasking mechanism.
*/
+#define STD_MI_OPCODE_MASK 0xFF800000
+#define STD_3D_OPCODE_MASK 0xFFFF0000
+#define STD_2D_OPCODE_MASK 0xFFC00000
+#define STD_MFX_OPCODE_MASK 0xFFFF0000
+
+#define CMD(op, opm, f, lm, fl, ...) \
+ { \
+ .flags = (fl) | ((f) ? CMD_DESC_FIXED : 0), \
+ .cmd = { (op), (opm) }, \
+ .length = { (lm) }, \
+ __VA_ARGS__ \
+ }
+
+/* Convenience macros to compress the tables */
+#define SMI STD_MI_OPCODE_MASK
+#define S3D STD_3D_OPCODE_MASK
+#define S2D STD_2D_OPCODE_MASK
+#define SMFX STD_MFX_OPCODE_MASK
+#define F true
+#define S CMD_DESC_SKIP
+#define R CMD_DESC_REJECT
+#define W CMD_DESC_REGISTER
+#define B CMD_DESC_BITMASK
+#define M CMD_DESC_MASTER
+
+/* Command Mask Fixed Len Action
+ ---------------------------------------------------------- */
+static const struct drm_i915_cmd_descriptor common_cmds[] = {
+ CMD( MI_NOOP, SMI, F, 1, S ),
+ CMD( MI_USER_INTERRUPT, SMI, F, 1, R ),
+ CMD( MI_WAIT_FOR_EVENT, SMI, F, 1, M ),
+ CMD( MI_ARB_CHECK, SMI, F, 1, S ),
+ CMD( MI_REPORT_HEAD, SMI, F, 1, S ),
+ CMD( MI_SUSPEND_FLUSH, SMI, F, 1, S ),
+ CMD( MI_SEMAPHORE_MBOX, SMI, !F, 0xFF, R ),
+ CMD( MI_STORE_DWORD_INDEX, SMI, !F, 0xFF, R ),
+ CMD( MI_LOAD_REGISTER_IMM(1), SMI, !F, 0xFF, W,
+ .reg = { .offset = 1, .mask = 0x007FFFFC } ),
+ CMD( MI_STORE_REGISTER_MEM(1), SMI, !F, 0xFF, W | B,
+ .reg = { .offset = 1, .mask = 0x007FFFFC },
+ .bits = {{
+ .offset = 0,
+ .mask = MI_GLOBAL_GTT,
+ .expected = 0,
+ }}, ),
+ CMD( MI_LOAD_REGISTER_MEM, SMI, !F, 0xFF, W | B,
+ .reg = { .offset = 1, .mask = 0x007FFFFC },
+ .bits = {{
+ .offset = 0,
+ .mask = MI_GLOBAL_GTT,
+ .expected = 0,
+ }}, ),
+ CMD( MI_BATCH_BUFFER_START, SMI, !F, 0xFF, S ),
+};
+
+static const struct drm_i915_cmd_descriptor render_cmds[] = {
+ CMD( MI_FLUSH, SMI, F, 1, S ),
+ CMD( MI_ARB_ON_OFF, SMI, F, 1, R ),
+ CMD( MI_PREDICATE, SMI, F, 1, S ),
+ CMD( MI_TOPOLOGY_FILTER, SMI, F, 1, S ),
+ CMD( MI_DISPLAY_FLIP, SMI, !F, 0xFF, R ),
+ CMD( MI_SET_CONTEXT, SMI, !F, 0xFF, R ),
+ CMD( MI_URB_CLEAR, SMI, !F, 0xFF, S ),
+ CMD( MI_STORE_DWORD_IMM, SMI, !F, 0x3F, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_GLOBAL_GTT,
+ .expected = 0,
+ }}, ),
+ CMD( MI_UPDATE_GTT, SMI, !F, 0xFF, R ),
+ CMD( MI_CLFLUSH, SMI, !F, 0x3FF, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_GLOBAL_GTT,
+ .expected = 0,
+ }}, ),
+ CMD( MI_REPORT_PERF_COUNT, SMI, !F, 0x3F, B,
+ .bits = {{
+ .offset = 1,
+ .mask = MI_REPORT_PERF_COUNT_GGTT,
+ .expected = 0,
+ }}, ),
+ CMD( MI_CONDITIONAL_BATCH_BUFFER_END, SMI, !F, 0xFF, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_GLOBAL_GTT,
+ .expected = 0,
+ }}, ),
+ CMD( GFX_OP_3DSTATE_VF_STATISTICS, S3D, F, 1, S ),
+ CMD( PIPELINE_SELECT, S3D, F, 1, S ),
+ CMD( MEDIA_VFE_STATE, S3D, !F, 0xFFFF, B,
+ .bits = {{
+ .offset = 2,
+ .mask = MEDIA_VFE_STATE_MMIO_ACCESS_MASK,
+ .expected = 0,
+ }}, ),
+ CMD( GPGPU_OBJECT, S3D, !F, 0xFF, S ),
+ CMD( GPGPU_WALKER, S3D, !F, 0xFF, S ),
+ CMD( GFX_OP_3DSTATE_SO_DECL_LIST, S3D, !F, 0x1FF, S ),
+ CMD( GFX_OP_PIPE_CONTROL(5), S3D, !F, 0xFF, B,
+ .bits = {{
+ .offset = 1,
+ .mask = (PIPE_CONTROL_MMIO_WRITE | PIPE_CONTROL_NOTIFY),
+ .expected = 0,
+ },
+ {
+ .offset = 1,
+ .mask = (PIPE_CONTROL_GLOBAL_GTT_IVB |
+ PIPE_CONTROL_STORE_DATA_INDEX),
+ .expected = 0,
+ .condition_offset = 1,
+ .condition_mask = PIPE_CONTROL_POST_SYNC_OP_MASK,
+ }}, ),
+};
+
+static const struct drm_i915_cmd_descriptor hsw_render_cmds[] = {
+ CMD( MI_SET_PREDICATE, SMI, F, 1, S ),
+ CMD( MI_RS_CONTROL, SMI, F, 1, S ),
+ CMD( MI_URB_ATOMIC_ALLOC, SMI, F, 1, S ),
+ CMD( MI_RS_CONTEXT, SMI, F, 1, S ),
+ CMD( MI_LOAD_SCAN_LINES_INCL, SMI, !F, 0x3F, M ),
+ CMD( MI_LOAD_SCAN_LINES_EXCL, SMI, !F, 0x3F, R ),
+ CMD( MI_LOAD_REGISTER_REG, SMI, !F, 0xFF, R ),
+ CMD( MI_RS_STORE_DATA_IMM, SMI, !F, 0xFF, S ),
+ CMD( MI_LOAD_URB_MEM, SMI, !F, 0xFF, S ),
+ CMD( MI_STORE_URB_MEM, SMI, !F, 0xFF, S ),
+ CMD( GFX_OP_3DSTATE_DX9_CONSTANTF_VS, S3D, !F, 0x7FF, S ),
+ CMD( GFX_OP_3DSTATE_DX9_CONSTANTF_PS, S3D, !F, 0x7FF, S ),
+
+ CMD( GFX_OP_3DSTATE_BINDING_TABLE_EDIT_VS, S3D, !F, 0x1FF, S ),
+ CMD( GFX_OP_3DSTATE_BINDING_TABLE_EDIT_GS, S3D, !F, 0x1FF, S ),
+ CMD( GFX_OP_3DSTATE_BINDING_TABLE_EDIT_HS, S3D, !F, 0x1FF, S ),
+ CMD( GFX_OP_3DSTATE_BINDING_TABLE_EDIT_DS, S3D, !F, 0x1FF, S ),
+ CMD( GFX_OP_3DSTATE_BINDING_TABLE_EDIT_PS, S3D, !F, 0x1FF, S ),
+};
+
+static const struct drm_i915_cmd_descriptor video_cmds[] = {
+ CMD( MI_ARB_ON_OFF, SMI, F, 1, R ),
+ CMD( MI_STORE_DWORD_IMM, SMI, !F, 0xFF, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_GLOBAL_GTT,
+ .expected = 0,
+ }}, ),
+ CMD( MI_UPDATE_GTT, SMI, !F, 0x3F, R ),
+ CMD( MI_FLUSH_DW, SMI, !F, 0x3F, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_FLUSH_DW_NOTIFY,
+ .expected = 0,
+ },
+ {
+ .offset = 1,
+ .mask = MI_FLUSH_DW_USE_GTT,
+ .expected = 0,
+ .condition_offset = 0,
+ .condition_mask = MI_FLUSH_DW_OP_MASK,
+ },
+ {
+ .offset = 0,
+ .mask = MI_FLUSH_DW_STORE_INDEX,
+ .expected = 0,
+ .condition_offset = 0,
+ .condition_mask = MI_FLUSH_DW_OP_MASK,
+ }}, ),
+ CMD( MI_CONDITIONAL_BATCH_BUFFER_END, SMI, !F, 0xFF, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_GLOBAL_GTT,
+ .expected = 0,
+ }}, ),
+ /*
+ * MFX_WAIT doesn't fit the way we handle length for most commands.
+ * It has a length field but it uses a non-standard length bias.
+ * It is always 1 dword though, so just treat it as fixed length.
+ */
+ CMD( MFX_WAIT, SMFX, F, 1, S ),
+};
+
+static const struct drm_i915_cmd_descriptor vecs_cmds[] = {
+ CMD( MI_ARB_ON_OFF, SMI, F, 1, R ),
+ CMD( MI_STORE_DWORD_IMM, SMI, !F, 0xFF, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_GLOBAL_GTT,
+ .expected = 0,
+ }}, ),
+ CMD( MI_UPDATE_GTT, SMI, !F, 0x3F, R ),
+ CMD( MI_FLUSH_DW, SMI, !F, 0x3F, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_FLUSH_DW_NOTIFY,
+ .expected = 0,
+ },
+ {
+ .offset = 1,
+ .mask = MI_FLUSH_DW_USE_GTT,
+ .expected = 0,
+ .condition_offset = 0,
+ .condition_mask = MI_FLUSH_DW_OP_MASK,
+ },
+ {
+ .offset = 0,
+ .mask = MI_FLUSH_DW_STORE_INDEX,
+ .expected = 0,
+ .condition_offset = 0,
+ .condition_mask = MI_FLUSH_DW_OP_MASK,
+ }}, ),
+ CMD( MI_CONDITIONAL_BATCH_BUFFER_END, SMI, !F, 0xFF, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_GLOBAL_GTT,
+ .expected = 0,
+ }}, ),
+};
+
+static const struct drm_i915_cmd_descriptor blt_cmds[] = {
+ CMD( MI_DISPLAY_FLIP, SMI, !F, 0xFF, R ),
+ CMD( MI_STORE_DWORD_IMM, SMI, !F, 0x3FF, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_GLOBAL_GTT,
+ .expected = 0,
+ }}, ),
+ CMD( MI_UPDATE_GTT, SMI, !F, 0x3F, R ),
+ CMD( MI_FLUSH_DW, SMI, !F, 0x3F, B,
+ .bits = {{
+ .offset = 0,
+ .mask = MI_FLUSH_DW_NOTIFY,
+ .expected = 0,
+ },
+ {
+ .offset = 1,
+ .mask = MI_FLUSH_DW_USE_GTT,
+ .expected = 0,
+ .condition_offset = 0,
+ .condition_mask = MI_FLUSH_DW_OP_MASK,
+ },
+ {
+ .offset = 0,
+ .mask = MI_FLUSH_DW_STORE_INDEX,
+ .expected = 0,
+ .condition_offset = 0,
+ .condition_mask = MI_FLUSH_DW_OP_MASK,
+ }}, ),
+ CMD( COLOR_BLT, S2D, !F, 0x3F, S ),
+ CMD( SRC_COPY_BLT, S2D, !F, 0x3F, S ),
+};
+
+static const struct drm_i915_cmd_descriptor hsw_blt_cmds[] = {
+ CMD( MI_LOAD_SCAN_LINES_INCL, SMI, !F, 0x3F, M ),
+ CMD( MI_LOAD_SCAN_LINES_EXCL, SMI, !F, 0x3F, R ),
+};
+
+#undef CMD
+#undef SMI
+#undef S3D
+#undef S2D
+#undef SMFX
+#undef F
+#undef S
+#undef R
+#undef W
+#undef B
+#undef M
+
+static const struct drm_i915_cmd_table gen7_render_cmds[] = {
+ { common_cmds, ARRAY_SIZE(common_cmds) },
+ { render_cmds, ARRAY_SIZE(render_cmds) },
+};
+
+static const struct drm_i915_cmd_table hsw_render_ring_cmds[] = {
+ { common_cmds, ARRAY_SIZE(common_cmds) },
+ { render_cmds, ARRAY_SIZE(render_cmds) },
+ { hsw_render_cmds, ARRAY_SIZE(hsw_render_cmds) },
+};
+
+static const struct drm_i915_cmd_table gen7_video_cmds[] = {
+ { common_cmds, ARRAY_SIZE(common_cmds) },
+ { video_cmds, ARRAY_SIZE(video_cmds) },
+};
+
+static const struct drm_i915_cmd_table hsw_vebox_cmds[] = {
+ { common_cmds, ARRAY_SIZE(common_cmds) },
+ { vecs_cmds, ARRAY_SIZE(vecs_cmds) },
+};
+
+static const struct drm_i915_cmd_table gen7_blt_cmds[] = {
+ { common_cmds, ARRAY_SIZE(common_cmds) },
+ { blt_cmds, ARRAY_SIZE(blt_cmds) },
+};
+
+static const struct drm_i915_cmd_table hsw_blt_ring_cmds[] = {
+ { common_cmds, ARRAY_SIZE(common_cmds) },
+ { blt_cmds, ARRAY_SIZE(blt_cmds) },
+ { hsw_blt_cmds, ARRAY_SIZE(hsw_blt_cmds) },
+};
+
+/*
+ * Register whitelists, sorted by increasing register offset.
+ *
+ * Some registers that userspace accesses are 64 bits. The register
+ * access commands only allow 32-bit accesses. Hence, we have to include
+ * entries for both halves of the 64-bit registers.
+ */
+
+/* Convenience macro for adding 64-bit registers */
+#define REG64(addr) (addr), (addr + sizeof(u32))
+
+static const u32 gen7_render_regs[] = {
+ REG64(HS_INVOCATION_COUNT),
+ REG64(DS_INVOCATION_COUNT),
+ REG64(IA_VERTICES_COUNT),
+ REG64(IA_PRIMITIVES_COUNT),
+ REG64(VS_INVOCATION_COUNT),
+ REG64(GS_INVOCATION_COUNT),
+ REG64(GS_PRIMITIVES_COUNT),
+ REG64(CL_INVOCATION_COUNT),
+ REG64(CL_PRIMITIVES_COUNT),
+ REG64(PS_INVOCATION_COUNT),
+ REG64(PS_DEPTH_COUNT),
+ OACONTROL, /* Only allowed for LRI and SRM. See below. */
+ GEN7_3DPRIM_END_OFFSET,
+ GEN7_3DPRIM_START_VERTEX,
+ GEN7_3DPRIM_VERTEX_COUNT,
+ GEN7_3DPRIM_INSTANCE_COUNT,
+ GEN7_3DPRIM_START_INSTANCE,
+ GEN7_3DPRIM_BASE_VERTEX,
+ REG64(GEN7_SO_NUM_PRIMS_WRITTEN(0)),
+ REG64(GEN7_SO_NUM_PRIMS_WRITTEN(1)),
+ REG64(GEN7_SO_NUM_PRIMS_WRITTEN(2)),
+ REG64(GEN7_SO_NUM_PRIMS_WRITTEN(3)),
+ REG64(GEN7_SO_PRIM_STORAGE_NEEDED(0)),
+ REG64(GEN7_SO_PRIM_STORAGE_NEEDED(1)),
+ REG64(GEN7_SO_PRIM_STORAGE_NEEDED(2)),
+ REG64(GEN7_SO_PRIM_STORAGE_NEEDED(3)),
+ GEN7_SO_WRITE_OFFSET(0),
+ GEN7_SO_WRITE_OFFSET(1),
+ GEN7_SO_WRITE_OFFSET(2),
+ GEN7_SO_WRITE_OFFSET(3),
+};
+
+static const u32 gen7_blt_regs[] = {
+ BCS_SWCTRL,
+};
+
+static const u32 ivb_master_regs[] = {
+ FORCEWAKE_MT,
+ DERRMR,
+ GEN7_PIPE_DE_LOAD_SL(PIPE_A),
+ GEN7_PIPE_DE_LOAD_SL(PIPE_B),
+ GEN7_PIPE_DE_LOAD_SL(PIPE_C),
+};
+
+static const u32 hsw_master_regs[] = {
+ FORCEWAKE_MT,
+ DERRMR,
+};
+
+#undef REG64
+
static u32 gen7_render_get_cmd_length_mask(u32 cmd_header)
{
u32 client = (cmd_header & INSTR_CLIENT_MASK) >> INSTR_CLIENT_SHIFT;
@@ -137,15 +498,18 @@ static u32 gen7_blt_get_cmd_length_mask(u32 cmd_header)
return 0;
}
-static void validate_cmds_sorted(struct intel_ring_buffer *ring)
+static bool validate_cmds_sorted(struct intel_engine_cs *ring,
+ const struct drm_i915_cmd_table *cmd_tables,
+ int cmd_table_count)
{
int i;
+ bool ret = true;
- if (!ring->cmd_tables || ring->cmd_table_count == 0)
- return;
+ if (!cmd_tables || cmd_table_count == 0)
+ return true;
- for (i = 0; i < ring->cmd_table_count; i++) {
- const struct drm_i915_cmd_table *table = &ring->cmd_tables[i];
+ for (i = 0; i < cmd_table_count; i++) {
+ const struct drm_i915_cmd_table *table = &cmd_tables[i];
u32 previous = 0;
int j;
@@ -154,35 +518,107 @@ static void validate_cmds_sorted(struct intel_ring_buffer *ring)
&table->table[i];
u32 curr = desc->cmd.value & desc->cmd.mask;
- if (curr < previous)
+ if (curr < previous) {
DRM_ERROR("CMD: table not sorted ring=%d table=%d entry=%d cmd=0x%08X prev=0x%08X\n",
ring->id, i, j, curr, previous);
+ ret = false;
+ }
previous = curr;
}
}
+
+ return ret;
}
-static void check_sorted(int ring_id, const u32 *reg_table, int reg_count)
+static bool check_sorted(int ring_id, const u32 *reg_table, int reg_count)
{
int i;
u32 previous = 0;
+ bool ret = true;
for (i = 0; i < reg_count; i++) {
u32 curr = reg_table[i];
- if (curr < previous)
+ if (curr < previous) {
DRM_ERROR("CMD: table not sorted ring=%d entry=%d reg=0x%08X prev=0x%08X\n",
ring_id, i, curr, previous);
+ ret = false;
+ }
previous = curr;
}
+
+ return ret;
+}
+
+static bool validate_regs_sorted(struct intel_engine_cs *ring)
+{
+ return check_sorted(ring->id, ring->reg_table, ring->reg_count) &&
+ check_sorted(ring->id, ring->master_reg_table,
+ ring->master_reg_count);
+}
+
+struct cmd_node {
+ const struct drm_i915_cmd_descriptor *desc;
+ struct hlist_node node;
+};
+
+/*
+ * Different command ranges have different numbers of bits for the opcode. For
+ * example, MI commands use bits 31:23 while 3D commands use bits 31:16. The
+ * problem is that, for example, MI commands use bits 22:16 for other fields
+ * such as GGTT vs PPGTT bits. If we include those bits in the mask then when
+ * we mask a command from a batch it could hash to the wrong bucket due to
+ * non-opcode bits being set. But if we don't include those bits, some 3D
+ * commands may hash to the same bucket due to not including opcode bits that
+ * make the command unique. For now, we will risk hashing to the same bucket.
+ *
+ * If we attempt to generate a perfect hash, we should be able to look at bits
+ * 31:29 of a command from a batch buffer and use the full mask for that
+ * client. The existing INSTR_CLIENT_MASK/SHIFT defines can be used for this.
+ */
+#define CMD_HASH_MASK STD_MI_OPCODE_MASK
+
+static int init_hash_table(struct intel_engine_cs *ring,
+ const struct drm_i915_cmd_table *cmd_tables,
+ int cmd_table_count)
+{
+ int i, j;
+
+ hash_init(ring->cmd_hash);
+
+ for (i = 0; i < cmd_table_count; i++) {
+ const struct drm_i915_cmd_table *table = &cmd_tables[i];
+
+ for (j = 0; j < table->count; j++) {
+ const struct drm_i915_cmd_descriptor *desc =
+ &table->table[j];
+ struct cmd_node *desc_node =
+ kmalloc(sizeof(*desc_node), GFP_KERNEL);
+
+ if (!desc_node)
+ return -ENOMEM;
+
+ desc_node->desc = desc;
+ hash_add(ring->cmd_hash, &desc_node->node,
+ desc->cmd.value & CMD_HASH_MASK);
+ }
+ }
+
+ return 0;
}
-static void validate_regs_sorted(struct intel_ring_buffer *ring)
+static void fini_hash_table(struct intel_engine_cs *ring)
{
- check_sorted(ring->id, ring->reg_table, ring->reg_count);
- check_sorted(ring->id, ring->master_reg_table, ring->master_reg_count);
+ struct hlist_node *tmp;
+ struct cmd_node *desc_node;
+ int i;
+
+ hash_for_each_safe(ring->cmd_hash, i, tmp, desc_node, node) {
+ hash_del(&desc_node->node);
+ kfree(desc_node);
+ }
}
/**
@@ -190,25 +626,74 @@ static void validate_regs_sorted(struct intel_ring_buffer *ring)
* @ring: the ringbuffer to initialize
*
* Optionally initializes fields related to batch buffer command parsing in the
- * struct intel_ring_buffer based on whether the platform requires software
+ * struct intel_engine_cs based on whether the platform requires software
* command parsing.
+ *
+ * Return: non-zero if initialization fails
*/
-void i915_cmd_parser_init_ring(struct intel_ring_buffer *ring)
+int i915_cmd_parser_init_ring(struct intel_engine_cs *ring)
{
+ const struct drm_i915_cmd_table *cmd_tables;
+ int cmd_table_count;
+ int ret;
+
if (!IS_GEN7(ring->dev))
- return;
+ return 0;
switch (ring->id) {
case RCS:
+ if (IS_HASWELL(ring->dev)) {
+ cmd_tables = hsw_render_ring_cmds;
+ cmd_table_count =
+ ARRAY_SIZE(hsw_render_ring_cmds);
+ } else {
+ cmd_tables = gen7_render_cmds;
+ cmd_table_count = ARRAY_SIZE(gen7_render_cmds);
+ }
+
+ ring->reg_table = gen7_render_regs;
+ ring->reg_count = ARRAY_SIZE(gen7_render_regs);
+
+ if (IS_HASWELL(ring->dev)) {
+ ring->master_reg_table = hsw_master_regs;
+ ring->master_reg_count = ARRAY_SIZE(hsw_master_regs);
+ } else {
+ ring->master_reg_table = ivb_master_regs;
+ ring->master_reg_count = ARRAY_SIZE(ivb_master_regs);
+ }
+
ring->get_cmd_length_mask = gen7_render_get_cmd_length_mask;
break;
case VCS:
+ cmd_tables = gen7_video_cmds;
+ cmd_table_count = ARRAY_SIZE(gen7_video_cmds);
ring->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
break;
case BCS:
+ if (IS_HASWELL(ring->dev)) {
+ cmd_tables = hsw_blt_ring_cmds;
+ cmd_table_count = ARRAY_SIZE(hsw_blt_ring_cmds);
+ } else {
+ cmd_tables = gen7_blt_cmds;
+ cmd_table_count = ARRAY_SIZE(gen7_blt_cmds);
+ }
+
+ ring->reg_table = gen7_blt_regs;
+ ring->reg_count = ARRAY_SIZE(gen7_blt_regs);
+
+ if (IS_HASWELL(ring->dev)) {
+ ring->master_reg_table = hsw_master_regs;
+ ring->master_reg_count = ARRAY_SIZE(hsw_master_regs);
+ } else {
+ ring->master_reg_table = ivb_master_regs;
+ ring->master_reg_count = ARRAY_SIZE(ivb_master_regs);
+ }
+
ring->get_cmd_length_mask = gen7_blt_get_cmd_length_mask;
break;
case VECS:
+ cmd_tables = hsw_vebox_cmds;
+ cmd_table_count = ARRAY_SIZE(hsw_vebox_cmds);
/* VECS can use the same length_mask function as VCS */
ring->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
break;
@@ -218,18 +703,45 @@ void i915_cmd_parser_init_ring(struct intel_ring_buffer *ring)
BUG();
}
- validate_cmds_sorted(ring);
- validate_regs_sorted(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;
+ }
+
+ ring->needs_cmd_parser = true;
+
+ return 0;
+}
+
+/**
+ * i915_cmd_parser_fini_ring() - clean up cmd parser related fields
+ * @ring: the ringbuffer to clean up
+ *
+ * Releases any resources related to command parsing that may have been
+ * initialized for the specified ring.
+ */
+void i915_cmd_parser_fini_ring(struct intel_engine_cs *ring)
+{
+ if (!ring->needs_cmd_parser)
+ return;
+
+ fini_hash_table(ring);
}
static const struct drm_i915_cmd_descriptor*
-find_cmd_in_table(const struct drm_i915_cmd_table *table,
+find_cmd_in_table(struct intel_engine_cs *ring,
u32 cmd_header)
{
- int i;
+ struct cmd_node *desc_node;
- for (i = 0; i < table->count; i++) {
- const struct drm_i915_cmd_descriptor *desc = &table->table[i];
+ hash_for_each_possible(ring->cmd_hash, desc_node, node,
+ cmd_header & CMD_HASH_MASK) {
+ const struct drm_i915_cmd_descriptor *desc = desc_node->desc;
u32 masked_cmd = desc->cmd.mask & cmd_header;
u32 masked_value = desc->cmd.value & desc->cmd.mask;
@@ -249,20 +761,16 @@ find_cmd_in_table(const struct drm_i915_cmd_table *table,
* ring's default length encoding and returns default_desc.
*/
static const struct drm_i915_cmd_descriptor*
-find_cmd(struct intel_ring_buffer *ring,
+find_cmd(struct intel_engine_cs *ring,
u32 cmd_header,
struct drm_i915_cmd_descriptor *default_desc)
{
+ const struct drm_i915_cmd_descriptor *desc;
u32 mask;
- int i;
- for (i = 0; i < ring->cmd_table_count; i++) {
- const struct drm_i915_cmd_descriptor *desc;
-
- desc = find_cmd_in_table(&ring->cmd_tables[i], cmd_header);
- if (desc)
- return desc;
- }
+ desc = find_cmd_in_table(ring, cmd_header);
+ if (desc)
+ return desc;
mask = ring->get_cmd_length_mask(cmd_header);
if (!mask)
@@ -329,15 +837,112 @@ finish:
*
* Return: true if the ring requires software command parsing
*/
-bool i915_needs_cmd_parser(struct intel_ring_buffer *ring)
+bool i915_needs_cmd_parser(struct intel_engine_cs *ring)
{
- /* No command tables indicates a platform without parsing */
- if (!ring->cmd_tables)
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+
+ if (!ring->needs_cmd_parser)
+ return false;
+
+ /*
+ * XXX: VLV is Gen7 and therefore has cmd_tables, but has PPGTT
+ * 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)
return false;
return (i915.enable_cmd_parser == 1);
}
+static bool check_cmd(const struct intel_engine_cs *ring,
+ const struct drm_i915_cmd_descriptor *desc,
+ const u32 *cmd,
+ const bool is_master,
+ bool *oacontrol_set)
+{
+ if (desc->flags & CMD_DESC_REJECT) {
+ DRM_DEBUG_DRIVER("CMD: Rejected command: 0x%08X\n", *cmd);
+ return false;
+ }
+
+ if ((desc->flags & CMD_DESC_MASTER) && !is_master) {
+ DRM_DEBUG_DRIVER("CMD: Rejected master-only command: 0x%08X\n",
+ *cmd);
+ return false;
+ }
+
+ if (desc->flags & CMD_DESC_REGISTER) {
+ u32 reg_addr = cmd[desc->reg.offset] & desc->reg.mask;
+
+ /*
+ * OACONTROL requires some special handling for writes. We
+ * want to make sure that any batch which enables OA also
+ * disables it before the end of the batch. The goal is to
+ * prevent one process from snooping on the perf data from
+ * another process. To do that, we need to check the value
+ * that will be written to the register. Hence, limit
+ * OACONTROL writes to only MI_LOAD_REGISTER_IMM commands.
+ */
+ if (reg_addr == OACONTROL) {
+ if (desc->cmd.value == MI_LOAD_REGISTER_MEM)
+ return false;
+
+ if (desc->cmd.value == MI_LOAD_REGISTER_IMM(1))
+ *oacontrol_set = (cmd[2] != 0);
+ }
+
+ if (!valid_reg(ring->reg_table,
+ ring->reg_count, reg_addr)) {
+ if (!is_master ||
+ !valid_reg(ring->master_reg_table,
+ ring->master_reg_count,
+ reg_addr)) {
+ DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (ring=%d)\n",
+ reg_addr,
+ *cmd,
+ ring->id);
+ return false;
+ }
+ }
+ }
+
+ if (desc->flags & CMD_DESC_BITMASK) {
+ int i;
+
+ for (i = 0; i < MAX_CMD_DESC_BITMASKS; i++) {
+ u32 dword;
+
+ if (desc->bits[i].mask == 0)
+ break;
+
+ if (desc->bits[i].condition_mask != 0) {
+ u32 offset =
+ desc->bits[i].condition_offset;
+ u32 condition = cmd[offset] &
+ desc->bits[i].condition_mask;
+
+ if (condition == 0)
+ continue;
+ }
+
+ dword = cmd[desc->bits[i].offset] &
+ desc->bits[i].mask;
+
+ if (dword != desc->bits[i].expected) {
+ DRM_DEBUG_DRIVER("CMD: Rejected command 0x%08X for bitmask 0x%08X (exp=0x%08X act=0x%08X) (ring=%d)\n",
+ *cmd,
+ desc->bits[i].mask,
+ desc->bits[i].expected,
+ dword, ring->id);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
#define LENGTH_BIAS 2
/**
@@ -352,7 +957,7 @@ bool i915_needs_cmd_parser(struct intel_ring_buffer *ring)
*
* Return: non-zero if the parser finds violations or otherwise fails
*/
-int i915_parse_cmds(struct intel_ring_buffer *ring,
+int i915_parse_cmds(struct intel_engine_cs *ring,
struct drm_i915_gem_object *batch_obj,
u32 batch_start_offset,
bool is_master)
@@ -361,6 +966,7 @@ int i915_parse_cmds(struct intel_ring_buffer *ring,
u32 *cmd, *batch_base, *batch_end;
struct drm_i915_cmd_descriptor default_desc = { 0 };
int needs_clflush = 0;
+ bool oacontrol_set = false; /* OACONTROL tracking. See check_cmd() */
ret = i915_gem_obj_prepare_shmem_read(batch_obj, &needs_clflush);
if (ret) {
@@ -402,76 +1008,27 @@ int i915_parse_cmds(struct intel_ring_buffer *ring,
length = ((*cmd & desc->length.mask) + LENGTH_BIAS);
if ((batch_end - cmd) < length) {
- DRM_DEBUG_DRIVER("CMD: Command length exceeds batch length: 0x%08X length=%d batchlen=%td\n",
+ DRM_DEBUG_DRIVER("CMD: Command length exceeds batch length: 0x%08X length=%u batchlen=%td\n",
*cmd,
length,
- (unsigned long)(batch_end - cmd));
+ batch_end - cmd);
ret = -EINVAL;
break;
}
- if (desc->flags & CMD_DESC_REJECT) {
- DRM_DEBUG_DRIVER("CMD: Rejected command: 0x%08X\n", *cmd);
+ if (!check_cmd(ring, desc, cmd, is_master, &oacontrol_set)) {
ret = -EINVAL;
break;
}
- if ((desc->flags & CMD_DESC_MASTER) && !is_master) {
- DRM_DEBUG_DRIVER("CMD: Rejected master-only command: 0x%08X\n",
- *cmd);
- ret = -EINVAL;
- break;
- }
-
- if (desc->flags & CMD_DESC_REGISTER) {
- u32 reg_addr = cmd[desc->reg.offset] & desc->reg.mask;
-
- if (!valid_reg(ring->reg_table,
- ring->reg_count, reg_addr)) {
- if (!is_master ||
- !valid_reg(ring->master_reg_table,
- ring->master_reg_count,
- reg_addr)) {
- DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (ring=%d)\n",
- reg_addr,
- *cmd,
- ring->id);
- ret = -EINVAL;
- break;
- }
- }
- }
-
- if (desc->flags & CMD_DESC_BITMASK) {
- int i;
-
- for (i = 0; i < MAX_CMD_DESC_BITMASKS; i++) {
- u32 dword;
-
- if (desc->bits[i].mask == 0)
- break;
-
- dword = cmd[desc->bits[i].offset] &
- desc->bits[i].mask;
-
- if (dword != desc->bits[i].expected) {
- DRM_DEBUG_DRIVER("CMD: Rejected command 0x%08X for bitmask 0x%08X (exp=0x%08X act=0x%08X) (ring=%d)\n",
- *cmd,
- desc->bits[i].mask,
- desc->bits[i].expected,
- dword, ring->id);
- ret = -EINVAL;
- break;
- }
- }
-
- if (ret)
- break;
- }
-
cmd += length;
}
+ if (oacontrol_set) {
+ DRM_DEBUG_DRIVER("CMD: batch set OACONTROL but did not clear it\n");
+ ret = -EINVAL;
+ }
+
if (cmd >= batch_end) {
DRM_DEBUG_DRIVER("CMD: Got to the end of the buffer w/o a BBE cmd!\n");
ret = -EINVAL;
@@ -483,3 +1040,22 @@ int i915_parse_cmds(struct intel_ring_buffer *ring,
return ret;
}
+
+/**
+ * i915_cmd_parser_get_version() - get the cmd parser version number
+ *
+ * The cmd parser maintains a simple increasing integer version number suitable
+ * for passing to userspace clients to determine what operations are permitted.
+ *
+ * Return: the current version number of the cmd parser
+ */
+int i915_cmd_parser_get_version(void)
+{
+ /*
+ * Command parser version history
+ *
+ * 1. Initial version. Checks batches and reports violations, but leaves
+ * hardware parsing enabled (so does not allow new use cases).
+ */
+ return 1;
+}
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 195fe5bc0aac..601caa88c092 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -79,7 +79,7 @@ drm_add_fake_info_node(struct drm_minor *minor,
static int i915_capabilities(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
const struct intel_device_info *info = INTEL_INFO(dev);
@@ -172,7 +172,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
seq_printf(m, " (%s)", obj->ring->name);
}
-static void describe_ctx(struct seq_file *m, struct i915_hw_context *ctx)
+static void describe_ctx(struct seq_file *m, struct intel_context *ctx)
{
seq_putc(m, ctx->is_initialized ? 'I' : 'i');
seq_putc(m, ctx->remap_slice ? 'R' : 'r');
@@ -181,7 +181,7 @@ static void describe_ctx(struct seq_file *m, struct i915_hw_context *ctx)
static int i915_gem_object_list_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
uintptr_t list = (uintptr_t) node->info_ent->data;
struct list_head *head;
struct drm_device *dev = node->minor->dev;
@@ -239,7 +239,7 @@ static int obj_rank_by_stolen(void *priv,
static int i915_gem_stolen_list_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj;
@@ -371,7 +371,7 @@ static int per_file_stats(int id, void *ptr, void *data)
static int i915_gem_object_info(struct seq_file *m, void* data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 count, mappable_count, purgeable_count;
@@ -474,7 +474,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
static int i915_gem_gtt_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
uintptr_t list = (uintptr_t) node->info_ent->data;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -509,12 +509,12 @@ static int i915_gem_gtt_info(struct seq_file *m, void *data)
static int i915_gem_pageflip_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
unsigned long flags;
struct intel_crtc *crtc;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+ for_each_intel_crtc(dev, crtc) {
const char pipe = pipe_name(crtc->pipe);
const char plane = plane_name(crtc->plane);
struct intel_unpin_work *work;
@@ -559,10 +559,10 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data)
static int i915_gem_request_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
struct drm_i915_gem_request *gem_request;
int ret, count, i;
@@ -594,7 +594,7 @@ static int i915_gem_request_info(struct seq_file *m, void *data)
}
static void i915_ring_seqno_info(struct seq_file *m,
- struct intel_ring_buffer *ring)
+ struct intel_engine_cs *ring)
{
if (ring->get_seqno) {
seq_printf(m, "Current sequence (%s): %u\n",
@@ -604,10 +604,10 @@ static void i915_ring_seqno_info(struct seq_file *m,
static int i915_gem_seqno_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int ret, i;
ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -627,10 +627,10 @@ static int i915_gem_seqno_info(struct seq_file *m, void *data)
static int i915_interrupt_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int ret, i, pipe;
ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -638,7 +638,47 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
return ret;
intel_runtime_pm_get(dev_priv);
- if (INTEL_INFO(dev)->gen >= 8) {
+ if (IS_CHERRYVIEW(dev)) {
+ int i;
+ seq_printf(m, "Master Interrupt Control:\t%08x\n",
+ I915_READ(GEN8_MASTER_IRQ));
+
+ seq_printf(m, "Display IER:\t%08x\n",
+ I915_READ(VLV_IER));
+ seq_printf(m, "Display IIR:\t%08x\n",
+ I915_READ(VLV_IIR));
+ seq_printf(m, "Display IIR_RW:\t%08x\n",
+ I915_READ(VLV_IIR_RW));
+ seq_printf(m, "Display IMR:\t%08x\n",
+ I915_READ(VLV_IMR));
+ for_each_pipe(pipe)
+ seq_printf(m, "Pipe %c stat:\t%08x\n",
+ pipe_name(pipe),
+ I915_READ(PIPESTAT(pipe)));
+
+ seq_printf(m, "Port hotplug:\t%08x\n",
+ I915_READ(PORT_HOTPLUG_EN));
+ seq_printf(m, "DPFLIPSTAT:\t%08x\n",
+ I915_READ(VLV_DPFLIPSTAT));
+ seq_printf(m, "DPINVGTT:\t%08x\n",
+ I915_READ(DPINVGTT));
+
+ for (i = 0; i < 4; i++) {
+ seq_printf(m, "GT Interrupt IMR %d:\t%08x\n",
+ i, I915_READ(GEN8_GT_IMR(i)));
+ seq_printf(m, "GT Interrupt IIR %d:\t%08x\n",
+ i, I915_READ(GEN8_GT_IIR(i)));
+ seq_printf(m, "GT Interrupt IER %d:\t%08x\n",
+ i, I915_READ(GEN8_GT_IER(i)));
+ }
+
+ seq_printf(m, "PCU interrupt mask:\t%08x\n",
+ I915_READ(GEN8_PCU_IMR));
+ seq_printf(m, "PCU interrupt identity:\t%08x\n",
+ I915_READ(GEN8_PCU_IIR));
+ seq_printf(m, "PCU interrupt enable:\t%08x\n",
+ I915_READ(GEN8_PCU_IER));
+ } else if (INTEL_INFO(dev)->gen >= 8) {
seq_printf(m, "Master Interrupt Control:\t%08x\n",
I915_READ(GEN8_MASTER_IRQ));
@@ -768,7 +808,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int i, ret;
@@ -797,10 +837,10 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
static int i915_hws_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
const u32 *hws;
int i;
@@ -945,7 +985,7 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_next_seqno_fops,
static int i915_rstdby_delays(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ 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;
@@ -966,9 +1006,9 @@ static int i915_rstdby_delays(struct seq_file *m, void *unused)
return 0;
}
-static int i915_cur_delayinfo(struct seq_file *m, void *unused)
+static int i915_frequency_info(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ 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 = 0;
@@ -991,6 +1031,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
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);
+ u32 rpmodectl, rpinclimit, rpdeclimit;
u32 rpstat, cagf, reqf;
u32 rpupei, rpcurup, rpprevup;
u32 rpdownei, rpcurdown, rpprevdown;
@@ -1011,6 +1052,10 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
reqf >>= 25;
reqf *= GT_FREQUENCY_MULTIPLIER;
+ rpmodectl = I915_READ(GEN6_RP_CONTROL);
+ rpinclimit = I915_READ(GEN6_RP_UP_THRESHOLD);
+ rpdeclimit = I915_READ(GEN6_RP_DOWN_THRESHOLD);
+
rpstat = I915_READ(GEN6_RPSTAT1);
rpupei = I915_READ(GEN6_RP_CUR_UP_EI);
rpcurup = I915_READ(GEN6_RP_CUR_UP);
@@ -1027,14 +1072,23 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL);
mutex_unlock(&dev->struct_mutex);
+ 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));
seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status);
- seq_printf(m, "RPSTAT1: 0x%08x\n", rpstat);
seq_printf(m, "Render p-state ratio: %d\n",
(gt_perf_status & 0xff00) >> 8);
seq_printf(m, "Render p-state VID: %d\n",
gt_perf_status & 0xff);
seq_printf(m, "Render p-state limit: %d\n",
rp_state_limits & 0xff);
+ seq_printf(m, "RPSTAT1: 0x%08x\n", rpstat);
+ seq_printf(m, "RPMODECTL: 0x%08x\n", rpmodectl);
+ seq_printf(m, "RPINCLIMIT: 0x%08x\n", rpinclimit);
+ seq_printf(m, "RPDECLIMIT: 0x%08x\n", rpdeclimit);
seq_printf(m, "RPNSWREQ: %dMHz\n", reqf);
seq_printf(m, "CAGF: %dMHz\n", cagf);
seq_printf(m, "RP CUR UP EI: %dus\n", rpupei &
@@ -1094,7 +1148,7 @@ out:
static int i915_delayfreq_table(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ 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;
@@ -1125,7 +1179,7 @@ static inline int MAP_TO_MV(int map)
static int i915_inttoext_table(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ 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;
@@ -1149,7 +1203,7 @@ static int i915_inttoext_table(struct seq_file *m, void *unused)
static int ironlake_drpc_info(struct seq_file *m)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 rgvmodectl, rstdbyctl;
@@ -1219,15 +1273,19 @@ static int ironlake_drpc_info(struct seq_file *m)
static int vlv_drpc_info(struct seq_file *m)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 rpmodectl1, rcctl1;
unsigned fw_rendercount = 0, fw_mediacount = 0;
+ intel_runtime_pm_get(dev_priv);
+
rpmodectl1 = I915_READ(GEN6_RP_CONTROL);
rcctl1 = I915_READ(GEN6_RC_CONTROL);
+ intel_runtime_pm_put(dev_priv);
+
seq_printf(m, "Video Turbo Mode: %s\n",
yesno(rpmodectl1 & GEN6_RP_MEDIA_TURBO));
seq_printf(m, "Turbo enabled: %s\n",
@@ -1247,6 +1305,11 @@ static int vlv_drpc_info(struct seq_file *m)
(I915_READ(VLV_GTLC_PW_STATUS) &
VLV_GTLC_PW_MEDIA_STATUS_MASK) ? "Up" : "Down");
+ seq_printf(m, "Render RC6 residency since boot: %u\n",
+ I915_READ(VLV_GT_RENDER_RC6));
+ seq_printf(m, "Media RC6 residency since boot: %u\n",
+ I915_READ(VLV_GT_MEDIA_RC6));
+
spin_lock_irq(&dev_priv->uncore.lock);
fw_rendercount = dev_priv->uncore.fw_rendercount;
fw_mediacount = dev_priv->uncore.fw_mediacount;
@@ -1263,7 +1326,7 @@ static int vlv_drpc_info(struct seq_file *m)
static int gen6_drpc_info(struct seq_file *m)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 rpmodectl1, gt_core_status, rcctl1, rc6vids = 0;
@@ -1362,7 +1425,7 @@ static int gen6_drpc_info(struct seq_file *m)
static int i915_drpc_info(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
if (IS_VALLEYVIEW(dev))
@@ -1375,7 +1438,7 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
static int i915_fbc_status(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1437,7 +1500,7 @@ static int i915_fbc_status(struct seq_file *m, void *unused)
static int i915_ips_status(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1460,7 +1523,7 @@ static int i915_ips_status(struct seq_file *m, void *unused)
static int i915_sr_status(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
bool sr_enabled = false;
@@ -1486,7 +1549,7 @@ static int i915_sr_status(struct seq_file *m, void *unused)
static int i915_emon_status(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ 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 temp, chipset, gfx;
@@ -1514,7 +1577,7 @@ static int i915_emon_status(struct seq_file *m, void *unused)
static int i915_ring_freq_table(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ 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 = 0;
@@ -1557,7 +1620,7 @@ out:
static int i915_gfxec(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ 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;
@@ -1577,7 +1640,7 @@ static int i915_gfxec(struct seq_file *m, void *unused)
static int i915_opregion(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_opregion *opregion = &dev_priv->opregion;
@@ -1605,7 +1668,7 @@ out:
static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct intel_fbdev *ifbdev = NULL;
struct intel_framebuffer *fb;
@@ -1651,11 +1714,11 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
static int i915_context_status(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
- struct i915_hw_context *ctx;
+ struct intel_engine_cs *ring;
+ struct intel_context *ctx;
int ret, i;
ret = mutex_lock_interruptible(&dev->mode_config.mutex);
@@ -1675,6 +1738,9 @@ 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)
+ continue;
+
seq_puts(m, "HW context ");
describe_ctx(m, ctx);
for_each_ring(ring, dev_priv, i)
@@ -1692,7 +1758,7 @@ static int i915_context_status(struct seq_file *m, void *unused)
static int i915_gen6_forcewake_count_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned forcewake_count = 0, fw_rendercount = 0, fw_mediacount = 0;
@@ -1740,7 +1806,7 @@ static const char *swizzle_string(unsigned swizzle)
static int i915_swizzle_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ 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;
@@ -1788,10 +1854,14 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
static int per_file_ctx(int id, void *ptr, void *data)
{
- struct i915_hw_context *ctx = ptr;
+ struct intel_context *ctx = ptr;
struct seq_file *m = data;
struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(ctx);
+ if (i915_gem_context_is_default(ctx))
+ seq_puts(m, " default context:\n");
+ else
+ seq_printf(m, " context %d:\n", ctx->id);
ppgtt->debug_dump(ppgtt, m);
return 0;
@@ -1800,7 +1870,7 @@ static int per_file_ctx(int id, void *ptr, void *data)
static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
int unused, i;
@@ -1816,8 +1886,7 @@ static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev)
u64 pdp = I915_READ(ring->mmio_base + offset + 4);
pdp <<= 32;
pdp |= I915_READ(ring->mmio_base + offset);
- for (i = 0; i < 4; i++)
- seq_printf(m, "\tPDP%d 0x%016llx\n", i, pdp);
+ seq_printf(m, "\tPDP%d 0x%016llx\n", i, pdp);
}
}
}
@@ -1825,7 +1894,7 @@ static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev)
static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
struct drm_file *file;
int i;
@@ -1852,12 +1921,9 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
list_for_each_entry_reverse(file, &dev->filelist, lhead) {
struct drm_i915_file_private *file_priv = file->driver_priv;
- struct i915_hw_ppgtt *pvt_ppgtt;
- pvt_ppgtt = ctx_to_ppgtt(file_priv->private_default_ctx);
seq_printf(m, "proc: %s\n",
get_pid_task(file->pid, PIDTYPE_PID)->comm);
- seq_puts(m, " default context:\n");
idr_for_each(&file_priv->context_idr, per_file_ctx, m);
}
seq_printf(m, "ECOCHK: 0x%08x\n", I915_READ(GAM_ECOCHK));
@@ -1865,7 +1931,7 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
static int i915_ppgtt_info(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1885,56 +1951,9 @@ static int i915_ppgtt_info(struct seq_file *m, void *data)
return 0;
}
-static int i915_dpio_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_i915_private *dev_priv = dev->dev_private;
- int ret;
-
-
- if (!IS_VALLEYVIEW(dev)) {
- seq_puts(m, "unsupported\n");
- return 0;
- }
-
- ret = mutex_lock_interruptible(&dev_priv->dpio_lock);
- if (ret)
- return ret;
-
- seq_printf(m, "DPIO_CTL: 0x%08x\n", I915_READ(DPIO_CTL));
-
- seq_printf(m, "DPIO PLL DW3 CH0 : 0x%08x\n",
- vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW3(0)));
- seq_printf(m, "DPIO PLL DW3 CH1: 0x%08x\n",
- vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW3(1)));
-
- seq_printf(m, "DPIO PLL DW5 CH0: 0x%08x\n",
- vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW5(0)));
- seq_printf(m, "DPIO PLL DW5 CH1: 0x%08x\n",
- vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW5(1)));
-
- seq_printf(m, "DPIO PLL DW7 CH0: 0x%08x\n",
- vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW7(0)));
- seq_printf(m, "DPIO PLL DW7 CH1: 0x%08x\n",
- vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW7(1)));
-
- seq_printf(m, "DPIO PLL DW10 CH0: 0x%08x\n",
- vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW10(0)));
- seq_printf(m, "DPIO PLL DW10 CH1: 0x%08x\n",
- vlv_dpio_read(dev_priv, PIPE_A, VLV_PLL_DW10(1)));
-
- seq_printf(m, "DPIO_FASTCLK_DISABLE: 0x%08x\n",
- vlv_dpio_read(dev_priv, PIPE_A, VLV_CMN_DW0));
-
- mutex_unlock(&dev_priv->dpio_lock);
-
- return 0;
-}
-
static int i915_llc(struct seq_file *m, void *data)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2040,11 +2059,11 @@ static int i915_energy_uJ(struct seq_file *m, void *data)
static int i915_pc8_status(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- if (!IS_HASWELL(dev)) {
+ if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
seq_puts(m, "not supported\n");
return 0;
}
@@ -2115,7 +2134,7 @@ static const char *power_domain_str(enum intel_display_power_domain domain)
static int i915_power_domain_info(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct i915_power_domains *power_domains = &dev_priv->power_domains;
@@ -2170,7 +2189,7 @@ static void intel_encoder_info(struct seq_file *m,
struct intel_crtc *intel_crtc,
struct intel_encoder *intel_encoder)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_crtc *crtc = &intel_crtc->base;
struct intel_connector *intel_connector;
@@ -2178,12 +2197,12 @@ static void intel_encoder_info(struct seq_file *m,
encoder = &intel_encoder->base;
seq_printf(m, "\tencoder %d: type: %s, connectors:\n",
- encoder->base.id, drm_get_encoder_name(encoder));
+ encoder->base.id, encoder->name);
for_each_connector_on_encoder(dev, encoder, intel_connector) {
struct drm_connector *connector = &intel_connector->base;
seq_printf(m, "\t\tconnector %d: type: %s, status: %s",
connector->base.id,
- drm_get_connector_name(connector),
+ connector->name,
drm_get_connector_status_name(connector->status));
if (connector->status == connector_status_connected) {
struct drm_display_mode *mode = &crtc->mode;
@@ -2197,7 +2216,7 @@ static void intel_encoder_info(struct seq_file *m,
static void intel_crtc_info(struct seq_file *m, struct intel_crtc *intel_crtc)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_crtc *crtc = &intel_crtc->base;
struct intel_encoder *intel_encoder;
@@ -2254,7 +2273,7 @@ static void intel_connector_info(struct seq_file *m,
struct drm_display_mode *mode;
seq_printf(m, "connector %d: type %s, status: %s\n",
- connector->base.id, drm_get_connector_name(connector),
+ connector->base.id, connector->name,
drm_get_connector_status_name(connector->status));
if (connector->status == connector_status_connected) {
seq_printf(m, "\tname: %s\n", connector->display_info.name);
@@ -2286,10 +2305,8 @@ static bool cursor_active(struct drm_device *dev, int pipe)
if (IS_845G(dev) || IS_I865G(dev))
state = I915_READ(_CURACNTR) & CURSOR_ENABLE;
- else if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev))
- state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
else
- state = I915_READ(CURCNTR_IVB(pipe)) & CURSOR_MODE;
+ state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
return state;
}
@@ -2299,10 +2316,7 @@ static bool cursor_position(struct drm_device *dev, int pipe, int *x, int *y)
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pos;
- if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev))
- pos = I915_READ(CURPOS_IVB(pipe));
- else
- pos = I915_READ(CURPOS(pipe));
+ pos = I915_READ(CURPOS(pipe));
*x = (pos >> CURSOR_X_SHIFT) & CURSOR_POS_MASK;
if (pos & (CURSOR_POS_SIGN << CURSOR_X_SHIFT))
@@ -2317,7 +2331,7 @@ static bool cursor_position(struct drm_device *dev, int pipe, int *x, int *y)
static int i915_display_info(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *crtc;
@@ -2327,7 +2341,7 @@ static int i915_display_info(struct seq_file *m, void *unused)
drm_modeset_lock_all(dev);
seq_printf(m, "CRTC info\n");
seq_printf(m, "---------\n");
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+ for_each_intel_crtc(dev, crtc) {
bool active;
int x, y;
@@ -2339,10 +2353,14 @@ static int i915_display_info(struct seq_file *m, void *unused)
active = cursor_position(dev, crtc->pipe, &x, &y);
seq_printf(m, "\tcursor visible? %s, position (%d, %d), addr 0x%08x, active? %s\n",
- yesno(crtc->cursor_visible),
+ yesno(crtc->cursor_base),
x, y, crtc->cursor_addr,
yesno(active));
}
+
+ seq_printf(m, "\tunderrun reporting: cpu=%s pch=%s \n",
+ yesno(!crtc->cpu_fifo_underrun_disabled),
+ yesno(!crtc->pch_fifo_underrun_disabled));
}
seq_printf(m, "\n");
@@ -2595,7 +2613,7 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
*source = INTEL_PIPE_CRC_SOURCE_PIPE;
- mutex_lock(&dev->mode_config.mutex);
+ drm_modeset_lock_all(dev);
list_for_each_entry(encoder, &dev->mode_config.encoder_list,
base.head) {
if (!encoder->base.crtc)
@@ -2631,7 +2649,7 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
break;
}
}
- mutex_unlock(&dev->mode_config.mutex);
+ drm_modeset_unlock_all(dev);
return ret;
}
@@ -3106,7 +3124,7 @@ static const struct file_operations i915_display_crc_ctl_fops = {
static void wm_latency_show(struct seq_file *m, const uint16_t wm[5])
{
struct drm_device *dev = m->private;
- int num_levels = IS_HASWELL(dev) || IS_BROADWELL(dev) ? 5 : 4;
+ int num_levels = ilk_wm_max_level(dev) + 1;
int level;
drm_modeset_lock_all(dev);
@@ -3189,7 +3207,7 @@ static ssize_t wm_latency_write(struct file *file, const char __user *ubuf,
struct seq_file *m = file->private_data;
struct drm_device *dev = m->private;
uint16_t new[5] = { 0 };
- int num_levels = IS_HASWELL(dev) || IS_BROADWELL(dev) ? 5 : 4;
+ int num_levels = ilk_wm_max_level(dev) + 1;
int level;
int ret;
char tmp[32];
@@ -3286,9 +3304,15 @@ static int
i915_wedged_set(void *data, u64 val)
{
struct drm_device *dev = data;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ intel_runtime_pm_get(dev_priv);
i915_handle_error(dev, val,
"Manually setting wedged to %llu", val);
+
+ intel_runtime_pm_put(dev_priv);
+
return 0;
}
@@ -3774,7 +3798,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
{"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_cur_delayinfo", i915_cur_delayinfo, 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},
@@ -3790,7 +3814,6 @@ static const struct drm_info_list i915_debugfs_list[] = {
{"i915_gen6_forcewake_count", i915_gen6_forcewake_count_info, 0},
{"i915_swizzle_info", i915_swizzle_info, 0},
{"i915_ppgtt_info", i915_ppgtt_info, 0},
- {"i915_dpio", i915_dpio_info, 0},
{"i915_llc", i915_llc, 0},
{"i915_edp_psr_status", i915_edp_psr_status, 0},
{"i915_sink_crc_eDP1", i915_sink_crc, 0},
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index eedb023af27d..4c22a5b7f4c5 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -44,6 +44,7 @@
#include <acpi/video.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/oom.h>
#define LP_RING(d) (&((struct drm_i915_private *)(d))->ring[RCS])
@@ -63,7 +64,7 @@
* has access to the ring.
*/
#define RING_LOCK_TEST_WITH_RETURN(dev, file) do { \
- if (LP_RING(dev->dev_private)->obj == NULL) \
+ if (LP_RING(dev->dev_private)->buffer->obj == NULL) \
LOCK_TEST_WITH_RETURN(dev, file); \
} while (0)
@@ -119,7 +120,7 @@ static void i915_write_hws_pga(struct drm_device *dev)
static void i915_free_hws(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = LP_RING(dev_priv);
+ struct intel_engine_cs *ring = LP_RING(dev_priv);
if (dev_priv->status_page_dmah) {
drm_pci_free(dev, dev_priv->status_page_dmah);
@@ -139,7 +140,8 @@ 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;
- struct intel_ring_buffer *ring = LP_RING(dev_priv);
+ struct intel_engine_cs *ring = LP_RING(dev_priv);
+ struct intel_ringbuffer *ringbuf = ring->buffer;
/*
* We should never lose context on the ring with modesetting
@@ -148,17 +150,17 @@ void i915_kernel_lost_context(struct drm_device * dev)
if (drm_core_check_feature(dev, DRIVER_MODESET))
return;
- ring->head = I915_READ_HEAD(ring) & HEAD_ADDR;
- ring->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
- ring->space = ring->head - (ring->tail + I915_RING_FREE_SPACE);
- if (ring->space < 0)
- ring->space += ring->size;
+ ringbuf->head = I915_READ_HEAD(ring) & HEAD_ADDR;
+ ringbuf->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
+ ringbuf->space = ringbuf->head - (ringbuf->tail + I915_RING_FREE_SPACE);
+ if (ringbuf->space < 0)
+ ringbuf->space += ringbuf->size;
if (!dev->primary->master)
return;
master_priv = dev->primary->master->driver_priv;
- if (ring->head == ring->tail && master_priv->sarea_priv)
+ if (ringbuf->head == ringbuf->tail && master_priv->sarea_priv)
master_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;
}
@@ -201,7 +203,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
}
if (init->ring_size != 0) {
- if (LP_RING(dev_priv)->obj != NULL) {
+ if (LP_RING(dev_priv)->buffer->obj != NULL) {
i915_dma_cleanup(dev);
DRM_ERROR("Client tried to initialize ringbuffer in "
"GEM mode\n");
@@ -234,11 +236,11 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
static int i915_dma_resume(struct drm_device * dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = LP_RING(dev_priv);
+ struct intel_engine_cs *ring = LP_RING(dev_priv);
DRM_DEBUG_DRIVER("%s\n", __func__);
- if (ring->virtual_start == NULL) {
+ if (ring->buffer->virtual_start == NULL) {
DRM_ERROR("can not ioremap virtual address for"
" ring buffer\n");
return -ENOMEM;
@@ -360,7 +362,7 @@ 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;
- if ((dwords+1) * sizeof(int) >= LP_RING(dev_priv)->size - 8)
+ if ((dwords+1) * sizeof(int) >= LP_RING(dev_priv)->buffer->size - 8)
return -EINVAL;
for (i = 0; i < dwords;) {
@@ -782,7 +784,7 @@ 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;
int ret = 0;
- struct intel_ring_buffer *ring = LP_RING(dev_priv);
+ struct intel_engine_cs *ring = LP_RING(dev_priv);
DRM_DEBUG_DRIVER("irq_nr=%d breadcrumb=%d\n", irq_nr,
READ_BREADCRUMB(dev_priv));
@@ -823,7 +825,7 @@ static int i915_irq_emit(struct drm_device *dev, void *data,
if (drm_core_check_feature(dev, DRIVER_MODESET))
return -ENODEV;
- if (!dev_priv || !LP_RING(dev_priv)->virtual_start) {
+ if (!dev_priv || !LP_RING(dev_priv)->buffer->virtual_start) {
DRM_ERROR("called with no initialization\n");
return -EINVAL;
}
@@ -1017,6 +1019,9 @@ static int i915_getparam(struct drm_device *dev, void *data,
case I915_PARAM_HAS_EXEC_HANDLE_LUT:
value = 1;
break;
+ case I915_PARAM_CMD_PARSER_VERSION:
+ value = i915_cmd_parser_get_version();
+ break;
default:
DRM_DEBUG("Unknown parameter %d\n", param->param);
return -EINVAL;
@@ -1070,7 +1075,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data,
{
struct drm_i915_private *dev_priv = dev->dev_private;
drm_i915_hws_addr_t *hws = data;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
if (drm_core_check_feature(dev, DRIVER_MODESET))
return -ENODEV;
@@ -1277,12 +1282,13 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_
static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
- bool can_switch;
- spin_lock(&dev->count_lock);
- can_switch = (dev->open_count == 0);
- spin_unlock(&dev->count_lock);
- return can_switch;
+ /*
+ * FIXME: open_count is protected by drm_global_mutex but that would lead to
+ * locking inversion with the driver load path. And the access here is
+ * completely racy anyway. So don't bother with locking for now.
+ */
+ return dev->open_count == 0;
}
static const struct vga_switcheroo_client_ops i915_switcheroo_ops = {
@@ -1326,7 +1332,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
intel_power_domains_init_hw(dev_priv);
- ret = drm_irq_install(dev);
+ ret = drm_irq_install(dev, dev->pdev->irq);
if (ret)
goto cleanup_gem_stolen;
@@ -1336,7 +1342,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
ret = i915_gem_init(dev);
if (ret)
- goto cleanup_power;
+ goto cleanup_irq;
INIT_WORK(&dev_priv->console_resume_work, intel_console_resume);
@@ -1345,10 +1351,8 @@ static int i915_load_modeset_init(struct drm_device *dev)
/* Always safe in the mode setting case. */
/* FIXME: do pre/post-mode set stuff in core KMS code */
dev->vblank_disable_allowed = true;
- if (INTEL_INFO(dev)->num_pipes == 0) {
- intel_display_power_put(dev_priv, POWER_DOMAIN_VGA);
+ if (INTEL_INFO(dev)->num_pipes == 0)
return 0;
- }
ret = intel_fbdev_init(dev);
if (ret)
@@ -1383,8 +1387,7 @@ cleanup_gem:
mutex_unlock(&dev->struct_mutex);
WARN_ON(dev_priv->mm.aliasing_ppgtt);
drm_mm_takedown(&dev_priv->gtt.base.mm);
-cleanup_power:
- intel_display_power_put(dev_priv, POWER_DOMAIN_VGA);
+cleanup_irq:
drm_irq_uninstall(dev);
cleanup_gem_stolen:
i915_gem_cleanup_stolen(dev);
@@ -1739,8 +1742,8 @@ out_power_well:
intel_power_domains_remove(dev_priv);
drm_vblank_cleanup(dev);
out_gem_unload:
- if (dev_priv->mm.inactive_shrinker.scan_objects)
- unregister_shrinker(&dev_priv->mm.inactive_shrinker);
+ WARN_ON(unregister_oom_notifier(&dev_priv->mm.oom_notifier));
+ unregister_shrinker(&dev_priv->mm.shrinker);
if (dev->pdev->msi_enabled)
pci_disable_msi(dev->pdev);
@@ -1791,8 +1794,8 @@ int i915_driver_unload(struct drm_device *dev)
i915_teardown_sysfs(dev);
- if (dev_priv->mm.inactive_shrinker.scan_objects)
- unregister_shrinker(&dev_priv->mm.inactive_shrinker);
+ WARN_ON(unregister_oom_notifier(&dev_priv->mm.oom_notifier));
+ unregister_shrinker(&dev_priv->mm.shrinker);
io_mapping_free(dev_priv->gtt.mappable);
arch_phys_wc_del(dev_priv->gtt.mtrr);
@@ -1864,7 +1867,7 @@ int i915_driver_unload(struct drm_device *dev)
kmem_cache_destroy(dev_priv->slab);
pci_dev_put(dev_priv->bridge_dev);
- kfree(dev->dev_private);
+ kfree(dev_priv);
return 0;
}
@@ -1925,6 +1928,8 @@ void i915_driver_postclose(struct drm_device *dev, struct drm_file *file)
{
struct drm_i915_file_private *file_priv = file->driver_priv;
+ if (file_priv && file_priv->bsd_ring)
+ file_priv->bsd_ring = NULL;
kfree(file_priv);
}
@@ -1978,9 +1983,10 @@ const struct drm_ioctl_desc i915_ioctls[] = {
DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_DESTROY, i915_gem_context_destroy_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(I915_REG_READ, i915_reg_read_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(I915_GET_RESET_STATS, i915_get_reset_stats_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(I915_GEM_USERPTR, i915_gem_userptr_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW),
};
-int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
+int i915_max_ioctl = ARRAY_SIZE(i915_ioctls);
/*
* This is really ugly: Because old userspace abused the linux agp interface to
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 82f4d1f47d3b..651e65e051c0 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -36,6 +36,7 @@
#include <linux/console.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <drm/drm_crtc_helper.h>
static struct drm_driver driver;
@@ -49,12 +50,30 @@ static struct drm_driver driver;
.dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET }, \
.palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET }
+#define GEN_CHV_PIPEOFFSETS \
+ .pipe_offsets = { PIPE_A_OFFSET, PIPE_B_OFFSET, \
+ 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 }
+
+#define CURSOR_OFFSETS \
+ .cursor_offsets = { CURSOR_A_OFFSET, CURSOR_B_OFFSET, CHV_CURSOR_C_OFFSET }
+
+#define IVB_CURSOR_OFFSETS \
+ .cursor_offsets = { CURSOR_A_OFFSET, IVB_CURSOR_B_OFFSET, IVB_CURSOR_C_OFFSET }
static const struct intel_device_info intel_i830_info = {
.gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .num_pipes = 2,
.has_overlay = 1, .overlay_needs_physical = 1,
.ring_mask = RENDER_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_845g_info = {
@@ -62,6 +81,7 @@ static const struct intel_device_info intel_845g_info = {
.has_overlay = 1, .overlay_needs_physical = 1,
.ring_mask = RENDER_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_i85x_info = {
@@ -71,6 +91,7 @@ static const struct intel_device_info intel_i85x_info = {
.has_fbc = 1,
.ring_mask = RENDER_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_i865g_info = {
@@ -78,6 +99,7 @@ static const struct intel_device_info intel_i865g_info = {
.has_overlay = 1, .overlay_needs_physical = 1,
.ring_mask = RENDER_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_i915g_info = {
@@ -85,6 +107,7 @@ static const struct intel_device_info intel_i915g_info = {
.has_overlay = 1, .overlay_needs_physical = 1,
.ring_mask = RENDER_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_i915gm_info = {
.gen = 3, .is_mobile = 1, .num_pipes = 2,
@@ -94,12 +117,14 @@ static const struct intel_device_info intel_i915gm_info = {
.has_fbc = 1,
.ring_mask = RENDER_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_i945g_info = {
.gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .num_pipes = 2,
.has_overlay = 1, .overlay_needs_physical = 1,
.ring_mask = RENDER_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_i945gm_info = {
.gen = 3, .is_i945gm = 1, .is_mobile = 1, .num_pipes = 2,
@@ -109,6 +134,7 @@ static const struct intel_device_info intel_i945gm_info = {
.has_fbc = 1,
.ring_mask = RENDER_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_i965g_info = {
@@ -117,6 +143,7 @@ static const struct intel_device_info intel_i965g_info = {
.has_overlay = 1,
.ring_mask = RENDER_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_i965gm_info = {
@@ -126,6 +153,7 @@ static const struct intel_device_info intel_i965gm_info = {
.supports_tv = 1,
.ring_mask = RENDER_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_g33_info = {
@@ -134,6 +162,7 @@ static const struct intel_device_info intel_g33_info = {
.has_overlay = 1,
.ring_mask = RENDER_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_g45_info = {
@@ -141,6 +170,7 @@ static const struct intel_device_info intel_g45_info = {
.has_pipe_cxsr = 1, .has_hotplug = 1,
.ring_mask = RENDER_RING | BSD_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_gm45_info = {
@@ -150,6 +180,7 @@ static const struct intel_device_info intel_gm45_info = {
.supports_tv = 1,
.ring_mask = RENDER_RING | BSD_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_pineview_info = {
@@ -157,6 +188,7 @@ static const struct intel_device_info intel_pineview_info = {
.need_gfx_hws = 1, .has_hotplug = 1,
.has_overlay = 1,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_ironlake_d_info = {
@@ -164,6 +196,7 @@ static const struct intel_device_info intel_ironlake_d_info = {
.need_gfx_hws = 1, .has_hotplug = 1,
.ring_mask = RENDER_RING | BSD_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_ironlake_m_info = {
@@ -172,6 +205,7 @@ static const struct intel_device_info intel_ironlake_m_info = {
.has_fbc = 1,
.ring_mask = RENDER_RING | BSD_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_sandybridge_d_info = {
@@ -181,6 +215,7 @@ static const struct intel_device_info intel_sandybridge_d_info = {
.ring_mask = RENDER_RING | BSD_RING | BLT_RING,
.has_llc = 1,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_sandybridge_m_info = {
@@ -190,6 +225,7 @@ static const struct intel_device_info intel_sandybridge_m_info = {
.ring_mask = RENDER_RING | BSD_RING | BLT_RING,
.has_llc = 1,
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
#define GEN7_FEATURES \
@@ -203,6 +239,7 @@ static const struct intel_device_info intel_ivybridge_d_info = {
GEN7_FEATURES,
.is_ivybridge = 1,
GEN_DEFAULT_PIPEOFFSETS,
+ IVB_CURSOR_OFFSETS,
};
static const struct intel_device_info intel_ivybridge_m_info = {
@@ -210,6 +247,7 @@ static const struct intel_device_info intel_ivybridge_m_info = {
.is_ivybridge = 1,
.is_mobile = 1,
GEN_DEFAULT_PIPEOFFSETS,
+ IVB_CURSOR_OFFSETS,
};
static const struct intel_device_info intel_ivybridge_q_info = {
@@ -217,6 +255,7 @@ static const struct intel_device_info intel_ivybridge_q_info = {
.is_ivybridge = 1,
.num_pipes = 0, /* legal, last one wins */
GEN_DEFAULT_PIPEOFFSETS,
+ IVB_CURSOR_OFFSETS,
};
static const struct intel_device_info intel_valleyview_m_info = {
@@ -228,6 +267,7 @@ static const struct intel_device_info intel_valleyview_m_info = {
.has_fbc = 0, /* legal, last one wins */
.has_llc = 0, /* legal, last one wins */
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_valleyview_d_info = {
@@ -238,6 +278,7 @@ static const struct intel_device_info intel_valleyview_d_info = {
.has_fbc = 0, /* legal, last one wins */
.has_llc = 0, /* legal, last one wins */
GEN_DEFAULT_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
static const struct intel_device_info intel_haswell_d_info = {
@@ -247,6 +288,7 @@ static const struct intel_device_info intel_haswell_d_info = {
.has_fpga_dbg = 1,
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ IVB_CURSOR_OFFSETS,
};
static const struct intel_device_info intel_haswell_m_info = {
@@ -257,6 +299,7 @@ static const struct intel_device_info intel_haswell_m_info = {
.has_fpga_dbg = 1,
.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
GEN_DEFAULT_PIPEOFFSETS,
+ IVB_CURSOR_OFFSETS,
};
static const struct intel_device_info intel_broadwell_d_info = {
@@ -267,6 +310,7 @@ static const struct intel_device_info intel_broadwell_d_info = {
.has_ddi = 1,
.has_fbc = 1,
GEN_DEFAULT_PIPEOFFSETS,
+ IVB_CURSOR_OFFSETS,
};
static const struct intel_device_info intel_broadwell_m_info = {
@@ -277,6 +321,40 @@ static const struct intel_device_info intel_broadwell_m_info = {
.has_ddi = 1,
.has_fbc = 1,
GEN_DEFAULT_PIPEOFFSETS,
+ IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_broadwell_gt3d_info = {
+ .gen = 8, .num_pipes = 3,
+ .need_gfx_hws = 1, .has_hotplug = 1,
+ .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
+ .has_llc = 1,
+ .has_ddi = 1,
+ .has_fbc = 1,
+ GEN_DEFAULT_PIPEOFFSETS,
+ IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_broadwell_gt3m_info = {
+ .gen = 8, .is_mobile = 1, .num_pipes = 3,
+ .need_gfx_hws = 1, .has_hotplug = 1,
+ .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
+ .has_llc = 1,
+ .has_ddi = 1,
+ .has_fbc = 1,
+ GEN_DEFAULT_PIPEOFFSETS,
+ IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_cherryview_info = {
+ .is_preliminary = 1,
+ .gen = 8, .num_pipes = 3,
+ .need_gfx_hws = 1, .has_hotplug = 1,
+ .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+ .is_valleyview = 1,
+ .display_mmio_offset = VLV_DISPLAY_BASE,
+ GEN_CHV_PIPEOFFSETS,
+ CURSOR_OFFSETS,
};
/*
@@ -311,8 +389,11 @@ static const struct intel_device_info intel_broadwell_m_info = {
INTEL_HSW_M_IDS(&intel_haswell_m_info), \
INTEL_VLV_M_IDS(&intel_valleyview_m_info), \
INTEL_VLV_D_IDS(&intel_valleyview_d_info), \
- INTEL_BDW_M_IDS(&intel_broadwell_m_info), \
- INTEL_BDW_D_IDS(&intel_broadwell_d_info)
+ INTEL_BDW_GT12M_IDS(&intel_broadwell_m_info), \
+ INTEL_BDW_GT12D_IDS(&intel_broadwell_d_info), \
+ INTEL_BDW_GT3M_IDS(&intel_broadwell_gt3m_info), \
+ INTEL_BDW_GT3D_IDS(&intel_broadwell_gt3d_info), \
+ INTEL_CHV_IDS(&intel_cherryview_info)
static const struct pci_device_id pciidlist[] = { /* aka */
INTEL_PCI_IDS,
@@ -445,18 +526,20 @@ static int i915_drm_freeze(struct drm_device *dev)
return error;
}
- cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work);
-
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.
*/
- mutex_lock(&dev->mode_config.mutex);
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ drm_modeset_lock_all(dev);
+ for_each_crtc(dev, crtc) {
dev_priv->display.crtc_disable(crtc);
- mutex_unlock(&dev->mode_config.mutex);
+ }
+ drm_modeset_unlock_all(dev);
intel_modeset_suspend_hw(dev);
}
@@ -519,24 +602,6 @@ void intel_console_resume(struct work_struct *work)
console_unlock();
}
-static void intel_resume_hotplug(struct drm_device *dev)
-{
- struct drm_mode_config *mode_config = &dev->mode_config;
- struct intel_encoder *encoder;
-
- mutex_lock(&mode_config->mutex);
- DRM_DEBUG_KMS("running encoder hotplug functions\n");
-
- list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
- if (encoder->hot_plug)
- encoder->hot_plug(encoder);
-
- mutex_unlock(&mode_config->mutex);
-
- /* Just fire off a uevent and let userspace tell us what to do */
- drm_helper_hpd_irq_event(dev);
-}
-
static int i915_drm_thaw_early(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -551,7 +616,6 @@ static int i915_drm_thaw_early(struct drm_device *dev)
static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- int error = 0;
if (drm_core_check_feature(dev, DRIVER_MODESET) &&
restore_gtt_mappings) {
@@ -569,12 +633,14 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
drm_mode_config_reset(dev);
mutex_lock(&dev->struct_mutex);
-
- error = i915_gem_init_hw(dev);
+ if (i915_gem_init_hw(dev)) {
+ DRM_ERROR("failed to re-initialize GPU, declaring wedged!\n");
+ atomic_set_mask(I915_WEDGED, &dev_priv->gpu_error.reset_counter);
+ }
mutex_unlock(&dev->struct_mutex);
/* We need working interrupts for modeset enabling ... */
- drm_irq_install(dev);
+ drm_irq_install(dev, dev->pdev->irq);
intel_modeset_init_hw(dev);
@@ -591,7 +657,7 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
intel_hpd_init(dev);
dev_priv->enable_hotplug_processing = true;
/* Config may have changed between suspend and resume */
- intel_resume_hotplug(dev);
+ drm_helper_hpd_irq_event(dev);
}
intel_opregion_init(dev);
@@ -613,7 +679,7 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
mutex_unlock(&dev_priv->modeset_restore_lock);
intel_runtime_pm_put(dev_priv);
- return error;
+ return 0;
}
static int i915_drm_thaw(struct drm_device *dev)
@@ -746,18 +812,20 @@ int i915_reset(struct drm_device *dev)
return ret;
}
- drm_irq_uninstall(dev);
- drm_irq_install(dev);
+ /*
+ * FIXME: This races pretty badly against concurrent holders of
+ * ring interrupts. This is possible since we've started to drop
+ * dev->struct_mutex in select places when waiting for the gpu.
+ */
- /* rps/rc6 re-init is necessary to restore state lost after the
- * reset and the re-install of drm irq. Skip for ironlake per
+ /*
+ * rps/rc6 re-init is necessary to restore state lost after the
+ * reset and the re-install of gt irqs. Skip for ironlake per
* previous concerns that it doesn't respond well to some forms
- * of re-init after reset. */
- if (INTEL_INFO(dev)->gen > 5) {
- mutex_lock(&dev->struct_mutex);
- intel_enable_gt_powersave(dev);
- mutex_unlock(&dev->struct_mutex);
- }
+ * of re-init after reset.
+ */
+ if (INTEL_INFO(dev)->gen > 5)
+ intel_reset_gt_powersave(dev);
intel_hpd_init(dev);
} else {
@@ -891,21 +959,453 @@ static int i915_pm_poweroff(struct device *dev)
return i915_drm_freeze(drm_dev);
}
-static int i915_runtime_suspend(struct device *device)
+static int hsw_runtime_suspend(struct drm_i915_private *dev_priv)
+{
+ hsw_enable_pc8(dev_priv);
+
+ return 0;
+}
+
+static int snb_runtime_resume(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = dev_priv->dev;
+
+ intel_init_pch_refclk(dev);
+
+ return 0;
+}
+
+static int hsw_runtime_resume(struct drm_i915_private *dev_priv)
+{
+ hsw_disable_pc8(dev_priv);
+
+ return 0;
+}
+
+/*
+ * Save all Gunit registers that may be lost after a D3 and a subsequent
+ * S0i[R123] transition. The list of registers needing a save/restore is
+ * defined in the VLV2_S0IXRegs document. This documents marks all Gunit
+ * registers in the following way:
+ * - Driver: saved/restored by the driver
+ * - Punit : saved/restored by the Punit firmware
+ * - No, w/o marking: no need to save/restore, since the register is R/O or
+ * used internally by the HW in a way that doesn't depend
+ * keeping the content across a suspend/resume.
+ * - Debug : used for debugging
+ *
+ * We save/restore all registers marked with 'Driver', with the following
+ * exceptions:
+ * - Registers out of use, including also registers marked with 'Debug'.
+ * These have no effect on the driver's operation, so we don't save/restore
+ * them to reduce the overhead.
+ * - Registers that are fully setup by an initialization function called from
+ * the resume path. For example many clock gating and RPS/RC6 registers.
+ * - Registers that provide the right functionality with their reset defaults.
+ *
+ * TODO: Except for registers that based on the above 3 criteria can be safely
+ * ignored, we save/restore all others, practically treating the HW context as
+ * a black-box for the driver. Further investigation is needed to reduce the
+ * saved/restored registers even further, by following the same 3 criteria.
+ */
+static void vlv_save_gunit_s0ix_state(struct drm_i915_private *dev_priv)
+{
+ struct vlv_s0ix_state *s = &dev_priv->vlv_s0ix_state;
+ int i;
+
+ /* GAM 0x4000-0x4770 */
+ s->wr_watermark = I915_READ(GEN7_WR_WATERMARK);
+ s->gfx_prio_ctrl = I915_READ(GEN7_GFX_PRIO_CTRL);
+ s->arb_mode = I915_READ(ARB_MODE);
+ s->gfx_pend_tlb0 = I915_READ(GEN7_GFX_PEND_TLB0);
+ s->gfx_pend_tlb1 = I915_READ(GEN7_GFX_PEND_TLB1);
+
+ for (i = 0; i < ARRAY_SIZE(s->lra_limits); i++)
+ s->lra_limits[i] = I915_READ(GEN7_LRA_LIMITS_BASE + i * 4);
+
+ s->media_max_req_count = I915_READ(GEN7_MEDIA_MAX_REQ_COUNT);
+ s->gfx_max_req_count = I915_READ(GEN7_MEDIA_MAX_REQ_COUNT);
+
+ s->render_hwsp = I915_READ(RENDER_HWS_PGA_GEN7);
+ s->ecochk = I915_READ(GAM_ECOCHK);
+ s->bsd_hwsp = I915_READ(BSD_HWS_PGA_GEN7);
+ s->blt_hwsp = I915_READ(BLT_HWS_PGA_GEN7);
+
+ s->tlb_rd_addr = I915_READ(GEN7_TLB_RD_ADDR);
+
+ /* MBC 0x9024-0x91D0, 0x8500 */
+ s->g3dctl = I915_READ(VLV_G3DCTL);
+ s->gsckgctl = I915_READ(VLV_GSCKGCTL);
+ s->mbctl = I915_READ(GEN6_MBCTL);
+
+ /* GCP 0x9400-0x9424, 0x8100-0x810C */
+ s->ucgctl1 = I915_READ(GEN6_UCGCTL1);
+ s->ucgctl3 = I915_READ(GEN6_UCGCTL3);
+ s->rcgctl1 = I915_READ(GEN6_RCGCTL1);
+ s->rcgctl2 = I915_READ(GEN6_RCGCTL2);
+ s->rstctl = I915_READ(GEN6_RSTCTL);
+ s->misccpctl = I915_READ(GEN7_MISCCPCTL);
+
+ /* GPM 0xA000-0xAA84, 0x8000-0x80FC */
+ s->gfxpause = I915_READ(GEN6_GFXPAUSE);
+ s->rpdeuhwtc = I915_READ(GEN6_RPDEUHWTC);
+ s->rpdeuc = I915_READ(GEN6_RPDEUC);
+ s->ecobus = I915_READ(ECOBUS);
+ s->pwrdwnupctl = I915_READ(VLV_PWRDWNUPCTL);
+ s->rp_down_timeout = I915_READ(GEN6_RP_DOWN_TIMEOUT);
+ s->rp_deucsw = I915_READ(GEN6_RPDEUCSW);
+ s->rcubmabdtmr = I915_READ(GEN6_RCUBMABDTMR);
+ s->rcedata = I915_READ(VLV_RCEDATA);
+ s->spare2gh = I915_READ(VLV_SPAREG2H);
+
+ /* Display CZ domain, 0x4400C-0x4402C, 0x4F000-0x4F11F */
+ s->gt_imr = I915_READ(GTIMR);
+ s->gt_ier = I915_READ(GTIER);
+ s->pm_imr = I915_READ(GEN6_PMIMR);
+ s->pm_ier = I915_READ(GEN6_PMIER);
+
+ for (i = 0; i < ARRAY_SIZE(s->gt_scratch); i++)
+ s->gt_scratch[i] = I915_READ(GEN7_GT_SCRATCH_BASE + i * 4);
+
+ /* GT SA CZ domain, 0x100000-0x138124 */
+ s->tilectl = I915_READ(TILECTL);
+ s->gt_fifoctl = I915_READ(GTFIFOCTL);
+ s->gtlc_wake_ctrl = I915_READ(VLV_GTLC_WAKE_CTRL);
+ s->gtlc_survive = I915_READ(VLV_GTLC_SURVIVABILITY_REG);
+ s->pmwgicz = I915_READ(VLV_PMWGICZ);
+
+ /* Gunit-Display CZ domain, 0x182028-0x1821CF */
+ s->gu_ctl0 = I915_READ(VLV_GU_CTL0);
+ s->gu_ctl1 = I915_READ(VLV_GU_CTL1);
+ s->clock_gate_dis2 = I915_READ(VLV_GUNIT_CLOCK_GATE2);
+
+ /*
+ * Not saving any of:
+ * DFT, 0x9800-0x9EC0
+ * SARB, 0xB000-0xB1FC
+ * GAC, 0x5208-0x524C, 0x14000-0x14C000
+ * PCI CFG
+ */
+}
+
+static void vlv_restore_gunit_s0ix_state(struct drm_i915_private *dev_priv)
+{
+ struct vlv_s0ix_state *s = &dev_priv->vlv_s0ix_state;
+ u32 val;
+ int i;
+
+ /* GAM 0x4000-0x4770 */
+ I915_WRITE(GEN7_WR_WATERMARK, s->wr_watermark);
+ I915_WRITE(GEN7_GFX_PRIO_CTRL, s->gfx_prio_ctrl);
+ I915_WRITE(ARB_MODE, s->arb_mode | (0xffff << 16));
+ I915_WRITE(GEN7_GFX_PEND_TLB0, s->gfx_pend_tlb0);
+ I915_WRITE(GEN7_GFX_PEND_TLB1, s->gfx_pend_tlb1);
+
+ for (i = 0; i < ARRAY_SIZE(s->lra_limits); i++)
+ I915_WRITE(GEN7_LRA_LIMITS_BASE + i * 4, s->lra_limits[i]);
+
+ I915_WRITE(GEN7_MEDIA_MAX_REQ_COUNT, s->media_max_req_count);
+ I915_WRITE(GEN7_MEDIA_MAX_REQ_COUNT, s->gfx_max_req_count);
+
+ I915_WRITE(RENDER_HWS_PGA_GEN7, s->render_hwsp);
+ I915_WRITE(GAM_ECOCHK, s->ecochk);
+ I915_WRITE(BSD_HWS_PGA_GEN7, s->bsd_hwsp);
+ I915_WRITE(BLT_HWS_PGA_GEN7, s->blt_hwsp);
+
+ I915_WRITE(GEN7_TLB_RD_ADDR, s->tlb_rd_addr);
+
+ /* MBC 0x9024-0x91D0, 0x8500 */
+ I915_WRITE(VLV_G3DCTL, s->g3dctl);
+ I915_WRITE(VLV_GSCKGCTL, s->gsckgctl);
+ I915_WRITE(GEN6_MBCTL, s->mbctl);
+
+ /* GCP 0x9400-0x9424, 0x8100-0x810C */
+ I915_WRITE(GEN6_UCGCTL1, s->ucgctl1);
+ I915_WRITE(GEN6_UCGCTL3, s->ucgctl3);
+ I915_WRITE(GEN6_RCGCTL1, s->rcgctl1);
+ I915_WRITE(GEN6_RCGCTL2, s->rcgctl2);
+ I915_WRITE(GEN6_RSTCTL, s->rstctl);
+ I915_WRITE(GEN7_MISCCPCTL, s->misccpctl);
+
+ /* GPM 0xA000-0xAA84, 0x8000-0x80FC */
+ I915_WRITE(GEN6_GFXPAUSE, s->gfxpause);
+ I915_WRITE(GEN6_RPDEUHWTC, s->rpdeuhwtc);
+ I915_WRITE(GEN6_RPDEUC, s->rpdeuc);
+ I915_WRITE(ECOBUS, s->ecobus);
+ I915_WRITE(VLV_PWRDWNUPCTL, s->pwrdwnupctl);
+ I915_WRITE(GEN6_RP_DOWN_TIMEOUT,s->rp_down_timeout);
+ I915_WRITE(GEN6_RPDEUCSW, s->rp_deucsw);
+ I915_WRITE(GEN6_RCUBMABDTMR, s->rcubmabdtmr);
+ I915_WRITE(VLV_RCEDATA, s->rcedata);
+ I915_WRITE(VLV_SPAREG2H, s->spare2gh);
+
+ /* Display CZ domain, 0x4400C-0x4402C, 0x4F000-0x4F11F */
+ I915_WRITE(GTIMR, s->gt_imr);
+ I915_WRITE(GTIER, s->gt_ier);
+ I915_WRITE(GEN6_PMIMR, s->pm_imr);
+ I915_WRITE(GEN6_PMIER, s->pm_ier);
+
+ for (i = 0; i < ARRAY_SIZE(s->gt_scratch); i++)
+ I915_WRITE(GEN7_GT_SCRATCH_BASE + i * 4, s->gt_scratch[i]);
+
+ /* GT SA CZ domain, 0x100000-0x138124 */
+ I915_WRITE(TILECTL, s->tilectl);
+ I915_WRITE(GTFIFOCTL, s->gt_fifoctl);
+ /*
+ * Preserve the GT allow wake and GFX force clock bit, they are not
+ * be restored, as they are used to control the s0ix suspend/resume
+ * sequence by the caller.
+ */
+ val = I915_READ(VLV_GTLC_WAKE_CTRL);
+ val &= VLV_GTLC_ALLOWWAKEREQ;
+ val |= s->gtlc_wake_ctrl & ~VLV_GTLC_ALLOWWAKEREQ;
+ I915_WRITE(VLV_GTLC_WAKE_CTRL, val);
+
+ val = I915_READ(VLV_GTLC_SURVIVABILITY_REG);
+ val &= VLV_GFX_CLK_FORCE_ON_BIT;
+ val |= s->gtlc_survive & ~VLV_GFX_CLK_FORCE_ON_BIT;
+ I915_WRITE(VLV_GTLC_SURVIVABILITY_REG, val);
+
+ I915_WRITE(VLV_PMWGICZ, s->pmwgicz);
+
+ /* Gunit-Display CZ domain, 0x182028-0x1821CF */
+ I915_WRITE(VLV_GU_CTL0, s->gu_ctl0);
+ I915_WRITE(VLV_GU_CTL1, s->gu_ctl1);
+ I915_WRITE(VLV_GUNIT_CLOCK_GATE2, s->clock_gate_dis2);
+}
+
+int vlv_force_gfx_clock(struct drm_i915_private *dev_priv, bool force_on)
+{
+ u32 val;
+ int err;
+
+ val = I915_READ(VLV_GTLC_SURVIVABILITY_REG);
+ WARN_ON(!!(val & VLV_GFX_CLK_FORCE_ON_BIT) == force_on);
+
+#define COND (I915_READ(VLV_GTLC_SURVIVABILITY_REG) & VLV_GFX_CLK_STATUS_BIT)
+ /* Wait for a previous force-off to settle */
+ if (force_on) {
+ err = wait_for(!COND, 20);
+ if (err) {
+ DRM_ERROR("timeout waiting for GFX clock force-off (%08x)\n",
+ I915_READ(VLV_GTLC_SURVIVABILITY_REG));
+ return err;
+ }
+ }
+
+ val = I915_READ(VLV_GTLC_SURVIVABILITY_REG);
+ val &= ~VLV_GFX_CLK_FORCE_ON_BIT;
+ if (force_on)
+ val |= VLV_GFX_CLK_FORCE_ON_BIT;
+ I915_WRITE(VLV_GTLC_SURVIVABILITY_REG, val);
+
+ if (!force_on)
+ return 0;
+
+ err = wait_for(COND, 20);
+ if (err)
+ DRM_ERROR("timeout waiting for GFX clock force-on (%08x)\n",
+ I915_READ(VLV_GTLC_SURVIVABILITY_REG));
+
+ return err;
+#undef COND
+}
+
+static int vlv_allow_gt_wake(struct drm_i915_private *dev_priv, bool allow)
+{
+ u32 val;
+ int err = 0;
+
+ val = I915_READ(VLV_GTLC_WAKE_CTRL);
+ val &= ~VLV_GTLC_ALLOWWAKEREQ;
+ if (allow)
+ val |= VLV_GTLC_ALLOWWAKEREQ;
+ I915_WRITE(VLV_GTLC_WAKE_CTRL, val);
+ POSTING_READ(VLV_GTLC_WAKE_CTRL);
+
+#define COND (!!(I915_READ(VLV_GTLC_PW_STATUS) & VLV_GTLC_ALLOWWAKEACK) == \
+ allow)
+ err = wait_for(COND, 1);
+ if (err)
+ DRM_ERROR("timeout disabling GT waking\n");
+ return err;
+#undef COND
+}
+
+static int vlv_wait_for_gt_wells(struct drm_i915_private *dev_priv,
+ bool wait_for_on)
+{
+ u32 mask;
+ u32 val;
+ int err;
+
+ mask = VLV_GTLC_PW_MEDIA_STATUS_MASK | VLV_GTLC_PW_RENDER_STATUS_MASK;
+ val = wait_for_on ? mask : 0;
+#define COND ((I915_READ(VLV_GTLC_PW_STATUS) & mask) == val)
+ if (COND)
+ return 0;
+
+ DRM_DEBUG_KMS("waiting for GT wells to go %s (%08x)\n",
+ wait_for_on ? "on" : "off",
+ I915_READ(VLV_GTLC_PW_STATUS));
+
+ /*
+ * RC6 transitioning can be delayed up to 2 msec (see
+ * valleyview_enable_rps), use 3 msec for safety.
+ */
+ err = wait_for(COND, 3);
+ if (err)
+ DRM_ERROR("timeout waiting for GT wells to go %s\n",
+ wait_for_on ? "on" : "off");
+
+ return err;
+#undef COND
+}
+
+static void vlv_check_no_gt_access(struct drm_i915_private *dev_priv)
+{
+ if (!(I915_READ(VLV_GTLC_PW_STATUS) & VLV_GTLC_ALLOWWAKEERR))
+ return;
+
+ DRM_ERROR("GT register access while GT waking disabled\n");
+ I915_WRITE(VLV_GTLC_PW_STATUS, VLV_GTLC_ALLOWWAKEERR);
+}
+
+static int vlv_runtime_suspend(struct drm_i915_private *dev_priv)
+{
+ u32 mask;
+ int err;
+
+ /*
+ * Bspec defines the following GT well on flags as debug only, so
+ * don't treat them as hard failures.
+ */
+ (void)vlv_wait_for_gt_wells(dev_priv, false);
+
+ mask = VLV_GTLC_RENDER_CTX_EXISTS | VLV_GTLC_MEDIA_CTX_EXISTS;
+ WARN_ON((I915_READ(VLV_GTLC_WAKE_CTRL) & mask) != mask);
+
+ vlv_check_no_gt_access(dev_priv);
+
+ err = vlv_force_gfx_clock(dev_priv, true);
+ if (err)
+ goto err1;
+
+ err = vlv_allow_gt_wake(dev_priv, false);
+ if (err)
+ goto err2;
+ vlv_save_gunit_s0ix_state(dev_priv);
+
+ err = vlv_force_gfx_clock(dev_priv, false);
+ if (err)
+ goto err2;
+
+ return 0;
+
+err2:
+ /* For safety always re-enable waking and disable gfx clock forcing */
+ vlv_allow_gt_wake(dev_priv, true);
+err1:
+ vlv_force_gfx_clock(dev_priv, false);
+
+ return err;
+}
+
+static int vlv_runtime_resume(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = dev_priv->dev;
+ int err;
+ int ret;
+
+ /*
+ * If any of the steps fail just try to continue, that's the best we
+ * can do at this point. Return the first error code (which will also
+ * leave RPM permanently disabled).
+ */
+ ret = vlv_force_gfx_clock(dev_priv, true);
+
+ vlv_restore_gunit_s0ix_state(dev_priv);
+
+ err = vlv_allow_gt_wake(dev_priv, true);
+ if (!ret)
+ ret = err;
+
+ err = vlv_force_gfx_clock(dev_priv, false);
+ if (!ret)
+ ret = err;
+
+ vlv_check_no_gt_access(dev_priv);
+
+ intel_init_clock_gating(dev);
+ i915_gem_restore_fences(dev);
+
+ return ret;
+}
+
+static int intel_runtime_suspend(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct drm_device *dev = pci_get_drvdata(pdev);
struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
+ if (WARN_ON_ONCE(!(dev_priv->rps.enabled && intel_enable_rc6(dev))))
+ return -ENODEV;
WARN_ON(!HAS_RUNTIME_PM(dev));
assert_force_wake_inactive(dev_priv);
DRM_DEBUG_KMS("Suspending device\n");
- if (HAS_PC8(dev))
- hsw_enable_pc8(dev_priv);
+ /*
+ * We could deadlock here in case another thread holding struct_mutex
+ * calls RPM suspend concurrently, since the RPM suspend will wait
+ * first for this RPM suspend to finish. In this case the concurrent
+ * RPM resume will be followed by its RPM suspend counterpart. Still
+ * for consistency return -EAGAIN, which will reschedule this suspend.
+ */
+ if (!mutex_trylock(&dev->struct_mutex)) {
+ DRM_DEBUG_KMS("device lock contention, deffering suspend\n");
+ /*
+ * Bump the expiration timestamp, otherwise the suspend won't
+ * be rescheduled.
+ */
+ pm_runtime_mark_last_busy(device);
+ return -EAGAIN;
+ }
+ /*
+ * We are safe here against re-faults, since the fault handler takes
+ * an RPM reference.
+ */
i915_gem_release_all_mmaps(dev_priv);
+ mutex_unlock(&dev->struct_mutex);
+
+ /*
+ * rps.work can't be rearmed here, since we get here only after making
+ * sure the GPU is idle and the RPS freq is set to the minimum. See
+ * intel_mark_idle().
+ */
+ 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);
+ }
+
+ if (ret) {
+ DRM_ERROR("Runtime suspend failed, disabling it (%d)\n", ret);
+ intel_runtime_pm_restore_interrupts(dev);
+
+ return ret;
+ }
del_timer_sync(&dev_priv->gpu_error.hangcheck_timer);
dev_priv->pm.suspended = true;
@@ -923,11 +1423,12 @@ static int i915_runtime_suspend(struct device *device)
return 0;
}
-static int i915_runtime_resume(struct device *device)
+static int intel_runtime_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct drm_device *dev = pci_get_drvdata(pdev);
struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
WARN_ON(!HAS_RUNTIME_PM(dev));
@@ -936,11 +1437,33 @@ static int i915_runtime_resume(struct device *device)
intel_opregion_notify_adapter(dev, PCI_D0);
dev_priv->pm.suspended = false;
- if (HAS_PC8(dev))
- hsw_disable_pc8(dev_priv);
+ 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;
+ }
- DRM_DEBUG_KMS("Device resumed\n");
- return 0;
+ /*
+ * 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).
+ */
+ i915_gem_init_swizzling(dev);
+ gen6_update_ring_freq(dev);
+
+ intel_runtime_pm_restore_interrupts(dev);
+ intel_reset_gt_powersave(dev);
+
+ if (ret)
+ DRM_ERROR("Runtime resume failed, disabling it (%d)\n", ret);
+ else
+ DRM_DEBUG_KMS("Device resumed\n");
+
+ return ret;
}
static const struct dev_pm_ops i915_pm_ops = {
@@ -954,8 +1477,8 @@ static const struct dev_pm_ops i915_pm_ops = {
.poweroff = i915_pm_poweroff,
.restore_early = i915_pm_resume_early,
.restore = i915_pm_resume,
- .runtime_suspend = i915_runtime_suspend,
- .runtime_resume = i915_runtime_resume,
+ .runtime_suspend = intel_runtime_suspend,
+ .runtime_resume = intel_runtime_resume,
};
static const struct vm_operations_struct i915_gem_vm_ops = {
@@ -1062,6 +1585,7 @@ static int __init i915_init(void)
driver.get_vblank_timestamp = NULL;
#ifndef CONFIG_DRM_I915_UMS
/* Silently fail loading to not upset userspace. */
+ DRM_DEBUG_DRIVER("KMS and UMS disabled.\n");
return 0;
#endif
}
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 388c028e223c..49414d30e8d4 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -35,11 +35,13 @@
#include "i915_reg.h"
#include "intel_bios.h"
#include "intel_ringbuffer.h"
+#include "i915_gem_gtt.h"
#include <linux/io-mapping.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <drm/intel-gtt.h>
#include <linux/backlight.h>
+#include <linux/hashtable.h>
#include <linux/intel-iommu.h>
#include <linux/kref.h>
#include <linux/pm_qos.h>
@@ -91,7 +93,7 @@ enum port {
};
#define port_name(p) ((p) + 'A')
-#define I915_NUM_PHYS_VLV 1
+#define I915_NUM_PHYS_VLV 2
enum dpio_channel {
DPIO_CH0,
@@ -162,6 +164,12 @@ enum hpd_pin {
#define for_each_pipe(p) for ((p) = 0; (p) < INTEL_INFO(dev)->num_pipes; (p)++)
#define for_each_sprite(p, s) for ((s) = 0; (s) < INTEL_INFO(dev)->num_sprites[(p)]; (s)++)
+#define for_each_crtc(dev, crtc) \
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+
+#define for_each_intel_crtc(dev, intel_crtc) \
+ list_for_each_entry(intel_crtc, &dev->mode_config.crtc_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))
@@ -171,6 +179,7 @@ enum hpd_pin {
if ((intel_connector)->base.encoder == (__encoder))
struct drm_i915_private;
+struct i915_mmu_object;
enum intel_dpll_id {
DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */
@@ -312,7 +321,6 @@ struct drm_i915_error_state {
u32 gab_ctl;
u32 gfx_mode;
u32 extra_instdone[I915_NUM_INSTDONE_REG];
- u32 pipestat[I915_MAX_PIPES];
u64 fence[I915_MAX_NUM_FENCES];
struct intel_overlay_error_state *overlay;
struct intel_display_error_state *display;
@@ -346,7 +354,7 @@ struct drm_i915_error_state {
u64 bbaddr;
u64 acthd;
u32 fault_reg;
- u32 faddr;
+ u64 faddr;
u32 rc_psmi; /* sleep state */
u32 semaphore_mboxes[I915_NUM_RINGS - 1];
@@ -385,6 +393,7 @@ struct drm_i915_error_state {
u32 tiling:2;
u32 dirty:1;
u32 purgeable:1;
+ u32 userptr:1;
s32 ring:4;
u32 cache_level:3;
} **active_bo, **pinned_bo;
@@ -449,10 +458,11 @@ struct drm_i915_display_funcs {
int (*queue_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);
- int (*update_primary_plane)(struct drm_crtc *crtc,
- struct drm_framebuffer *fb,
- int x, int y);
+ void (*update_primary_plane)(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y);
void (*hpd_irq_setup)(struct drm_device *dev);
/* clock updates for mode set */
/* cursor updates */
@@ -545,6 +555,7 @@ struct intel_device_info {
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];
};
#undef DEFINE_FLAG
@@ -560,168 +571,6 @@ enum i915_cache_level {
I915_CACHE_WT, /* hsw:gt3e WriteThrough for scanouts */
};
-typedef uint32_t gen6_gtt_pte_t;
-
-/**
- * A VMA represents a GEM BO that is bound into an address space. Therefore, a
- * VMA's presence cannot be guaranteed before binding, or after unbinding the
- * object into/from the address space.
- *
- * To make things as simple as possible (ie. no refcounting), a VMA's lifetime
- * will always be <= an objects lifetime. So object refcounting should cover us.
- */
-struct i915_vma {
- struct drm_mm_node node;
- struct drm_i915_gem_object *obj;
- struct i915_address_space *vm;
-
- /** This object's place on the active/inactive lists */
- struct list_head mm_list;
-
- struct list_head vma_link; /* Link in the object's VMA list */
-
- /** This vma's place in the batchbuffer or on the eviction list */
- struct list_head exec_list;
-
- /**
- * Used for performing relocations during execbuffer insertion.
- */
- struct hlist_node exec_node;
- unsigned long exec_handle;
- struct drm_i915_gem_exec_object2 *exec_entry;
-
- /**
- * How many users have pinned this object in GTT space. The following
- * users can each hold at most one reference: pwrite/pread, pin_ioctl
- * (via user_pin_count), execbuffer (objects are not allowed multiple
- * times for the same batchbuffer), and the framebuffer code. When
- * switching/pageflipping, the framebuffer code has at most two buffers
- * pinned per crtc.
- *
- * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3
- * bits with absolutely no headroom. So use 4 bits. */
- unsigned int pin_count:4;
-#define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf
-
- /** Unmap an object from an address space. This usually consists of
- * setting the valid PTE entries to a reserved scratch page. */
- void (*unbind_vma)(struct i915_vma *vma);
- /* Map an object into an address space with the given cache flags. */
-#define GLOBAL_BIND (1<<0)
- void (*bind_vma)(struct i915_vma *vma,
- enum i915_cache_level cache_level,
- u32 flags);
-};
-
-struct i915_address_space {
- struct drm_mm mm;
- struct drm_device *dev;
- struct list_head global_link;
- unsigned long start; /* Start offset always 0 for dri2 */
- size_t total; /* size addr space maps (ex. 2GB for ggtt) */
-
- struct {
- dma_addr_t addr;
- struct page *page;
- } scratch;
-
- /**
- * List of objects currently involved in rendering.
- *
- * Includes buffers having the contents of their GPU caches
- * flushed, not necessarily primitives. last_rendering_seqno
- * represents when the rendering involved will be completed.
- *
- * A reference is held on the buffer while on this list.
- */
- struct list_head active_list;
-
- /**
- * LRU list of objects which are not in the ringbuffer and
- * are ready to unbind, but are still in the GTT.
- *
- * last_rendering_seqno is 0 while an object is in this list.
- *
- * A reference is not held on the buffer while on this list,
- * as merely being GTT-bound shouldn't prevent its being
- * freed, and we'll pull it off the list in the free path.
- */
- struct list_head inactive_list;
-
- /* 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 */
- void (*clear_range)(struct i915_address_space *vm,
- uint64_t start,
- uint64_t length,
- bool use_scratch);
- void (*insert_entries)(struct i915_address_space *vm,
- struct sg_table *st,
- uint64_t start,
- enum i915_cache_level cache_level);
- void (*cleanup)(struct i915_address_space *vm);
-};
-
-/* The Graphics Translation Table is the way in which GEN hardware translates a
- * Graphics Virtual Address into a Physical Address. In addition to the normal
- * collateral associated with any va->pa translations GEN hardware also has a
- * portion of the GTT which can be mapped by the CPU and remain both coherent
- * and correct (in cases like swizzling). That region is referred to as GMADR in
- * the spec.
- */
-struct i915_gtt {
- struct i915_address_space base;
- size_t stolen_size; /* Total size of stolen memory */
-
- unsigned long mappable_end; /* End offset that we can CPU map */
- struct io_mapping *mappable; /* Mapping to our CPU mappable region */
- phys_addr_t mappable_base; /* PA of our GMADR */
-
- /** "Graphics Stolen Memory" holds the global PTEs */
- void __iomem *gsm;
-
- bool do_idle_maps;
-
- int mtrr;
-
- /* global gtt ops */
- int (*gtt_probe)(struct drm_device *dev, size_t *gtt_total,
- size_t *stolen, phys_addr_t *mappable_base,
- unsigned long *mappable_end);
-};
-#define gtt_total_entries(gtt) ((gtt).base.total >> PAGE_SHIFT)
-
-#define GEN8_LEGACY_PDPS 4
-struct i915_hw_ppgtt {
- struct i915_address_space base;
- struct kref ref;
- struct drm_mm_node node;
- unsigned num_pd_entries;
- unsigned num_pd_pages; /* gen8+ */
- union {
- struct page **pt_pages;
- struct page **gen8_pt_pages[GEN8_LEGACY_PDPS];
- };
- struct page *pd_pages;
- union {
- uint32_t pd_offset;
- dma_addr_t pd_dma_addr[GEN8_LEGACY_PDPS];
- };
- union {
- dma_addr_t *pt_dma_addr;
- dma_addr_t *gen8_pt_dma_addr[4];
- };
-
- struct i915_hw_context *ctx;
-
- int (*enable)(struct i915_hw_ppgtt *ppgtt);
- int (*switch_mm)(struct i915_hw_ppgtt *ppgtt,
- struct intel_ring_buffer *ring,
- bool synchronous);
- void (*debug_dump)(struct i915_hw_ppgtt *ppgtt, struct seq_file *m);
-};
-
struct i915_ctx_hang_stats {
/* This context had batch pending when hang was declared */
unsigned batch_pending;
@@ -738,13 +587,13 @@ struct i915_ctx_hang_stats {
/* This must match up with the value previously used for execbuf2.rsvd1. */
#define DEFAULT_CONTEXT_ID 0
-struct i915_hw_context {
+struct intel_context {
struct kref ref;
int id;
bool is_initialized;
uint8_t remap_slice;
struct drm_i915_file_private *file_priv;
- struct intel_ring_buffer *last_ring;
+ struct intel_engine_cs *last_ring;
struct drm_i915_gem_object *obj;
struct i915_ctx_hang_stats hang_stats;
struct i915_address_space *vm;
@@ -782,6 +631,10 @@ struct i915_fbc {
} no_fbc_reason;
};
+struct i915_drrs {
+ struct intel_connector *connector;
+};
+
struct i915_psr {
bool sink_support;
bool source_ok;
@@ -965,6 +818,67 @@ struct i915_suspend_saved_registers {
u32 savePCH_PORT_HOTPLUG;
};
+struct vlv_s0ix_state {
+ /* GAM */
+ u32 wr_watermark;
+ u32 gfx_prio_ctrl;
+ u32 arb_mode;
+ u32 gfx_pend_tlb0;
+ u32 gfx_pend_tlb1;
+ u32 lra_limits[GEN7_LRA_LIMITS_REG_NUM];
+ u32 media_max_req_count;
+ u32 gfx_max_req_count;
+ u32 render_hwsp;
+ u32 ecochk;
+ u32 bsd_hwsp;
+ u32 blt_hwsp;
+ u32 tlb_rd_addr;
+
+ /* MBC */
+ u32 g3dctl;
+ u32 gsckgctl;
+ u32 mbctl;
+
+ /* GCP */
+ u32 ucgctl1;
+ u32 ucgctl3;
+ u32 rcgctl1;
+ u32 rcgctl2;
+ u32 rstctl;
+ u32 misccpctl;
+
+ /* GPM */
+ u32 gfxpause;
+ u32 rpdeuhwtc;
+ u32 rpdeuc;
+ u32 ecobus;
+ u32 pwrdwnupctl;
+ u32 rp_down_timeout;
+ u32 rp_deucsw;
+ u32 rcubmabdtmr;
+ u32 rcedata;
+ u32 spare2gh;
+
+ /* Display 1 CZ domain */
+ u32 gt_imr;
+ u32 gt_ier;
+ u32 pm_imr;
+ u32 pm_ier;
+ u32 gt_scratch[GEN7_GT_SCRATCH_REG_NUM];
+
+ /* GT SA CZ domain */
+ u32 tilectl;
+ u32 gt_fifoctl;
+ u32 gtlc_wake_ctrl;
+ u32 gtlc_survive;
+ u32 pmwgicz;
+
+ /* Display 2 CZ domain */
+ u32 gu_ctl0;
+ u32 gu_ctl1;
+ u32 clock_gate_dis2;
+};
+
struct intel_gen6_power_mgmt {
/* work and pm_iir are protected by dev_priv->irq_lock */
struct work_struct work;
@@ -1074,6 +988,7 @@ struct i915_power_domains {
* time are on. They are kept on until after the first modeset.
*/
bool init_power_on;
+ bool initializing;
int power_well_count;
struct mutex lock;
@@ -1132,7 +1047,8 @@ struct i915_gem_mm {
/** PPGTT used for aliasing the PPGTT with the GTT */
struct i915_hw_ppgtt *aliasing_ppgtt;
- struct shrinker inactive_shrinker;
+ struct notifier_block oom_notifier;
+ struct shrinker shrinker;
bool shrinker_no_lock_stealing;
/** LRU list of objects with fence regs on them. */
@@ -1170,6 +1086,9 @@ struct i915_gem_mm {
*/
bool busy;
+ /* the indicator for dispatch video commands on two BSD rings */
+ int bsd_ring_dispatch_index;
+
/** Bit 6 swizzling required for X tiling */
uint32_t bit_6_swizzle_x;
/** Bit 6 swizzling required for Y tiling */
@@ -1245,8 +1164,12 @@ struct i915_gpu_error {
*/
wait_queue_head_t reset_queue;
- /* For gpu hang simulation. */
- unsigned int stop_rings;
+ /* Userspace knobs for gpu hang simulation;
+ * combines both a ring mask, and extra flags
+ */
+ u32 stop_rings;
+#define I915_STOP_RING_ALLOW_BAN (1 << 31)
+#define I915_STOP_RING_ALLOW_WARN (1 << 30)
/* For missed irq/seqno simulation. */
unsigned int test_irq_rings;
@@ -1266,6 +1189,12 @@ struct ddi_vbt_port_info {
uint8_t supports_dp:1;
};
+enum drrs_support_type {
+ DRRS_NOT_SUPPORTED = 0,
+ STATIC_DRRS_SUPPORT = 1,
+ SEAMLESS_DRRS_SUPPORT = 2
+};
+
struct intel_vbt_data {
struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
@@ -1278,9 +1207,12 @@ struct intel_vbt_data {
unsigned int lvds_use_ssc:1;
unsigned int display_clock_mode:1;
unsigned int fdi_rx_polarity_inverted:1;
+ unsigned int has_mipi:1;
int lvds_ssc_freq;
unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */
+ enum drrs_support_type drrs_type;
+
/* eDP */
int edp_rate;
int edp_lanes;
@@ -1299,7 +1231,14 @@ struct intel_vbt_data {
/* MIPI DSI */
struct {
+ u16 port;
u16 panel_id;
+ struct mipi_config *config;
+ struct mipi_pps_data *pps;
+ u8 seq_version;
+ u32 size;
+ u8 *data;
+ u8 *sequence[MIPI_SEQ_MAX];
} dsi;
int crt_ddc_pin;
@@ -1351,23 +1290,13 @@ struct ilk_wm_values {
* goes back to false exactly before we reenable the IRQs. We use this variable
* to check if someone is trying to enable/disable IRQs while they're supposed
* to be disabled. This shouldn't happen and we'll print some error messages in
- * case it happens, but if it actually happens we'll also update the variables
- * inside struct regsave so when we restore the IRQs they will contain the
- * latest expected values.
+ * case it happens.
*
* For more, read the Documentation/power/runtime_pm.txt.
*/
struct i915_runtime_pm {
bool suspended;
bool irqs_disabled;
-
- struct {
- uint32_t deimr;
- uint32_t sdeimr;
- uint32_t gtimr;
- uint32_t gtier;
- uint32_t gen6_pmimr;
- } regsave;
};
enum intel_pipe_crc_source {
@@ -1400,7 +1329,7 @@ struct intel_pipe_crc {
wait_queue_head_t wq;
};
-typedef struct drm_i915_private {
+struct drm_i915_private {
struct drm_device *dev;
struct kmem_cache *slab;
@@ -1424,10 +1353,13 @@ typedef struct drm_i915_private {
*/
uint32_t gpio_mmio_base;
+ /* MMIO base address for MIPI regs */
+ uint32_t mipi_mmio_base;
+
wait_queue_head_t gmbus_wait_queue;
struct pci_dev *bridge_dev;
- struct intel_ring_buffer ring[I915_NUM_RINGS];
+ struct intel_engine_cs ring[I915_NUM_RINGS];
uint32_t last_seqno, next_seqno;
drm_dma_handle_t *status_page_dmah;
@@ -1469,6 +1401,7 @@ typedef struct drm_i915_private {
struct timer_list hotplug_reenable_timer;
struct i915_fbc fbc;
+ struct i915_drrs drrs;
struct intel_opregion opregion;
struct intel_vbt_data vbt;
@@ -1486,6 +1419,7 @@ typedef struct drm_i915_private {
int num_fence_regs; /* 8 on pre-965, 16 otherwise */
unsigned int fsb_freq, mem_freq, is_ddr3;
+ unsigned int vlv_cdclk_freq;
/**
* wq - Driver workqueue for GEM.
@@ -1509,9 +1443,12 @@ typedef struct drm_i915_private {
struct mutex modeset_restore_lock;
struct list_head vm_list; /* Global list of all address spaces */
- struct i915_gtt gtt; /* VMA representing the global address space */
+ 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
/* Kernel Modesetting */
@@ -1580,6 +1517,7 @@ typedef struct drm_i915_private {
u32 suspend_count;
struct i915_suspend_saved_registers regfile;
+ struct vlv_s0ix_state vlv_s0ix_state;
struct {
/*
@@ -1605,7 +1543,12 @@ typedef struct drm_i915_private {
struct i915_dri1_state dri1;
/* Old ums support infrastructure, same warning applies. */
struct i915_ums_state ums;
-} drm_i915_private_t;
+
+ /*
+ * NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
+ * will be rejected. Instead look for a better place.
+ */
+};
static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
{
@@ -1642,6 +1585,8 @@ struct drm_i915_gem_object_ops {
*/
int (*get_pages)(struct drm_i915_gem_object *);
void (*put_pages)(struct drm_i915_gem_object *);
+ int (*dmabuf_export)(struct drm_i915_gem_object *);
+ void (*release)(struct drm_i915_gem_object *);
};
struct drm_i915_gem_object {
@@ -1732,7 +1677,7 @@ struct drm_i915_gem_object {
void *dma_buf_vmapping;
int vmapping_count;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
/** Breadcrumb of last rendering to the buffer. */
uint32_t last_read_seqno;
@@ -1755,8 +1700,20 @@ struct drm_i915_gem_object {
/** for phy allocated objects */
drm_dma_handle_t *phys_handle;
-};
+ union {
+ struct i915_gem_userptr {
+ uintptr_t ptr;
+ unsigned read_only :1;
+ unsigned workers :4;
+#define I915_GEM_USERPTR_MAX_WORKERS 15
+
+ struct mm_struct *mm;
+ struct i915_mmu_object *mn;
+ struct work_struct *work;
+ } userptr;
+ };
+};
#define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base)
/**
@@ -1771,7 +1728,7 @@ struct drm_i915_gem_object {
*/
struct drm_i915_gem_request {
/** On Which ring this request was generated */
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
/** GEM sequence number associated with this request. */
uint32_t seqno;
@@ -1783,7 +1740,7 @@ struct drm_i915_gem_request {
u32 tail;
/** Context related to this request */
- struct i915_hw_context *ctx;
+ struct intel_context *ctx;
/** Batch buffer related to this request if any */
struct drm_i915_gem_object *batch_obj;
@@ -1810,8 +1767,8 @@ struct drm_i915_file_private {
} mm;
struct idr context_idr;
- struct i915_hw_context *private_default_ctx;
atomic_t rps_wait_boost;
+ struct intel_engine_cs *bsd_ring;
};
/*
@@ -1879,11 +1836,17 @@ struct drm_i915_cmd_descriptor {
* the expected value, the parser rejects it. Only valid if flags has
* the CMD_DESC_BITMASK bit set. Only entries where mask is non-zero
* are valid.
+ *
+ * If the check specifies a non-zero condition_mask then the parser
+ * only performs the check when the bits specified by condition_mask
+ * are non-zero.
*/
struct {
u32 offset;
u32 mask;
u32 expected;
+ u32 condition_offset;
+ u32 condition_mask;
} bits[MAX_CMD_DESC_BITMASKS];
};
@@ -1925,8 +1888,9 @@ struct drm_i915_cmd_table {
(dev)->pdev->device == 0x0106 || \
(dev)->pdev->device == 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)->gen == 8)
+#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)
@@ -1962,17 +1926,21 @@ struct drm_i915_cmd_table {
#define BSD_RING (1<<VCS)
#define BLT_RING (1<<BCS)
#define VEBOX_RING (1<<VECS)
-#define HAS_BSD(dev) (INTEL_INFO(dev)->ring_mask & BSD_RING)
-#define HAS_BLT(dev) (INTEL_INFO(dev)->ring_mask & BLT_RING)
-#define HAS_VEBOX(dev) (INTEL_INFO(dev)->ring_mask & VEBOX_RING)
-#define HAS_LLC(dev) (INTEL_INFO(dev)->has_llc)
-#define HAS_WT(dev) (IS_HASWELL(dev) && to_i915(dev)->ellc_size)
+#define BSD2_RING (1<<VCS2)
+#define HAS_BSD(dev) (INTEL_INFO(dev)->ring_mask & BSD_RING)
+#define HAS_BSD2(dev) (INTEL_INFO(dev)->ring_mask & BSD2_RING)
+#define HAS_BLT(dev) (INTEL_INFO(dev)->ring_mask & BLT_RING)
+#define HAS_VEBOX(dev) (INTEL_INFO(dev)->ring_mask & VEBOX_RING)
+#define HAS_LLC(dev) (INTEL_INFO(dev)->has_llc)
+#define HAS_WT(dev) ((IS_HASWELL(dev) || IS_BROADWELL(dev)) && \
+ to_i915(dev)->ellc_size)
#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))
-#define HAS_PPGTT(dev) (INTEL_INFO(dev)->gen >= 7 && !IS_VALLEYVIEW(dev) \
- && !IS_BROADWELL(dev))
+#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)
@@ -2010,8 +1978,8 @@ struct drm_i915_cmd_table {
#define HAS_DDI(dev) (INTEL_INFO(dev)->has_ddi)
#define HAS_FPGA_DBG_UNCLAIMED(dev) (INTEL_INFO(dev)->has_fpga_dbg)
#define HAS_PSR(dev) (IS_HASWELL(dev) || IS_BROADWELL(dev))
-#define HAS_PC8(dev) (IS_HASWELL(dev)) /* XXX HSW:ULX */
-#define HAS_RUNTIME_PM(dev) (IS_HASWELL(dev))
+#define HAS_RUNTIME_PM(dev) (IS_GEN6(dev) || IS_HASWELL(dev) || \
+ IS_BROADWELL(dev) || IS_VALLEYVIEW(dev))
#define INTEL_PCH_DEVICE_ID_MASK 0xff00
#define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00
@@ -2068,6 +2036,7 @@ struct i915_params {
bool prefault_disable;
bool reset;
bool disable_display;
+ bool disable_vtd_wa;
};
extern struct i915_params i915 __read_mostly;
@@ -2096,6 +2065,7 @@ extern unsigned long i915_chipset_val(struct drm_i915_private *dev_priv);
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);
@@ -2170,6 +2140,9 @@ int i915_gem_set_tiling(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_get_tiling(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+int i915_gem_init_userptr(struct drm_device *dev);
+int i915_gem_userptr_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file);
int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_wait_ioctl(struct drm_device *dev, void *data,
@@ -2227,9 +2200,9 @@ static inline void i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
int __must_check i915_mutex_lock_interruptible(struct drm_device *dev);
int i915_gem_object_sync(struct drm_i915_gem_object *obj,
- struct intel_ring_buffer *to);
+ struct intel_engine_cs *to);
void i915_vma_move_to_active(struct i915_vma *vma,
- struct intel_ring_buffer *ring);
+ struct intel_engine_cs *ring);
int i915_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
struct drm_mode_create_dumb *args);
@@ -2249,31 +2222,14 @@ int __must_check i915_gem_set_seqno(struct drm_device *dev, u32 seqno);
int __must_check i915_gem_object_get_fence(struct drm_i915_gem_object *obj);
int __must_check i915_gem_object_put_fence(struct drm_i915_gem_object *obj);
-static inline bool
-i915_gem_object_pin_fence(struct drm_i915_gem_object *obj)
-{
- if (obj->fence_reg != I915_FENCE_REG_NONE) {
- struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
- dev_priv->fence_regs[obj->fence_reg].pin_count++;
- return true;
- } else
- return false;
-}
-
-static inline void
-i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj)
-{
- if (obj->fence_reg != I915_FENCE_REG_NONE) {
- struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
- WARN_ON(dev_priv->fence_regs[obj->fence_reg].pin_count <= 0);
- dev_priv->fence_regs[obj->fence_reg].pin_count--;
- }
-}
+bool i915_gem_object_pin_fence(struct drm_i915_gem_object *obj);
+void i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj);
struct drm_i915_gem_request *
-i915_gem_find_active_request(struct intel_ring_buffer *ring);
+i915_gem_find_active_request(struct intel_engine_cs *ring);
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);
static inline bool i915_reset_in_progress(struct i915_gpu_error *error)
@@ -2292,23 +2248,35 @@ static inline u32 i915_reset_count(struct i915_gpu_error *error)
return ((atomic_read(&error->reset_counter) & ~I915_WEDGED) + 1) / 2;
}
+static inline bool i915_stop_ring_allow_ban(struct drm_i915_private *dev_priv)
+{
+ return dev_priv->gpu_error.stop_rings == 0 ||
+ dev_priv->gpu_error.stop_rings & I915_STOP_RING_ALLOW_BAN;
+}
+
+static inline bool i915_stop_ring_allow_warn(struct drm_i915_private *dev_priv)
+{
+ return dev_priv->gpu_error.stop_rings == 0 ||
+ dev_priv->gpu_error.stop_rings & I915_STOP_RING_ALLOW_WARN;
+}
+
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 __must_check i915_gem_init_hw(struct drm_device *dev);
-int i915_gem_l3_remap(struct intel_ring_buffer *ring, int slice);
+int i915_gem_l3_remap(struct intel_engine_cs *ring, int slice);
void i915_gem_init_swizzling(struct drm_device *dev);
void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
int __must_check i915_gpu_idle(struct drm_device *dev);
int __must_check i915_gem_suspend(struct drm_device *dev);
-int __i915_add_request(struct intel_ring_buffer *ring,
+int __i915_add_request(struct intel_engine_cs *ring,
struct drm_file *file,
struct drm_i915_gem_object *batch_obj,
u32 *seqno);
#define i915_add_request(ring, seqno) \
__i915_add_request(ring, NULL, NULL, seqno)
-int __must_check i915_wait_seqno(struct intel_ring_buffer *ring,
+int __must_check i915_wait_seqno(struct intel_engine_cs *ring,
uint32_t seqno);
int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
int __must_check
@@ -2319,7 +2287,7 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write);
int __must_check
i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
u32 alignment,
- struct intel_ring_buffer *pipelined);
+ struct intel_engine_cs *pipelined);
void i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj);
int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj,
int align);
@@ -2416,22 +2384,22 @@ void i915_gem_context_reset(struct drm_device *dev);
int i915_gem_context_open(struct drm_device *dev, struct drm_file *file);
int i915_gem_context_enable(struct drm_i915_private *dev_priv);
void i915_gem_context_close(struct drm_device *dev, struct drm_file *file);
-int i915_switch_context(struct intel_ring_buffer *ring,
- struct i915_hw_context *to);
-struct i915_hw_context *
+int i915_switch_context(struct intel_engine_cs *ring,
+ struct intel_context *to);
+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);
-static inline void i915_gem_context_reference(struct i915_hw_context *ctx)
+static inline void i915_gem_context_reference(struct intel_context *ctx)
{
kref_get(&ctx->ref);
}
-static inline void i915_gem_context_unreference(struct i915_hw_context *ctx)
+static inline void i915_gem_context_unreference(struct intel_context *ctx)
{
kref_put(&ctx->ref, i915_gem_context_free);
}
-static inline bool i915_gem_context_is_default(const struct i915_hw_context *c)
+static inline bool i915_gem_context_is_default(const struct intel_context *c)
{
return c->id == DEFAULT_CONTEXT_ID;
}
@@ -2441,6 +2409,8 @@ 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,
@@ -2453,23 +2423,12 @@ int __must_check i915_gem_evict_something(struct drm_device *dev,
int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle);
int i915_gem_evict_everything(struct drm_device *dev);
-/* i915_gem_gtt.c */
-void i915_check_and_clear_faults(struct drm_device *dev);
-void i915_gem_suspend_gtt_mappings(struct drm_device *dev);
-void i915_gem_restore_gtt_mappings(struct drm_device *dev);
-int __must_check i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj);
-void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj);
-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);
-int i915_gem_gtt_init(struct drm_device *dev);
+/* belongs in i915_gem_gtt.h */
static inline void i915_gem_chipset_flush(struct drm_device *dev)
{
if (INTEL_INFO(dev)->gen < 6)
intel_gtt_chipset_flush();
}
-int i915_gem_init_ppgtt(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt);
-bool intel_enable_ppgtt(struct drm_device *dev, bool full);
/* i915_gem_stolen.c */
int i915_gem_init_stolen(struct drm_device *dev);
@@ -2537,9 +2496,11 @@ void i915_get_extra_instdone(struct drm_device *dev, uint32_t *instdone);
const char *i915_cache_level_str(int type);
/* i915_cmd_parser.c */
-void i915_cmd_parser_init_ring(struct intel_ring_buffer *ring);
-bool i915_needs_cmd_parser(struct intel_ring_buffer *ring);
-int i915_parse_cmds(struct intel_ring_buffer *ring,
+int i915_cmd_parser_get_version(void);
+int i915_cmd_parser_init_ring(struct intel_engine_cs *ring);
+void i915_cmd_parser_fini_ring(struct intel_engine_cs *ring);
+bool i915_needs_cmd_parser(struct intel_engine_cs *ring);
+int i915_parse_cmds(struct intel_engine_cs *ring,
struct drm_i915_gem_object *batch_obj,
u32 batch_start_offset,
bool is_master);
@@ -2688,20 +2649,6 @@ void vlv_flisdsi_write(struct drm_i915_private *dev_priv, u32 reg, u32 val);
int vlv_gpu_freq(struct drm_i915_private *dev_priv, int val);
int vlv_freq_opcode(struct drm_i915_private *dev_priv, int val);
-void vlv_force_wake_get(struct drm_i915_private *dev_priv, int fw_engine);
-void vlv_force_wake_put(struct drm_i915_private *dev_priv, int fw_engine);
-
-#define FORCEWAKE_VLV_RENDER_RANGE_OFFSET(reg) \
- (((reg) >= 0x2000 && (reg) < 0x4000) ||\
- ((reg) >= 0x5000 && (reg) < 0x8000) ||\
- ((reg) >= 0xB000 && (reg) < 0x12000) ||\
- ((reg) >= 0x2E000 && (reg) < 0x30000))
-
-#define FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(reg)\
- (((reg) >= 0x12000 && (reg) < 0x14000) ||\
- ((reg) >= 0x22000 && (reg) < 0x24000) ||\
- ((reg) >= 0x30000 && (reg) < 0x40000))
-
#define FORCEWAKE_RENDER (1 << 0)
#define FORCEWAKE_MEDIA (1 << 1)
#define FORCEWAKE_ALL (FORCEWAKE_RENDER | FORCEWAKE_MEDIA)
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 3326770c9ed2..f36126383d26 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -31,6 +31,7 @@
#include "i915_drv.h"
#include "i915_trace.h"
#include "intel_drv.h"
+#include <linux/oom.h>
#include <linux/shmem_fs.h>
#include <linux/slab.h>
#include <linux/swap.h>
@@ -43,6 +44,8 @@ static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *o
static __must_check int
i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
bool readonly);
+static void
+i915_gem_object_retire(struct drm_i915_gem_object *obj);
static void i915_gem_write_fence(struct drm_device *dev, int reg,
struct drm_i915_gem_object *obj);
@@ -50,14 +53,15 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj,
struct drm_i915_fence_reg *fence,
bool enable);
-static unsigned long i915_gem_inactive_count(struct shrinker *shrinker,
+static unsigned long i915_gem_shrinker_count(struct shrinker *shrinker,
struct shrink_control *sc);
-static unsigned long i915_gem_inactive_scan(struct shrinker *shrinker,
+static unsigned long i915_gem_shrinker_scan(struct shrinker *shrinker,
struct shrink_control *sc);
+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 void i915_gem_object_truncate(struct drm_i915_gem_object *obj);
-static void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring);
static bool cpu_cache_is_coherent(struct drm_device *dev,
enum i915_cache_level level)
@@ -470,6 +474,8 @@ int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj,
ret = i915_gem_object_wait_rendering(obj, true);
if (ret)
return ret;
+
+ i915_gem_object_retire(obj);
}
ret = i915_gem_object_get_pages(obj);
@@ -885,6 +891,8 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
ret = i915_gem_object_wait_rendering(obj, false);
if (ret)
return ret;
+
+ i915_gem_object_retire(obj);
}
/* Same trick applies to invalidate partially written cachelines read
* before writing. */
@@ -1088,7 +1096,7 @@ i915_gem_check_wedge(struct i915_gpu_error *error,
* equal.
*/
static int
-i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno)
+i915_gem_check_olr(struct intel_engine_cs *ring, u32 seqno)
{
int ret;
@@ -1107,7 +1115,7 @@ static void fake_irq(unsigned long data)
}
static bool missed_irq(struct drm_i915_private *dev_priv,
- struct intel_ring_buffer *ring)
+ struct intel_engine_cs *ring)
{
return test_bit(ring->id, &dev_priv->gpu_error.missed_irq_rings);
}
@@ -1138,7 +1146,7 @@ static bool can_wait_boost(struct drm_i915_file_private *file_priv)
* Returns 0 if the seqno was found within the alloted time. Else returns the
* errno with remaining time filled in timeout argument.
*/
-static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
+static int __wait_seqno(struct intel_engine_cs *ring, u32 seqno,
unsigned reset_counter,
bool interruptible,
struct timespec *timeout,
@@ -1245,7 +1253,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno,
* request and object lists appropriately for that event.
*/
int
-i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
+i915_wait_seqno(struct intel_engine_cs *ring, uint32_t seqno)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1270,9 +1278,10 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno)
static int
i915_gem_object_wait_rendering__tail(struct drm_i915_gem_object *obj,
- struct intel_ring_buffer *ring)
+ struct intel_engine_cs *ring)
{
- i915_gem_retire_requests_ring(ring);
+ if (!obj->active)
+ return 0;
/* Manually manage the write flush as we may have not yet
* retired the buffer.
@@ -1282,7 +1291,6 @@ i915_gem_object_wait_rendering__tail(struct drm_i915_gem_object *obj,
* we know we have passed the last write.
*/
obj->last_write_seqno = 0;
- obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
return 0;
}
@@ -1295,7 +1303,7 @@ static __must_check int
i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
bool readonly)
{
- struct intel_ring_buffer *ring = obj->ring;
+ struct intel_engine_cs *ring = obj->ring;
u32 seqno;
int ret;
@@ -1320,7 +1328,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
{
struct drm_device *dev = obj->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = obj->ring;
+ struct intel_engine_cs *ring = obj->ring;
unsigned reset_counter;
u32 seqno;
int ret;
@@ -1536,7 +1544,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
/* Access to snoopable pages through the GTT is incoherent. */
if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(dev)) {
- ret = -EINVAL;
+ ret = -EFAULT;
goto unlock;
}
@@ -1803,12 +1811,16 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset);
}
+static inline int
+i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj)
+{
+ return obj->madv == I915_MADV_DONTNEED;
+}
+
/* Immediately discard the backing storage */
static void
i915_gem_object_truncate(struct drm_i915_gem_object *obj)
{
- struct inode *inode;
-
i915_gem_object_free_mmap_offset(obj);
if (obj->base.filp == NULL)
@@ -1819,16 +1831,28 @@ i915_gem_object_truncate(struct drm_i915_gem_object *obj)
* To do this we must instruct the shmfs to drop all of its
* backing pages, *now*.
*/
- inode = file_inode(obj->base.filp);
- shmem_truncate_range(inode, 0, (loff_t)-1);
-
+ shmem_truncate_range(file_inode(obj->base.filp), 0, (loff_t)-1);
obj->madv = __I915_MADV_PURGED;
}
-static inline int
-i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj)
+/* Try to discard unwanted pages */
+static void
+i915_gem_object_invalidate(struct drm_i915_gem_object *obj)
{
- return obj->madv == I915_MADV_DONTNEED;
+ struct address_space *mapping;
+
+ switch (obj->madv) {
+ case I915_MADV_DONTNEED:
+ i915_gem_object_truncate(obj);
+ case __I915_MADV_PURGED:
+ return;
+ }
+
+ if (obj->base.filp == NULL)
+ return;
+
+ mapping = file_inode(obj->base.filp)->i_mapping,
+ invalidate_mapping_pages(mapping, 0, (loff_t)-1);
}
static void
@@ -1893,8 +1917,7 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
ops->put_pages(obj);
obj->pages = NULL;
- if (i915_gem_object_is_purgeable(obj))
- i915_gem_object_truncate(obj);
+ i915_gem_object_invalidate(obj);
return 0;
}
@@ -1903,58 +1926,58 @@ static unsigned long
__i915_gem_shrink(struct drm_i915_private *dev_priv, long target,
bool purgeable_only)
{
- struct list_head still_bound_list;
- struct drm_i915_gem_object *obj, *next;
+ struct list_head still_in_list;
+ struct drm_i915_gem_object *obj;
unsigned long count = 0;
- list_for_each_entry_safe(obj, next,
- &dev_priv->mm.unbound_list,
- global_list) {
- if ((i915_gem_object_is_purgeable(obj) || !purgeable_only) &&
- i915_gem_object_put_pages(obj) == 0) {
- count += obj->base.size >> PAGE_SHIFT;
- if (count >= target)
- return count;
- }
- }
-
/*
- * As we may completely rewrite the bound list whilst unbinding
+ * As we may completely rewrite the (un)bound list whilst unbinding
* (due to retiring requests) we have to strictly process only
* one element of the list at the time, and recheck the list
* on every iteration.
+ *
+ * In particular, we must hold a reference whilst removing the
+ * object as we may end up waiting for and/or retiring the objects.
+ * This might release the final reference (held by the active list)
+ * and result in the object being freed from under us. This is
+ * similar to the precautions the eviction code must take whilst
+ * removing objects.
+ *
+ * Also note that although these lists do not hold a reference to
+ * the object we can safely grab one here: The final object
+ * unreferencing and the bound_list are both protected by the
+ * 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_bound_list);
+ 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 (!i915_gem_object_is_purgeable(obj) && purgeable_only)
+ continue;
+
+ drm_gem_object_reference(&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);
+
+ INIT_LIST_HEAD(&still_in_list);
while (count < target && !list_empty(&dev_priv->mm.bound_list)) {
struct i915_vma *vma, *v;
obj = list_first_entry(&dev_priv->mm.bound_list,
typeof(*obj), global_list);
- list_move_tail(&obj->global_list, &still_bound_list);
+ list_move_tail(&obj->global_list, &still_in_list);
if (!i915_gem_object_is_purgeable(obj) && purgeable_only)
continue;
- /*
- * Hold a reference whilst we unbind this object, as we may
- * end up waiting for and retiring requests. This might
- * release the final reference (held by the active list)
- * and result in the object being freed from under us.
- * in this object being freed.
- *
- * Note 1: Shrinking the bound list is special since only active
- * (and hence bound objects) can contain such limbo objects, so
- * we don't need special tricks for shrinking the unbound list.
- * The only other place where we have to be careful with active
- * objects suddenly disappearing due to retiring requests is the
- * eviction code.
- *
- * Note 2: Even though the bound list doesn't hold a reference
- * to the object we can safely grab one here: The final object
- * unreferencing and the bound_list are both protected by the
- * 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.
- */
drm_gem_object_reference(&obj->base);
list_for_each_entry_safe(vma, v, &obj->vma_list, vma_link)
@@ -1966,7 +1989,7 @@ __i915_gem_shrink(struct drm_i915_private *dev_priv, long target,
drm_gem_object_unreference(&obj->base);
}
- list_splice(&still_bound_list, &dev_priv->mm.bound_list);
+ list_splice(&still_in_list, &dev_priv->mm.bound_list);
return count;
}
@@ -1980,17 +2003,8 @@ i915_gem_purge(struct drm_i915_private *dev_priv, long target)
static unsigned long
i915_gem_shrink_all(struct drm_i915_private *dev_priv)
{
- struct drm_i915_gem_object *obj, *next;
- long freed = 0;
-
i915_gem_evict_everything(dev_priv->dev);
-
- list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list,
- global_list) {
- if (i915_gem_object_put_pages(obj) == 0)
- freed += obj->base.size >> PAGE_SHIFT;
- }
- return freed;
+ return __i915_gem_shrink(dev_priv, LONG_MAX, false);
}
static int
@@ -2094,7 +2108,19 @@ err_pages:
page_cache_release(sg_page_iter_page(&sg_iter));
sg_free_table(st);
kfree(st);
- return PTR_ERR(page);
+
+ /* shmemfs first checks if there is enough memory to allocate the page
+ * and reports ENOSPC should there be insufficient, along with the usual
+ * ENOMEM for a genuine allocation failure.
+ *
+ * We use ENOSPC in our driver to mean that we have run out of aperture
+ * space and so want to translate the error from shmemfs back to our
+ * usual understanding of ENOMEM.
+ */
+ if (PTR_ERR(page) == -ENOSPC)
+ return -ENOMEM;
+ else
+ return PTR_ERR(page);
}
/* Ensure that the associated pages are gathered from the backing storage
@@ -2131,7 +2157,7 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
static void
i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
- struct intel_ring_buffer *ring)
+ struct intel_engine_cs *ring)
{
struct drm_device *dev = obj->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2169,7 +2195,7 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
}
void i915_vma_move_to_active(struct i915_vma *vma,
- struct intel_ring_buffer *ring)
+ struct intel_engine_cs *ring)
{
list_move_tail(&vma->mm_list, &vma->vm->active_list);
return i915_gem_object_move_to_active(vma->obj, ring);
@@ -2207,11 +2233,24 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj)
WARN_ON(i915_verify_lists(dev));
}
+static void
+i915_gem_object_retire(struct drm_i915_gem_object *obj)
+{
+ struct intel_engine_cs *ring = obj->ring;
+
+ if (ring == NULL)
+ return;
+
+ if (i915_seqno_passed(ring->get_seqno(ring, true),
+ obj->last_read_seqno))
+ i915_gem_object_move_to_inactive(obj);
+}
+
static int
i915_gem_init_seqno(struct drm_device *dev, u32 seqno)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int ret, i, j;
/* Carefully retire all requests without writing to the rings */
@@ -2226,8 +2265,8 @@ i915_gem_init_seqno(struct drm_device *dev, u32 seqno)
for_each_ring(ring, dev_priv, i) {
intel_ring_init_seqno(ring, seqno);
- for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++)
- ring->sync_seqno[j] = 0;
+ for (j = 0; j < ARRAY_SIZE(ring->semaphore.sync_seqno); j++)
+ ring->semaphore.sync_seqno[j] = 0;
}
return 0;
@@ -2277,7 +2316,7 @@ i915_gem_get_seqno(struct drm_device *dev, u32 *seqno)
return 0;
}
-int __i915_add_request(struct intel_ring_buffer *ring,
+int __i915_add_request(struct intel_engine_cs *ring,
struct drm_file *file,
struct drm_i915_gem_object *obj,
u32 *out_seqno)
@@ -2382,7 +2421,7 @@ i915_gem_request_remove_from_client(struct drm_i915_gem_request *request)
}
static bool i915_context_is_banned(struct drm_i915_private *dev_priv,
- const struct i915_hw_context *ctx)
+ const struct intel_context *ctx)
{
unsigned long elapsed;
@@ -2395,8 +2434,9 @@ static bool i915_context_is_banned(struct drm_i915_private *dev_priv,
if (!i915_gem_context_is_default(ctx)) {
DRM_DEBUG("context hanging too fast, banning!\n");
return true;
- } else if (dev_priv->gpu_error.stop_rings == 0) {
- DRM_ERROR("gpu hanging too fast, banning!\n");
+ } else if (i915_stop_ring_allow_ban(dev_priv)) {
+ if (i915_stop_ring_allow_warn(dev_priv))
+ DRM_ERROR("gpu hanging too fast, banning!\n");
return true;
}
}
@@ -2405,7 +2445,7 @@ static bool i915_context_is_banned(struct drm_i915_private *dev_priv,
}
static void i915_set_reset_status(struct drm_i915_private *dev_priv,
- struct i915_hw_context *ctx,
+ struct intel_context *ctx,
const bool guilty)
{
struct i915_ctx_hang_stats *hs;
@@ -2436,7 +2476,7 @@ static void i915_gem_free_request(struct drm_i915_gem_request *request)
}
struct drm_i915_gem_request *
-i915_gem_find_active_request(struct intel_ring_buffer *ring)
+i915_gem_find_active_request(struct intel_engine_cs *ring)
{
struct drm_i915_gem_request *request;
u32 completed_seqno;
@@ -2454,7 +2494,7 @@ i915_gem_find_active_request(struct intel_ring_buffer *ring)
}
static void i915_gem_reset_ring_status(struct drm_i915_private *dev_priv,
- struct intel_ring_buffer *ring)
+ struct intel_engine_cs *ring)
{
struct drm_i915_gem_request *request;
bool ring_hung;
@@ -2473,7 +2513,7 @@ static void i915_gem_reset_ring_status(struct drm_i915_private *dev_priv,
}
static void i915_gem_reset_ring_cleanup(struct drm_i915_private *dev_priv,
- struct intel_ring_buffer *ring)
+ struct intel_engine_cs *ring)
{
while (!list_empty(&ring->active_list)) {
struct drm_i915_gem_object *obj;
@@ -2501,6 +2541,11 @@ static void i915_gem_reset_ring_cleanup(struct drm_i915_private *dev_priv,
i915_gem_free_request(request);
}
+
+ /* These may not have been flush before the reset, do so now */
+ kfree(ring->preallocated_lazy_request);
+ ring->preallocated_lazy_request = NULL;
+ ring->outstanding_lazy_seqno = 0;
}
void i915_gem_restore_fences(struct drm_device *dev)
@@ -2527,7 +2572,7 @@ void i915_gem_restore_fences(struct drm_device *dev)
void i915_gem_reset(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int i;
/*
@@ -2541,8 +2586,6 @@ void i915_gem_reset(struct drm_device *dev)
for_each_ring(ring, dev_priv, i)
i915_gem_reset_ring_cleanup(dev_priv, ring);
- i915_gem_cleanup_ringbuffer(dev);
-
i915_gem_context_reset(dev);
i915_gem_restore_fences(dev);
@@ -2551,8 +2594,8 @@ void i915_gem_reset(struct drm_device *dev)
/**
* This function clears the request list as sequence numbers are passed.
*/
-static void
-i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
+void
+i915_gem_retire_requests_ring(struct intel_engine_cs *ring)
{
uint32_t seqno;
@@ -2597,7 +2640,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
* of tail of the request to update the last known position
* of the GPU head.
*/
- ring->last_retired_head = request->tail;
+ ring->buffer->last_retired_head = request->tail;
i915_gem_free_request(request);
}
@@ -2615,7 +2658,7 @@ bool
i915_gem_retire_requests(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
bool idle = true;
int i;
@@ -2709,7 +2752,7 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_wait *args = data;
struct drm_i915_gem_object *obj;
- struct intel_ring_buffer *ring = NULL;
+ struct intel_engine_cs *ring = NULL;
struct timespec timeout_stack, *timeout = NULL;
unsigned reset_counter;
u32 seqno = 0;
@@ -2780,9 +2823,9 @@ out:
*/
int
i915_gem_object_sync(struct drm_i915_gem_object *obj,
- struct intel_ring_buffer *to)
+ struct intel_engine_cs *to)
{
- struct intel_ring_buffer *from = obj->ring;
+ struct intel_engine_cs *from = obj->ring;
u32 seqno;
int ret, idx;
@@ -2795,7 +2838,7 @@ i915_gem_object_sync(struct drm_i915_gem_object *obj,
idx = intel_ring_sync_index(from, to);
seqno = obj->last_read_seqno;
- if (seqno <= from->sync_seqno[idx])
+ if (seqno <= from->semaphore.sync_seqno[idx])
return 0;
ret = i915_gem_check_olr(obj->ring, seqno);
@@ -2803,13 +2846,13 @@ i915_gem_object_sync(struct drm_i915_gem_object *obj,
return ret;
trace_i915_gem_ring_sync_to(from, to, seqno);
- ret = to->sync_to(to, from, seqno);
+ ret = to->semaphore.sync_to(to, from, seqno);
if (!ret)
/* We use last_read_seqno because sync_to()
* might have just caused seqno wrap under
* the radar.
*/
- from->sync_seqno[idx] = obj->last_read_seqno;
+ from->semaphore.sync_seqno[idx] = obj->last_read_seqno;
return ret;
}
@@ -2865,12 +2908,14 @@ int i915_vma_unbind(struct i915_vma *vma)
* cause memory corruption through use-after-free.
*/
- i915_gem_object_finish_gtt(obj);
+ if (i915_is_ggtt(vma->vm)) {
+ i915_gem_object_finish_gtt(obj);
- /* release the fence reg _after_ flushing */
- ret = i915_gem_object_put_fence(obj);
- if (ret)
- return ret;
+ /* release the fence reg _after_ flushing */
+ ret = i915_gem_object_put_fence(obj);
+ if (ret)
+ return ret;
+ }
trace_i915_vma_unbind(vma);
@@ -2903,7 +2948,7 @@ int i915_vma_unbind(struct i915_vma *vma)
int i915_gpu_idle(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int ret, i;
/* Flush everything onto the inactive list. */
@@ -3144,6 +3189,9 @@ i915_gem_object_put_fence(struct drm_i915_gem_object *obj)
fence = &dev_priv->fence_regs[obj->fence_reg];
+ if (WARN_ON(fence->pin_count))
+ return -EBUSY;
+
i915_gem_object_fence_lost(obj);
i915_gem_object_update_fence(obj, fence, false);
@@ -3548,6 +3596,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
if (ret)
return ret;
+ i915_gem_object_retire(obj);
i915_gem_object_flush_cpu_write_domain(obj, false);
/* Serialise direct access to this object with the barriers for
@@ -3646,6 +3695,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
* in obj->write_domain and have been skipping the clflushes.
* Just set it to the CPU cache for now.
*/
+ i915_gem_object_retire(obj);
WARN_ON(obj->base.write_domain & ~I915_GEM_DOMAIN_CPU);
old_read_domains = obj->base.read_domains;
@@ -3743,6 +3793,15 @@ unlock:
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;
+
/* There are 3 sources that pin objects:
* 1. The display engine (scanouts, sprites, cursors);
* 2. Reservations for execbuffer;
@@ -3754,7 +3813,7 @@ static bool is_pin_display(struct drm_i915_gem_object *obj)
* subtracting the potential reference by the user, any pin_count
* remains, it must be due to another use by the display engine.
*/
- return i915_gem_obj_to_ggtt(obj)->pin_count - !!obj->user_pin_count;
+ return vma->pin_count - !!obj->user_pin_count;
}
/*
@@ -3765,9 +3824,10 @@ static bool is_pin_display(struct drm_i915_gem_object *obj)
int
i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
u32 alignment,
- struct intel_ring_buffer *pipelined)
+ struct intel_engine_cs *pipelined)
{
u32 old_read_domains, old_write_domain;
+ bool was_pin_display;
int ret;
if (pipelined != obj->ring) {
@@ -3779,6 +3839,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
/* Mark the pin_display early so that we account for the
* display coherency whilst setting up the cache domains.
*/
+ was_pin_display = obj->pin_display;
obj->pin_display = true;
/* The display engine is not coherent with the LLC cache on gen6. As
@@ -3821,7 +3882,8 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
return 0;
err_unpin_display:
- obj->pin_display = is_pin_display(obj);
+ WARN_ON(was_pin_display != is_pin_display(obj));
+ obj->pin_display = was_pin_display;
return ret;
}
@@ -3868,6 +3930,7 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write)
if (ret)
return ret;
+ i915_gem_object_retire(obj);
i915_gem_object_flush_gtt_write_domain(obj);
old_write_domain = obj->base.write_domain;
@@ -3917,7 +3980,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
struct drm_i915_file_private *file_priv = file->driver_priv;
unsigned long recent_enough = jiffies - msecs_to_jiffies(20);
struct drm_i915_gem_request *request;
- struct intel_ring_buffer *ring = NULL;
+ struct intel_engine_cs *ring = NULL;
unsigned reset_counter;
u32 seqno = 0;
int ret;
@@ -3976,9 +4039,13 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj,
uint32_t alignment,
uint64_t flags)
{
+ struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
struct i915_vma *vma;
int ret;
+ if (WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base))
+ return -ENODEV;
+
if (WARN_ON(flags & (PIN_GLOBAL | PIN_MAPPABLE) && !i915_is_ggtt(vm)))
return -EINVAL;
@@ -4032,6 +4099,32 @@ i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj)
obj->pin_mappable = false;
}
+bool
+i915_gem_object_pin_fence(struct drm_i915_gem_object *obj)
+{
+ if (obj->fence_reg != I915_FENCE_REG_NONE) {
+ struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
+ struct i915_vma *ggtt_vma = i915_gem_obj_to_ggtt(obj);
+
+ WARN_ON(!ggtt_vma ||
+ dev_priv->fence_regs[obj->fence_reg].pin_count >
+ ggtt_vma->pin_count);
+ dev_priv->fence_regs[obj->fence_reg].pin_count++;
+ return true;
+ } else
+ return false;
+}
+
+void
+i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj)
+{
+ if (obj->fence_reg != I915_FENCE_REG_NONE) {
+ struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
+ WARN_ON(dev_priv->fence_regs[obj->fence_reg].pin_count <= 0);
+ dev_priv->fence_regs[obj->fence_reg].pin_count--;
+ }
+}
+
int
i915_gem_pin_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
@@ -4292,6 +4385,30 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
return obj;
}
+static bool discard_backing_storage(struct drm_i915_gem_object *obj)
+{
+ /* If we are the last user of the backing storage (be it shmemfs
+ * pages or stolen etc), we know that the pages are going to be
+ * immediately released. In this case, we can then skip copying
+ * back the contents from the GPU.
+ */
+
+ if (obj->madv != I915_MADV_WILLNEED)
+ return false;
+
+ if (obj->base.filp == NULL)
+ return true;
+
+ /* At first glance, this looks racy, but then again so would be
+ * userspace racing mmap against close. However, the first external
+ * reference to the filp can only be obtained through the
+ * i915_gem_mmap_ioctl() which safeguards us against the user
+ * acquiring such a reference whilst we are in the middle of
+ * freeing the object.
+ */
+ return atomic_long_read(&obj->base.filp->f_count) == 1;
+}
+
void i915_gem_free_object(struct drm_gem_object *gem_obj)
{
struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
@@ -4329,6 +4446,8 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
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);
@@ -4338,6 +4457,9 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
if (obj->base.import_attach)
drm_prime_gem_destroy(&obj->base, NULL);
+ if (obj->ops->release)
+ obj->ops->release(obj);
+
drm_gem_object_release(&obj->base);
i915_gem_info_remove_obj(dev_priv, obj->base.size);
@@ -4371,6 +4493,17 @@ void i915_gem_vma_destroy(struct i915_vma *vma)
kfree(vma);
}
+static void
+i915_gem_stop_ringbuffers(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring;
+ int i;
+
+ for_each_ring(ring, dev_priv, i)
+ intel_stop_ring_buffer(ring);
+}
+
int
i915_gem_suspend(struct drm_device *dev)
{
@@ -4392,7 +4525,7 @@ i915_gem_suspend(struct drm_device *dev)
i915_gem_evict_everything(dev);
i915_kernel_lost_context(dev);
- i915_gem_cleanup_ringbuffer(dev);
+ i915_gem_stop_ringbuffers(dev);
/* Hack! Don't let anybody do execbuf while we don't control the chip.
* We need to replace this with a semaphore, or something.
@@ -4413,7 +4546,7 @@ err:
return ret;
}
-int i915_gem_l3_remap(struct intel_ring_buffer *ring, int slice)
+int i915_gem_l3_remap(struct intel_engine_cs *ring, int slice)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4512,13 +4645,20 @@ static int i915_gem_init_rings(struct drm_device *dev)
goto cleanup_blt_ring;
}
+ if (HAS_BSD2(dev)) {
+ ret = intel_init_bsd2_ring_buffer(dev);
+ if (ret)
+ goto cleanup_vebox_ring;
+ }
ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000));
if (ret)
- goto cleanup_vebox_ring;
+ goto cleanup_bsd2_ring;
return 0;
+cleanup_bsd2_ring:
+ intel_cleanup_ring_buffer(&dev_priv->ring[VCS2]);
cleanup_vebox_ring:
intel_cleanup_ring_buffer(&dev_priv->ring[VECS]);
cleanup_blt_ring:
@@ -4576,15 +4716,11 @@ i915_gem_init_hw(struct drm_device *dev)
* the do_switch), but before enabling PPGTT. So don't move this.
*/
ret = i915_gem_context_enable(dev_priv);
- if (ret) {
+ if (ret && ret != -EIO) {
DRM_ERROR("Context enable failed %d\n", ret);
- goto err_out;
+ i915_gem_cleanup_ringbuffer(dev);
}
- return 0;
-
-err_out:
- i915_gem_cleanup_ringbuffer(dev);
return ret;
}
@@ -4597,11 +4733,13 @@ int i915_gem_init(struct drm_device *dev)
if (IS_VALLEYVIEW(dev)) {
/* VLVA0 (potential hack), BIOS isn't actually waking us */
- I915_WRITE(VLV_GTLC_WAKE_CTRL, 1);
- if (wait_for((I915_READ(VLV_GTLC_PW_STATUS) & 1) == 1, 10))
+ I915_WRITE(VLV_GTLC_WAKE_CTRL, VLV_GTLC_ALLOWWAKEREQ);
+ if (wait_for((I915_READ(VLV_GTLC_PW_STATUS) &
+ VLV_GTLC_ALLOWWAKEACK), 10))
DRM_DEBUG_DRIVER("allow wake ack timed out\n");
}
+ i915_gem_init_userptr(dev);
i915_gem_init_global_gtt(dev);
ret = i915_gem_context_init(dev);
@@ -4611,25 +4749,28 @@ int i915_gem_init(struct drm_device *dev)
}
ret = i915_gem_init_hw(dev);
- mutex_unlock(&dev->struct_mutex);
- if (ret) {
- WARN_ON(dev_priv->mm.aliasing_ppgtt);
- i915_gem_context_fini(dev);
- drm_mm_takedown(&dev_priv->gtt.base.mm);
- return ret;
+ if (ret == -EIO) {
+ /* Allow ring initialisation to fail by marking the GPU as
+ * wedged. But we only want to do this where the GPU is angry,
+ * for all other failure, such as an allocation failure, bail.
+ */
+ DRM_ERROR("Failed to initialize GPU, declaring it wedged\n");
+ atomic_set_mask(I915_WEDGED, &dev_priv->gpu_error.reset_counter);
+ ret = 0;
}
+ mutex_unlock(&dev->struct_mutex);
/* Allow hardware batchbuffers unless told otherwise, but not for KMS. */
if (!drm_core_check_feature(dev, DRIVER_MODESET))
dev_priv->dri1.allow_batchbuffer = 1;
- return 0;
+ return ret;
}
void
i915_gem_cleanup_ringbuffer(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int i;
for_each_ring(ring, dev_priv, i)
@@ -4661,16 +4802,15 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
}
BUG_ON(!list_empty(&dev_priv->gtt.base.active_list));
- mutex_unlock(&dev->struct_mutex);
- ret = drm_irq_install(dev);
+ ret = drm_irq_install(dev, dev->pdev->irq);
if (ret)
goto cleanup_ringbuffer;
+ mutex_unlock(&dev->struct_mutex);
return 0;
cleanup_ringbuffer:
- mutex_lock(&dev->struct_mutex);
i915_gem_cleanup_ringbuffer(dev);
dev_priv->ums.mm_suspended = 1;
mutex_unlock(&dev->struct_mutex);
@@ -4685,7 +4825,9 @@ i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
if (drm_core_check_feature(dev, DRIVER_MODESET))
return 0;
+ mutex_lock(&dev->struct_mutex);
drm_irq_uninstall(dev);
+ mutex_unlock(&dev->struct_mutex);
return i915_gem_suspend(dev);
}
@@ -4704,7 +4846,7 @@ i915_gem_lastclose(struct drm_device *dev)
}
static void
-init_ring_lists(struct intel_ring_buffer *ring)
+init_ring_lists(struct intel_engine_cs *ring)
{
INIT_LIST_HEAD(&ring->active_list);
INIT_LIST_HEAD(&ring->request_list);
@@ -4752,7 +4894,7 @@ i915_gem_load(struct drm_device *dev)
init_waitqueue_head(&dev_priv->gpu_error.reset_queue);
/* On GEN3 we really need to make sure the ARB C3 LP bit is set */
- if (IS_GEN3(dev)) {
+ if (!drm_core_check_feature(dev, DRIVER_MODESET) && IS_GEN3(dev)) {
I915_WRITE(MI_ARB_STATE,
_MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE));
}
@@ -4779,10 +4921,13 @@ i915_gem_load(struct drm_device *dev)
dev_priv->mm.interruptible = true;
- dev_priv->mm.inactive_shrinker.scan_objects = i915_gem_inactive_scan;
- dev_priv->mm.inactive_shrinker.count_objects = i915_gem_inactive_count;
- dev_priv->mm.inactive_shrinker.seeks = DEFAULT_SEEKS;
- register_shrinker(&dev_priv->mm.inactive_shrinker);
+ dev_priv->mm.shrinker.scan_objects = i915_gem_shrinker_scan;
+ dev_priv->mm.shrinker.count_objects = i915_gem_shrinker_count;
+ dev_priv->mm.shrinker.seeks = DEFAULT_SEEKS;
+ register_shrinker(&dev_priv->mm.shrinker);
+
+ dev_priv->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom;
+ register_oom_notifier(&dev_priv->mm.oom_notifier);
}
void i915_gem_release(struct drm_device *dev, struct drm_file *file)
@@ -4857,27 +5002,46 @@ static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
#endif
}
+static bool i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
+{
+ if (!mutex_trylock(&dev->struct_mutex)) {
+ if (!mutex_is_locked_by(&dev->struct_mutex, current))
+ return false;
+
+ if (to_i915(dev)->mm.shrinker_no_lock_stealing)
+ return false;
+
+ *unlock = false;
+ } else
+ *unlock = true;
+
+ return true;
+}
+
+static int num_vma_bound(struct drm_i915_gem_object *obj)
+{
+ struct i915_vma *vma;
+ int count = 0;
+
+ list_for_each_entry(vma, &obj->vma_list, vma_link)
+ if (drm_mm_node_allocated(&vma->node))
+ count++;
+
+ return count;
+}
+
static unsigned long
-i915_gem_inactive_count(struct shrinker *shrinker, struct shrink_control *sc)
+i915_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
{
struct drm_i915_private *dev_priv =
- container_of(shrinker,
- struct drm_i915_private,
- mm.inactive_shrinker);
+ container_of(shrinker, struct drm_i915_private, mm.shrinker);
struct drm_device *dev = dev_priv->dev;
struct drm_i915_gem_object *obj;
- bool unlock = true;
unsigned long count;
+ bool unlock;
- if (!mutex_trylock(&dev->struct_mutex)) {
- if (!mutex_is_locked_by(&dev->struct_mutex, current))
- return 0;
-
- if (dev_priv->mm.shrinker_no_lock_stealing)
- return 0;
-
- unlock = false;
- }
+ if (!i915_gem_shrinker_lock(dev, &unlock))
+ return 0;
count = 0;
list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list)
@@ -4885,10 +5049,8 @@ i915_gem_inactive_count(struct shrinker *shrinker, struct shrink_control *sc)
count += obj->base.size >> PAGE_SHIFT;
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
- if (obj->active)
- continue;
-
- if (!i915_gem_obj_is_pinned(obj) && obj->pages_pin_count == 0)
+ if (!i915_gem_obj_is_pinned(obj) &&
+ obj->pages_pin_count == num_vma_bound(obj))
count += obj->base.size >> PAGE_SHIFT;
}
@@ -4961,44 +5123,99 @@ unsigned long i915_gem_obj_size(struct drm_i915_gem_object *o,
}
static unsigned long
-i915_gem_inactive_scan(struct shrinker *shrinker, struct shrink_control *sc)
+i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
{
struct drm_i915_private *dev_priv =
- container_of(shrinker,
- struct drm_i915_private,
- mm.inactive_shrinker);
+ container_of(shrinker, struct drm_i915_private, mm.shrinker);
struct drm_device *dev = dev_priv->dev;
unsigned long freed;
- bool unlock = true;
+ bool unlock;
- if (!mutex_trylock(&dev->struct_mutex)) {
- if (!mutex_is_locked_by(&dev->struct_mutex, current))
- return SHRINK_STOP;
-
- if (dev_priv->mm.shrinker_no_lock_stealing)
- return SHRINK_STOP;
-
- unlock = false;
- }
+ if (!i915_gem_shrinker_lock(dev, &unlock))
+ return SHRINK_STOP;
freed = i915_gem_purge(dev_priv, sc->nr_to_scan);
if (freed < sc->nr_to_scan)
freed += __i915_gem_shrink(dev_priv,
sc->nr_to_scan - freed,
false);
- if (freed < sc->nr_to_scan)
- freed += i915_gem_shrink_all(dev_priv);
-
if (unlock)
mutex_unlock(&dev->struct_mutex);
return freed;
}
+static int
+i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr)
+{
+ struct drm_i915_private *dev_priv =
+ container_of(nb, struct drm_i915_private, mm.oom_notifier);
+ struct drm_device *dev = dev_priv->dev;
+ struct drm_i915_gem_object *obj;
+ unsigned long timeout = msecs_to_jiffies(5000) + 1;
+ unsigned long pinned, bound, unbound, freed;
+ bool was_interruptible;
+ bool unlock;
+
+ while (!i915_gem_shrinker_lock(dev, &unlock) && --timeout)
+ schedule_timeout_killable(1);
+ if (timeout == 0) {
+ pr_err("Unable to purge GPU memory due lock contention.\n");
+ return NOTIFY_DONE;
+ }
+
+ was_interruptible = dev_priv->mm.interruptible;
+ dev_priv->mm.interruptible = false;
+
+ freed = i915_gem_shrink_all(dev_priv);
+
+ dev_priv->mm.interruptible = was_interruptible;
+
+ /* Because we may be allocating inside our own driver, we cannot
+ * assert that there are no objects with pinned pages that are not
+ * being pointed to by hardware.
+ */
+ unbound = bound = pinned = 0;
+ list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list) {
+ if (!obj->base.filp) /* not backed by a freeable object */
+ continue;
+
+ if (obj->pages_pin_count)
+ pinned += obj->base.size;
+ else
+ unbound += obj->base.size;
+ }
+ list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
+ if (!obj->base.filp)
+ continue;
+
+ if (obj->pages_pin_count)
+ pinned += obj->base.size;
+ else
+ bound += obj->base.size;
+ }
+
+ if (unlock)
+ mutex_unlock(&dev->struct_mutex);
+
+ pr_info("Purging GPU memory, %lu bytes freed, %lu bytes still pinned.\n",
+ freed, pinned);
+ if (unbound || bound)
+ pr_err("%lu and %lu bytes still available in the "
+ "bound and unbound GPU page lists.\n",
+ bound, unbound);
+
+ *(unsigned long *)ptr += freed;
+ return NOTIFY_DONE;
+}
+
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;
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index d72db15afa02..3ffe308d5893 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -178,7 +178,7 @@ static int get_context_size(struct drm_device *dev)
void i915_gem_context_free(struct kref *ctx_ref)
{
- struct i915_hw_context *ctx = container_of(ctx_ref,
+ struct intel_context *ctx = container_of(ctx_ref,
typeof(*ctx), ref);
struct i915_hw_ppgtt *ppgtt = NULL;
@@ -199,7 +199,7 @@ void i915_gem_context_free(struct kref *ctx_ref)
}
static struct i915_hw_ppgtt *
-create_vm_for_ctx(struct drm_device *dev, struct i915_hw_context *ctx)
+create_vm_for_ctx(struct drm_device *dev, struct intel_context *ctx)
{
struct i915_hw_ppgtt *ppgtt;
int ret;
@@ -218,12 +218,12 @@ create_vm_for_ctx(struct drm_device *dev, struct i915_hw_context *ctx)
return ppgtt;
}
-static struct i915_hw_context *
+static struct intel_context *
__create_hw_context(struct drm_device *dev,
struct drm_i915_file_private *file_priv)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct i915_hw_context *ctx;
+ struct intel_context *ctx;
int ret;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -240,7 +240,15 @@ __create_hw_context(struct drm_device *dev,
goto err_out;
}
- if (INTEL_INFO(dev)->gen >= 7) {
+ /*
+ * 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 */
@@ -277,14 +285,14 @@ err_out:
* context state of the GPU for applications that don't utilize HW contexts, as
* well as an idle case.
*/
-static struct i915_hw_context *
+static struct intel_context *
i915_gem_create_context(struct drm_device *dev,
struct drm_i915_file_private *file_priv,
bool create_vm)
{
const bool is_global_default_ctx = file_priv == NULL;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct i915_hw_context *ctx;
+ struct intel_context *ctx;
int ret = 0;
BUG_ON(!mutex_is_locked(&dev->struct_mutex));
@@ -356,8 +364,8 @@ void i915_gem_context_reset(struct drm_device *dev)
/* Prevent the hardware from restoring the last context (which hung) on
* the next switch */
for (i = 0; i < I915_NUM_RINGS; i++) {
- struct intel_ring_buffer *ring = &dev_priv->ring[i];
- struct i915_hw_context *dctx = ring->default_context;
+ 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)
@@ -383,7 +391,7 @@ void i915_gem_context_reset(struct drm_device *dev)
int i915_gem_context_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct i915_hw_context *ctx;
+ struct intel_context *ctx;
int i;
/* Init should only be called once per module load. Eventually the
@@ -418,7 +426,7 @@ int i915_gem_context_init(struct drm_device *dev)
void i915_gem_context_fini(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct i915_hw_context *dctx = dev_priv->ring[RCS].default_context;
+ struct intel_context *dctx = dev_priv->ring[RCS].default_context;
int i;
if (dctx->obj) {
@@ -441,10 +449,12 @@ void i915_gem_context_fini(struct drm_device *dev)
i915_gem_context_unreference(dctx);
dev_priv->ring[RCS].last_context = NULL;
}
+
+ i915_gem_object_ggtt_unpin(dctx->obj);
}
for (i = 0; i < I915_NUM_RINGS; i++) {
- struct intel_ring_buffer *ring = &dev_priv->ring[i];
+ struct intel_engine_cs *ring = &dev_priv->ring[i];
if (ring->last_context)
i915_gem_context_unreference(ring->last_context);
@@ -453,13 +463,12 @@ void i915_gem_context_fini(struct drm_device *dev)
ring->last_context = NULL;
}
- i915_gem_object_ggtt_unpin(dctx->obj);
i915_gem_context_unreference(dctx);
}
int i915_gem_context_enable(struct drm_i915_private *dev_priv)
{
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int ret, i;
/* This is the only place the aliasing PPGTT gets enabled, which means
@@ -486,11 +495,7 @@ int i915_gem_context_enable(struct drm_i915_private *dev_priv)
static int context_idr_cleanup(int id, void *p, void *data)
{
- struct i915_hw_context *ctx = p;
-
- /* Ignore the default context because close will handle it */
- if (i915_gem_context_is_default(ctx))
- return 0;
+ struct intel_context *ctx = p;
i915_gem_context_unreference(ctx);
return 0;
@@ -499,17 +504,17 @@ static int context_idr_cleanup(int id, void *p, void *data)
int i915_gem_context_open(struct drm_device *dev, struct drm_file *file)
{
struct drm_i915_file_private *file_priv = file->driver_priv;
+ struct intel_context *ctx;
idr_init(&file_priv->context_idr);
mutex_lock(&dev->struct_mutex);
- file_priv->private_default_ctx =
- i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev));
+ ctx = i915_gem_create_context(dev, file_priv, USES_FULL_PPGTT(dev));
mutex_unlock(&dev->struct_mutex);
- if (IS_ERR(file_priv->private_default_ctx)) {
+ if (IS_ERR(ctx)) {
idr_destroy(&file_priv->context_idr);
- return PTR_ERR(file_priv->private_default_ctx);
+ return PTR_ERR(ctx);
}
return 0;
@@ -521,16 +526,14 @@ void i915_gem_context_close(struct drm_device *dev, struct drm_file *file)
idr_for_each(&file_priv->context_idr, context_idr_cleanup, NULL);
idr_destroy(&file_priv->context_idr);
-
- i915_gem_context_unreference(file_priv->private_default_ctx);
}
-struct i915_hw_context *
+struct intel_context *
i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id)
{
- struct i915_hw_context *ctx;
+ struct intel_context *ctx;
- ctx = (struct i915_hw_context *)idr_find(&file_priv->context_idr, id);
+ ctx = (struct intel_context *)idr_find(&file_priv->context_idr, id);
if (!ctx)
return ERR_PTR(-ENOENT);
@@ -538,8 +541,8 @@ i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id)
}
static inline int
-mi_set_context(struct intel_ring_buffer *ring,
- struct i915_hw_context *new_context,
+mi_set_context(struct intel_engine_cs *ring,
+ struct intel_context *new_context,
u32 hw_flags)
{
int ret;
@@ -549,7 +552,7 @@ mi_set_context(struct intel_ring_buffer *ring,
* explicitly, so we rely on the value at ring init, stored in
* itlb_before_ctx_switch.
*/
- if (IS_GEN6(ring->dev) && ring->itlb_before_ctx_switch) {
+ if (IS_GEN6(ring->dev)) {
ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, 0);
if (ret)
return ret;
@@ -559,8 +562,8 @@ mi_set_context(struct intel_ring_buffer *ring,
if (ret)
return ret;
- /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw */
- if (IS_GEN7(ring->dev))
+ /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw,bdw,chv */
+ if (INTEL_INFO(ring->dev)->gen >= 7)
intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE);
else
intel_ring_emit(ring, MI_NOOP);
@@ -578,7 +581,7 @@ mi_set_context(struct intel_ring_buffer *ring,
*/
intel_ring_emit(ring, MI_NOOP);
- if (IS_GEN7(ring->dev))
+ if (INTEL_INFO(ring->dev)->gen >= 7)
intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_ENABLE);
else
intel_ring_emit(ring, MI_NOOP);
@@ -588,11 +591,11 @@ mi_set_context(struct intel_ring_buffer *ring,
return ret;
}
-static int do_switch(struct intel_ring_buffer *ring,
- struct i915_hw_context *to)
+static int do_switch(struct intel_engine_cs *ring,
+ struct intel_context *to)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
- struct i915_hw_context *from = ring->last_context;
+ struct intel_context *from = ring->last_context;
struct i915_hw_ppgtt *ppgtt = ctx_to_ppgtt(to);
u32 hw_flags = 0;
int ret, i;
@@ -693,13 +696,19 @@ static int do_switch(struct intel_ring_buffer *ring,
i915_gem_context_unreference(from);
}
- to->is_initialized = true;
-
done:
i915_gem_context_reference(to);
ring->last_context = to;
to->last_ring = ring;
+ if (ring->id == RCS && !to->is_initialized && from == NULL) {
+ ret = i915_gem_render_state_init(ring);
+ if (ret)
+ DRM_ERROR("init render state: %d\n", ret);
+ }
+
+ to->is_initialized = true;
+
return 0;
unpin_out:
@@ -718,8 +727,8 @@ unpin_out:
* it will have a refoucnt > 1. This allows us to destroy the context abstract
* object while letting the normal object tracking destroy the backing BO.
*/
-int i915_switch_context(struct intel_ring_buffer *ring,
- struct i915_hw_context *to)
+int i915_switch_context(struct intel_engine_cs *ring,
+ struct intel_context *to)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -748,7 +757,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
{
struct drm_i915_gem_context_create *args = data;
struct drm_i915_file_private *file_priv = file->driver_priv;
- struct i915_hw_context *ctx;
+ struct intel_context *ctx;
int ret;
if (!hw_context_enabled(dev))
@@ -774,7 +783,7 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
{
struct drm_i915_gem_context_destroy *args = data;
struct drm_i915_file_private *file_priv = file->driver_priv;
- struct i915_hw_context *ctx;
+ struct intel_context *ctx;
int ret;
if (args->ctx_id == DEFAULT_CONTEXT_ID)
diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c
index 9bb533e0d762..580aa42443ed 100644
--- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c
+++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c
@@ -161,12 +161,8 @@ static void i915_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
{
struct drm_i915_gem_object *obj = dma_buf_to_obj(dma_buf);
struct drm_device *dev = obj->base.dev;
- int ret;
-
- ret = i915_mutex_lock_interruptible(dev);
- if (ret)
- return;
+ mutex_lock(&dev->struct_mutex);
if (--obj->vmapping_count == 0) {
vunmap(obj->dma_buf_vmapping);
obj->dma_buf_vmapping = NULL;
@@ -233,6 +229,14 @@ static const struct dma_buf_ops i915_dmabuf_ops = {
struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *gem_obj, int flags)
{
+ struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
+
+ if (obj->ops->dmabuf_export) {
+ int ret = obj->ops->dmabuf_export(obj);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
return dma_buf_export(gem_obj, &i915_dmabuf_ops, gem_obj->size, flags);
}
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 20fef6c50267..3a30133f93e8 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -265,10 +265,12 @@ static inline int use_cpu_reloc(struct drm_i915_gem_object *obj)
static int
relocate_entry_cpu(struct drm_i915_gem_object *obj,
- struct drm_i915_gem_relocation_entry *reloc)
+ struct drm_i915_gem_relocation_entry *reloc,
+ uint64_t target_offset)
{
struct drm_device *dev = obj->base.dev;
uint32_t page_offset = offset_in_page(reloc->offset);
+ uint64_t delta = reloc->delta + target_offset;
char *vaddr;
int ret;
@@ -278,7 +280,7 @@ relocate_entry_cpu(struct drm_i915_gem_object *obj,
vaddr = kmap_atomic(i915_gem_object_get_page(obj,
reloc->offset >> PAGE_SHIFT));
- *(uint32_t *)(vaddr + page_offset) = reloc->delta;
+ *(uint32_t *)(vaddr + page_offset) = lower_32_bits(delta);
if (INTEL_INFO(dev)->gen >= 8) {
page_offset = offset_in_page(page_offset + sizeof(uint32_t));
@@ -289,7 +291,7 @@ relocate_entry_cpu(struct drm_i915_gem_object *obj,
(reloc->offset + sizeof(uint32_t)) >> PAGE_SHIFT));
}
- *(uint32_t *)(vaddr + page_offset) = 0;
+ *(uint32_t *)(vaddr + page_offset) = upper_32_bits(delta);
}
kunmap_atomic(vaddr);
@@ -299,10 +301,12 @@ relocate_entry_cpu(struct drm_i915_gem_object *obj,
static int
relocate_entry_gtt(struct drm_i915_gem_object *obj,
- struct drm_i915_gem_relocation_entry *reloc)
+ struct drm_i915_gem_relocation_entry *reloc,
+ uint64_t target_offset)
{
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;
void __iomem *reloc_page;
int ret;
@@ -321,7 +325,7 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj,
reloc->offset & PAGE_MASK);
reloc_entry = (uint32_t __iomem *)
(reloc_page + offset_in_page(reloc->offset));
- iowrite32(reloc->delta, reloc_entry);
+ iowrite32(lower_32_bits(delta), reloc_entry);
if (INTEL_INFO(dev)->gen >= 8) {
reloc_entry += 1;
@@ -334,7 +338,7 @@ relocate_entry_gtt(struct drm_i915_gem_object *obj,
reloc_entry = reloc_page;
}
- iowrite32(0, reloc_entry);
+ iowrite32(upper_32_bits(delta), reloc_entry);
}
io_mapping_unmap_atomic(reloc_page);
@@ -351,7 +355,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
struct drm_gem_object *target_obj;
struct drm_i915_gem_object *target_i915_obj;
struct i915_vma *target_vma;
- uint32_t target_offset;
+ uint64_t target_offset;
int ret;
/* we've already hold a reference to all valid objects */
@@ -429,11 +433,10 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
if (obj->active && in_atomic())
return -EFAULT;
- reloc->delta += target_offset;
if (use_cpu_reloc(obj))
- ret = relocate_entry_cpu(obj, reloc);
+ ret = relocate_entry_cpu(obj, reloc, target_offset);
else
- ret = relocate_entry_gtt(obj, reloc);
+ ret = relocate_entry_gtt(obj, reloc, target_offset);
if (ret)
return ret;
@@ -541,7 +544,7 @@ need_reloc_mappable(struct i915_vma *vma)
static int
i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
- struct intel_ring_buffer *ring,
+ struct intel_engine_cs *ring,
bool *need_reloc)
{
struct drm_i915_gem_object *obj = vma->obj;
@@ -628,7 +631,7 @@ eb_vma_misplaced(struct i915_vma *vma, bool has_fenced_gpu_access)
}
static int
-i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
+i915_gem_execbuffer_reserve(struct intel_engine_cs *ring,
struct list_head *vmas,
bool *need_relocs)
{
@@ -642,6 +645,8 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
if (list_empty(vmas))
return 0;
+ i915_gem_retire_requests_ring(ring);
+
vm = list_first_entry(vmas, struct i915_vma, exec_list)->vm;
INIT_LIST_HEAD(&ordered_vmas);
@@ -727,7 +732,7 @@ static int
i915_gem_execbuffer_relocate_slow(struct drm_device *dev,
struct drm_i915_gem_execbuffer2 *args,
struct drm_file *file,
- struct intel_ring_buffer *ring,
+ struct intel_engine_cs *ring,
struct eb_vmas *eb,
struct drm_i915_gem_exec_object2 *exec)
{
@@ -843,7 +848,7 @@ err:
}
static int
-i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring,
+i915_gem_execbuffer_move_to_gpu(struct intel_engine_cs *ring,
struct list_head *vmas)
{
struct i915_vma *vma;
@@ -926,11 +931,11 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
return 0;
}
-static struct i915_hw_context *
+static struct intel_context *
i915_gem_validate_context(struct drm_device *dev, struct drm_file *file,
- struct intel_ring_buffer *ring, const u32 ctx_id)
+ struct intel_engine_cs *ring, const u32 ctx_id)
{
- struct i915_hw_context *ctx = NULL;
+ struct intel_context *ctx = NULL;
struct i915_ctx_hang_stats *hs;
if (ring->id != RCS && ctx_id != DEFAULT_CONTEXT_ID)
@@ -951,7 +956,7 @@ i915_gem_validate_context(struct drm_device *dev, struct drm_file *file,
static void
i915_gem_execbuffer_move_to_active(struct list_head *vmas,
- struct intel_ring_buffer *ring)
+ struct intel_engine_cs *ring)
{
struct i915_vma *vma;
@@ -974,6 +979,9 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas,
if (i915_gem_obj_ggtt_bound(obj) &&
i915_gem_obj_to_ggtt(obj)->pin_count)
intel_mark_fb_busy(obj, ring);
+
+ /* update for the implicit flush after a batch */
+ obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
}
trace_i915_gem_object_change_domain(obj, old_read, old_write);
@@ -983,7 +991,7 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas,
static void
i915_gem_execbuffer_retire_commands(struct drm_device *dev,
struct drm_file *file,
- struct intel_ring_buffer *ring,
+ struct intel_engine_cs *ring,
struct drm_i915_gem_object *obj)
{
/* Unconditionally force add_request to emit a full flush. */
@@ -995,13 +1003,15 @@ i915_gem_execbuffer_retire_commands(struct drm_device *dev,
static int
i915_reset_gen7_sol_offsets(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_engine_cs *ring)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int ret, i;
- if (!IS_GEN7(dev) || ring != &dev_priv->ring[RCS])
- return 0;
+ if (!IS_GEN7(dev) || ring != &dev_priv->ring[RCS]) {
+ DRM_DEBUG("sol reset is gen7/rcs only\n");
+ return -EINVAL;
+ }
ret = intel_ring_begin(ring, 4 * 3);
if (ret)
@@ -1018,6 +1028,37 @@ i915_reset_gen7_sol_offsets(struct drm_device *dev,
return 0;
}
+/**
+ * Find one BSD ring to dispatch the corresponding BSD command.
+ * The Ring ID is returned.
+ */
+static int gen8_dispatch_bsd_ring(struct drm_device *dev,
+ struct drm_file *file)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_file_private *file_priv = file->driver_priv;
+
+ /* Check whether the file_priv is using one ring */
+ if (file_priv->bsd_ring)
+ return file_priv->bsd_ring->id;
+ else {
+ /* If no, use the ping-pong mechanism to select one ring */
+ int ring_id;
+
+ mutex_lock(&dev->struct_mutex);
+ if (dev_priv->mm.bsd_ring_dispatch_index == 0) {
+ ring_id = VCS;
+ dev_priv->mm.bsd_ring_dispatch_index = 1;
+ } else {
+ ring_id = VCS2;
+ dev_priv->mm.bsd_ring_dispatch_index = 0;
+ }
+ file_priv->bsd_ring = &dev_priv->ring[ring_id];
+ mutex_unlock(&dev->struct_mutex);
+ return ring_id;
+ }
+}
+
static struct drm_i915_gem_object *
eb_get_batch(struct eb_vmas *eb)
{
@@ -1047,11 +1088,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
struct eb_vmas *eb;
struct drm_i915_gem_object *batch_obj;
struct drm_clip_rect *cliprects = NULL;
- struct intel_ring_buffer *ring;
- struct i915_hw_context *ctx;
+ struct intel_engine_cs *ring;
+ struct intel_context *ctx;
struct i915_address_space *vm;
const u32 ctx_id = i915_execbuffer2_get_context_id(*args);
- u32 exec_start = args->batch_start_offset, exec_len;
+ u64 exec_start = args->batch_start_offset, exec_len;
u32 mask, flags;
int ret, mode, i;
bool need_relocs;
@@ -1073,7 +1114,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
if (args->flags & I915_EXEC_IS_PINNED)
flags |= I915_DISPATCH_PINNED;
- if ((args->flags & I915_EXEC_RING_MASK) > I915_NUM_RINGS) {
+ if ((args->flags & I915_EXEC_RING_MASK) > LAST_USER_RING) {
DRM_DEBUG("execbuf with unknown ring: %d\n",
(int)(args->flags & I915_EXEC_RING_MASK));
return -EINVAL;
@@ -1081,7 +1122,14 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
if ((args->flags & I915_EXEC_RING_MASK) == I915_EXEC_DEFAULT)
ring = &dev_priv->ring[RCS];
- else
+ else if ((args->flags & I915_EXEC_RING_MASK) == I915_EXEC_BSD) {
+ if (HAS_BSD2(dev)) {
+ int ring_id;
+ ring_id = gen8_dispatch_bsd_ring(dev, file);
+ ring = &dev_priv->ring[ring_id];
+ } else
+ ring = &dev_priv->ring[VCS];
+ } else
ring = &dev_priv->ring[(args->flags & I915_EXEC_RING_MASK) - 1];
if (!intel_ring_initialized(ring)) {
@@ -1096,14 +1144,22 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
case I915_EXEC_CONSTANTS_REL_GENERAL:
case I915_EXEC_CONSTANTS_ABSOLUTE:
case I915_EXEC_CONSTANTS_REL_SURFACE:
- if (ring == &dev_priv->ring[RCS] &&
- mode != dev_priv->relative_constants_mode) {
- if (INTEL_INFO(dev)->gen < 4)
+ 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)
+ 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)
@@ -1151,6 +1207,16 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
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);
@@ -1170,7 +1236,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
mutex_unlock(&dev->struct_mutex);
ret = PTR_ERR(ctx);
goto pre_mutex_err;
- }
+ }
i915_gem_context_reference(ctx);
@@ -1180,6 +1246,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
eb = eb_create(args);
if (eb == NULL) {
+ i915_gem_context_unreference(ctx);
mutex_unlock(&dev->struct_mutex);
ret = -ENOMEM;
goto pre_mutex_err;
@@ -1430,6 +1497,11 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
return -EINVAL;
}
+ if (args->rsvd2 != 0) {
+ DRM_DEBUG("dirty rvsd2 field\n");
+ return -EINVAL;
+ }
+
exec2_list = kmalloc(sizeof(*exec2_list)*args->buffer_count,
GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
if (exec2_list == NULL)
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 5deb22864c52..eec820aec022 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -30,7 +30,8 @@
#include "i915_trace.h"
#include "intel_drv.h"
-static void gen8_setup_private_ppat(struct drm_i915_private *dev_priv);
+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)
{
@@ -65,59 +66,6 @@ static int sanitize_enable_ppgtt(struct drm_device *dev, int enable_ppgtt)
return HAS_ALIASING_PPGTT(dev) ? 1 : 0;
}
-#define GEN6_PPGTT_PD_ENTRIES 512
-#define I915_PPGTT_PT_ENTRIES (PAGE_SIZE / sizeof(gen6_gtt_pte_t))
-typedef uint64_t gen8_gtt_pte_t;
-typedef gen8_gtt_pte_t gen8_ppgtt_pde_t;
-
-/* PPGTT stuff */
-#define GEN6_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0xff0))
-#define HSW_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0x7f0))
-
-#define GEN6_PDE_VALID (1 << 0)
-/* gen6+ has bit 11-4 for physical addr bit 39-32 */
-#define GEN6_PDE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
-
-#define GEN6_PTE_VALID (1 << 0)
-#define GEN6_PTE_UNCACHED (1 << 1)
-#define HSW_PTE_UNCACHED (0)
-#define GEN6_PTE_CACHE_LLC (2 << 1)
-#define GEN7_PTE_CACHE_L3_LLC (3 << 1)
-#define GEN6_PTE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
-#define HSW_PTE_ADDR_ENCODE(addr) HSW_GTT_ADDR_ENCODE(addr)
-
-/* Cacheability Control is a 4-bit value. The low three bits are stored in *
- * bits 3:1 of the PTE, while the fourth bit is stored in bit 11 of the PTE.
- */
-#define HSW_CACHEABILITY_CONTROL(bits) ((((bits) & 0x7) << 1) | \
- (((bits) & 0x8) << (11 - 3)))
-#define HSW_WB_LLC_AGE3 HSW_CACHEABILITY_CONTROL(0x2)
-#define HSW_WB_LLC_AGE0 HSW_CACHEABILITY_CONTROL(0x3)
-#define HSW_WB_ELLC_LLC_AGE0 HSW_CACHEABILITY_CONTROL(0xb)
-#define HSW_WB_ELLC_LLC_AGE3 HSW_CACHEABILITY_CONTROL(0x8)
-#define HSW_WT_ELLC_LLC_AGE0 HSW_CACHEABILITY_CONTROL(0x6)
-#define HSW_WT_ELLC_LLC_AGE3 HSW_CACHEABILITY_CONTROL(0x7)
-
-#define GEN8_PTES_PER_PAGE (PAGE_SIZE / sizeof(gen8_gtt_pte_t))
-#define GEN8_PDES_PER_PAGE (PAGE_SIZE / sizeof(gen8_ppgtt_pde_t))
-
-/* GEN8 legacy style addressis defined as a 3 level page table:
- * 31:30 | 29:21 | 20:12 | 11:0
- * PDPE | PDE | PTE | offset
- * The difference as compared to normal x86 3 level page table is the PDPEs are
- * programmed via register.
- */
-#define GEN8_PDPE_SHIFT 30
-#define GEN8_PDPE_MASK 0x3
-#define GEN8_PDE_SHIFT 21
-#define GEN8_PDE_MASK 0x1ff
-#define GEN8_PTE_SHIFT 12
-#define GEN8_PTE_MASK 0x1ff
-
-#define PPAT_UNCACHED_INDEX (_PAGE_PWT | _PAGE_PCD)
-#define PPAT_CACHED_PDE_INDEX 0 /* WB LLC */
-#define PPAT_CACHED_INDEX _PAGE_PAT /* WB LLCeLLC */
-#define PPAT_DISPLAY_ELLC_INDEX _PAGE_PCD /* WT eLLC */
static void ppgtt_bind_vma(struct i915_vma *vma,
enum i915_cache_level cache_level,
@@ -131,10 +79,19 @@ static inline gen8_gtt_pte_t gen8_pte_encode(dma_addr_t addr,
{
gen8_gtt_pte_t pte = valid ? _PAGE_PRESENT | _PAGE_RW : 0;
pte |= addr;
- if (level != I915_CACHE_NONE)
- pte |= PPAT_CACHED_INDEX;
- else
+
+ switch (level) {
+ case I915_CACHE_NONE:
pte |= PPAT_UNCACHED_INDEX;
+ break;
+ case I915_CACHE_WT:
+ pte |= PPAT_DISPLAY_ELLC_INDEX;
+ break;
+ default:
+ pte |= PPAT_CACHED_INDEX;
+ break;
+ }
+
return pte;
}
@@ -197,9 +154,6 @@ static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr,
return pte;
}
-#define BYT_PTE_WRITEABLE (1 << 1)
-#define BYT_PTE_SNOOPED_BY_CPU_CACHES (1 << 2)
-
static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
enum i915_cache_level level,
bool valid)
@@ -253,7 +207,7 @@ static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr,
}
/* Broadwell Page Directory Pointer Descriptors */
-static int gen8_write_pdp(struct intel_ring_buffer *ring, unsigned entry,
+static int gen8_write_pdp(struct intel_engine_cs *ring, unsigned entry,
uint64_t val, bool synchronous)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -283,7 +237,7 @@ static int gen8_write_pdp(struct intel_ring_buffer *ring, unsigned entry,
}
static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt,
- struct intel_ring_buffer *ring,
+ struct intel_engine_cs *ring,
bool synchronous)
{
int i, ret;
@@ -332,6 +286,8 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
num_entries--;
}
+ if (!HAS_LLC(ppgtt->base.dev))
+ drm_clflush_virt_range(pt_vaddr, PAGE_SIZE);
kunmap_atomic(pt_vaddr);
pte = 0;
@@ -368,6 +324,8 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
gen8_pte_encode(sg_page_iter_dma_address(&sg_iter),
cache_level, true);
if (++pte == GEN8_PTES_PER_PAGE) {
+ if (!HAS_LLC(ppgtt->base.dev))
+ drm_clflush_virt_range(pt_vaddr, PAGE_SIZE);
kunmap_atomic(pt_vaddr);
pt_vaddr = NULL;
if (++pde == GEN8_PDES_PER_PAGE) {
@@ -377,8 +335,11 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
pte = 0;
}
}
- if (pt_vaddr)
+ if (pt_vaddr) {
+ if (!HAS_LLC(ppgtt->base.dev))
+ drm_clflush_virt_range(pt_vaddr, PAGE_SIZE);
kunmap_atomic(pt_vaddr);
+ }
}
static void gen8_free_page_tables(struct page **pt_pages)
@@ -641,6 +602,8 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
pd_vaddr[j] = gen8_pde_encode(ppgtt->base.dev, addr,
I915_CACHE_LLC);
}
+ if (!HAS_LLC(ppgtt->base.dev))
+ drm_clflush_virt_range(pd_vaddr, PAGE_SIZE);
kunmap_atomic(pd_vaddr);
}
@@ -753,7 +716,7 @@ static uint32_t get_pd_offset(struct i915_hw_ppgtt *ppgtt)
}
static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt,
- struct intel_ring_buffer *ring,
+ struct intel_engine_cs *ring,
bool synchronous)
{
struct drm_device *dev = ppgtt->base.dev;
@@ -797,7 +760,7 @@ static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt,
}
static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt,
- struct intel_ring_buffer *ring,
+ struct intel_engine_cs *ring,
bool synchronous)
{
struct drm_device *dev = ppgtt->base.dev;
@@ -848,7 +811,7 @@ static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt,
}
static int gen6_mm_switch(struct i915_hw_ppgtt *ppgtt,
- struct intel_ring_buffer *ring,
+ struct intel_engine_cs *ring,
bool synchronous)
{
struct drm_device *dev = ppgtt->base.dev;
@@ -869,7 +832,7 @@ static int gen8_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
{
struct drm_device *dev = ppgtt->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int j, ret;
for_each_ring(ring, dev_priv, j) {
@@ -899,7 +862,7 @@ static int gen7_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
{
struct drm_device *dev = ppgtt->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
uint32_t ecochk, ecobits;
int i;
@@ -938,7 +901,7 @@ static int gen6_ppgtt_enable(struct i915_hw_ppgtt *ppgtt)
{
struct drm_device *dev = ppgtt->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
uint32_t ecochk, gab_ctl, ecobits;
int i;
@@ -1067,8 +1030,6 @@ static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
static int gen6_ppgtt_allocate_page_directories(struct i915_hw_ppgtt *ppgtt)
{
-#define GEN6_PD_ALIGN (PAGE_SIZE * 16)
-#define GEN6_PD_SIZE (GEN6_PPGTT_PD_ENTRIES * PAGE_SIZE)
struct drm_device *dev = ppgtt->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
bool retried = false;
@@ -1084,8 +1045,7 @@ alloc:
&ppgtt->node, GEN6_PD_SIZE,
GEN6_PD_ALIGN, 0,
0, dev_priv->gtt.base.total,
- DRM_MM_SEARCH_DEFAULT,
- DRM_MM_CREATE_DEFAULT);
+ DRM_MM_TOPDOWN);
if (ret == -ENOSPC && !retried) {
ret = i915_gem_evict_something(dev, &dev_priv->gtt.base,
GEN6_PD_SIZE, GEN6_PD_ALIGN,
@@ -1311,7 +1271,7 @@ static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible)
void i915_check_and_clear_faults(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int i;
if (INTEL_INFO(dev)->gen < 6)
@@ -1386,7 +1346,11 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
if (INTEL_INFO(dev)->gen >= 8) {
- gen8_setup_private_ppat(dev_priv);
+ if (IS_CHERRYVIEW(dev))
+ chv_setup_private_ppat(dev_priv);
+ else
+ bdw_setup_private_ppat(dev_priv);
+
return;
}
@@ -1438,7 +1402,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;
+ dma_addr_t addr = 0;
for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) {
addr = sg_dma_address(sg_iter.sg) +
@@ -1811,9 +1775,27 @@ static inline unsigned int gen8_get_total_gtt_size(u16 bdw_gmch_ctl)
bdw_gmch_ctl &= BDW_GMCH_GGMS_MASK;
if (bdw_gmch_ctl)
bdw_gmch_ctl = 1 << bdw_gmch_ctl;
+
+#ifdef CONFIG_X86_32
+ /* Limit 32b platforms to a 2GB GGTT: 4 << 20 / pte size * PAGE_SIZE */
+ if (bdw_gmch_ctl > 4)
+ bdw_gmch_ctl = 4;
+#endif
+
return bdw_gmch_ctl << 20;
}
+static inline unsigned int chv_get_total_gtt_size(u16 gmch_ctrl)
+{
+ gmch_ctrl >>= SNB_GMCH_GGMS_SHIFT;
+ gmch_ctrl &= SNB_GMCH_GGMS_MASK;
+
+ if (gmch_ctrl)
+ return 1 << (20 + gmch_ctrl);
+
+ return 0;
+}
+
static inline size_t gen6_get_stolen_size(u16 snb_gmch_ctl)
{
snb_gmch_ctl >>= SNB_GMCH_GMS_SHIFT;
@@ -1828,6 +1810,24 @@ static inline size_t gen8_get_stolen_size(u16 bdw_gmch_ctl)
return bdw_gmch_ctl << 25; /* 32 MB units */
}
+static size_t chv_get_stolen_size(u16 gmch_ctrl)
+{
+ gmch_ctrl >>= SNB_GMCH_GMS_SHIFT;
+ gmch_ctrl &= SNB_GMCH_GMS_MASK;
+
+ /*
+ * 0x0 to 0x10: 32MB increments starting at 0MB
+ * 0x11 to 0x16: 4MB increments starting at 8MB
+ * 0x17 to 0x1d: 4MB increments start at 36MB
+ */
+ if (gmch_ctrl < 0x11)
+ return gmch_ctrl << 25;
+ else if (gmch_ctrl < 0x17)
+ return (gmch_ctrl - 0x11 + 2) << 22;
+ else
+ return (gmch_ctrl - 0x17 + 9) << 22;
+}
+
static int ggtt_probe_common(struct drm_device *dev,
size_t gtt_size)
{
@@ -1858,19 +1858,8 @@ static int ggtt_probe_common(struct drm_device *dev,
/* The GGTT and PPGTT need a private PPAT setup in order to handle cacheability
* bits. When using advanced contexts each context stores its own PAT, but
* writing this data shouldn't be harmful even in those cases. */
-static void gen8_setup_private_ppat(struct drm_i915_private *dev_priv)
-{
-#define GEN8_PPAT_UC (0<<0)
-#define GEN8_PPAT_WC (1<<0)
-#define GEN8_PPAT_WT (2<<0)
-#define GEN8_PPAT_WB (3<<0)
-#define GEN8_PPAT_ELLC_OVERRIDE (0<<2)
-/* FIXME(BDW): Bspec is completely confused about cache control bits. */
-#define GEN8_PPAT_LLC (1<<2)
-#define GEN8_PPAT_LLCELLC (2<<2)
-#define GEN8_PPAT_LLCeLLC (3<<2)
-#define GEN8_PPAT_AGE(x) (x<<4)
-#define GEN8_PPAT(i, x) ((uint64_t) (x) << ((i) * 8))
+static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv)
+{
uint64_t pat;
pat = GEN8_PPAT(0, GEN8_PPAT_WB | GEN8_PPAT_LLC) | /* for normal objects, no eLLC */
@@ -1888,6 +1877,33 @@ static void gen8_setup_private_ppat(struct drm_i915_private *dev_priv)
I915_WRITE(GEN8_PRIVATE_PAT + 4, pat >> 32);
}
+static void chv_setup_private_ppat(struct drm_i915_private *dev_priv)
+{
+ uint64_t pat;
+
+ /*
+ * Map WB on BDW to snooped on CHV.
+ *
+ * Only the snoop bit has meaning for CHV, the rest is
+ * ignored.
+ *
+ * Note that the harware enforces snooping for all page
+ * table accesses. The snoop bit is actually ignored for
+ * PDEs.
+ */
+ pat = GEN8_PPAT(0, CHV_PPAT_SNOOP) |
+ GEN8_PPAT(1, 0) |
+ GEN8_PPAT(2, 0) |
+ GEN8_PPAT(3, 0) |
+ GEN8_PPAT(4, CHV_PPAT_SNOOP) |
+ GEN8_PPAT(5, CHV_PPAT_SNOOP) |
+ GEN8_PPAT(6, CHV_PPAT_SNOOP) |
+ GEN8_PPAT(7, CHV_PPAT_SNOOP);
+
+ I915_WRITE(GEN8_PRIVATE_PAT, pat);
+ I915_WRITE(GEN8_PRIVATE_PAT + 4, pat >> 32);
+}
+
static int gen8_gmch_probe(struct drm_device *dev,
size_t *gtt_total,
size_t *stolen,
@@ -1908,12 +1924,20 @@ static int gen8_gmch_probe(struct drm_device *dev,
pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
- *stolen = gen8_get_stolen_size(snb_gmch_ctl);
+ if (IS_CHERRYVIEW(dev)) {
+ *stolen = chv_get_stolen_size(snb_gmch_ctl);
+ gtt_size = chv_get_total_gtt_size(snb_gmch_ctl);
+ } else {
+ *stolen = gen8_get_stolen_size(snb_gmch_ctl);
+ gtt_size = gen8_get_total_gtt_size(snb_gmch_ctl);
+ }
- gtt_size = gen8_get_total_gtt_size(snb_gmch_ctl);
*gtt_total = (gtt_size / sizeof(gen8_gtt_pte_t)) << PAGE_SHIFT;
- gen8_setup_private_ppat(dev_priv);
+ if (IS_CHERRYVIEW(dev))
+ chv_setup_private_ppat(dev_priv);
+ else
+ bdw_setup_private_ppat(dev_priv);
ret = ggtt_probe_common(dev, gtt_size);
@@ -2043,6 +2067,10 @@ int i915_gem_gtt_init(struct drm_device *dev)
gtt->base.total >> 20);
DRM_DEBUG_DRIVER("GMADR size = %ldM\n", gtt->mappable_end >> 20);
DRM_DEBUG_DRIVER("GTT stolen size = %zdM\n", gtt->stolen_size >> 20);
+#ifdef CONFIG_INTEL_IOMMU
+ if (intel_iommu_gfx_mapped)
+ DRM_INFO("VT-d active for gfx access\n");
+#endif
/*
* i915.enable_ppgtt is read-only, so do an early pass to validate the
* user's requested state against the hardware/driver capabilities. We
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
new file mode 100644
index 000000000000..1b96a06be3cb
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ *
+ * Please try to maintain the following order within this file unless it makes
+ * sense to do otherwise. From top to bottom:
+ * 1. typedefs
+ * 2. #defines, and macros
+ * 3. structure definitions
+ * 4. function prototypes
+ *
+ * Within each section, please try to order by generation in ascending order,
+ * from top to bottom (ie. gen6 on the top, gen8 on the bottom).
+ */
+
+#ifndef __I915_GEM_GTT_H__
+#define __I915_GEM_GTT_H__
+
+typedef uint32_t gen6_gtt_pte_t;
+typedef uint64_t gen8_gtt_pte_t;
+typedef gen8_gtt_pte_t gen8_ppgtt_pde_t;
+
+#define gtt_total_entries(gtt) ((gtt).base.total >> PAGE_SHIFT)
+
+#define I915_PPGTT_PT_ENTRIES (PAGE_SIZE / sizeof(gen6_gtt_pte_t))
+/* gen6-hsw has bit 11-4 for physical addr bit 39-32 */
+#define GEN6_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0xff0))
+#define GEN6_PTE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
+#define GEN6_PDE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
+#define GEN6_PTE_CACHE_LLC (2 << 1)
+#define GEN6_PTE_UNCACHED (1 << 1)
+#define GEN6_PTE_VALID (1 << 0)
+
+#define GEN6_PPGTT_PD_ENTRIES 512
+#define GEN6_PD_SIZE (GEN6_PPGTT_PD_ENTRIES * PAGE_SIZE)
+#define GEN6_PD_ALIGN (PAGE_SIZE * 16)
+#define GEN6_PDE_VALID (1 << 0)
+
+#define GEN7_PTE_CACHE_L3_LLC (3 << 1)
+
+#define BYT_PTE_SNOOPED_BY_CPU_CACHES (1 << 2)
+#define BYT_PTE_WRITEABLE (1 << 1)
+
+/* Cacheability Control is a 4-bit value. The low three bits are stored in bits
+ * 3:1 of the PTE, while the fourth bit is stored in bit 11 of the PTE.
+ */
+#define HSW_CACHEABILITY_CONTROL(bits) ((((bits) & 0x7) << 1) | \
+ (((bits) & 0x8) << (11 - 3)))
+#define HSW_WB_LLC_AGE3 HSW_CACHEABILITY_CONTROL(0x2)
+#define HSW_WB_LLC_AGE0 HSW_CACHEABILITY_CONTROL(0x3)
+#define HSW_WB_ELLC_LLC_AGE3 HSW_CACHEABILITY_CONTROL(0x8)
+#define HSW_WB_ELLC_LLC_AGE0 HSW_CACHEABILITY_CONTROL(0xb)
+#define HSW_WT_ELLC_LLC_AGE3 HSW_CACHEABILITY_CONTROL(0x7)
+#define HSW_WT_ELLC_LLC_AGE0 HSW_CACHEABILITY_CONTROL(0x6)
+#define HSW_PTE_UNCACHED (0)
+#define HSW_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0x7f0))
+#define HSW_PTE_ADDR_ENCODE(addr) HSW_GTT_ADDR_ENCODE(addr)
+
+/* GEN8 legacy style address is defined as a 3 level page table:
+ * 31:30 | 29:21 | 20:12 | 11:0
+ * PDPE | PDE | PTE | offset
+ * The difference as compared to normal x86 3 level page table is the PDPEs are
+ * programmed via register.
+ */
+#define GEN8_PDPE_SHIFT 30
+#define GEN8_PDPE_MASK 0x3
+#define GEN8_PDE_SHIFT 21
+#define GEN8_PDE_MASK 0x1ff
+#define GEN8_PTE_SHIFT 12
+#define GEN8_PTE_MASK 0x1ff
+#define GEN8_LEGACY_PDPS 4
+#define GEN8_PTES_PER_PAGE (PAGE_SIZE / sizeof(gen8_gtt_pte_t))
+#define GEN8_PDES_PER_PAGE (PAGE_SIZE / sizeof(gen8_ppgtt_pde_t))
+
+#define PPAT_UNCACHED_INDEX (_PAGE_PWT | _PAGE_PCD)
+#define PPAT_CACHED_PDE_INDEX 0 /* WB LLC */
+#define PPAT_CACHED_INDEX _PAGE_PAT /* WB LLCeLLC */
+#define PPAT_DISPLAY_ELLC_INDEX _PAGE_PCD /* WT eLLC */
+
+#define CHV_PPAT_SNOOP (1<<6)
+#define GEN8_PPAT_AGE(x) (x<<4)
+#define GEN8_PPAT_LLCeLLC (3<<2)
+#define GEN8_PPAT_LLCELLC (2<<2)
+#define GEN8_PPAT_LLC (1<<2)
+#define GEN8_PPAT_WB (3<<0)
+#define GEN8_PPAT_WT (2<<0)
+#define GEN8_PPAT_WC (1<<0)
+#define GEN8_PPAT_UC (0<<0)
+#define GEN8_PPAT_ELLC_OVERRIDE (0<<2)
+#define GEN8_PPAT(i, x) ((uint64_t) (x) << ((i) * 8))
+
+enum i915_cache_level;
+/**
+ * A VMA represents a GEM BO that is bound into an address space. Therefore, a
+ * VMA's presence cannot be guaranteed before binding, or after unbinding the
+ * object into/from the address space.
+ *
+ * To make things as simple as possible (ie. no refcounting), a VMA's lifetime
+ * will always be <= an objects lifetime. So object refcounting should cover us.
+ */
+struct i915_vma {
+ struct drm_mm_node node;
+ struct drm_i915_gem_object *obj;
+ struct i915_address_space *vm;
+
+ /** This object's place on the active/inactive lists */
+ struct list_head mm_list;
+
+ struct list_head vma_link; /* Link in the object's VMA list */
+
+ /** This vma's place in the batchbuffer or on the eviction list */
+ struct list_head exec_list;
+
+ /**
+ * Used for performing relocations during execbuffer insertion.
+ */
+ struct hlist_node exec_node;
+ unsigned long exec_handle;
+ struct drm_i915_gem_exec_object2 *exec_entry;
+
+ /**
+ * How many users have pinned this object in GTT space. The following
+ * users can each hold at most one reference: pwrite/pread, pin_ioctl
+ * (via user_pin_count), execbuffer (objects are not allowed multiple
+ * times for the same batchbuffer), and the framebuffer code. When
+ * switching/pageflipping, the framebuffer code has at most two buffers
+ * pinned per crtc.
+ *
+ * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3
+ * bits with absolutely no headroom. So use 4 bits. */
+ unsigned int pin_count:4;
+#define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf
+
+ /** Unmap an object from an address space. This usually consists of
+ * setting the valid PTE entries to a reserved scratch page. */
+ void (*unbind_vma)(struct i915_vma *vma);
+ /* Map an object into an address space with the given cache flags. */
+#define GLOBAL_BIND (1<<0)
+ void (*bind_vma)(struct i915_vma *vma,
+ enum i915_cache_level cache_level,
+ u32 flags);
+};
+
+struct i915_address_space {
+ struct drm_mm mm;
+ struct drm_device *dev;
+ struct list_head global_link;
+ unsigned long start; /* Start offset always 0 for dri2 */
+ size_t total; /* size addr space maps (ex. 2GB for ggtt) */
+
+ struct {
+ dma_addr_t addr;
+ struct page *page;
+ } scratch;
+
+ /**
+ * List of objects currently involved in rendering.
+ *
+ * Includes buffers having the contents of their GPU caches
+ * flushed, not necessarily primitives. last_rendering_seqno
+ * represents when the rendering involved will be completed.
+ *
+ * A reference is held on the buffer while on this list.
+ */
+ struct list_head active_list;
+
+ /**
+ * LRU list of objects which are not in the ringbuffer and
+ * are ready to unbind, but are still in the GTT.
+ *
+ * last_rendering_seqno is 0 while an object is in this list.
+ *
+ * A reference is not held on the buffer while on this list,
+ * as merely being GTT-bound shouldn't prevent its being
+ * freed, and we'll pull it off the list in the free path.
+ */
+ struct list_head inactive_list;
+
+ /* 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 */
+ void (*clear_range)(struct i915_address_space *vm,
+ uint64_t start,
+ uint64_t length,
+ bool use_scratch);
+ void (*insert_entries)(struct i915_address_space *vm,
+ struct sg_table *st,
+ uint64_t start,
+ enum i915_cache_level cache_level);
+ void (*cleanup)(struct i915_address_space *vm);
+};
+
+/* The Graphics Translation Table is the way in which GEN hardware translates a
+ * Graphics Virtual Address into a Physical Address. In addition to the normal
+ * collateral associated with any va->pa translations GEN hardware also has a
+ * portion of the GTT which can be mapped by the CPU and remain both coherent
+ * and correct (in cases like swizzling). That region is referred to as GMADR in
+ * the spec.
+ */
+struct i915_gtt {
+ struct i915_address_space base;
+ size_t stolen_size; /* Total size of stolen memory */
+
+ unsigned long mappable_end; /* End offset that we can CPU map */
+ struct io_mapping *mappable; /* Mapping to our CPU mappable region */
+ phys_addr_t mappable_base; /* PA of our GMADR */
+
+ /** "Graphics Stolen Memory" holds the global PTEs */
+ void __iomem *gsm;
+
+ bool do_idle_maps;
+
+ int mtrr;
+
+ /* global gtt ops */
+ int (*gtt_probe)(struct drm_device *dev, size_t *gtt_total,
+ size_t *stolen, phys_addr_t *mappable_base,
+ unsigned long *mappable_end);
+};
+
+struct i915_hw_ppgtt {
+ struct i915_address_space base;
+ struct kref ref;
+ struct drm_mm_node node;
+ unsigned num_pd_entries;
+ unsigned num_pd_pages; /* gen8+ */
+ union {
+ struct page **pt_pages;
+ struct page **gen8_pt_pages[GEN8_LEGACY_PDPS];
+ };
+ struct page *pd_pages;
+ union {
+ uint32_t pd_offset;
+ dma_addr_t pd_dma_addr[GEN8_LEGACY_PDPS];
+ };
+ union {
+ dma_addr_t *pt_dma_addr;
+ dma_addr_t *gen8_pt_dma_addr[4];
+ };
+
+ struct intel_context *ctx;
+
+ int (*enable)(struct i915_hw_ppgtt *ppgtt);
+ int (*switch_mm)(struct i915_hw_ppgtt *ppgtt,
+ struct intel_engine_cs *ring,
+ bool synchronous);
+ 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);
+
+void i915_check_and_clear_faults(struct drm_device *dev);
+void i915_gem_suspend_gtt_mappings(struct drm_device *dev);
+void i915_gem_restore_gtt_mappings(struct drm_device *dev);
+
+int __must_check i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj);
+void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj);
+
+#endif
diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c
new file mode 100644
index 000000000000..3521f998a178
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_gem_render_state.c
@@ -0,0 +1,198 @@
+/*
+ * 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:
+ * Mika Kuoppala <mika.kuoppala@intel.com>
+ *
+ */
+
+#include "i915_drv.h"
+#include "intel_renderstate.h"
+
+struct i915_render_state {
+ struct drm_i915_gem_object *obj;
+ unsigned long ggtt_offset;
+ void *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(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)
+{
+ switch (gen) {
+ case 6:
+ return &gen6_null_state;
+ case 7:
+ return &gen7_null_state;
+ case 8:
+ return &gen8_null_state;
+ }
+
+ return NULL;
+}
+
+static int render_state_setup(const int gen,
+ const struct intel_renderstate_rodata *rodata,
+ struct i915_render_state *so)
+{
+ 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)
+ return -EINVAL;
+
+ ret = i915_gem_object_set_to_cpu_domain(so->obj, true);
+ if (ret)
+ return ret;
+
+ 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 + 1 >= rodata->batch_items ||
+ rodata->batch[i + 1] != 0)
+ return -EINVAL;
+
+ d[i] = s;
+ i++;
+ s = (goffset & 0xffffffff00000000ull) >> 32;
+ }
+
+ reloc_index++;
+ }
+
+ d[i] = s;
+ i++;
+ }
+
+ 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);
+ return -EINVAL;
+ }
+
+ so->len = rodata->batch_items * 4;
+
+ return 0;
+}
+
+int i915_gem_render_state_init(struct intel_engine_cs *ring)
+{
+ 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)
+ return 0;
+
+ so = render_state_alloc(ring->dev);
+ if (IS_ERR(so))
+ return PTR_ERR(so);
+
+ ret = render_state_setup(gen, rodata, so);
+ if (ret)
+ goto out;
+
+ ret = ring->dispatch_execbuffer(ring,
+ i915_gem_obj_ggtt_offset(so->obj),
+ so->len,
+ 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, NULL, so->obj, NULL);
+ /* __i915_add_request moves object to inactive if it fails */
+out:
+ render_state_free(so);
+ return ret;
+}
diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c
new file mode 100644
index 000000000000..21ea92886a56
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_gem_userptr.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright © 2012-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.
+ *
+ */
+
+#include "drmP.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "i915_trace.h"
+#include "intel_drv.h"
+#include <linux/mmu_context.h>
+#include <linux/mmu_notifier.h>
+#include <linux/mempolicy.h>
+#include <linux/swap.h>
+
+#if defined(CONFIG_MMU_NOTIFIER)
+#include <linux/interval_tree.h>
+
+struct i915_mmu_notifier {
+ spinlock_t lock;
+ 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;
+ unsigned long serial;
+};
+
+struct i915_mmu_object {
+ struct i915_mmu_notifier *mmu;
+ struct interval_tree_node it;
+ struct drm_i915_gem_object *obj;
+};
+
+static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
+ struct mm_struct *mm,
+ unsigned long start,
+ unsigned long end)
+{
+ struct i915_mmu_notifier *mn = container_of(_mn, struct i915_mmu_notifier, mn);
+ struct interval_tree_node *it = NULL;
+ unsigned long serial = 0;
+
+ end--; /* interval ranges are inclusive, but invalidate range is exclusive */
+ while (start < end) {
+ struct drm_i915_gem_object *obj;
+
+ obj = NULL;
+ spin_lock(&mn->lock);
+ if (serial == mn->serial)
+ it = interval_tree_iter_next(it, start, end);
+ else
+ it = interval_tree_iter_first(&mn->objects, start, end);
+ if (it != NULL) {
+ obj = container_of(it, struct i915_mmu_object, it)->obj;
+ drm_gem_object_reference(&obj->base);
+ serial = mn->serial;
+ }
+ spin_unlock(&mn->lock);
+ 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);
+ }
+}
+
+static const struct mmu_notifier_ops i915_gem_userptr_notifier = {
+ .invalidate_range_start = i915_gem_userptr_mn_invalidate_range_start,
+};
+
+static struct i915_mmu_notifier *
+__i915_mmu_notifier_lookup(struct drm_device *dev, 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;
+ 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)
+ 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;
+
+ /* Protected by mmap_sem (write-lock) */
+ ret = __mmu_notifier_register(&mmu->mn, mm);
+ if (ret) {
+ kfree(mmu);
+ 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);
+}
+
+static void
+__i915_mmu_notifier_destroy(struct i915_mmu_notifier *mmu)
+{
+ 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);
+}
+
+static int
+i915_mmu_notifier_add(struct i915_mmu_notifier *mmu,
+ struct i915_mmu_object *mn)
+{
+ struct interval_tree_node *it;
+ int ret;
+
+ ret = i915_mutex_lock_interruptible(mmu->dev);
+ if (ret)
+ return ret;
+
+ /* Make sure we drop the final active reference (and thereby
+ * remove the objects from the interval tree) before we do
+ * the check for overlapping objects.
+ */
+ i915_gem_retire_requests(mmu->dev);
+
+ /* Disallow overlapping userptr objects */
+ spin_lock(&mmu->lock);
+ it = interval_tree_iter_first(&mmu->objects,
+ mn->it.start, mn->it.last);
+ if (it) {
+ struct drm_i915_gem_object *obj;
+
+ /* We only need to check the first object in the range as it
+ * either has cancelled gup work queued and we need to
+ * return back to the user to give time for the gup-workers
+ * 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.
+ */
+
+ 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;
+ }
+ spin_unlock(&mmu->lock);
+ mutex_unlock(&mmu->dev->struct_mutex);
+
+ return ret;
+}
+
+static void
+i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj)
+{
+ struct i915_mmu_object *mn;
+
+ mn = obj->userptr.mn;
+ if (mn == NULL)
+ return;
+
+ i915_mmu_notifier_del(mn->mmu, mn);
+ obj->userptr.mn = NULL;
+}
+
+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;
+ 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;
+
+ mn = kzalloc(sizeof(*mn), GFP_KERNEL);
+ if (mn == NULL) {
+ ret = -ENOMEM;
+ goto destroy_mmu;
+ }
+
+ mn->mmu = mmu;
+ mn->it.start = obj->userptr.ptr;
+ mn->it.last = mn->it.start + obj->base.size - 1;
+ mn->obj = obj;
+
+ ret = i915_mmu_notifier_add(mmu, mn);
+ if (ret)
+ goto free_mn;
+
+ obj->userptr.mn = mn;
+ return 0;
+
+free_mn:
+ 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
+
+static void
+i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj)
+{
+}
+
+static int
+i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj,
+ unsigned flags)
+{
+ if ((flags & I915_USERPTR_UNSYNCHRONIZED) == 0)
+ return -ENODEV;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ return 0;
+}
+#endif
+
+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
+#define swiotlb_active() 0
+#endif
+
+static int
+st_set_pages(struct sg_table **st, struct page **pvec, int num_pages)
+{
+ struct scatterlist *sg;
+ int ret, n;
+
+ *st = kmalloc(sizeof(**st), GFP_KERNEL);
+ if (*st == NULL)
+ return -ENOMEM;
+
+ if (swiotlb_active()) {
+ ret = sg_alloc_table(*st, num_pages, GFP_KERNEL);
+ if (ret)
+ goto err;
+
+ for_each_sg((*st)->sgl, sg, num_pages, n)
+ sg_set_page(sg, pvec[n], PAGE_SIZE, 0);
+ } else {
+ ret = sg_alloc_table_from_pages(*st, pvec, num_pages,
+ 0, num_pages << PAGE_SHIFT,
+ GFP_KERNEL);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ kfree(*st);
+ *st = NULL;
+ return ret;
+}
+
+static void
+__i915_gem_userptr_get_pages_worker(struct work_struct *_work)
+{
+ struct get_pages_work *work = container_of(_work, typeof(*work), work);
+ struct drm_i915_gem_object *obj = work->obj;
+ struct drm_device *dev = obj->base.dev;
+ const int num_pages = obj->base.size >> PAGE_SHIFT;
+ struct page **pvec;
+ int pinned, ret;
+
+ ret = -ENOMEM;
+ pinned = 0;
+
+ pvec = kmalloc(num_pages*sizeof(struct page *),
+ GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
+ if (pvec == NULL)
+ pvec = drm_malloc_ab(num_pages, sizeof(struct page *));
+ if (pvec != NULL) {
+ struct mm_struct *mm = obj->userptr.mm;
+
+ down_read(&mm->mmap_sem);
+ while (pinned < num_pages) {
+ ret = get_user_pages(work->task, mm,
+ obj->userptr.ptr + pinned * PAGE_SIZE,
+ num_pages - pinned,
+ !obj->userptr.read_only, 0,
+ pvec + pinned, NULL);
+ if (ret < 0)
+ break;
+
+ pinned += ret;
+ }
+ up_read(&mm->mmap_sem);
+ }
+
+ mutex_lock(&dev->struct_mutex);
+ if (obj->userptr.work != &work->work) {
+ ret = 0;
+ } else if (pinned == num_pages) {
+ ret = st_set_pages(&obj->pages, pvec, num_pages);
+ if (ret == 0) {
+ list_add_tail(&obj->global_list, &to_i915(dev)->mm.unbound_list);
+ pinned = 0;
+ }
+ }
+
+ obj->userptr.work = ERR_PTR(ret);
+ obj->userptr.workers--;
+ drm_gem_object_unreference(&obj->base);
+ mutex_unlock(&dev->struct_mutex);
+
+ release_pages(pvec, pinned, 0);
+ drm_free_large(pvec);
+
+ put_task_struct(work->task);
+ kfree(work);
+}
+
+static int
+i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
+{
+ const int num_pages = obj->base.size >> PAGE_SHIFT;
+ struct page **pvec;
+ int pinned, ret;
+
+ /* If userspace should engineer that these pages are replaced in
+ * the vma between us binding this page into the GTT and completion
+ * of rendering... Their loss. If they change the mapping of their
+ * pages they need to create a new bo to point to the new vma.
+ *
+ * However, that still leaves open the possibility of the vma
+ * being copied upon fork. Which falls under the same userspace
+ * synchronisation issue as a regular bo, except that this time
+ * the process may not be expecting that a particular piece of
+ * memory is tied to the GPU.
+ *
+ * Fortunately, we can hook into the mmu_notifier in order to
+ * discard the page references prior to anything nasty happening
+ * to the vma (discard or cloning) which should prevent the more
+ * egregious cases from causing harm.
+ */
+
+ pvec = NULL;
+ pinned = 0;
+ if (obj->userptr.mm == current->mm) {
+ pvec = kmalloc(num_pages*sizeof(struct page *),
+ GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
+ if (pvec == NULL) {
+ pvec = drm_malloc_ab(num_pages, sizeof(struct page *));
+ if (pvec == NULL)
+ return -ENOMEM;
+ }
+
+ pinned = __get_user_pages_fast(obj->userptr.ptr, num_pages,
+ !obj->userptr.read_only, pvec);
+ }
+ if (pinned < num_pages) {
+ if (pinned < 0) {
+ ret = pinned;
+ pinned = 0;
+ } else {
+ /* Spawn a worker so that we can acquire the
+ * user pages without holding our mutex. Access
+ * to the user pages requires mmap_sem, and we have
+ * a strict lock ordering of mmap_sem, struct_mutex -
+ * we already hold struct_mutex here and so cannot
+ * call gup without encountering a lock inversion.
+ *
+ * Userspace will keep on repeating the operation
+ * (thanks to EAGAIN) until either we hit the fast
+ * path or the worker completes. If the worker is
+ * cancelled or superseded, the task is still run
+ * but the results ignored. (This leads to
+ * complications that we may have a stray object
+ * refcount that we need to be wary of when
+ * checking for existing objects during creation.)
+ * If the worker encounters an error, it reports
+ * that error back to this function through
+ * obj->userptr.work = ERR_PTR.
+ */
+ ret = -EAGAIN;
+ if (obj->userptr.work == NULL &&
+ obj->userptr.workers < I915_GEM_USERPTR_MAX_WORKERS) {
+ struct get_pages_work *work;
+
+ work = kmalloc(sizeof(*work), GFP_KERNEL);
+ if (work != NULL) {
+ obj->userptr.work = &work->work;
+ obj->userptr.workers++;
+
+ work->obj = obj;
+ drm_gem_object_reference(&obj->base);
+
+ work->task = current;
+ get_task_struct(work->task);
+
+ INIT_WORK(&work->work, __i915_gem_userptr_get_pages_worker);
+ schedule_work(&work->work);
+ } else
+ ret = -ENOMEM;
+ } else {
+ if (IS_ERR(obj->userptr.work)) {
+ ret = PTR_ERR(obj->userptr.work);
+ obj->userptr.work = NULL;
+ }
+ }
+ }
+ } else {
+ ret = st_set_pages(&obj->pages, pvec, num_pages);
+ if (ret == 0) {
+ obj->userptr.work = NULL;
+ pinned = 0;
+ }
+ }
+
+ release_pages(pvec, pinned, 0);
+ drm_free_large(pvec);
+ return ret;
+}
+
+static void
+i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj)
+{
+ struct scatterlist *sg;
+ int i;
+
+ 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);
+
+ if (obj->dirty)
+ set_page_dirty(page);
+
+ mark_page_accessed(page);
+ page_cache_release(page);
+ }
+ obj->dirty = 0;
+
+ sg_free_table(obj->pages);
+ kfree(obj->pages);
+}
+
+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;
+ }
+}
+
+static int
+i915_gem_userptr_dmabuf_export(struct drm_i915_gem_object *obj)
+{
+ if (obj->userptr.mn)
+ return 0;
+
+ return i915_gem_userptr_init__mmu_notifier(obj, 0);
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = {
+ .dmabuf_export = i915_gem_userptr_dmabuf_export,
+ .get_pages = i915_gem_userptr_get_pages,
+ .put_pages = i915_gem_userptr_put_pages,
+ .release = i915_gem_userptr_release,
+};
+
+/**
+ * Creates a new mm object that wraps some normal memory from the process
+ * context - user memory.
+ *
+ * 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
+ * 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,
+ * 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
+ * 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).
+ *
+ * Synchronisation between multiple users and the GPU is left to userspace
+ * through the normal set-domain-ioctl. The kernel will enforce that the
+ * GPU relinquishes the VMA before it is returned back to the system
+ * i.e. upon free(), munmap() or process termination. However, the userspace
+ * malloc() library may not immediately relinquish the VMA after free() and
+ * instead reuse it whilst the GPU is still reading and writing to the VMA.
+ * Caveat emptor.
+ *
+ * Also note, that the object created here is not currently a "first class"
+ * object, in that several ioctls are banned. These are the CPU access
+ * ioctls: mmap(), pwrite and pread. In practice, you are expected to use
+ * direct access via your pointer rather than use those ioctls.
+ *
+ * If you think this is a good interface to use to pass GPU memory between
+ * drivers, please use dma-buf instead. In fact, wherever possible use
+ * dma-buf instead.
+ */
+int
+i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_userptr *args = data;
+ struct drm_i915_gem_object *obj;
+ int ret;
+ u32 handle;
+
+ if (args->flags & ~(I915_USERPTR_READ_ONLY |
+ I915_USERPTR_UNSYNCHRONIZED))
+ return -EINVAL;
+
+ if (offset_in_page(args->user_ptr | args->user_size))
+ return -EINVAL;
+
+ if (args->user_size > dev_priv->gtt.base.total)
+ return -E2BIG;
+
+ if (!access_ok(args->flags & I915_USERPTR_READ_ONLY ? VERIFY_READ : VERIFY_WRITE,
+ (char __user *)(unsigned long)args->user_ptr, args->user_size))
+ return -EFAULT;
+
+ if (args->flags & I915_USERPTR_READ_ONLY) {
+ /* On almost all of the current hw, we cannot tell the GPU that a
+ * page is readonly, so this is just a placeholder in the uAPI.
+ */
+ return -ENODEV;
+ }
+
+ /* Allocate the new object */
+ obj = i915_gem_object_alloc(dev);
+ if (obj == NULL)
+ return -ENOMEM;
+
+ drm_gem_private_object_init(dev, &obj->base, args->user_size);
+ i915_gem_object_init(obj, &i915_gem_userptr_ops);
+ obj->cache_level = I915_CACHE_LLC;
+ obj->base.write_domain = I915_GEM_DOMAIN_CPU;
+ obj->base.read_domains = I915_GEM_DOMAIN_CPU;
+
+ obj->userptr.ptr = args->user_ptr;
+ obj->userptr.read_only = !!(args->flags & I915_USERPTR_READ_ONLY);
+
+ /* And keep a pointer to the current->mm for resolving the user pages
+ * 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__mmu_notifier(obj, args->flags);
+ if (ret == 0)
+ ret = drm_gem_handle_create(file, &obj->base, &handle);
+
+ /* drop reference from allocate - handle holds it now */
+ drm_gem_object_unreference_unlocked(&obj->base);
+ if (ret)
+ return ret;
+
+ args->handle = handle;
+ return 0;
+}
+
+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
+ return 0;
+}
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 12f1d43b2d68..87ec60e181a7 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -42,6 +42,7 @@ static const char *ring_str(int ring)
case VCS: return "bsd";
case BCS: return "blt";
case VECS: return "vebox";
+ case VCS2: return "bsd2";
default: return "";
}
}
@@ -204,6 +205,7 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m,
err_puts(m, tiling_flag(err->tiling));
err_puts(m, dirty_flag(err->dirty));
err_puts(m, purgeable_flag(err->purgeable));
+ 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));
@@ -257,7 +259,8 @@ static void i915_ring_error_state(struct drm_i915_error_state_buf *m,
err_printf(m, " INSTPS: 0x%08x\n", ring->instps);
}
err_printf(m, " INSTPM: 0x%08x\n", ring->instpm);
- err_printf(m, " FADDR: 0x%08x\n", ring->faddr);
+ err_printf(m, " FADDR: 0x%08x %08x\n", upper_32_bits(ring->faddr),
+ lower_32_bits(ring->faddr));
if (INTEL_INFO(dev)->gen >= 6) {
err_printf(m, " RC PSMI: 0x%08x\n", ring->rc_psmi);
err_printf(m, " FAULT_REG: 0x%08x\n", ring->fault_reg);
@@ -452,16 +455,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
err_printf(m, "%s --- HW Context = 0x%08x\n",
dev_priv->ring[i].name,
obj->gtt_offset);
- offset = 0;
- for (elt = 0; elt < PAGE_SIZE/16; elt += 4) {
- err_printf(m, "[%04x] %08x %08x %08x %08x\n",
- offset,
- obj->pages[0][elt],
- obj->pages[0][elt+1],
- obj->pages[0][elt+2],
- obj->pages[0][elt+3]);
- offset += 16;
- }
+ print_error_obj(m, obj);
}
}
@@ -648,6 +642,7 @@ static void capture_bo(struct drm_i915_error_buffer *err,
err->tiling = obj->tiling_mode;
err->dirty = obj->dirty;
err->purgeable = obj->madv != I915_MADV_WILLNEED;
+ err->userptr = obj->userptr.mm != NULL;
err->ring = obj->ring ? obj->ring->id : -1;
err->cache_level = obj->cache_level;
}
@@ -752,7 +747,7 @@ static void i915_gem_record_fences(struct drm_device *dev,
}
static void i915_record_ring_state(struct drm_device *dev,
- struct intel_ring_buffer *ring,
+ struct intel_engine_cs *ring,
struct drm_i915_error_ring *ering)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -764,14 +759,14 @@ static void i915_record_ring_state(struct drm_device *dev,
= 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->sync_seqno[0];
- ering->semaphore_seqno[1] = ring->sync_seqno[1];
+ 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->sync_seqno[2];
+ ering->semaphore_seqno[2] = ring->semaphore.sync_seqno[2];
}
if (INTEL_INFO(dev)->gen >= 4) {
@@ -781,8 +776,10 @@ static void i915_record_ring_state(struct drm_device *dev,
ering->instdone = I915_READ(RING_INSTDONE(ring->mmio_base));
ering->instps = I915_READ(RING_INSTPS(ring->mmio_base));
ering->bbaddr = I915_READ(RING_BBADDR(ring->mmio_base));
- if (INTEL_INFO(dev)->gen >= 8)
+ if (INTEL_INFO(dev)->gen >= 8) {
+ ering->faddr |= (u64) I915_READ(RING_DMA_FADD_UDW(ring->mmio_base)) << 32;
ering->bbaddr |= (u64) I915_READ(RING_BBADDR_UDW(ring->mmio_base)) << 32;
+ }
ering->bbstate = I915_READ(RING_BBSTATE(ring->mmio_base));
} else {
ering->faddr = I915_READ(DMA_FADD_I8XX);
@@ -828,8 +825,8 @@ static void i915_record_ring_state(struct drm_device *dev,
ering->hws = I915_READ(mmio);
}
- ering->cpu_ring_head = ring->head;
- ering->cpu_ring_tail = ring->tail;
+ 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;
@@ -862,7 +859,7 @@ static void i915_record_ring_state(struct drm_device *dev,
}
-static void i915_gem_record_active_context(struct intel_ring_buffer *ring,
+static void i915_gem_record_active_context(struct intel_engine_cs *ring,
struct drm_i915_error_state *error,
struct drm_i915_error_ring *ering)
{
@@ -875,10 +872,7 @@ static void i915_gem_record_active_context(struct intel_ring_buffer *ring,
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
if ((error->ccid & PAGE_MASK) == i915_gem_obj_ggtt_offset(obj)) {
- ering->ctx = i915_error_object_create_sized(dev_priv,
- obj,
- &dev_priv->gtt.base,
- 1);
+ ering->ctx = i915_error_ggtt_object_create(dev_priv, obj);
break;
}
}
@@ -892,7 +886,7 @@ static void i915_gem_record_rings(struct drm_device *dev,
int i, count;
for (i = 0; i < I915_NUM_RINGS; i++) {
- struct intel_ring_buffer *ring = &dev_priv->ring[i];
+ struct intel_engine_cs *ring = &dev_priv->ring[i];
if (ring->dev == NULL)
continue;
@@ -936,7 +930,7 @@ static void i915_gem_record_rings(struct drm_device *dev,
}
error->ring[i].ringbuffer =
- i915_error_ggtt_object_create(dev_priv, ring->obj);
+ i915_error_ggtt_object_create(dev_priv, ring->buffer->obj);
if (ring->status_page.obj)
error->ring[i].hws_page =
@@ -1037,7 +1031,6 @@ 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 pipe;
/* General organization
* 1. Registers specific to a single generation
@@ -1062,9 +1055,6 @@ static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
error->gfx_mode = I915_READ(GFX_MODE);
}
- if (IS_GEN2(dev))
- error->ier = I915_READ16(IER);
-
/* 2: Registers which belong to multiple generations */
if (INTEL_INFO(dev)->gen >= 7)
error->forcewake = I915_READ(FORCEWAKE_MT);
@@ -1088,9 +1078,10 @@ static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
if (HAS_PCH_SPLIT(dev))
error->ier = I915_READ(DEIER) | I915_READ(GTIER);
else {
- error->ier = I915_READ(IER);
- for_each_pipe(pipe)
- error->pipestat[pipe] = I915_READ(PIPESTAT(pipe));
+ if (IS_GEN2(dev))
+ error->ier = I915_READ16(IER);
+ else
+ error->ier = I915_READ(IER);
}
/* 4: Everything else */
diff --git a/drivers/gpu/drm/i915/i915_ioc32.c b/drivers/gpu/drm/i915/i915_ioc32.c
index 3c59584161c2..2e0613e26251 100644
--- a/drivers/gpu/drm/i915/i915_ioc32.c
+++ b/drivers/gpu/drm/i915/i915_ioc32.c
@@ -208,7 +208,7 @@ long i915_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (nr < DRM_COMMAND_BASE)
return drm_compat_ioctl(filp, cmd, arg);
- if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(i915_compat_ioctls))
+ if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(i915_compat_ioctls))
fn = i915_compat_ioctls[nr - DRM_COMMAND_BASE];
if (fn != NULL)
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 0b99de95593b..6f8017a7e937 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -80,17 +80,64 @@ static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */
[HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
};
+/* IIR can theoretically queue up two events. Be paranoid. */
+#define GEN8_IRQ_RESET_NDX(type, which) do { \
+ I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
+ POSTING_READ(GEN8_##type##_IMR(which)); \
+ I915_WRITE(GEN8_##type##_IER(which), 0); \
+ I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+ POSTING_READ(GEN8_##type##_IIR(which)); \
+ I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+ POSTING_READ(GEN8_##type##_IIR(which)); \
+} while (0)
+
+#define GEN5_IRQ_RESET(type) do { \
+ I915_WRITE(type##IMR, 0xffffffff); \
+ POSTING_READ(type##IMR); \
+ I915_WRITE(type##IER, 0); \
+ I915_WRITE(type##IIR, 0xffffffff); \
+ POSTING_READ(type##IIR); \
+ I915_WRITE(type##IIR, 0xffffffff); \
+ POSTING_READ(type##IIR); \
+} while (0)
+
+/*
+ * We should clear IMR at preinstall/uninstall, and just check at postinstall.
+ */
+#define GEN5_ASSERT_IIR_IS_ZERO(reg) do { \
+ u32 val = I915_READ(reg); \
+ if (val) { \
+ WARN(1, "Interrupt register 0x%x is not zero: 0x%08x\n", \
+ (reg), val); \
+ I915_WRITE((reg), 0xffffffff); \
+ POSTING_READ(reg); \
+ I915_WRITE((reg), 0xffffffff); \
+ POSTING_READ(reg); \
+ } \
+} while (0)
+
+#define GEN8_IRQ_INIT_NDX(type, which, imr_val, ier_val) do { \
+ GEN5_ASSERT_IIR_IS_ZERO(GEN8_##type##_IIR(which)); \
+ I915_WRITE(GEN8_##type##_IMR(which), (imr_val)); \
+ I915_WRITE(GEN8_##type##_IER(which), (ier_val)); \
+ POSTING_READ(GEN8_##type##_IER(which)); \
+} while (0)
+
+#define GEN5_IRQ_INIT(type, imr_val, ier_val) do { \
+ GEN5_ASSERT_IIR_IS_ZERO(type##IIR); \
+ I915_WRITE(type##IMR, (imr_val)); \
+ I915_WRITE(type##IER, (ier_val)); \
+ POSTING_READ(type##IER); \
+} while (0)
+
/* For display hotplug interrupt */
static void
ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
{
assert_spin_locked(&dev_priv->irq_lock);
- if (dev_priv->pm.irqs_disabled) {
- WARN(1, "IRQs disabled\n");
- dev_priv->pm.regsave.deimr &= ~mask;
+ if (WARN_ON(dev_priv->pm.irqs_disabled))
return;
- }
if ((dev_priv->irq_mask & mask) != 0) {
dev_priv->irq_mask &= ~mask;
@@ -104,11 +151,8 @@ ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
{
assert_spin_locked(&dev_priv->irq_lock);
- if (dev_priv->pm.irqs_disabled) {
- WARN(1, "IRQs disabled\n");
- dev_priv->pm.regsave.deimr |= mask;
+ if (WARN_ON(dev_priv->pm.irqs_disabled))
return;
- }
if ((dev_priv->irq_mask & mask) != mask) {
dev_priv->irq_mask |= mask;
@@ -129,13 +173,8 @@ static void ilk_update_gt_irq(struct drm_i915_private *dev_priv,
{
assert_spin_locked(&dev_priv->irq_lock);
- if (dev_priv->pm.irqs_disabled) {
- WARN(1, "IRQs disabled\n");
- dev_priv->pm.regsave.gtimr &= ~interrupt_mask;
- dev_priv->pm.regsave.gtimr |= (~enabled_irq_mask &
- interrupt_mask);
+ if (WARN_ON(dev_priv->pm.irqs_disabled))
return;
- }
dev_priv->gt_irq_mask &= ~interrupt_mask;
dev_priv->gt_irq_mask |= (~enabled_irq_mask & interrupt_mask);
@@ -167,13 +206,8 @@ static void snb_update_pm_irq(struct drm_i915_private *dev_priv,
assert_spin_locked(&dev_priv->irq_lock);
- if (dev_priv->pm.irqs_disabled) {
- WARN(1, "IRQs disabled\n");
- dev_priv->pm.regsave.gen6_pmimr &= ~interrupt_mask;
- dev_priv->pm.regsave.gen6_pmimr |= (~enabled_irq_mask &
- interrupt_mask);
+ if (WARN_ON(dev_priv->pm.irqs_disabled))
return;
- }
new_val = dev_priv->pm_irq_mask;
new_val &= ~interrupt_mask;
@@ -214,6 +248,46 @@ static bool ivb_can_enable_err_int(struct drm_device *dev)
return true;
}
+/**
+ * bdw_update_pm_irq - update GT interrupt 2
+ * @dev_priv: driver private
+ * @interrupt_mask: mask of interrupt bits to update
+ * @enabled_irq_mask: mask of interrupt bits to enable
+ *
+ * Copied from the snb function, updated with relevant register offsets
+ */
+static void bdw_update_pm_irq(struct drm_i915_private *dev_priv,
+ uint32_t interrupt_mask,
+ uint32_t enabled_irq_mask)
+{
+ uint32_t new_val;
+
+ assert_spin_locked(&dev_priv->irq_lock);
+
+ if (WARN_ON(dev_priv->pm.irqs_disabled))
+ return;
+
+ new_val = dev_priv->pm_irq_mask;
+ new_val &= ~interrupt_mask;
+ new_val |= (~enabled_irq_mask & interrupt_mask);
+
+ if (new_val != dev_priv->pm_irq_mask) {
+ dev_priv->pm_irq_mask = new_val;
+ I915_WRITE(GEN8_GT_IMR(2), dev_priv->pm_irq_mask);
+ POSTING_READ(GEN8_GT_IMR(2));
+ }
+}
+
+void bdw_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)
+{
+ bdw_update_pm_irq(dev_priv, mask, 0);
+}
+
static bool cpt_can_enable_serr_int(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -232,16 +306,51 @@ static bool cpt_can_enable_serr_int(struct drm_device *dev)
return true;
}
-static void i9xx_clear_fifo_underrun(struct drm_device *dev, enum pipe pipe)
+void i9xx_check_fifo_underruns(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *crtc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev_priv->irq_lock, flags);
+
+ for_each_intel_crtc(dev, crtc) {
+ u32 reg = PIPESTAT(crtc->pipe);
+ u32 pipestat;
+
+ if (crtc->cpu_fifo_underrun_disabled)
+ continue;
+
+ pipestat = I915_READ(reg) & 0xffff0000;
+ if ((pipestat & PIPE_FIFO_UNDERRUN_STATUS) == 0)
+ continue;
+
+ I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS);
+ POSTING_READ(reg);
+
+ DRM_ERROR("pipe %c underrun\n", pipe_name(crtc->pipe));
+ }
+
+ spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+}
+
+static void i9xx_set_fifo_underrun_reporting(struct drm_device *dev,
+ enum pipe pipe,
+ bool enable, bool old)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 reg = PIPESTAT(pipe);
- u32 pipestat = I915_READ(reg) & 0x7fff0000;
+ u32 pipestat = I915_READ(reg) & 0xffff0000;
assert_spin_locked(&dev_priv->irq_lock);
- I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS);
- POSTING_READ(reg);
+ if (enable) {
+ I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS);
+ POSTING_READ(reg);
+ } else {
+ if (old && pipestat & PIPE_FIFO_UNDERRUN_STATUS)
+ DRM_ERROR("pipe %c underrun\n", pipe_name(pipe));
+ }
}
static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev,
@@ -258,7 +367,8 @@ static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev,
}
static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
- enum pipe pipe, bool enable)
+ enum pipe pipe,
+ bool enable, bool old)
{
struct drm_i915_private *dev_priv = dev->dev_private;
if (enable) {
@@ -269,15 +379,12 @@ static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
} else {
- bool was_enabled = !(I915_READ(DEIMR) & DE_ERR_INT_IVB);
-
- /* Change the state _after_ we've read out the current one. */
ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
- if (!was_enabled &&
- (I915_READ(GEN7_ERR_INT) & ERR_INT_FIFO_UNDERRUN(pipe))) {
- DRM_DEBUG_KMS("uncleared fifo underrun on pipe %c\n",
- pipe_name(pipe));
+ if (old &&
+ I915_READ(GEN7_ERR_INT) & ERR_INT_FIFO_UNDERRUN(pipe)) {
+ DRM_ERROR("uncleared fifo underrun on pipe %c\n",
+ pipe_name(pipe));
}
}
}
@@ -313,14 +420,8 @@ static void ibx_display_interrupt_update(struct drm_i915_private *dev_priv,
assert_spin_locked(&dev_priv->irq_lock);
- if (dev_priv->pm.irqs_disabled &&
- (interrupt_mask & SDE_HOTPLUG_MASK_CPT)) {
- WARN(1, "IRQs disabled\n");
- dev_priv->pm.regsave.sdeimr &= ~interrupt_mask;
- dev_priv->pm.regsave.sdeimr |= (~enabled_irq_mask &
- interrupt_mask);
+ if (WARN_ON(dev_priv->pm.irqs_disabled))
return;
- }
I915_WRITE(SDEIMR, sdeimr);
POSTING_READ(SDEIMR);
@@ -346,7 +447,7 @@ static void ibx_set_fifo_underrun_reporting(struct drm_device *dev,
static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
enum transcoder pch_transcoder,
- bool enable)
+ bool enable, bool old)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -359,16 +460,12 @@ static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
ibx_enable_display_interrupt(dev_priv, SDE_ERROR_CPT);
} else {
- uint32_t tmp = I915_READ(SERR_INT);
- bool was_enabled = !(I915_READ(SDEIMR) & SDE_ERROR_CPT);
-
- /* Change the state _after_ we've read out the current one. */
ibx_disable_display_interrupt(dev_priv, SDE_ERROR_CPT);
- if (!was_enabled &&
- (tmp & SERR_INT_TRANS_FIFO_UNDERRUN(pch_transcoder))) {
- DRM_DEBUG_KMS("uncleared pch fifo underrun on pch transcoder %c\n",
- transcoder_name(pch_transcoder));
+ if (old && I915_READ(SERR_INT) &
+ SERR_INT_TRANS_FIFO_UNDERRUN(pch_transcoder)) {
+ DRM_ERROR("uncleared pch fifo underrun on pch transcoder %c\n",
+ transcoder_name(pch_transcoder));
}
}
}
@@ -387,34 +484,29 @@ static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
*
* Returns the previous state of underrun reporting.
*/
-bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
- enum pipe pipe, bool enable)
+static bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
+ enum pipe pipe, bool enable)
{
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);
- bool ret;
+ bool old;
assert_spin_locked(&dev_priv->irq_lock);
- ret = !intel_crtc->cpu_fifo_underrun_disabled;
-
- if (enable == ret)
- goto done;
-
+ old = !intel_crtc->cpu_fifo_underrun_disabled;
intel_crtc->cpu_fifo_underrun_disabled = !enable;
- if (enable && (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev)))
- i9xx_clear_fifo_underrun(dev, pipe);
+ if (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(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);
else if (IS_GEN7(dev))
- ivybridge_set_fifo_underrun_reporting(dev, pipe, enable);
+ ivybridge_set_fifo_underrun_reporting(dev, pipe, enable, old);
else if (IS_GEN8(dev))
broadwell_set_fifo_underrun_reporting(dev, pipe, enable);
-done:
- return ret;
+ return old;
}
bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
@@ -463,7 +555,7 @@ bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pch_transcoder];
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
unsigned long flags;
- bool ret;
+ bool old;
/*
* NOTE: Pre-LPT has a fixed cpu pipe -> pch transcoder mapping, but LPT
@@ -476,21 +568,16 @@ bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
spin_lock_irqsave(&dev_priv->irq_lock, flags);
- ret = !intel_crtc->pch_fifo_underrun_disabled;
-
- if (enable == ret)
- goto done;
-
+ old = !intel_crtc->pch_fifo_underrun_disabled;
intel_crtc->pch_fifo_underrun_disabled = !enable;
if (HAS_PCH_IBX(dev))
ibx_set_fifo_underrun_reporting(dev, pch_transcoder, enable);
else
- cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable);
+ cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable, old);
-done:
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
- return ret;
+ return old;
}
@@ -503,8 +590,10 @@ __i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
assert_spin_locked(&dev_priv->irq_lock);
- if (WARN_ON_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
- status_mask & ~PIPESTAT_INT_STATUS_MASK))
+ if (WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
+ status_mask & ~PIPESTAT_INT_STATUS_MASK,
+ "pipe %c: enable_mask=0x%x, status_mask=0x%x\n",
+ pipe_name(pipe), enable_mask, status_mask))
return;
if ((pipestat & enable_mask) == enable_mask)
@@ -527,8 +616,10 @@ __i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
assert_spin_locked(&dev_priv->irq_lock);
- if (WARN_ON_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
- status_mask & ~PIPESTAT_INT_STATUS_MASK))
+ if (WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
+ status_mask & ~PIPESTAT_INT_STATUS_MASK,
+ "pipe %c: enable_mask=0x%x, status_mask=0x%x\n",
+ pipe_name(pipe), enable_mask, status_mask))
return;
if ((pipestat & enable_mask) == 0)
@@ -546,11 +637,17 @@ static u32 vlv_get_pipestat_enable_mask(struct drm_device *dev, u32 status_mask)
u32 enable_mask = status_mask << 16;
/*
- * On pipe A we don't support the PSR interrupt yet, on pipe B the
- * same bit MBZ.
+ * On pipe A we don't support the PSR interrupt yet,
+ * on pipe B and C the same bit MBZ.
*/
if (WARN_ON_ONCE(status_mask & PIPE_A_PSR_STATUS_VLV))
return 0;
+ /*
+ * On pipe B and C we don't support the PSR interrupt yet, on pipe
+ * A the same bit is for perf counters which we don't use either.
+ */
+ if (WARN_ON_ONCE(status_mask & PIPE_B_PSR_STATUS_VLV))
+ return 0;
enable_mask &= ~(PIPE_FIFO_UNDERRUN_STATUS |
SPRITE0_FLIP_DONE_INT_EN_VLV |
@@ -637,6 +734,56 @@ i915_pipe_enabled(struct drm_device *dev, int pipe)
}
}
+/*
+ * This timing diagram depicts the video signal in and
+ * around the vertical blanking period.
+ *
+ * Assumptions about the fictitious mode used in this example:
+ * vblank_start >= 3
+ * vsync_start = vblank_start + 1
+ * vsync_end = vblank_start + 2
+ * vtotal = vblank_start + 3
+ *
+ * start of vblank:
+ * latch double buffered registers
+ * increment frame counter (ctg+)
+ * generate start of vblank interrupt (gen4+)
+ * |
+ * | frame start:
+ * | generate frame start interrupt (aka. vblank interrupt) (gmch)
+ * | may be shifted forward 1-3 extra lines via PIPECONF
+ * | |
+ * | | start of vsync:
+ * | | generate vsync interrupt
+ * | | |
+ * ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx
+ * . \hs/ . \hs/ \hs/ \hs/ . \hs/
+ * ----va---> <-----------------vb--------------------> <--------va-------------
+ * | | <----vs-----> |
+ * -vbs-----> <---vbs+1---> <---vbs+2---> <-----0-----> <-----1-----> <-----2--- (scanline counter gen2)
+ * -vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2---> <-----0--- (scanline counter gen3+)
+ * -vbs-2---> <---vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2- (scanline counter hsw+ hdmi)
+ * | | |
+ * last visible pixel first visible pixel
+ * | increment frame counter (gen3/4)
+ * pixel counter = vblank_start * htotal pixel counter = 0 (gen3/4)
+ *
+ * x = horizontal active
+ * _ = horizontal blanking
+ * hs = horizontal sync
+ * va = vertical active
+ * vb = vertical blanking
+ * vs = vertical sync
+ * vbs = vblank_start (number)
+ *
+ * Summary:
+ * - most events happen at the start of horizontal sync
+ * - frame start happens at the start of horizontal blank, 1-4 lines
+ * (depending on PIPECONF settings) after the start of vblank
+ * - gen3/4 pixel and frame counter are synchronized with the start
+ * of horizontal active on the first line of vertical active
+ */
+
static u32 i8xx_get_vblank_counter(struct drm_device *dev, int pipe)
{
/* Gen2 doesn't have a hardware frame counter */
@@ -651,7 +798,7 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long high_frame;
unsigned long low_frame;
- u32 high1, high2, low, pixel, vbl_start;
+ u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
if (!i915_pipe_enabled(dev, pipe)) {
DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
@@ -665,17 +812,28 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
const struct drm_display_mode *mode =
&intel_crtc->config.adjusted_mode;
- vbl_start = mode->crtc_vblank_start * mode->crtc_htotal;
+ htotal = mode->crtc_htotal;
+ hsync_start = mode->crtc_hsync_start;
+ vbl_start = mode->crtc_vblank_start;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ vbl_start = DIV_ROUND_UP(vbl_start, 2);
} else {
enum transcoder cpu_transcoder = (enum transcoder) pipe;
- u32 htotal;
htotal = ((I915_READ(HTOTAL(cpu_transcoder)) >> 16) & 0x1fff) + 1;
+ hsync_start = (I915_READ(HSYNC(cpu_transcoder)) & 0x1fff) + 1;
vbl_start = (I915_READ(VBLANK(cpu_transcoder)) & 0x1fff) + 1;
-
- vbl_start *= htotal;
+ if ((I915_READ(PIPECONF(cpu_transcoder)) &
+ PIPECONF_INTERLACE_MASK) != PIPECONF_PROGRESSIVE)
+ vbl_start = DIV_ROUND_UP(vbl_start, 2);
}
+ /* Convert to pixel count */
+ vbl_start *= htotal;
+
+ /* Start of vblank event occurs at start of hsync */
+ vbl_start -= htotal - hsync_start;
+
high_frame = PIPEFRAME(pipe);
low_frame = PIPEFRAMEPIXEL(pipe);
@@ -719,24 +877,28 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
/* raw reads, only for fast reads of display block, no need for forcewake etc. */
#define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__))
-static bool ilk_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe)
+static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
{
+ struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- uint32_t status;
- int reg;
+ const struct drm_display_mode *mode = &crtc->config.adjusted_mode;
+ enum pipe pipe = crtc->pipe;
+ int position, vtotal;
- if (INTEL_INFO(dev)->gen >= 8) {
- status = GEN8_PIPE_VBLANK;
- reg = GEN8_DE_PIPE_ISR(pipe);
- } else if (INTEL_INFO(dev)->gen >= 7) {
- status = DE_PIPE_VBLANK_IVB(pipe);
- reg = DEISR;
- } else {
- status = DE_PIPE_VBLANK(pipe);
- reg = DEISR;
- }
+ vtotal = mode->crtc_vtotal;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ vtotal /= 2;
+
+ if (IS_GEN2(dev))
+ position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN2;
+ else
+ position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
- return __raw_i915_read32(dev_priv, reg) & status;
+ /*
+ * See update_scanline_offset() for the details on the
+ * scanline_offset adjustment.
+ */
+ return (position + crtc->scanline_offset) % vtotal;
}
static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
@@ -748,7 +910,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
const struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode;
int position;
- int vbl_start, vbl_end, htotal, vtotal;
+ int vbl_start, vbl_end, hsync_start, htotal, vtotal;
bool in_vbl = true;
int ret = 0;
unsigned long irqflags;
@@ -760,6 +922,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
}
htotal = mode->crtc_htotal;
+ hsync_start = mode->crtc_hsync_start;
vtotal = mode->crtc_vtotal;
vbl_start = mode->crtc_vblank_start;
vbl_end = mode->crtc_vblank_end;
@@ -778,7 +941,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
* following code must not block on uncore.lock.
*/
spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
-
+
/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
/* Get optional system timestamp before query. */
@@ -789,68 +952,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
/* No obvious pixelcount register. Only query vertical
* scanout position from Display scan line register.
*/
- if (IS_GEN2(dev))
- position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN2;
- else
- position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
-
- if (HAS_DDI(dev)) {
- /*
- * On HSW HDMI outputs there seems to be a 2 line
- * difference, whereas eDP has the normal 1 line
- * difference that earlier platforms have. External
- * DP is unknown. For now just check for the 2 line
- * difference case on all output types on HSW+.
- *
- * This might misinterpret the scanline counter being
- * one line too far along on eDP, but that's less
- * dangerous than the alternative since that would lead
- * the vblank timestamp code astray when it sees a
- * scanline count before vblank_start during a vblank
- * interrupt.
- */
- in_vbl = ilk_pipe_in_vblank_locked(dev, pipe);
- if ((in_vbl && (position == vbl_start - 2 ||
- position == vbl_start - 1)) ||
- (!in_vbl && (position == vbl_end - 2 ||
- position == vbl_end - 1)))
- position = (position + 2) % vtotal;
- } else if (HAS_PCH_SPLIT(dev)) {
- /*
- * The scanline counter increments at the leading edge
- * of hsync, ie. it completely misses the active portion
- * of the line. Fix up the counter at both edges of vblank
- * to get a more accurate picture whether we're in vblank
- * or not.
- */
- in_vbl = ilk_pipe_in_vblank_locked(dev, pipe);
- if ((in_vbl && position == vbl_start - 1) ||
- (!in_vbl && position == vbl_end - 1))
- position = (position + 1) % vtotal;
- } else {
- /*
- * ISR vblank status bits don't work the way we'd want
- * them to work on non-PCH platforms (for
- * ilk_pipe_in_vblank_locked()), and there doesn't
- * appear any other way to determine if we're currently
- * in vblank.
- *
- * Instead let's assume that we're already in vblank if
- * we got called from the vblank interrupt and the
- * scanline counter value indicates that we're on the
- * line just prior to vblank start. This should result
- * in the correct answer, unless the vblank interrupt
- * delivery really got delayed for almost exactly one
- * full frame/field.
- */
- if (flags & DRM_CALLED_FROM_VBLIRQ &&
- position == vbl_start - 1) {
- position = (position + 1) % vtotal;
-
- /* Signal this correction as "applied". */
- ret |= 0x8;
- }
- }
+ position = __intel_get_crtc_scanline(intel_crtc);
} else {
/* Have access to pixelcount since start of frame.
* We can split this into vertical and horizontal
@@ -862,6 +964,29 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
vbl_start *= htotal;
vbl_end *= htotal;
vtotal *= htotal;
+
+ /*
+ * In interlaced modes, the pixel counter counts all pixels,
+ * so one field will have htotal more pixels. In order to avoid
+ * the reported position from jumping backwards when the pixel
+ * counter is beyond the length of the shorter field, just
+ * clamp the position the length of the shorter field. This
+ * matches how the scanline counter based position works since
+ * the scanline counter doesn't count the two half lines.
+ */
+ if (position >= vtotal)
+ position = vtotal - 1;
+
+ /*
+ * Start of vblank interrupt is triggered at start of hsync,
+ * just prior to the first active line of vblank. However we
+ * consider lines to start at the leading edge of horizontal
+ * active. So, should we get here before we've crossed into
+ * the horizontal active of the first line in vblank, we would
+ * not set the DRM_SCANOUTPOS_INVBL flag. In order to fix that,
+ * always add htotal-hsync_start to the current pixel position.
+ */
+ position = (position + htotal - hsync_start) % vtotal;
}
/* Get optional system timestamp after query. */
@@ -900,6 +1025,19 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
return ret;
}
+int intel_get_crtc_scanline(struct intel_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+ unsigned long irqflags;
+ int position;
+
+ spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
+ position = __intel_get_crtc_scanline(crtc);
+ spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
+
+ return position;
+}
+
static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe,
int *max_error,
struct timeval *vblank_time,
@@ -945,7 +1083,7 @@ static bool intel_hpd_irq_event(struct drm_device *dev,
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
connector->base.id,
- drm_get_connector_name(connector),
+ connector->name,
drm_get_connector_status_name(old_status),
drm_get_connector_status_name(connector->status));
@@ -990,7 +1128,7 @@ static void i915_hotplug_work_func(struct work_struct *work)
connector->polled == DRM_CONNECTOR_POLL_HPD) {
DRM_INFO("HPD interrupt storm detected on connector %s: "
"switching from hotplug detection to polling\n",
- drm_get_connector_name(connector));
+ connector->name);
dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark = HPD_DISABLED;
connector->polled = DRM_CONNECTOR_POLL_CONNECT
| DRM_CONNECTOR_POLL_DISCONNECT;
@@ -998,7 +1136,7 @@ static void i915_hotplug_work_func(struct work_struct *work)
}
if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
DRM_DEBUG_KMS("Connector %s (pin %i) received hotplug event.\n",
- drm_get_connector_name(connector), intel_encoder->hpd_pin);
+ connector->name, intel_encoder->hpd_pin);
}
}
/* if there were no outputs to poll, poll was disabled,
@@ -1073,9 +1211,9 @@ static void ironlake_rps_change_irq_handler(struct drm_device *dev)
}
static void notify_ring(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+ struct intel_engine_cs *ring)
{
- if (ring->obj == NULL)
+ if (!intel_ring_initialized(ring))
return;
trace_i915_gem_request_complete(ring);
@@ -1094,8 +1232,12 @@ 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;
- /* Make sure not to corrupt PMIMR state used by ringbuffer code */
- snb_enable_pm_irq(dev_priv, dev_priv->pm_rps_events);
+ if (IS_BROADWELL(dev_priv->dev))
+ bdw_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);
+ }
spin_unlock_irq(&dev_priv->irq_lock);
/* Make sure we didn't queue anything we're not going to process. */
@@ -1292,6 +1434,19 @@ static void snb_gt_irq_handler(struct drm_device *dev,
ivybridge_parity_error_irq_handler(dev, gt_iir);
}
+static void gen8_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
+{
+ if ((pm_iir & dev_priv->pm_rps_events) == 0)
+ return;
+
+ 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);
+ spin_unlock(&dev_priv->irq_lock);
+
+ queue_work(dev_priv->wq, &dev_priv->rps.work);
+}
+
static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
struct drm_i915_private *dev_priv,
u32 master_ctl)
@@ -1315,18 +1470,32 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev,
DRM_ERROR("The master control interrupt lied (GT0)!\n");
}
- if (master_ctl & GEN8_GT_VCS1_IRQ) {
+ if (master_ctl & (GEN8_GT_VCS1_IRQ | GEN8_GT_VCS2_IRQ)) {
tmp = I915_READ(GEN8_GT_IIR(1));
if (tmp) {
ret = IRQ_HANDLED;
vcs = tmp >> GEN8_VCS1_IRQ_SHIFT;
if (vcs & GT_RENDER_USER_INTERRUPT)
notify_ring(dev, &dev_priv->ring[VCS]);
+ vcs = tmp >> GEN8_VCS2_IRQ_SHIFT;
+ if (vcs & GT_RENDER_USER_INTERRUPT)
+ notify_ring(dev, &dev_priv->ring[VCS2]);
I915_WRITE(GEN8_GT_IIR(1), tmp);
} else
DRM_ERROR("The master control interrupt lied (GT1)!\n");
}
+ 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);
+ } else
+ DRM_ERROR("The master control interrupt lied (PM)!\n");
+ }
+
if (master_ctl & GEN8_GT_VECS_IRQ) {
tmp = I915_READ(GEN8_GT_IIR(3));
if (tmp) {
@@ -1549,6 +1718,19 @@ 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;
+}
+
static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1578,6 +1760,9 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
case PIPE_B:
iir_bit = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
break;
+ case PIPE_C:
+ iir_bit = I915_DISPLAY_PIPE_C_EVENT_INTERRUPT;
+ break;
}
if (iir & iir_bit)
mask |= dev_priv->pipestat_irq_mask[pipe];
@@ -1600,7 +1785,7 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
for_each_pipe(pipe) {
if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
- drm_handle_vblank(dev, pipe);
+ intel_pipe_handle_vblank(dev, pipe);
if (pipe_stats[pipe] & PLANE_FLIP_DONE_INT_STATUS_VLV) {
intel_prepare_page_flip(dev, pipe);
@@ -1619,9 +1804,36 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
gmbus_irq_handler(dev);
}
+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;
+
+ intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_g4x);
+ } else {
+ u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
+
+ intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915);
+ }
+
+ if ((IS_G4X(dev) || IS_VALLEYVIEW(dev)) &&
+ hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X)
+ dp_aux_irq_handler(dev);
+
+ 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);
+}
+
static irqreturn_t valleyview_irq_handler(int irq, void *arg)
{
- struct drm_device *dev = (struct drm_device *) arg;
+ struct drm_device *dev = arg;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 iir, gt_iir, pm_iir;
irqreturn_t ret = IRQ_NONE;
@@ -1641,19 +1853,8 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
valleyview_pipestat_irq_handler(dev, iir);
/* Consume port. Then clear IIR or we'll miss events */
- if (iir & I915_DISPLAY_PORT_INTERRUPT) {
- u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
- u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
-
- intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915);
-
- if (hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X)
- dp_aux_irq_handler(dev);
-
- I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
- I915_READ(PORT_HOTPLUG_STAT);
- }
-
+ if (iir & I915_DISPLAY_PORT_INTERRUPT)
+ i9xx_hpd_irq_handler(dev);
if (pm_iir)
gen6_rps_irq_handler(dev_priv, pm_iir);
@@ -1667,6 +1868,40 @@ out:
return ret;
}
+static irqreturn_t cherryview_irq_handler(int irq, void *arg)
+{
+ struct drm_device *dev = arg;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 master_ctl, iir;
+ irqreturn_t ret = IRQ_NONE;
+
+ for (;;) {
+ master_ctl = I915_READ(GEN8_MASTER_IRQ) & ~GEN8_MASTER_IRQ_CONTROL;
+ iir = I915_READ(VLV_IIR);
+
+ if (master_ctl == 0 && iir == 0)
+ break;
+
+ I915_WRITE(GEN8_MASTER_IRQ, 0);
+
+ gen8_gt_irq_handler(dev, dev_priv, master_ctl);
+
+ valleyview_pipestat_irq_handler(dev, iir);
+
+ /* Consume port. Then clear IIR or we'll miss events */
+ i9xx_hpd_irq_handler(dev);
+
+ I915_WRITE(VLV_IIR, iir);
+
+ I915_WRITE(GEN8_MASTER_IRQ, DE_MASTER_IRQ_CONTROL);
+ POSTING_READ(GEN8_MASTER_IRQ);
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1827,7 +2062,7 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
for_each_pipe(pipe) {
if (de_iir & DE_PIPE_VBLANK(pipe))
- drm_handle_vblank(dev, pipe);
+ intel_pipe_handle_vblank(dev, pipe);
if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false))
@@ -1877,7 +2112,7 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
for_each_pipe(pipe) {
if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)))
- drm_handle_vblank(dev, pipe);
+ intel_pipe_handle_vblank(dev, pipe);
/* plane/pipes map 1:1 on ilk+ */
if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe)) {
@@ -1899,7 +2134,7 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
static irqreturn_t ironlake_irq_handler(int irq, void *arg)
{
- struct drm_device *dev = (struct drm_device *) arg;
+ struct drm_device *dev = arg;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 de_iir, gt_iir, de_ier, sde_ier = 0;
irqreturn_t ret = IRQ_NONE;
@@ -2020,9 +2255,9 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
if (pipe_iir & GEN8_PIPE_VBLANK)
- drm_handle_vblank(dev, pipe);
+ intel_pipe_handle_vblank(dev, pipe);
- if (pipe_iir & GEN8_PIPE_FLIP_DONE) {
+ if (pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE) {
intel_prepare_page_flip(dev, pipe);
intel_finish_page_flip_plane(dev, pipe);
}
@@ -2075,7 +2310,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
static void i915_error_wake_up(struct drm_i915_private *dev_priv,
bool reset_completed)
{
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int i;
/*
@@ -2137,6 +2372,14 @@ static void i915_error_work_func(struct work_struct *work)
reset_event);
/*
+ * In most cases it's guaranteed that we get here with an RPM
+ * reference held, for example because there is a pending GPU
+ * request that won't finish until the reset is done. This
+ * isn't the case at least when we get here by doing a
+ * simulated reset via debugs, so get an RPM reference.
+ */
+ intel_runtime_pm_get(dev_priv);
+ /*
* All state reset _must_ be completed before we update the
* reset counter, for otherwise waiters might miss the reset
* pending state and not properly drop locks, resulting in
@@ -2146,6 +2389,8 @@ static void i915_error_work_func(struct work_struct *work)
intel_display_handle_reset(dev);
+ intel_runtime_pm_put(dev_priv);
+
if (ret == 0) {
/*
* After all the gem state is reset, increment the reset
@@ -2383,10 +2628,6 @@ static int i915_enable_vblank(struct drm_device *dev, int pipe)
else
i915_enable_pipestat(dev_priv, pipe,
PIPE_VBLANK_INTERRUPT_STATUS);
-
- /* maintain vblank delivery even in deep C-states */
- if (INTEL_INFO(dev)->gen == 3)
- I915_WRITE(INSTPM, _MASKED_BIT_DISABLE(INSTPM_AGPBUSY_DIS));
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
return 0;
@@ -2450,9 +2691,6 @@ static void i915_disable_vblank(struct drm_device *dev, int pipe)
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- if (INTEL_INFO(dev)->gen == 3)
- I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_AGPBUSY_DIS));
-
i915_disable_pipestat(dev_priv, pipe,
PIPE_VBLANK_INTERRUPT_STATUS |
PIPE_START_VBLANK_INTERRUPT_STATUS);
@@ -2498,29 +2736,77 @@ static void gen8_disable_vblank(struct drm_device *dev, int pipe)
}
static u32
-ring_last_seqno(struct intel_ring_buffer *ring)
+ring_last_seqno(struct intel_engine_cs *ring)
{
return list_entry(ring->request_list.prev,
struct drm_i915_gem_request, list)->seqno;
}
static bool
-ring_idle(struct intel_ring_buffer *ring, u32 seqno)
+ring_idle(struct intel_engine_cs *ring, u32 seqno)
{
return (list_empty(&ring->request_list) ||
i915_seqno_passed(seqno, ring_last_seqno(ring)));
}
-static struct intel_ring_buffer *
-semaphore_waits_for(struct intel_ring_buffer *ring, u32 *seqno)
+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;
+ } else {
+ ipehr &= ~MI_SEMAPHORE_SYNC_MASK;
+ return ipehr == (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE |
+ MI_SEMAPHORE_REGISTER);
+ }
+}
+
+static struct intel_engine_cs *
+semaphore_wait_to_signaller_ring(struct intel_engine_cs *ring, u32 ipehr)
+{
+ 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;
+ } else {
+ u32 sync_bits = ipehr & MI_SEMAPHORE_SYNC_MASK;
+
+ for_each_ring(signaller, dev_priv, i) {
+ if(ring == signaller)
+ continue;
+
+ if (sync_bits == signaller->semaphore.mbox.wait[ring->id])
+ return signaller;
+ }
+ }
+
+ DRM_ERROR("No signaller ring found for ring %i, ipehr 0x%08x\n",
+ ring->id, ipehr);
+
+ return NULL;
+}
+
+static struct intel_engine_cs *
+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;
ipehr = I915_READ(RING_IPEHR(ring->mmio_base));
- if ((ipehr & ~(0x3 << 16)) !=
- (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE | MI_SEMAPHORE_REGISTER))
+ if (!ipehr_is_semaphore_wait(ring->dev, ipehr))
return NULL;
/*
@@ -2538,10 +2824,10 @@ semaphore_waits_for(struct intel_ring_buffer *ring, u32 *seqno)
* our ring is smaller than what the hardware (and hence
* HEAD_ADDR) allows. Also handles wrap-around.
*/
- head &= ring->size - 1;
+ head &= ring->buffer->size - 1;
/* This here seems to blow up */
- cmd = ioread32(ring->virtual_start + head);
+ cmd = ioread32(ring->buffer->virtual_start + head);
if (cmd == ipehr)
break;
@@ -2551,14 +2837,14 @@ semaphore_waits_for(struct intel_ring_buffer *ring, u32 *seqno)
if (!i)
return NULL;
- *seqno = ioread32(ring->virtual_start + head + 4) + 1;
- return &dev_priv->ring[(ring->id + (((ipehr >> 17) & 1) + 1)) % 3];
+ *seqno = ioread32(ring->buffer->virtual_start + head + 4) + 1;
+ return semaphore_wait_to_signaller_ring(ring, ipehr);
}
-static int semaphore_passed(struct intel_ring_buffer *ring)
+static int semaphore_passed(struct intel_engine_cs *ring)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
- struct intel_ring_buffer *signaller;
+ struct intel_engine_cs *signaller;
u32 seqno, ctl;
ring->hangcheck.deadlock = true;
@@ -2577,7 +2863,7 @@ static int semaphore_passed(struct intel_ring_buffer *ring)
static void semaphore_clear_deadlocks(struct drm_i915_private *dev_priv)
{
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int i;
for_each_ring(ring, dev_priv, i)
@@ -2585,7 +2871,7 @@ static void semaphore_clear_deadlocks(struct drm_i915_private *dev_priv)
}
static enum intel_ring_hangcheck_action
-ring_stuck(struct intel_ring_buffer *ring, u64 acthd)
+ring_stuck(struct intel_engine_cs *ring, u64 acthd)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2641,7 +2927,7 @@ static void i915_hangcheck_elapsed(unsigned long data)
{
struct drm_device *dev = (struct drm_device *)data;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
int i;
int busy_count = 0, rings_hung = 0;
bool stuck[I915_NUM_RINGS] = { 0 };
@@ -2759,57 +3045,63 @@ void i915_queue_hangcheck(struct drm_device *dev)
round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES));
}
-static void ibx_irq_preinstall(struct drm_device *dev)
+static void ibx_irq_reset(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
if (HAS_PCH_NOP(dev))
return;
- /* south display irq */
- I915_WRITE(SDEIMR, 0xffffffff);
- /*
- * SDEIER is also touched by the interrupt handler to work around missed
- * PCH interrupts. Hence we can't update it after the interrupt handler
- * is enabled - instead we unconditionally enable all PCH interrupt
- * sources here, but then only unmask them as needed with SDEIMR.
- */
+ GEN5_IRQ_RESET(SDE);
+
+ if (HAS_PCH_CPT(dev) || HAS_PCH_LPT(dev))
+ I915_WRITE(SERR_INT, 0xffffffff);
+}
+
+/*
+ * SDEIER is also touched by the interrupt handler to work around missed PCH
+ * interrupts. Hence we can't update it after the interrupt handler is enabled -
+ * instead we unconditionally enable all PCH interrupt sources here, but then
+ * only unmask them as needed with SDEIMR.
+ *
+ * This function needs to be called before interrupts are enabled.
+ */
+static void ibx_irq_pre_postinstall(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (HAS_PCH_NOP(dev))
+ return;
+
+ WARN_ON(I915_READ(SDEIER) != 0);
I915_WRITE(SDEIER, 0xffffffff);
POSTING_READ(SDEIER);
}
-static void gen5_gt_irq_preinstall(struct drm_device *dev)
+static void gen5_gt_irq_reset(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- /* and GT */
- I915_WRITE(GTIMR, 0xffffffff);
- I915_WRITE(GTIER, 0x0);
- POSTING_READ(GTIER);
-
- if (INTEL_INFO(dev)->gen >= 6) {
- /* and PM */
- I915_WRITE(GEN6_PMIMR, 0xffffffff);
- I915_WRITE(GEN6_PMIER, 0x0);
- POSTING_READ(GEN6_PMIER);
- }
+ GEN5_IRQ_RESET(GT);
+ if (INTEL_INFO(dev)->gen >= 6)
+ GEN5_IRQ_RESET(GEN6_PM);
}
/* drm_dma.h hooks
*/
-static void ironlake_irq_preinstall(struct drm_device *dev)
+static void ironlake_irq_reset(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- I915_WRITE(HWSTAM, 0xeffe);
+ I915_WRITE(HWSTAM, 0xffffffff);
- I915_WRITE(DEIMR, 0xffffffff);
- I915_WRITE(DEIER, 0x0);
- POSTING_READ(DEIER);
+ GEN5_IRQ_RESET(DE);
+ if (IS_GEN7(dev))
+ I915_WRITE(GEN7_ERR_INT, 0xffffffff);
- gen5_gt_irq_preinstall(dev);
+ gen5_gt_irq_reset(dev);
- ibx_irq_preinstall(dev);
+ ibx_irq_reset(dev);
}
static void valleyview_irq_preinstall(struct drm_device *dev)
@@ -2827,7 +3119,7 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
I915_WRITE(GTIIR, I915_READ(GTIIR));
I915_WRITE(GTIIR, I915_READ(GTIIR));
- gen5_gt_irq_preinstall(dev);
+ gen5_gt_irq_reset(dev);
I915_WRITE(DPINVGTT, 0xff);
@@ -2841,7 +3133,15 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
POSTING_READ(VLV_IER);
}
-static void gen8_irq_preinstall(struct drm_device *dev)
+static void gen8_gt_irq_reset(struct drm_i915_private *dev_priv)
+{
+ GEN8_IRQ_RESET_NDX(GT, 0);
+ GEN8_IRQ_RESET_NDX(GT, 1);
+ GEN8_IRQ_RESET_NDX(GT, 2);
+ GEN8_IRQ_RESET_NDX(GT, 3);
+}
+
+static void gen8_irq_reset(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int pipe;
@@ -2849,43 +3149,44 @@ static void gen8_irq_preinstall(struct drm_device *dev)
I915_WRITE(GEN8_MASTER_IRQ, 0);
POSTING_READ(GEN8_MASTER_IRQ);
- /* IIR can theoretically queue up two events. Be paranoid */
-#define GEN8_IRQ_INIT_NDX(type, which) do { \
- I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
- POSTING_READ(GEN8_##type##_IMR(which)); \
- I915_WRITE(GEN8_##type##_IER(which), 0); \
- I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
- POSTING_READ(GEN8_##type##_IIR(which)); \
- I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
- } while (0)
-
-#define GEN8_IRQ_INIT(type) do { \
- I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \
- POSTING_READ(GEN8_##type##_IMR); \
- I915_WRITE(GEN8_##type##_IER, 0); \
- I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
- POSTING_READ(GEN8_##type##_IIR); \
- I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
- } while (0)
-
- GEN8_IRQ_INIT_NDX(GT, 0);
- GEN8_IRQ_INIT_NDX(GT, 1);
- GEN8_IRQ_INIT_NDX(GT, 2);
- GEN8_IRQ_INIT_NDX(GT, 3);
+ gen8_gt_irq_reset(dev_priv);
- for_each_pipe(pipe) {
- GEN8_IRQ_INIT_NDX(DE_PIPE, pipe);
- }
+ for_each_pipe(pipe)
+ GEN8_IRQ_RESET_NDX(DE_PIPE, pipe);
+
+ GEN5_IRQ_RESET(GEN8_DE_PORT_);
+ GEN5_IRQ_RESET(GEN8_DE_MISC_);
+ GEN5_IRQ_RESET(GEN8_PCU_);
+
+ ibx_irq_reset(dev);
+}
+
+static void cherryview_irq_preinstall(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int pipe;
+
+ I915_WRITE(GEN8_MASTER_IRQ, 0);
+ POSTING_READ(GEN8_MASTER_IRQ);
- GEN8_IRQ_INIT(DE_PORT);
- GEN8_IRQ_INIT(DE_MISC);
- GEN8_IRQ_INIT(PCU);
-#undef GEN8_IRQ_INIT
-#undef GEN8_IRQ_INIT_NDX
+ gen8_gt_irq_reset(dev_priv);
+
+ GEN5_IRQ_RESET(GEN8_PCU_);
POSTING_READ(GEN8_PCU_IIR);
- ibx_irq_preinstall(dev);
+ I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK_CHV);
+
+ I915_WRITE(PORT_HOTPLUG_EN, 0);
+ I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
+
+ for_each_pipe(pipe)
+ I915_WRITE(PIPESTAT(pipe), 0xffff);
+
+ I915_WRITE(VLV_IMR, 0xffffffff);
+ I915_WRITE(VLV_IER, 0x0);
+ I915_WRITE(VLV_IIR, 0xffffffff);
+ POSTING_READ(VLV_IIR);
}
static void ibx_hpd_irq_setup(struct drm_device *dev)
@@ -2931,15 +3232,12 @@ static void ibx_irq_postinstall(struct drm_device *dev)
if (HAS_PCH_NOP(dev))
return;
- if (HAS_PCH_IBX(dev)) {
+ if (HAS_PCH_IBX(dev))
mask = SDE_GMBUS | SDE_AUX_MASK | SDE_POISON;
- } else {
+ else
mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
- I915_WRITE(SERR_INT, I915_READ(SERR_INT));
- }
-
- I915_WRITE(SDEIIR, I915_READ(SDEIIR));
+ GEN5_ASSERT_IIR_IS_ZERO(SDEIIR);
I915_WRITE(SDEIMR, ~mask);
}
@@ -2965,10 +3263,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
gt_irqs |= GT_BLT_USER_INTERRUPT | GT_BSD_USER_INTERRUPT;
}
- I915_WRITE(GTIIR, I915_READ(GTIIR));
- I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
- I915_WRITE(GTIER, gt_irqs);
- POSTING_READ(GTIER);
+ GEN5_IRQ_INIT(GT, dev_priv->gt_irq_mask, gt_irqs);
if (INTEL_INFO(dev)->gen >= 6) {
pm_irqs |= dev_priv->pm_rps_events;
@@ -2977,10 +3272,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
pm_irqs |= PM_VEBOX_USER_INTERRUPT;
dev_priv->pm_irq_mask = 0xffffffff;
- I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
- I915_WRITE(GEN6_PMIMR, dev_priv->pm_irq_mask);
- I915_WRITE(GEN6_PMIER, pm_irqs);
- POSTING_READ(GEN6_PMIER);
+ GEN5_IRQ_INIT(GEN6_PM, dev_priv->pm_irq_mask, pm_irqs);
}
}
@@ -2997,8 +3289,6 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB);
extra_mask = (DE_PIPEC_VBLANK_IVB | DE_PIPEB_VBLANK_IVB |
DE_PIPEA_VBLANK_IVB | DE_ERR_INT_IVB);
-
- I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
} else {
display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
@@ -3011,11 +3301,11 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
dev_priv->irq_mask = ~display_mask;
- /* should always can generate irq */
- I915_WRITE(DEIIR, I915_READ(DEIIR));
- I915_WRITE(DEIMR, dev_priv->irq_mask);
- I915_WRITE(DEIER, display_mask | extra_mask);
- POSTING_READ(DEIER);
+ I915_WRITE(HWSTAM, 0xeffe);
+
+ ibx_irq_pre_postinstall(dev);
+
+ GEN5_IRQ_INIT(DE, dev_priv->irq_mask, display_mask | extra_mask);
gen5_gt_irq_postinstall(dev);
@@ -3175,21 +3465,16 @@ static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv)
GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT
};
- for (i = 0; i < ARRAY_SIZE(gt_interrupts); i++) {
- u32 tmp = I915_READ(GEN8_GT_IIR(i));
- if (tmp)
- DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n",
- i, tmp);
- I915_WRITE(GEN8_GT_IMR(i), ~gt_interrupts[i]);
- I915_WRITE(GEN8_GT_IER(i), gt_interrupts[i]);
- }
- POSTING_READ(GEN8_GT_IER(0));
+ 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;
}
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_FLIP_DONE |
+ uint32_t de_pipe_masked = GEN8_PIPE_PRIMARY_FLIP_DONE |
GEN8_PIPE_CDCLK_CRC_DONE |
GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
uint32_t de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK |
@@ -3199,25 +3484,19 @@ 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) {
- u32 tmp = I915_READ(GEN8_DE_PIPE_IIR(pipe));
- if (tmp)
- DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n",
- pipe, tmp);
- I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]);
- I915_WRITE(GEN8_DE_PIPE_IER(pipe), de_pipe_enables);
- }
- POSTING_READ(GEN8_DE_PIPE_ISR(0));
+ for_each_pipe(pipe)
+ GEN8_IRQ_INIT_NDX(DE_PIPE, pipe, dev_priv->de_irq_mask[pipe],
+ de_pipe_enables);
- I915_WRITE(GEN8_DE_PORT_IMR, ~GEN8_AUX_CHANNEL_A);
- I915_WRITE(GEN8_DE_PORT_IER, GEN8_AUX_CHANNEL_A);
- POSTING_READ(GEN8_DE_PORT_IER);
+ GEN5_IRQ_INIT(GEN8_DE_PORT_, ~GEN8_AUX_CHANNEL_A, GEN8_AUX_CHANNEL_A);
}
static int gen8_irq_postinstall(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ ibx_irq_pre_postinstall(dev);
+
gen8_gt_irq_postinstall(dev_priv);
gen8_de_irq_postinstall(dev_priv);
@@ -3229,44 +3508,55 @@ static int gen8_irq_postinstall(struct drm_device *dev)
return 0;
}
-static void gen8_irq_uninstall(struct drm_device *dev)
+static int cherryview_irq_postinstall(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 enable_mask = I915_DISPLAY_PORT_INTERRUPT |
+ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
+ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
+ I915_DISPLAY_PIPE_C_EVENT_INTERRUPT;
+ u32 pipestat_enable = PLANE_FLIP_DONE_INT_STATUS_VLV |
+ PIPE_CRC_DONE_INTERRUPT_STATUS;
+ unsigned long irqflags;
int pipe;
- if (!dev_priv)
- return;
+ /*
+ * Leave vblank interrupts masked initially. enable/disable will
+ * toggle them based on usage.
+ */
+ dev_priv->irq_mask = ~enable_mask;
- I915_WRITE(GEN8_MASTER_IRQ, 0);
+ for_each_pipe(pipe)
+ I915_WRITE(PIPESTAT(pipe), 0xffff);
-#define GEN8_IRQ_FINI_NDX(type, which) do { \
- I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
- I915_WRITE(GEN8_##type##_IER(which), 0); \
- I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
- } while (0)
+ spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+ i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
+ for_each_pipe(pipe)
+ i915_enable_pipestat(dev_priv, pipe, pipestat_enable);
+ spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
-#define GEN8_IRQ_FINI(type) do { \
- I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \
- I915_WRITE(GEN8_##type##_IER, 0); \
- I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
- } while (0)
+ I915_WRITE(VLV_IIR, 0xffffffff);
+ I915_WRITE(VLV_IMR, dev_priv->irq_mask);
+ I915_WRITE(VLV_IER, enable_mask);
- GEN8_IRQ_FINI_NDX(GT, 0);
- GEN8_IRQ_FINI_NDX(GT, 1);
- GEN8_IRQ_FINI_NDX(GT, 2);
- GEN8_IRQ_FINI_NDX(GT, 3);
+ gen8_gt_irq_postinstall(dev_priv);
- for_each_pipe(pipe) {
- GEN8_IRQ_FINI_NDX(DE_PIPE, pipe);
- }
+ I915_WRITE(GEN8_MASTER_IRQ, MASTER_INTERRUPT_ENABLE);
+ POSTING_READ(GEN8_MASTER_IRQ);
- GEN8_IRQ_FINI(DE_PORT);
- GEN8_IRQ_FINI(DE_MISC);
- GEN8_IRQ_FINI(PCU);
-#undef GEN8_IRQ_FINI
-#undef GEN8_IRQ_FINI_NDX
+ return 0;
+}
- POSTING_READ(GEN8_PCU_IIR);
+static void gen8_irq_uninstall(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (!dev_priv)
+ return;
+
+ intel_hpd_irq_uninstall(dev_priv);
+
+ gen8_irq_reset(dev);
}
static void valleyview_irq_uninstall(struct drm_device *dev)
@@ -3278,6 +3568,8 @@ static void valleyview_irq_uninstall(struct drm_device *dev)
if (!dev_priv)
return;
+ I915_WRITE(VLV_MASTER_IER, 0);
+
intel_hpd_irq_uninstall(dev_priv);
for_each_pipe(pipe)
@@ -3300,35 +3592,67 @@ static void valleyview_irq_uninstall(struct drm_device *dev)
POSTING_READ(VLV_IER);
}
-static void ironlake_irq_uninstall(struct drm_device *dev)
+static void cherryview_irq_uninstall(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
+ int pipe;
if (!dev_priv)
return;
- intel_hpd_irq_uninstall(dev_priv);
+ I915_WRITE(GEN8_MASTER_IRQ, 0);
+ POSTING_READ(GEN8_MASTER_IRQ);
- I915_WRITE(HWSTAM, 0xffffffff);
+#define GEN8_IRQ_FINI_NDX(type, which) \
+do { \
+ I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \
+ I915_WRITE(GEN8_##type##_IER(which), 0); \
+ I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+ POSTING_READ(GEN8_##type##_IIR(which)); \
+ I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \
+} while (0)
+
+#define GEN8_IRQ_FINI(type) \
+do { \
+ I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \
+ I915_WRITE(GEN8_##type##_IER, 0); \
+ I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
+ POSTING_READ(GEN8_##type##_IIR); \
+ I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \
+} while (0)
- I915_WRITE(DEIMR, 0xffffffff);
- I915_WRITE(DEIER, 0x0);
- I915_WRITE(DEIIR, I915_READ(DEIIR));
- if (IS_GEN7(dev))
- I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
+ GEN8_IRQ_FINI_NDX(GT, 0);
+ GEN8_IRQ_FINI_NDX(GT, 1);
+ GEN8_IRQ_FINI_NDX(GT, 2);
+ GEN8_IRQ_FINI_NDX(GT, 3);
- I915_WRITE(GTIMR, 0xffffffff);
- I915_WRITE(GTIER, 0x0);
- I915_WRITE(GTIIR, I915_READ(GTIIR));
+ GEN8_IRQ_FINI(PCU);
- if (HAS_PCH_NOP(dev))
+#undef GEN8_IRQ_FINI
+#undef GEN8_IRQ_FINI_NDX
+
+ I915_WRITE(PORT_HOTPLUG_EN, 0);
+ I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
+
+ for_each_pipe(pipe)
+ I915_WRITE(PIPESTAT(pipe), 0xffff);
+
+ I915_WRITE(VLV_IMR, 0xffffffff);
+ I915_WRITE(VLV_IER, 0x0);
+ I915_WRITE(VLV_IIR, 0xffffffff);
+ POSTING_READ(VLV_IIR);
+}
+
+static void ironlake_irq_uninstall(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (!dev_priv)
return;
- I915_WRITE(SDEIMR, 0xffffffff);
- I915_WRITE(SDEIER, 0x0);
- I915_WRITE(SDEIIR, I915_READ(SDEIIR));
- if (HAS_PCH_CPT(dev) || HAS_PCH_LPT(dev))
- I915_WRITE(SERR_INT, I915_READ(SERR_INT));
+ intel_hpd_irq_uninstall(dev_priv);
+
+ ironlake_irq_reset(dev);
}
static void i8xx_irq_preinstall(struct drm_device * dev)
@@ -3386,7 +3710,7 @@ static bool i8xx_handle_vblank(struct drm_device *dev,
struct drm_i915_private *dev_priv = dev->dev_private;
u16 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
- if (!drm_handle_vblank(dev, pipe))
+ if (!intel_pipe_handle_vblank(dev, pipe))
return false;
if ((iir & flip_pending) == 0)
@@ -3410,7 +3734,7 @@ static bool i8xx_handle_vblank(struct drm_device *dev,
static irqreturn_t i8xx_irq_handler(int irq, void *arg)
{
- struct drm_device *dev = (struct drm_device *) arg;
+ struct drm_device *dev = arg;
struct drm_i915_private *dev_priv = dev->dev_private;
u16 iir, new_iir;
u32 pipe_stats[2];
@@ -3571,7 +3895,7 @@ static bool i915_handle_vblank(struct drm_device *dev,
struct drm_i915_private *dev_priv = dev->dev_private;
u32 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
- if (!drm_handle_vblank(dev, pipe))
+ if (!intel_pipe_handle_vblank(dev, pipe))
return false;
if ((iir & flip_pending) == 0)
@@ -3595,7 +3919,7 @@ static bool i915_handle_vblank(struct drm_device *dev,
static irqreturn_t i915_irq_handler(int irq, void *arg)
{
- struct drm_device *dev = (struct drm_device *) arg;
+ struct drm_device *dev = arg;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 iir, new_iir, pipe_stats[I915_MAX_PIPES];
unsigned long irqflags;
@@ -3636,16 +3960,9 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
break;
/* Consume port. Then clear IIR or we'll miss events */
- if ((I915_HAS_HOTPLUG(dev)) &&
- (iir & I915_DISPLAY_PORT_INTERRUPT)) {
- u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
- u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
-
- intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915);
-
- I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
- POSTING_READ(PORT_HOTPLUG_STAT);
- }
+ if (I915_HAS_HOTPLUG(dev) &&
+ iir & I915_DISPLAY_PORT_INTERRUPT)
+ i9xx_hpd_irq_handler(dev);
I915_WRITE(IIR, iir & ~flip_mask);
new_iir = I915_READ(IIR); /* Flush posted writes */
@@ -3832,7 +4149,7 @@ static void i915_hpd_irq_setup(struct drm_device *dev)
static irqreturn_t i965_irq_handler(int irq, void *arg)
{
- struct drm_device *dev = (struct drm_device *) arg;
+ struct drm_device *dev = arg;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 iir, new_iir;
u32 pipe_stats[I915_MAX_PIPES];
@@ -3879,22 +4196,8 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
ret = IRQ_HANDLED;
/* Consume port. Then clear IIR or we'll miss events */
- if (iir & I915_DISPLAY_PORT_INTERRUPT) {
- u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
- u32 hotplug_trigger = hotplug_status & (IS_G4X(dev) ?
- HOTPLUG_INT_STATUS_G4X :
- HOTPLUG_INT_STATUS_I915);
-
- intel_hpd_irq_handler(dev, hotplug_trigger,
- IS_G4X(dev) ? hpd_status_g4x : hpd_status_i915);
-
- if (IS_G4X(dev) &&
- (hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X))
- dp_aux_irq_handler(dev);
-
- I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
- I915_READ(PORT_HOTPLUG_STAT);
- }
+ if (iir & I915_DISPLAY_PORT_INTERRUPT)
+ i9xx_hpd_irq_handler(dev);
I915_WRITE(IIR, iir & ~flip_mask);
new_iir = I915_READ(IIR); /* Flush posted writes */
@@ -3997,7 +4300,7 @@ static void intel_hpd_irq_reenable(unsigned long data)
if (intel_connector->encoder->hpd_pin == i) {
if (connector->polled != intel_connector->polled)
DRM_DEBUG_DRIVER("Reenabling HPD on connector %s\n",
- drm_get_connector_name(connector));
+ connector->name);
connector->polled = intel_connector->polled;
if (!connector->polled)
connector->polled = DRM_CONNECTOR_POLL_HPD;
@@ -4045,7 +4348,15 @@ void intel_irq_init(struct drm_device *dev)
dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
}
- if (IS_VALLEYVIEW(dev)) {
+ if (IS_CHERRYVIEW(dev)) {
+ dev->driver->irq_handler = cherryview_irq_handler;
+ dev->driver->irq_preinstall = cherryview_irq_preinstall;
+ dev->driver->irq_postinstall = cherryview_irq_postinstall;
+ dev->driver->irq_uninstall = cherryview_irq_uninstall;
+ dev->driver->enable_vblank = valleyview_enable_vblank;
+ dev->driver->disable_vblank = valleyview_disable_vblank;
+ dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
+ } else if (IS_VALLEYVIEW(dev)) {
dev->driver->irq_handler = valleyview_irq_handler;
dev->driver->irq_preinstall = valleyview_irq_preinstall;
dev->driver->irq_postinstall = valleyview_irq_postinstall;
@@ -4055,7 +4366,7 @@ void intel_irq_init(struct drm_device *dev)
dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
} else if (IS_GEN8(dev)) {
dev->driver->irq_handler = gen8_irq_handler;
- dev->driver->irq_preinstall = gen8_irq_preinstall;
+ dev->driver->irq_preinstall = gen8_irq_reset;
dev->driver->irq_postinstall = gen8_irq_postinstall;
dev->driver->irq_uninstall = gen8_irq_uninstall;
dev->driver->enable_vblank = gen8_enable_vblank;
@@ -4063,7 +4374,7 @@ void intel_irq_init(struct drm_device *dev)
dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup;
} else if (HAS_PCH_SPLIT(dev)) {
dev->driver->irq_handler = ironlake_irq_handler;
- dev->driver->irq_preinstall = ironlake_irq_preinstall;
+ dev->driver->irq_preinstall = ironlake_irq_reset;
dev->driver->irq_postinstall = ironlake_irq_postinstall;
dev->driver->irq_uninstall = ironlake_irq_uninstall;
dev->driver->enable_vblank = ironlake_enable_vblank;
@@ -4121,57 +4432,20 @@ void intel_hpd_init(struct drm_device *dev)
}
/* Disable interrupts so we can allow runtime PM. */
-void hsw_runtime_pm_disable_interrupts(struct drm_device *dev)
+void intel_runtime_pm_disable_interrupts(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- unsigned long irqflags;
-
- spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-
- dev_priv->pm.regsave.deimr = I915_READ(DEIMR);
- dev_priv->pm.regsave.sdeimr = I915_READ(SDEIMR);
- dev_priv->pm.regsave.gtimr = I915_READ(GTIMR);
- dev_priv->pm.regsave.gtier = I915_READ(GTIER);
- dev_priv->pm.regsave.gen6_pmimr = I915_READ(GEN6_PMIMR);
-
- ironlake_disable_display_irq(dev_priv, 0xffffffff);
- ibx_disable_display_interrupt(dev_priv, 0xffffffff);
- ilk_disable_gt_irq(dev_priv, 0xffffffff);
- snb_disable_pm_irq(dev_priv, 0xffffffff);
+ dev->driver->irq_uninstall(dev);
dev_priv->pm.irqs_disabled = true;
-
- spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
}
/* Restore interrupts so we can recover from runtime PM. */
-void hsw_runtime_pm_restore_interrupts(struct drm_device *dev)
+void intel_runtime_pm_restore_interrupts(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- unsigned long irqflags;
- uint32_t val;
-
- spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
-
- val = I915_READ(DEIMR);
- WARN(val != 0xffffffff, "DEIMR is 0x%08x\n", val);
-
- val = I915_READ(SDEIMR);
- WARN(val != 0xffffffff, "SDEIMR is 0x%08x\n", val);
-
- val = I915_READ(GTIMR);
- WARN(val != 0xffffffff, "GTIMR is 0x%08x\n", val);
-
- val = I915_READ(GEN6_PMIMR);
- WARN(val != 0xffffffff, "GEN6_PMIMR is 0x%08x\n", val);
dev_priv->pm.irqs_disabled = false;
-
- ironlake_enable_display_irq(dev_priv, ~dev_priv->pm.regsave.deimr);
- ibx_enable_display_interrupt(dev_priv, ~dev_priv->pm.regsave.sdeimr);
- ilk_enable_gt_irq(dev_priv, ~dev_priv->pm.regsave.gtimr);
- snb_enable_pm_irq(dev_priv, ~dev_priv->pm.regsave.gen6_pmimr);
- I915_WRITE(GTIER, dev_priv->pm.regsave.gtier);
-
- spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+ 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 d1d7980f0e01..d05a2afa17dc 100644
--- a/drivers/gpu/drm/i915/i915_params.c
+++ b/drivers/gpu/drm/i915/i915_params.c
@@ -46,7 +46,8 @@ struct i915_params i915 __read_mostly = {
.reset = true,
.invert_brightness = 0,
.disable_display = 0,
- .enable_cmd_parser = 0,
+ .enable_cmd_parser = 1,
+ .disable_vtd_wa = 0,
};
module_param_named(modeset, i915.modeset, int, 0400);
@@ -149,6 +150,9 @@ MODULE_PARM_DESC(invert_brightness,
module_param_named(disable_display, i915.disable_display, bool, 0600);
MODULE_PARM_DESC(disable_display, "Disable display (default: false)");
+module_param_named(disable_vtd_wa, i915.disable_vtd_wa, bool, 0600);
+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, 0=disabled [default])");
+ "Enable command parsing (1=enabled [default], 0=disabled)");
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index c77af69c2d8f..e691b30b2817 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -29,6 +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 _MASKED_BIT_ENABLE(a) (((a) << 16) | (a))
#define _MASKED_BIT_DISABLE(a) ((a) << 16)
@@ -77,13 +79,19 @@
/* Graphics reset regs */
#define I965_GDRST 0xc0 /* PCI config register */
-#define ILK_GDSR 0x2ca4 /* MCHBAR offset */
#define GRDOM_FULL (0<<2)
#define GRDOM_RENDER (1<<2)
#define GRDOM_MEDIA (3<<2)
#define GRDOM_MASK (3<<2)
#define GRDOM_RESET_ENABLE (1<<0)
+#define ILK_GDSR 0x2ca4 /* MCHBAR offset */
+#define ILK_GRDOM_FULL (0<<1)
+#define ILK_GRDOM_RENDER (1<<1)
+#define ILK_GRDOM_MEDIA (3<<1)
+#define ILK_GRDOM_MASK (3<<1)
+#define ILK_GRDOM_RESET_ENABLE (1<<0)
+
#define GEN6_MBCUNIT_SNPCR 0x900c /* for LLC config */
#define GEN6_MBC_SNPCR_SHIFT 21
#define GEN6_MBC_SNPCR_MASK (3<<21)
@@ -92,6 +100,9 @@
#define GEN6_MBC_SNPCR_LOW (2<<21)
#define GEN6_MBC_SNPCR_MIN (3<<21) /* only 1/16th of the cache is shared */
+#define VLV_G3DCTL 0x9024
+#define VLV_GSCKGCTL 0x9028
+
#define GEN6_MBCTL 0x0907c
#define GEN6_MBCTL_ENABLE_BOOT_FETCH (1 << 4)
#define GEN6_MBCTL_CTX_FETCH_NEEDED (1 << 3)
@@ -190,6 +201,8 @@
* Memory interface instructions used by the kernel
*/
#define MI_INSTR(opcode, flags) (((opcode) << 23) | (flags))
+/* Many MI commands use bit 22 of the header dword for GGTT vs PPGTT */
+#define MI_GLOBAL_GTT (1<<22)
#define MI_NOOP MI_INSTR(0, 0)
#define MI_USER_INTERRUPT MI_INSTR(0x02, 0)
@@ -244,7 +257,8 @@
#define MI_SEMAPHORE_SYNC_BVE (0<<16) /* VECS wait for BCS (VEBSYNC) */
#define MI_SEMAPHORE_SYNC_VVE (1<<16) /* VECS wait for VCS (VEVSYNC) */
#define MI_SEMAPHORE_SYNC_RVE (2<<16) /* VECS wait for RCS (VERSYNC) */
-#define MI_SEMAPHORE_SYNC_INVALID (3<<16)
+#define MI_SEMAPHORE_SYNC_INVALID (3<<16)
+#define MI_SEMAPHORE_SYNC_MASK (3<<16)
#define MI_SET_CONTEXT MI_INSTR(0x18, 0)
#define MI_MM_SPACE_GTT (1<<8)
#define MI_MM_SPACE_PHYSICAL (0<<8)
@@ -262,13 +276,16 @@
* - One can actually load arbitrary many arbitrary registers: Simply issue x
* 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_STORE_REGISTER_MEM(x) MI_INSTR(0x24, 2*x-1)
+#define MI_LOAD_REGISTER_IMM(x) MI_INSTR(0x22, 2*(x)-1)
+#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)
#define MI_FLUSH_DW MI_INSTR(0x26, 1) /* for GEN6 */
#define MI_FLUSH_DW_STORE_INDEX (1<<21)
#define MI_INVALIDATE_TLB (1<<18)
#define MI_FLUSH_DW_OP_STOREDW (1<<14)
+#define MI_FLUSH_DW_OP_MASK (3<<14)
+#define MI_FLUSH_DW_NOTIFY (1<<8)
#define MI_INVALIDATE_BSD (1<<7)
#define MI_FLUSH_DW_USE_GTT (1<<2)
#define MI_FLUSH_DW_USE_PPGTT (0<<2)
@@ -330,9 +347,12 @@
#define DISPLAY_PLANE_B (1<<20)
#define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|(len-2))
#define PIPE_CONTROL_GLOBAL_GTT_IVB (1<<24) /* gen7+ */
+#define PIPE_CONTROL_MMIO_WRITE (1<<23)
+#define PIPE_CONTROL_STORE_DATA_INDEX (1<<21)
#define PIPE_CONTROL_CS_STALL (1<<20)
#define PIPE_CONTROL_TLB_INVALIDATE (1<<18)
#define PIPE_CONTROL_QW_WRITE (1<<14)
+#define PIPE_CONTROL_POST_SYNC_OP_MASK (3<<14)
#define PIPE_CONTROL_DEPTH_STALL (1<<13)
#define PIPE_CONTROL_WRITE_FLUSH (1<<12)
#define PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH (1<<12) /* gen6+ */
@@ -347,6 +367,94 @@
#define PIPE_CONTROL_DEPTH_CACHE_FLUSH (1<<0)
#define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */
+/*
+ * Commands used only by the command parser
+ */
+#define MI_SET_PREDICATE MI_INSTR(0x01, 0)
+#define MI_ARB_CHECK MI_INSTR(0x05, 0)
+#define MI_RS_CONTROL MI_INSTR(0x06, 0)
+#define MI_URB_ATOMIC_ALLOC MI_INSTR(0x09, 0)
+#define MI_PREDICATE MI_INSTR(0x0C, 0)
+#define MI_RS_CONTEXT MI_INSTR(0x0F, 0)
+#define MI_TOPOLOGY_FILTER MI_INSTR(0x0D, 0)
+#define MI_LOAD_SCAN_LINES_EXCL MI_INSTR(0x13, 0)
+#define MI_URB_CLEAR MI_INSTR(0x19, 0)
+#define MI_UPDATE_GTT MI_INSTR(0x23, 0)
+#define MI_CLFLUSH MI_INSTR(0x27, 0)
+#define MI_REPORT_PERF_COUNT MI_INSTR(0x28, 0)
+#define MI_REPORT_PERF_COUNT_GGTT (1<<0)
+#define MI_LOAD_REGISTER_MEM MI_INSTR(0x29, 0)
+#define MI_LOAD_REGISTER_REG MI_INSTR(0x2A, 0)
+#define MI_RS_STORE_DATA_IMM MI_INSTR(0x2B, 0)
+#define MI_LOAD_URB_MEM MI_INSTR(0x2C, 0)
+#define MI_STORE_URB_MEM MI_INSTR(0x2D, 0)
+#define MI_CONDITIONAL_BATCH_BUFFER_END MI_INSTR(0x36, 0)
+
+#define PIPELINE_SELECT ((0x3<<29)|(0x1<<27)|(0x1<<24)|(0x4<<16))
+#define GFX_OP_3DSTATE_VF_STATISTICS ((0x3<<29)|(0x1<<27)|(0x0<<24)|(0xB<<16))
+#define MEDIA_VFE_STATE ((0x3<<29)|(0x2<<27)|(0x0<<24)|(0x0<<16))
+#define MEDIA_VFE_STATE_MMIO_ACCESS_MASK (0x18)
+#define GPGPU_OBJECT ((0x3<<29)|(0x2<<27)|(0x1<<24)|(0x4<<16))
+#define GPGPU_WALKER ((0x3<<29)|(0x2<<27)|(0x1<<24)|(0x5<<16))
+#define GFX_OP_3DSTATE_DX9_CONSTANTF_VS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x39<<16))
+#define GFX_OP_3DSTATE_DX9_CONSTANTF_PS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x3A<<16))
+#define GFX_OP_3DSTATE_SO_DECL_LIST \
+ ((0x3<<29)|(0x3<<27)|(0x1<<24)|(0x17<<16))
+
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_VS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x43<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_GS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x44<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_HS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x45<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_DS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x46<<16))
+#define GFX_OP_3DSTATE_BINDING_TABLE_EDIT_PS \
+ ((0x3<<29)|(0x3<<27)|(0x0<<24)|(0x47<<16))
+
+#define MFX_WAIT ((0x3<<29)|(0x1<<27)|(0x0<<16))
+
+#define COLOR_BLT ((0x2<<29)|(0x40<<22))
+#define SRC_COPY_BLT ((0x2<<29)|(0x43<<22))
+
+/*
+ * Registers used only by the command parser
+ */
+#define BCS_SWCTRL 0x22200
+
+#define HS_INVOCATION_COUNT 0x2300
+#define DS_INVOCATION_COUNT 0x2308
+#define IA_VERTICES_COUNT 0x2310
+#define IA_PRIMITIVES_COUNT 0x2318
+#define VS_INVOCATION_COUNT 0x2320
+#define GS_INVOCATION_COUNT 0x2328
+#define GS_PRIMITIVES_COUNT 0x2330
+#define CL_INVOCATION_COUNT 0x2338
+#define CL_PRIMITIVES_COUNT 0x2340
+#define PS_INVOCATION_COUNT 0x2348
+#define PS_DEPTH_COUNT 0x2350
+
+/* There are the 4 64-bit counter registers, one for each stream output */
+#define GEN7_SO_NUM_PRIMS_WRITTEN(n) (0x5200 + (n) * 8)
+
+#define GEN7_SO_PRIM_STORAGE_NEEDED(n) (0x5240 + (n) * 8)
+
+#define GEN7_3DPRIM_END_OFFSET 0x2420
+#define GEN7_3DPRIM_START_VERTEX 0x2430
+#define GEN7_3DPRIM_VERTEX_COUNT 0x2434
+#define GEN7_3DPRIM_INSTANCE_COUNT 0x2438
+#define GEN7_3DPRIM_START_INSTANCE 0x243C
+#define GEN7_3DPRIM_BASE_VERTEX 0x2440
+
+#define OACONTROL 0x2360
+
+#define _GEN7_PIPEA_DE_LOAD_SL 0x70068
+#define _GEN7_PIPEB_DE_LOAD_SL 0x71068
+#define GEN7_PIPE_DE_LOAD_SL(pipe) _PIPE(pipe, \
+ _GEN7_PIPEA_DE_LOAD_SL, \
+ _GEN7_PIPEB_DE_LOAD_SL)
/*
* Reset registers
@@ -370,6 +478,7 @@
#define IOSF_PORT_PUNIT 0x4
#define IOSF_PORT_NC 0x11
#define IOSF_PORT_DPIO 0x12
+#define IOSF_PORT_DPIO_2 0x1a
#define IOSF_PORT_GPIO_NC 0x13
#define IOSF_PORT_CCK 0x14
#define IOSF_PORT_CCU 0xA9
@@ -381,9 +490,6 @@
/* See configdb bunit SB addr map */
#define BUNIT_REG_BISOC 0x11
-#define PUNIT_OPCODE_REG_READ 6
-#define PUNIT_OPCODE_REG_WRITE 7
-
#define PUNIT_REG_DSPFREQ 0x36
#define DSPFREQSTAT_SHIFT 30
#define DSPFREQSTAT_MASK (0x3 << DSPFREQSTAT_SHIFT)
@@ -469,16 +575,91 @@ enum punit_power_well {
#define DSI_PLL_M1_DIV_MASK (0x1ff << 0)
#define CCK_DISPLAY_CLOCK_CONTROL 0x6b
-/*
- * DPIO - a special bus for various display related registers to hide behind
+/**
+ * DOC: DPIO
+ *
+ * VLV and CHV have slightly peculiar display PHYs for driving DP/HDMI
+ * ports. DPIO is the name given to such a display PHY. These PHYs
+ * don't follow the standard programming model using direct MMIO
+ * registers, and instead their registers must be accessed trough IOSF
+ * sideband. VLV has one such PHY for driving ports B and C, and CHV
+ * adds another PHY for driving port D. Each PHY responds to specific
+ * IOSF-SB port.
+ *
+ * Each display PHY is made up of one or two channels. Each channel
+ * houses a common lane part which contains the PLL and other common
+ * logic. CH0 common lane also contains the IOSF-SB logic for the
+ * Common Register Interface (CRI) ie. the DPIO registers. CRI clock
+ * must be running when any DPIO registers are accessed.
+ *
+ * In addition to having their own registers, the PHYs are also
+ * controlled through some dedicated signals from the display
+ * controller. These include PLL reference clock enable, PLL enable,
+ * and CRI clock selection, for example.
*
- * DPIO is VLV only.
+ * Eeach channel also has two splines (also called data lanes), and
+ * each spline is made up of one Physical Access Coding Sub-Layer
+ * (PCS) block and two TX lanes. So each channel has two PCS blocks
+ * and four TX lanes. The TX lanes are used as DP lanes or TMDS
+ * data/clock pairs depending on the output type.
+ *
+ * Additionally the PHY also contains an AUX lane with AUX blocks
+ * for each channel. This is used for DP AUX communication, but
+ * this fact isn't really relevant for the driver since AUX is
+ * controlled from the display controller side. No DPIO registers
+ * need to be accessed during AUX communication,
+ *
+ * Generally the common lane corresponds to the pipe and
+ * the spline (PCS/TX) correponds to the port.
+ *
+ * For dual channel PHY (VLV/CHV):
+ *
+ * pipe A == CMN/PLL/REF CH0
+ *
+ * pipe B == CMN/PLL/REF CH1
+ *
+ * port B == PCS/TX CH0
+ *
+ * port C == PCS/TX CH1
+ *
+ * This is especially important when we cross the streams
+ * ie. drive port B with pipe B, or port C with pipe A.
+ *
+ * For single channel PHY (CHV):
+ *
+ * pipe C == CMN/PLL/REF CH0
+ *
+ * port D == PCS/TX CH0
+ *
+ * Note: digital port B is DDI0, digital port C is DDI1,
+ * digital port D is DDI2
+ */
+/*
+ * Dual channel PHY (VLV/CHV)
+ * ---------------------------------
+ * | CH0 | CH1 |
+ * | CMN/PLL/REF | CMN/PLL/REF |
+ * |---------------|---------------| Display PHY
+ * | PCS01 | PCS23 | PCS01 | PCS23 |
+ * |-------|-------|-------|-------|
+ * |TX0|TX1|TX2|TX3|TX0|TX1|TX2|TX3|
+ * ---------------------------------
+ * | DDI0 | DDI1 | DP/HDMI ports
+ * ---------------------------------
*
- * Note: digital port B is DDI0, digital pot C is DDI1
+ * Single channel PHY (CHV)
+ * -----------------
+ * | CH0 |
+ * | CMN/PLL/REF |
+ * |---------------| Display PHY
+ * | PCS01 | PCS23 |
+ * |-------|-------|
+ * |TX0|TX1|TX2|TX3|
+ * -----------------
+ * | DDI2 | DP/HDMI port
+ * -----------------
*/
#define DPIO_DEVFN 0
-#define DPIO_OPCODE_REG_WRITE 1
-#define DPIO_OPCODE_REG_READ 0
#define DPIO_CTL (VLV_DISPLAY_BASE + 0x2110)
#define DPIO_MODSEL1 (1<<3) /* if ref clk b == 27 */
@@ -555,14 +736,29 @@ enum punit_power_well {
#define DPIO_PCS_TX_LANE1_RESET (1<<7)
#define VLV_PCS_DW0(ch) _PORT(ch, _VLV_PCS_DW0_CH0, _VLV_PCS_DW0_CH1)
+#define _VLV_PCS01_DW0_CH0 0x200
+#define _VLV_PCS23_DW0_CH0 0x400
+#define _VLV_PCS01_DW0_CH1 0x2600
+#define _VLV_PCS23_DW0_CH1 0x2800
+#define VLV_PCS01_DW0(ch) _PORT(ch, _VLV_PCS01_DW0_CH0, _VLV_PCS01_DW0_CH1)
+#define VLV_PCS23_DW0(ch) _PORT(ch, _VLV_PCS23_DW0_CH0, _VLV_PCS23_DW0_CH1)
+
#define _VLV_PCS_DW1_CH0 0x8204
#define _VLV_PCS_DW1_CH1 0x8404
+#define CHV_PCS_REQ_SOFTRESET_EN (1<<23)
#define DPIO_PCS_CLK_CRI_RXEB_EIOS_EN (1<<22)
#define DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN (1<<21)
#define DPIO_PCS_CLK_DATAWIDTH_SHIFT (6)
#define DPIO_PCS_CLK_SOFT_RESET (1<<5)
#define VLV_PCS_DW1(ch) _PORT(ch, _VLV_PCS_DW1_CH0, _VLV_PCS_DW1_CH1)
+#define _VLV_PCS01_DW1_CH0 0x204
+#define _VLV_PCS23_DW1_CH0 0x404
+#define _VLV_PCS01_DW1_CH1 0x2604
+#define _VLV_PCS23_DW1_CH1 0x2804
+#define VLV_PCS01_DW1(ch) _PORT(ch, _VLV_PCS01_DW1_CH0, _VLV_PCS01_DW1_CH1)
+#define VLV_PCS23_DW1(ch) _PORT(ch, _VLV_PCS23_DW1_CH0, _VLV_PCS23_DW1_CH1)
+
#define _VLV_PCS_DW8_CH0 0x8220
#define _VLV_PCS_DW8_CH1 0x8420
#define VLV_PCS_DW8(ch) _PORT(ch, _VLV_PCS_DW8_CH0, _VLV_PCS_DW8_CH1)
@@ -578,6 +774,19 @@ enum punit_power_well {
#define _VLV_PCS_DW9_CH1 0x8424
#define VLV_PCS_DW9(ch) _PORT(ch, _VLV_PCS_DW9_CH0, _VLV_PCS_DW9_CH1)
+#define _CHV_PCS_DW10_CH0 0x8228
+#define _CHV_PCS_DW10_CH1 0x8428
+#define DPIO_PCS_SWING_CALC_TX0_TX2 (1<<30)
+#define DPIO_PCS_SWING_CALC_TX1_TX3 (1<<31)
+#define CHV_PCS_DW10(ch) _PORT(ch, _CHV_PCS_DW10_CH0, _CHV_PCS_DW10_CH1)
+
+#define _VLV_PCS01_DW10_CH0 0x0228
+#define _VLV_PCS23_DW10_CH0 0x0428
+#define _VLV_PCS01_DW10_CH1 0x2628
+#define _VLV_PCS23_DW10_CH1 0x2828
+#define VLV_PCS01_DW10(port) _PORT(port, _VLV_PCS01_DW10_CH0, _VLV_PCS01_DW10_CH1)
+#define VLV_PCS23_DW10(port) _PORT(port, _VLV_PCS23_DW10_CH0, _VLV_PCS23_DW10_CH1)
+
#define _VLV_PCS_DW11_CH0 0x822c
#define _VLV_PCS_DW11_CH1 0x842c
#define VLV_PCS_DW11(ch) _PORT(ch, _VLV_PCS_DW11_CH0, _VLV_PCS_DW11_CH1)
@@ -596,14 +805,21 @@ 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_UNIQ_TRANS_SCALE_SHIFT 8
#define VLV_TX_DW2(ch) _PORT(ch, _VLV_TX_DW2_CH0, _VLV_TX_DW2_CH1)
#define _VLV_TX_DW3_CH0 0x828c
#define _VLV_TX_DW3_CH1 0x848c
+/* The following bit for CHV phy */
+#define DPIO_TX_UNIQ_TRANS_SCALE_EN (1<<27)
#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 VLV_TX_DW4(ch) _PORT(ch, _VLV_TX_DW4_CH0, _VLV_TX_DW4_CH1)
#define _VLV_TX3_DW4_CH0 0x690
@@ -623,6 +839,73 @@ enum punit_power_well {
#define _VLV_TX_DW14_CH1 0x84b8
#define VLV_TX_DW14(ch) _PORT(ch, _VLV_TX_DW14_CH0, _VLV_TX_DW14_CH1)
+/* CHV dpPhy registers */
+#define _CHV_PLL_DW0_CH0 0x8000
+#define _CHV_PLL_DW0_CH1 0x8180
+#define CHV_PLL_DW0(ch) _PIPE(ch, _CHV_PLL_DW0_CH0, _CHV_PLL_DW0_CH1)
+
+#define _CHV_PLL_DW1_CH0 0x8004
+#define _CHV_PLL_DW1_CH1 0x8184
+#define DPIO_CHV_N_DIV_SHIFT 8
+#define DPIO_CHV_M1_DIV_BY_2 (0 << 0)
+#define CHV_PLL_DW1(ch) _PIPE(ch, _CHV_PLL_DW1_CH0, _CHV_PLL_DW1_CH1)
+
+#define _CHV_PLL_DW2_CH0 0x8008
+#define _CHV_PLL_DW2_CH1 0x8188
+#define CHV_PLL_DW2(ch) _PIPE(ch, _CHV_PLL_DW2_CH0, _CHV_PLL_DW2_CH1)
+
+#define _CHV_PLL_DW3_CH0 0x800c
+#define _CHV_PLL_DW3_CH1 0x818c
+#define DPIO_CHV_FRAC_DIV_EN (1 << 16)
+#define DPIO_CHV_FIRST_MOD (0 << 8)
+#define DPIO_CHV_SECOND_MOD (1 << 8)
+#define DPIO_CHV_FEEDFWD_GAIN_SHIFT 0
+#define CHV_PLL_DW3(ch) _PIPE(ch, _CHV_PLL_DW3_CH0, _CHV_PLL_DW3_CH1)
+
+#define _CHV_PLL_DW6_CH0 0x8018
+#define _CHV_PLL_DW6_CH1 0x8198
+#define DPIO_CHV_GAIN_CTRL_SHIFT 16
+#define DPIO_CHV_INT_COEFF_SHIFT 8
+#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_DW13_CH0 0x8134
+#define _CHV_CMN_DW0_CH1 0x8080
+#define DPIO_CHV_S1_DIV_SHIFT 21
+#define DPIO_CHV_P1_DIV_SHIFT 13 /* 3 bits */
+#define DPIO_CHV_P2_DIV_SHIFT 8 /* 5 bits */
+#define DPIO_CHV_K_DIV_SHIFT 4
+#define DPIO_PLL_FREQLOCK (1 << 1)
+#define DPIO_PLL_LOCK (1 << 0)
+#define CHV_CMN_DW13(ch) _PIPE(ch, _CHV_CMN_DW13_CH0, _CHV_CMN_DW0_CH1)
+
+#define _CHV_CMN_DW14_CH0 0x8138
+#define _CHV_CMN_DW1_CH1 0x8084
+#define DPIO_AFC_RECAL (1 << 14)
+#define DPIO_DCLKP_EN (1 << 13)
+#define CHV_CMN_DW14(ch) _PIPE(ch, _CHV_CMN_DW14_CH0, _CHV_CMN_DW1_CH1)
+
+#define CHV_CMN_DW30 0x8178
+#define DPIO_LRC_BYPASS (1 << 3)
+
+#define _TXLANE(ch, lane, offset) ((ch ? 0x2400 : 0) + \
+ (lane) * 0x200 + (offset))
+
+#define CHV_TX_DW0(ch, lane) _TXLANE(ch, lane, 0x80)
+#define CHV_TX_DW1(ch, lane) _TXLANE(ch, lane, 0x84)
+#define CHV_TX_DW2(ch, lane) _TXLANE(ch, lane, 0x88)
+#define CHV_TX_DW3(ch, lane) _TXLANE(ch, lane, 0x8c)
+#define CHV_TX_DW4(ch, lane) _TXLANE(ch, lane, 0x90)
+#define CHV_TX_DW5(ch, lane) _TXLANE(ch, lane, 0x94)
+#define CHV_TX_DW6(ch, lane) _TXLANE(ch, lane, 0x98)
+#define CHV_TX_DW7(ch, lane) _TXLANE(ch, lane, 0x9c)
+#define CHV_TX_DW8(ch, lane) _TXLANE(ch, lane, 0xa0)
+#define CHV_TX_DW9(ch, lane) _TXLANE(ch, lane, 0xa4)
+#define CHV_TX_DW10(ch, lane) _TXLANE(ch, lane, 0xa8)
+#define CHV_TX_DW11(ch, lane) _TXLANE(ch, lane, 0xac)
+#define DPIO_FRC_LATENCY_SHFIT 8
+#define CHV_TX_DW14(ch, lane) _TXLANE(ch, lane, 0xb8)
+#define DPIO_UPAR_SHIFT 30
/*
* Fence registers
*/
@@ -663,6 +946,7 @@ enum punit_power_well {
#define RENDER_RING_BASE 0x02000
#define BSD_RING_BASE 0x04000
#define GEN6_BSD_RING_BASE 0x12000
+#define GEN8_BSD2_RING_BASE 0x1c000
#define VEBOX_RING_BASE 0x1a000
#define BLT_RING_BASE 0x22000
#define RING_TAIL(base) ((base)+0x30)
@@ -688,9 +972,20 @@ enum punit_power_well {
#define RING_MAX_IDLE(base) ((base)+0x54)
#define RING_HWS_PGA(base) ((base)+0x80)
#define RING_HWS_PGA_GEN6(base) ((base)+0x2080)
-#define ARB_MODE 0x04030
+
+#define GEN7_WR_WATERMARK 0x4028
+#define GEN7_GFX_PRIO_CTRL 0x402C
+#define ARB_MODE 0x4030
#define ARB_MODE_SWIZZLE_SNB (1<<4)
#define ARB_MODE_SWIZZLE_IVB (1<<5)
+#define GEN7_GFX_PEND_TLB0 0x4034
+#define GEN7_GFX_PEND_TLB1 0x4038
+/* L3, CVS, ZTLB, RCC, CASC LRA min, max values */
+#define GEN7_LRA_LIMITS_BASE 0x403C
+#define GEN7_LRA_LIMITS_REG_NUM 13
+#define GEN7_MEDIA_MAX_REQ_COUNT 0x4070
+#define GEN7_GFX_MAX_REQ_COUNT 0x4074
+
#define GAMTARBMODE 0x04a08
#define ARB_MODE_BWGTLB_DISABLE (1<<9)
#define ARB_MODE_SWIZZLE_BDW (1<<1)
@@ -725,6 +1020,9 @@ enum punit_power_well {
#define RING_WAIT_I8XX (1<<0) /* gen2, PRBx_HEAD */
#define RING_WAIT (1<<11) /* gen3+, PRBx_CTL */
#define RING_WAIT_SEMAPHORE (1<<10) /* gen6+ */
+
+#define GEN7_TLB_RD_ADDR 0x4700
+
#if 0
#define PRB0_TAIL 0x02030
#define PRB0_HEAD 0x02034
@@ -748,6 +1046,7 @@ enum punit_power_well {
#define RING_INSTDONE(base) ((base)+0x6c)
#define RING_INSTPS(base) ((base)+0x70)
#define RING_DMA_FADD(base) ((base)+0x78)
+#define RING_DMA_FADD_UDW(base) ((base)+0x60) /* gen8+ */
#define RING_INSTPM(base) ((base)+0xc0)
#define RING_MI_MODE(base) ((base)+0x9c)
#define INSTPS 0x02070 /* 965+ only */
@@ -842,21 +1141,26 @@ enum punit_power_well {
#define GFX_MODE_GEN7 0x0229c
#define RING_MODE_GEN7(ring) ((ring)->mmio_base+0x29c)
#define GFX_RUN_LIST_ENABLE (1<<15)
-#define GFX_TLB_INVALIDATE_ALWAYS (1<<13)
+#define GFX_TLB_INVALIDATE_EXPLICIT (1<<13)
#define GFX_SURFACE_FAULT_ENABLE (1<<12)
#define GFX_REPLAY_MODE (1<<11)
#define GFX_PSMI_GRANULARITY (1<<10)
#define GFX_PPGTT_ENABLE (1<<9)
#define VLV_DISPLAY_BASE 0x180000
+#define VLV_MIPI_BASE VLV_DISPLAY_BASE
+#define VLV_GU_CTL0 (VLV_DISPLAY_BASE + 0x2030)
+#define VLV_GU_CTL1 (VLV_DISPLAY_BASE + 0x2034)
#define SCPD0 0x0209c /* 915+ only */
#define IER 0x020a0
#define IIR 0x020a4
#define IMR 0x020a8
#define ISR 0x020ac
#define VLV_GUNIT_CLOCK_GATE (VLV_DISPLAY_BASE + 0x2060)
+#define GINT_DIS (1<<22)
#define GCFG_DIS (1<<8)
+#define VLV_GUNIT_CLOCK_GATE2 (VLV_DISPLAY_BASE + 0x2064)
#define VLV_IIR_RW (VLV_DISPLAY_BASE + 0x2084)
#define VLV_IER (VLV_DISPLAY_BASE + 0x20a0)
#define VLV_IIR (VLV_DISPLAY_BASE + 0x20a4)
@@ -875,7 +1179,7 @@ enum punit_power_well {
#define I915_ERROR_INSTRUCTION (1<<0)
#define INSTPM 0x020c0
#define INSTPM_SELF_EN (1<<12) /* 915GM only */
-#define INSTPM_AGPBUSY_DIS (1<<11) /* gen3: when disabled, pending interrupts
+#define INSTPM_AGPBUSY_INT_EN (1<<11) /* gen3: when disabled, pending interrupts
will not assert AGPBUSY# and will only
be delivered when out of C3. */
#define INSTPM_FORCE_ORDERING (1<<7) /* GEN6+ */
@@ -956,6 +1260,10 @@ enum punit_power_well {
#define MI_ARB_DISPLAY_PRIORITY_A_B (0 << 0) /* display A > display B */
#define MI_ARB_DISPLAY_PRIORITY_B_A (1 << 0) /* display B > display A */
+#define MI_STATE 0x020e4 /* gen2 only */
+#define MI_AGPBUSY_INT_EN (1 << 1) /* 85x only */
+#define MI_AGPBUSY_830_MODE (1 << 0) /* 85x only */
+
#define CACHE_MODE_0 0x02120 /* 915+ only */
#define CM0_PIPELINED_RENDER_FLUSH_DISABLE (1<<8)
#define CM0_IZ_OPT_DISABLE (1<<6)
@@ -973,6 +1281,7 @@ enum punit_power_well {
#define ECO_FLIP_DONE (1<<0)
#define CACHE_MODE_0_GEN7 0x7000 /* IVB+ */
+#define RC_OP_FLUSH_ENABLE (1<<0)
#define HIZ_RAW_STALL_OPT_DISABLE (1<<2)
#define CACHE_MODE_1 0x7004 /* IVB+ */
#define PIXEL_SUBSPAN_COLLECT_OPT_DISABLE (1<<6)
@@ -984,6 +1293,7 @@ enum punit_power_well {
#define GEN6_RC_SLEEP_PSMI_CONTROL 0x2050
#define GEN8_RC_SEMA_IDLE_MSG_DISABLE (1 << 12)
+#define GEN8_FF_DOP_CLOCK_GATE_DISABLE (1<<10)
#define GEN6_BSD_SLEEP_PSMI_CONTROL 0x12050
#define GEN6_BSD_SLEEP_MSG_DISABLE (1 << 0)
@@ -1024,24 +1334,43 @@ enum punit_power_well {
/* These are all the "old" interrupts */
#define ILK_BSD_USER_INTERRUPT (1<<5)
+
+#define I915_PM_INTERRUPT (1<<31)
+#define I915_ISP_INTERRUPT (1<<22)
+#define I915_LPE_PIPE_B_INTERRUPT (1<<21)
+#define I915_LPE_PIPE_A_INTERRUPT (1<<20)
+#define I915_MIPIB_INTERRUPT (1<<19)
+#define I915_MIPIA_INTERRUPT (1<<18)
#define I915_PIPE_CONTROL_NOTIFY_INTERRUPT (1<<18)
#define I915_DISPLAY_PORT_INTERRUPT (1<<17)
+#define I915_DISPLAY_PIPE_C_HBLANK_INTERRUPT (1<<16)
+#define I915_MASTER_ERROR_INTERRUPT (1<<15)
#define I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT (1<<15)
+#define I915_DISPLAY_PIPE_B_HBLANK_INTERRUPT (1<<14)
#define I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT (1<<14) /* p-state */
+#define I915_DISPLAY_PIPE_A_HBLANK_INTERRUPT (1<<13)
#define I915_HWB_OOM_INTERRUPT (1<<13)
+#define I915_LPE_PIPE_C_INTERRUPT (1<<12)
#define I915_SYNC_STATUS_INTERRUPT (1<<12)
+#define I915_MISC_INTERRUPT (1<<11)
#define I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT (1<<11)
+#define I915_DISPLAY_PIPE_C_VBLANK_INTERRUPT (1<<10)
#define I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT (1<<10)
+#define I915_DISPLAY_PIPE_C_EVENT_INTERRUPT (1<<9)
#define I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT (1<<9)
+#define I915_DISPLAY_PIPE_C_DPBM_INTERRUPT (1<<8)
#define I915_DISPLAY_PLANE_C_FLIP_PENDING_INTERRUPT (1<<8)
#define I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT (1<<7)
#define I915_DISPLAY_PIPE_A_EVENT_INTERRUPT (1<<6)
#define I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT (1<<5)
#define I915_DISPLAY_PIPE_B_EVENT_INTERRUPT (1<<4)
+#define I915_DISPLAY_PIPE_A_DPBM_INTERRUPT (1<<3)
+#define I915_DISPLAY_PIPE_B_DPBM_INTERRUPT (1<<2)
#define I915_DEBUG_INTERRUPT (1<<2)
+#define I915_WINVALID_INTERRUPT (1<<1)
#define I915_USER_INTERRUPT (1<<1)
#define I915_ASLE_INTERRUPT (1<<0)
-#define I915_BSD_USER_INTERRUPT (1 << 25)
+#define I915_BSD_USER_INTERRUPT (1<<25)
#define GEN6_BSD_RNCID 0x12198
@@ -1198,6 +1527,7 @@ enum punit_power_well {
#define GMBUS_PORT_SSC 1
#define GMBUS_PORT_VGADDC 2
#define GMBUS_PORT_PANEL 3
+#define GMBUS_PORT_DPD_CHV 3 /* HDMID_CHV */
#define GMBUS_PORT_DPC 4 /* HDMIC */
#define GMBUS_PORT_DPB 5 /* SDVO, HDMIB */
#define GMBUS_PORT_DPD 6 /* HDMID */
@@ -1239,6 +1569,7 @@ enum punit_power_well {
*/
#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)
@@ -1273,10 +1604,23 @@ enum punit_power_well {
#define DPLL_LOCK_VLV (1<<15)
#define DPLL_INTEGRATED_CRI_CLK_VLV (1<<14)
#define DPLL_INTEGRATED_CLOCK_VLV (1<<13)
+#define DPLL_SSC_REF_CLOCK_CHV (1<<13)
#define DPLL_PORTC_READY_MASK (0xf << 4)
#define DPLL_PORTB_READY_MASK (0xf)
#define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000
+
+/* Additional CHV pll/phy registers */
+#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 DISPLAY_PHY_STATUS (VLV_DISPLAY_BASE + 0x60104)
+#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
* this field (only one bit may be set).
@@ -1317,6 +1661,7 @@ enum punit_power_well {
#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)
@@ -1416,7 +1761,7 @@ enum punit_power_well {
# define DPIOUNIT_CLOCK_GATE_DISABLE (1 << 6) /* 915-945 */
# define OVFUNIT_CLOCK_GATE_DISABLE (1 << 5)
# define OVBUNIT_CLOCK_GATE_DISABLE (1 << 4)
-/**
+/*
* This bit must be set on the 830 to prevent hangs when turning off the
* overlay scaler.
*/
@@ -1436,12 +1781,12 @@ enum punit_power_well {
# define COLOR_CALCULATOR_CLOCK_GATE_DISABLE (1 << 7)
# define MOTION_COMP_CLOCK_GATE_DISABLE (1 << 6)
# define MAG_CLOCK_GATE_DISABLE (1 << 5)
-/** This bit must be unset on 855,865 */
+/* This bit must be unset on 855,865 */
# define MECI_CLOCK_GATE_DISABLE (1 << 4)
# define DCMP_CLOCK_GATE_DISABLE (1 << 3)
# define MEC_CLOCK_GATE_DISABLE (1 << 2)
# define MECO_CLOCK_GATE_DISABLE (1 << 1)
-/** This bit must be set on 855,865. */
+/* This bit must be set on 855,865. */
# define SV_CLOCK_GATE_DISABLE (1 << 0)
# define I915_MPEG_CLOCK_GATE_DISABLE (1 << 16)
# define I915_VLD_IP_PR_CLOCK_GATE_DISABLE (1 << 15)
@@ -1462,14 +1807,14 @@ enum punit_power_well {
# define I915_BY_CLOCK_GATE_DISABLE (1 << 0)
# define I965_RCZ_CLOCK_GATE_DISABLE (1 << 30)
-/** This bit must always be set on 965G/965GM */
+/* This bit must always be set on 965G/965GM */
# define I965_RCC_CLOCK_GATE_DISABLE (1 << 29)
# define I965_RCPB_CLOCK_GATE_DISABLE (1 << 28)
# define I965_DAP_CLOCK_GATE_DISABLE (1 << 27)
# define I965_ROC_CLOCK_GATE_DISABLE (1 << 26)
# define I965_GW_CLOCK_GATE_DISABLE (1 << 25)
# define I965_TD_CLOCK_GATE_DISABLE (1 << 24)
-/** This bit must always be set on 965G */
+/* This bit must always be set on 965G */
# define I965_ISC_CLOCK_GATE_DISABLE (1 << 23)
# define I965_IC_CLOCK_GATE_DISABLE (1 << 22)
# define I965_EU_CLOCK_GATE_DISABLE (1 << 21)
@@ -1494,6 +1839,10 @@ enum punit_power_well {
#define VF_UNIT_CLOCK_GATE_DISABLE (1 << 9)
#define GS_UNIT_CLOCK_GATE_DISABLE (1 << 7)
#define CL_UNIT_CLOCK_GATE_DISABLE (1 << 6)
+
+#define VDECCLK_GATE_D 0x620C /* g4x only */
+#define VCP_UNIT_CLOCK_GATE_DISABLE (1 << 4)
+
#define RAMCLK_GATE_D 0x6210 /* CRL only */
#define DEUC 0x6214 /* CRL only */
@@ -1513,6 +1862,7 @@ enum punit_power_well {
*/
#define PALETTE_A_OFFSET 0xa000
#define PALETTE_B_OFFSET 0xa800
+#define CHV_PALETTE_C_OFFSET 0xc000
#define PALETTE(pipe) (dev_priv->info.palette_offsets[pipe] + \
dev_priv->info.display_mmio_offset)
@@ -1535,7 +1885,7 @@ enum punit_power_well {
/* Memory controller frequency in MCHBAR for Haswell (possible SNB+) */
#define DCLK (MCHBAR_MIRROR_BASE_SNB + 0x5e04)
-/** 915-945 and GM965 MCH register controlling DRAM channel access */
+/* 915-945 and GM965 MCH register controlling DRAM channel access */
#define DCC 0x10200
#define DCC_ADDRESSING_MODE_SINGLE_CHANNEL (0 << 0)
#define DCC_ADDRESSING_MODE_DUAL_CHANNEL_ASYMMETRIC (1 << 0)
@@ -1544,15 +1894,15 @@ enum punit_power_well {
#define DCC_CHANNEL_XOR_DISABLE (1 << 10)
#define DCC_CHANNEL_XOR_BIT_17 (1 << 9)
-/** Pineview MCH register contains DDR3 setting */
+/* Pineview MCH register contains DDR3 setting */
#define CSHRDDR3CTL 0x101a8
#define CSHRDDR3CTL_DDR3 (1 << 2)
-/** 965 MCH register controlling DRAM channel configuration */
+/* 965 MCH register controlling DRAM channel configuration */
#define C0DRB3 0x10206
#define C1DRB3 0x10606
-/** snb MCH registers for reading the DRAM channel configuration */
+/* snb MCH registers for reading the DRAM channel configuration */
#define MAD_DIMM_C0 (MCHBAR_MIRROR_BASE_SNB + 0x5004)
#define MAD_DIMM_C1 (MCHBAR_MIRROR_BASE_SNB + 0x5008)
#define MAD_DIMM_C2 (MCHBAR_MIRROR_BASE_SNB + 0x500C)
@@ -1574,7 +1924,7 @@ enum punit_power_well {
#define MAD_DIMM_A_SIZE_SHIFT 0
#define MAD_DIMM_A_SIZE_MASK (0xff << MAD_DIMM_A_SIZE_SHIFT)
-/** snb MCH registers for priority tuning */
+/* snb MCH registers for priority tuning */
#define MCH_SSKPD (MCHBAR_MIRROR_BASE_SNB + 0x5d10)
#define MCH_SSKPD_WM0_MASK 0x3f
#define MCH_SSKPD_WM0_VAL 0xc
@@ -2002,6 +2352,7 @@ enum punit_power_well {
#define TRANSCODER_A_OFFSET 0x60000
#define TRANSCODER_B_OFFSET 0x61000
#define TRANSCODER_C_OFFSET 0x62000
+#define CHV_TRANSCODER_C_OFFSET 0x63000
#define TRANSCODER_EDP_OFFSET 0x6f000
#define _TRANSCODER2(pipe, reg) (dev_priv->info.trans_offsets[(pipe)] - \
@@ -2226,6 +2577,7 @@ enum punit_power_well {
#define GEN3_SDVOC 0x61160
#define GEN4_HDMIB GEN3_SDVOB
#define GEN4_HDMIC GEN3_SDVOC
+#define CHV_HDMID 0x6116C
#define PCH_SDVOB 0xe1140
#define PCH_HDMIB PCH_SDVOB
#define PCH_HDMIC 0xe1150
@@ -2246,7 +2598,7 @@ enum punit_power_well {
#define SDVO_PIPE_B_SELECT (1 << 30)
#define SDVO_STALL_SELECT (1 << 29)
#define SDVO_INTERRUPT_ENABLE (1 << 26)
-/**
+/*
* 915G/GM SDVO pixel multiplier.
* Programmed value is multiplier - 1, up to 5x.
* \sa DPLL_MD_UDI_MULTIPLIER_MASK
@@ -2286,6 +2638,10 @@ enum punit_power_well {
#define SDVO_PIPE_SEL_CPT(pipe) ((pipe) << 29)
#define SDVO_PIPE_SEL_MASK_CPT (3 << 29)
+/* CHV SDVO/HDMI bits: */
+#define SDVO_PIPE_SEL_CHV(pipe) ((pipe) << 24)
+#define SDVO_PIPE_SEL_MASK_CHV (3 << 24)
+
/* DVO port control */
#define DVOA 0x61120
@@ -2556,65 +2912,65 @@ enum punit_power_well {
/* TV port control */
#define TV_CTL 0x68000
-/** Enables the TV encoder */
+/* Enables the TV encoder */
# define TV_ENC_ENABLE (1 << 31)
-/** Sources the TV encoder input from pipe B instead of A. */
+/* Sources the TV encoder input from pipe B instead of A. */
# define TV_ENC_PIPEB_SELECT (1 << 30)
-/** Outputs composite video (DAC A only) */
+/* Outputs composite video (DAC A only) */
# define TV_ENC_OUTPUT_COMPOSITE (0 << 28)
-/** Outputs SVideo video (DAC B/C) */
+/* Outputs SVideo video (DAC B/C) */
# define TV_ENC_OUTPUT_SVIDEO (1 << 28)
-/** Outputs Component video (DAC A/B/C) */
+/* Outputs Component video (DAC A/B/C) */
# define TV_ENC_OUTPUT_COMPONENT (2 << 28)
-/** Outputs Composite and SVideo (DAC A/B/C) */
+/* Outputs Composite and SVideo (DAC A/B/C) */
# define TV_ENC_OUTPUT_SVIDEO_COMPOSITE (3 << 28)
# define TV_TRILEVEL_SYNC (1 << 21)
-/** Enables slow sync generation (945GM only) */
+/* Enables slow sync generation (945GM only) */
# define TV_SLOW_SYNC (1 << 20)
-/** Selects 4x oversampling for 480i and 576p */
+/* Selects 4x oversampling for 480i and 576p */
# define TV_OVERSAMPLE_4X (0 << 18)
-/** Selects 2x oversampling for 720p and 1080i */
+/* Selects 2x oversampling for 720p and 1080i */
# define TV_OVERSAMPLE_2X (1 << 18)
-/** Selects no oversampling for 1080p */
+/* Selects no oversampling for 1080p */
# define TV_OVERSAMPLE_NONE (2 << 18)
-/** Selects 8x oversampling */
+/* Selects 8x oversampling */
# define TV_OVERSAMPLE_8X (3 << 18)
-/** Selects progressive mode rather than interlaced */
+/* Selects progressive mode rather than interlaced */
# define TV_PROGRESSIVE (1 << 17)
-/** Sets the colorburst to PAL mode. Required for non-M PAL modes. */
+/* Sets the colorburst to PAL mode. Required for non-M PAL modes. */
# define TV_PAL_BURST (1 << 16)
-/** Field for setting delay of Y compared to C */
+/* Field for setting delay of Y compared to C */
# define TV_YC_SKEW_MASK (7 << 12)
-/** Enables a fix for 480p/576p standard definition modes on the 915GM only */
+/* Enables a fix for 480p/576p standard definition modes on the 915GM only */
# define TV_ENC_SDP_FIX (1 << 11)
-/**
+/*
* Enables a fix for the 915GM only.
*
* Not sure what it does.
*/
# define TV_ENC_C0_FIX (1 << 10)
-/** Bits that must be preserved by software */
+/* Bits that must be preserved by software */
# define TV_CTL_SAVE ((1 << 11) | (3 << 9) | (7 << 6) | 0xf)
# define TV_FUSE_STATE_MASK (3 << 4)
-/** Read-only state that reports all features enabled */
+/* Read-only state that reports all features enabled */
# define TV_FUSE_STATE_ENABLED (0 << 4)
-/** Read-only state that reports that Macrovision is disabled in hardware*/
+/* Read-only state that reports that Macrovision is disabled in hardware*/
# define TV_FUSE_STATE_NO_MACROVISION (1 << 4)
-/** Read-only state that reports that TV-out is disabled in hardware. */
+/* Read-only state that reports that TV-out is disabled in hardware. */
# define TV_FUSE_STATE_DISABLED (2 << 4)
-/** Normal operation */
+/* Normal operation */
# define TV_TEST_MODE_NORMAL (0 << 0)
-/** Encoder test pattern 1 - combo pattern */
+/* Encoder test pattern 1 - combo pattern */
# define TV_TEST_MODE_PATTERN_1 (1 << 0)
-/** Encoder test pattern 2 - full screen vertical 75% color bars */
+/* Encoder test pattern 2 - full screen vertical 75% color bars */
# define TV_TEST_MODE_PATTERN_2 (2 << 0)
-/** Encoder test pattern 3 - full screen horizontal 75% color bars */
+/* Encoder test pattern 3 - full screen horizontal 75% color bars */
# define TV_TEST_MODE_PATTERN_3 (3 << 0)
-/** Encoder test pattern 4 - random noise */
+/* Encoder test pattern 4 - random noise */
# define TV_TEST_MODE_PATTERN_4 (4 << 0)
-/** Encoder test pattern 5 - linear color ramps */
+/* Encoder test pattern 5 - linear color ramps */
# define TV_TEST_MODE_PATTERN_5 (5 << 0)
-/**
+/*
* This test mode forces the DACs to 50% of full output.
*
* This is used for load detection in combination with TVDAC_SENSE_MASK
@@ -2624,35 +2980,35 @@ enum punit_power_well {
#define TV_DAC 0x68004
# define TV_DAC_SAVE 0x00ffff00
-/**
+/*
* Reports that DAC state change logic has reported change (RO).
*
* This gets cleared when TV_DAC_STATE_EN is cleared
*/
# define TVDAC_STATE_CHG (1 << 31)
# define TVDAC_SENSE_MASK (7 << 28)
-/** Reports that DAC A voltage is above the detect threshold */
+/* Reports that DAC A voltage is above the detect threshold */
# define TVDAC_A_SENSE (1 << 30)
-/** Reports that DAC B voltage is above the detect threshold */
+/* Reports that DAC B voltage is above the detect threshold */
# define TVDAC_B_SENSE (1 << 29)
-/** Reports that DAC C voltage is above the detect threshold */
+/* Reports that DAC C voltage is above the detect threshold */
# define TVDAC_C_SENSE (1 << 28)
-/**
+/*
* Enables DAC state detection logic, for load-based TV detection.
*
* The PLL of the chosen pipe (in TV_CTL) must be running, and the encoder set
* to off, for load detection to work.
*/
# define TVDAC_STATE_CHG_EN (1 << 27)
-/** Sets the DAC A sense value to high */
+/* Sets the DAC A sense value to high */
# define TVDAC_A_SENSE_CTL (1 << 26)
-/** Sets the DAC B sense value to high */
+/* Sets the DAC B sense value to high */
# define TVDAC_B_SENSE_CTL (1 << 25)
-/** Sets the DAC C sense value to high */
+/* Sets the DAC C sense value to high */
# define TVDAC_C_SENSE_CTL (1 << 24)
-/** Overrides the ENC_ENABLE and DAC voltage levels */
+/* Overrides the ENC_ENABLE and DAC voltage levels */
# define DAC_CTL_OVERRIDE (1 << 7)
-/** Sets the slew rate. Must be preserved in software */
+/* Sets the slew rate. Must be preserved in software */
# define ENC_TVDAC_SLEW_FAST (1 << 6)
# define DAC_A_1_3_V (0 << 4)
# define DAC_A_1_1_V (1 << 4)
@@ -2667,7 +3023,7 @@ enum punit_power_well {
# define DAC_C_0_7_V (2 << 0)
# define DAC_C_MASK (3 << 0)
-/**
+/*
* CSC coefficients are stored in a floating point format with 9 bits of
* mantissa and 2 or 3 bits of exponent. The exponent is represented as 2**-n,
* where 2-bit exponents are unsigned n, and 3-bit exponents are signed n with
@@ -2682,7 +3038,7 @@ enum punit_power_well {
#define TV_CSC_Y2 0x68014
# define TV_BY_MASK 0x07ff0000
# define TV_BY_SHIFT 16
-/**
+/*
* Y attenuation for component video.
*
* Stored in 1.9 fixed point.
@@ -2699,7 +3055,7 @@ enum punit_power_well {
#define TV_CSC_U2 0x6801c
# define TV_BU_MASK 0x07ff0000
# define TV_BU_SHIFT 16
-/**
+/*
* U attenuation for component video.
*
* Stored in 1.9 fixed point.
@@ -2716,7 +3072,7 @@ enum punit_power_well {
#define TV_CSC_V2 0x68024
# define TV_BV_MASK 0x07ff0000
# define TV_BV_SHIFT 16
-/**
+/*
* V attenuation for component video.
*
* Stored in 1.9 fixed point.
@@ -2725,74 +3081,74 @@ enum punit_power_well {
# define TV_AV_SHIFT 0
#define TV_CLR_KNOBS 0x68028
-/** 2s-complement brightness adjustment */
+/* 2s-complement brightness adjustment */
# define TV_BRIGHTNESS_MASK 0xff000000
# define TV_BRIGHTNESS_SHIFT 24
-/** Contrast adjustment, as a 2.6 unsigned floating point number */
+/* Contrast adjustment, as a 2.6 unsigned floating point number */
# define TV_CONTRAST_MASK 0x00ff0000
# define TV_CONTRAST_SHIFT 16
-/** Saturation adjustment, as a 2.6 unsigned floating point number */
+/* Saturation adjustment, as a 2.6 unsigned floating point number */
# define TV_SATURATION_MASK 0x0000ff00
# define TV_SATURATION_SHIFT 8
-/** Hue adjustment, as an integer phase angle in degrees */
+/* Hue adjustment, as an integer phase angle in degrees */
# define TV_HUE_MASK 0x000000ff
# define TV_HUE_SHIFT 0
#define TV_CLR_LEVEL 0x6802c
-/** Controls the DAC level for black */
+/* Controls the DAC level for black */
# define TV_BLACK_LEVEL_MASK 0x01ff0000
# define TV_BLACK_LEVEL_SHIFT 16
-/** Controls the DAC level for blanking */
+/* Controls the DAC level for blanking */
# define TV_BLANK_LEVEL_MASK 0x000001ff
# define TV_BLANK_LEVEL_SHIFT 0
#define TV_H_CTL_1 0x68030
-/** Number of pixels in the hsync. */
+/* Number of pixels in the hsync. */
# define TV_HSYNC_END_MASK 0x1fff0000
# define TV_HSYNC_END_SHIFT 16
-/** Total number of pixels minus one in the line (display and blanking). */
+/* Total number of pixels minus one in the line (display and blanking). */
# define TV_HTOTAL_MASK 0x00001fff
# define TV_HTOTAL_SHIFT 0
#define TV_H_CTL_2 0x68034
-/** Enables the colorburst (needed for non-component color) */
+/* Enables the colorburst (needed for non-component color) */
# define TV_BURST_ENA (1 << 31)
-/** Offset of the colorburst from the start of hsync, in pixels minus one. */
+/* Offset of the colorburst from the start of hsync, in pixels minus one. */
# define TV_HBURST_START_SHIFT 16
# define TV_HBURST_START_MASK 0x1fff0000
-/** Length of the colorburst */
+/* Length of the colorburst */
# define TV_HBURST_LEN_SHIFT 0
# define TV_HBURST_LEN_MASK 0x0001fff
#define TV_H_CTL_3 0x68038
-/** End of hblank, measured in pixels minus one from start of hsync */
+/* End of hblank, measured in pixels minus one from start of hsync */
# define TV_HBLANK_END_SHIFT 16
# define TV_HBLANK_END_MASK 0x1fff0000
-/** Start of hblank, measured in pixels minus one from start of hsync */
+/* Start of hblank, measured in pixels minus one from start of hsync */
# define TV_HBLANK_START_SHIFT 0
# define TV_HBLANK_START_MASK 0x0001fff
#define TV_V_CTL_1 0x6803c
-/** XXX */
+/* XXX */
# define TV_NBR_END_SHIFT 16
# define TV_NBR_END_MASK 0x07ff0000
-/** XXX */
+/* XXX */
# define TV_VI_END_F1_SHIFT 8
# define TV_VI_END_F1_MASK 0x00003f00
-/** XXX */
+/* XXX */
# define TV_VI_END_F2_SHIFT 0
# define TV_VI_END_F2_MASK 0x0000003f
#define TV_V_CTL_2 0x68040
-/** Length of vsync, in half lines */
+/* Length of vsync, in half lines */
# define TV_VSYNC_LEN_MASK 0x07ff0000
# define TV_VSYNC_LEN_SHIFT 16
-/** Offset of the start of vsync in field 1, measured in one less than the
+/* Offset of the start of vsync in field 1, measured in one less than the
* number of half lines.
*/
# define TV_VSYNC_START_F1_MASK 0x00007f00
# define TV_VSYNC_START_F1_SHIFT 8
-/**
+/*
* Offset of the start of vsync in field 2, measured in one less than the
* number of half lines.
*/
@@ -2800,17 +3156,17 @@ enum punit_power_well {
# define TV_VSYNC_START_F2_SHIFT 0
#define TV_V_CTL_3 0x68044
-/** Enables generation of the equalization signal */
+/* Enables generation of the equalization signal */
# define TV_EQUAL_ENA (1 << 31)
-/** Length of vsync, in half lines */
+/* Length of vsync, in half lines */
# define TV_VEQ_LEN_MASK 0x007f0000
# define TV_VEQ_LEN_SHIFT 16
-/** Offset of the start of equalization in field 1, measured in one less than
+/* Offset of the start of equalization in field 1, measured in one less than
* the number of half lines.
*/
# define TV_VEQ_START_F1_MASK 0x0007f00
# define TV_VEQ_START_F1_SHIFT 8
-/**
+/*
* Offset of the start of equalization in field 2, measured in one less than
* the number of half lines.
*/
@@ -2818,13 +3174,13 @@ enum punit_power_well {
# define TV_VEQ_START_F2_SHIFT 0
#define TV_V_CTL_4 0x68048
-/**
+/*
* Offset to start of vertical colorburst, measured in one less than the
* number of lines from vertical start.
*/
# define TV_VBURST_START_F1_MASK 0x003f0000
# define TV_VBURST_START_F1_SHIFT 16
-/**
+/*
* Offset to the end of vertical colorburst, measured in one less than the
* number of lines from the start of NBR.
*/
@@ -2832,13 +3188,13 @@ enum punit_power_well {
# define TV_VBURST_END_F1_SHIFT 0
#define TV_V_CTL_5 0x6804c
-/**
+/*
* Offset to start of vertical colorburst, measured in one less than the
* number of lines from vertical start.
*/
# define TV_VBURST_START_F2_MASK 0x003f0000
# define TV_VBURST_START_F2_SHIFT 16
-/**
+/*
* Offset to the end of vertical colorburst, measured in one less than the
* number of lines from the start of NBR.
*/
@@ -2846,13 +3202,13 @@ enum punit_power_well {
# define TV_VBURST_END_F2_SHIFT 0
#define TV_V_CTL_6 0x68050
-/**
+/*
* Offset to start of vertical colorburst, measured in one less than the
* number of lines from vertical start.
*/
# define TV_VBURST_START_F3_MASK 0x003f0000
# define TV_VBURST_START_F3_SHIFT 16
-/**
+/*
* Offset to the end of vertical colorburst, measured in one less than the
* number of lines from the start of NBR.
*/
@@ -2860,13 +3216,13 @@ enum punit_power_well {
# define TV_VBURST_END_F3_SHIFT 0
#define TV_V_CTL_7 0x68054
-/**
+/*
* Offset to start of vertical colorburst, measured in one less than the
* number of lines from vertical start.
*/
# define TV_VBURST_START_F4_MASK 0x003f0000
# define TV_VBURST_START_F4_SHIFT 16
-/**
+/*
* Offset to the end of vertical colorburst, measured in one less than the
* number of lines from the start of NBR.
*/
@@ -2874,56 +3230,56 @@ enum punit_power_well {
# define TV_VBURST_END_F4_SHIFT 0
#define TV_SC_CTL_1 0x68060
-/** Turns on the first subcarrier phase generation DDA */
+/* Turns on the first subcarrier phase generation DDA */
# define TV_SC_DDA1_EN (1 << 31)
-/** Turns on the first subcarrier phase generation DDA */
+/* Turns on the first subcarrier phase generation DDA */
# define TV_SC_DDA2_EN (1 << 30)
-/** Turns on the first subcarrier phase generation DDA */
+/* Turns on the first subcarrier phase generation DDA */
# define TV_SC_DDA3_EN (1 << 29)
-/** Sets the subcarrier DDA to reset frequency every other field */
+/* Sets the subcarrier DDA to reset frequency every other field */
# define TV_SC_RESET_EVERY_2 (0 << 24)
-/** Sets the subcarrier DDA to reset frequency every fourth field */
+/* Sets the subcarrier DDA to reset frequency every fourth field */
# define TV_SC_RESET_EVERY_4 (1 << 24)
-/** Sets the subcarrier DDA to reset frequency every eighth field */
+/* Sets the subcarrier DDA to reset frequency every eighth field */
# define TV_SC_RESET_EVERY_8 (2 << 24)
-/** Sets the subcarrier DDA to never reset the frequency */
+/* Sets the subcarrier DDA to never reset the frequency */
# define TV_SC_RESET_NEVER (3 << 24)
-/** Sets the peak amplitude of the colorburst.*/
+/* Sets the peak amplitude of the colorburst.*/
# define TV_BURST_LEVEL_MASK 0x00ff0000
# define TV_BURST_LEVEL_SHIFT 16
-/** Sets the increment of the first subcarrier phase generation DDA */
+/* Sets the increment of the first subcarrier phase generation DDA */
# define TV_SCDDA1_INC_MASK 0x00000fff
# define TV_SCDDA1_INC_SHIFT 0
#define TV_SC_CTL_2 0x68064
-/** Sets the rollover for the second subcarrier phase generation DDA */
+/* Sets the rollover for the second subcarrier phase generation DDA */
# define TV_SCDDA2_SIZE_MASK 0x7fff0000
# define TV_SCDDA2_SIZE_SHIFT 16
-/** Sets the increent of the second subcarrier phase generation DDA */
+/* Sets the increent of the second subcarrier phase generation DDA */
# define TV_SCDDA2_INC_MASK 0x00007fff
# define TV_SCDDA2_INC_SHIFT 0
#define TV_SC_CTL_3 0x68068
-/** Sets the rollover for the third subcarrier phase generation DDA */
+/* Sets the rollover for the third subcarrier phase generation DDA */
# define TV_SCDDA3_SIZE_MASK 0x7fff0000
# define TV_SCDDA3_SIZE_SHIFT 16
-/** Sets the increent of the third subcarrier phase generation DDA */
+/* Sets the increent of the third subcarrier phase generation DDA */
# define TV_SCDDA3_INC_MASK 0x00007fff
# define TV_SCDDA3_INC_SHIFT 0
#define TV_WIN_POS 0x68070
-/** X coordinate of the display from the start of horizontal active */
+/* X coordinate of the display from the start of horizontal active */
# define TV_XPOS_MASK 0x1fff0000
# define TV_XPOS_SHIFT 16
-/** Y coordinate of the display from the start of vertical active (NBR) */
+/* Y coordinate of the display from the start of vertical active (NBR) */
# define TV_YPOS_MASK 0x00000fff
# define TV_YPOS_SHIFT 0
#define TV_WIN_SIZE 0x68074
-/** Horizontal size of the display window, measured in pixels*/
+/* Horizontal size of the display window, measured in pixels*/
# define TV_XSIZE_MASK 0x1fff0000
# define TV_XSIZE_SHIFT 16
-/**
+/*
* Vertical size of the display window, measured in pixels.
*
* Must be even for interlaced modes.
@@ -2932,28 +3288,28 @@ enum punit_power_well {
# define TV_YSIZE_SHIFT 0
#define TV_FILTER_CTL_1 0x68080
-/**
+/*
* Enables automatic scaling calculation.
*
* If set, the rest of the registers are ignored, and the calculated values can
* be read back from the register.
*/
# define TV_AUTO_SCALE (1 << 31)
-/**
+/*
* Disables the vertical filter.
*
* This is required on modes more than 1024 pixels wide */
# define TV_V_FILTER_BYPASS (1 << 29)
-/** Enables adaptive vertical filtering */
+/* Enables adaptive vertical filtering */
# define TV_VADAPT (1 << 28)
# define TV_VADAPT_MODE_MASK (3 << 26)
-/** Selects the least adaptive vertical filtering mode */
+/* Selects the least adaptive vertical filtering mode */
# define TV_VADAPT_MODE_LEAST (0 << 26)
-/** Selects the moderately adaptive vertical filtering mode */
+/* Selects the moderately adaptive vertical filtering mode */
# define TV_VADAPT_MODE_MODERATE (1 << 26)
-/** Selects the most adaptive vertical filtering mode */
+/* Selects the most adaptive vertical filtering mode */
# define TV_VADAPT_MODE_MOST (3 << 26)
-/**
+/*
* Sets the horizontal scaling factor.
*
* This should be the fractional part of the horizontal scaling factor divided
@@ -2965,14 +3321,14 @@ enum punit_power_well {
# define TV_HSCALE_FRAC_SHIFT 0
#define TV_FILTER_CTL_2 0x68084
-/**
+/*
* Sets the integer part of the 3.15 fixed-point vertical scaling factor.
*
* TV_VSCALE should be (src height - 1) / ((interlace * dest height) - 1)
*/
# define TV_VSCALE_INT_MASK 0x00038000
# define TV_VSCALE_INT_SHIFT 15
-/**
+/*
* Sets the fractional part of the 3.15 fixed-point vertical scaling factor.
*
* \sa TV_VSCALE_INT_MASK
@@ -2981,7 +3337,7 @@ enum punit_power_well {
# define TV_VSCALE_FRAC_SHIFT 0
#define TV_FILTER_CTL_3 0x68088
-/**
+/*
* Sets the integer part of the 3.15 fixed-point vertical scaling factor.
*
* TV_VSCALE should be (src height - 1) / (1/4 * (dest height - 1))
@@ -2990,7 +3346,7 @@ enum punit_power_well {
*/
# define TV_VSCALE_IP_INT_MASK 0x00038000
# define TV_VSCALE_IP_INT_SHIFT 15
-/**
+/*
* Sets the fractional part of the 3.15 fixed-point vertical scaling factor.
*
* For progressive modes, TV_VSCALE_IP_INT should be set to zeroes.
@@ -3002,26 +3358,26 @@ enum punit_power_well {
#define TV_CC_CONTROL 0x68090
# define TV_CC_ENABLE (1 << 31)
-/**
+/*
* Specifies which field to send the CC data in.
*
* CC data is usually sent in field 0.
*/
# define TV_CC_FID_MASK (1 << 27)
# define TV_CC_FID_SHIFT 27
-/** Sets the horizontal position of the CC data. Usually 135. */
+/* Sets the horizontal position of the CC data. Usually 135. */
# define TV_CC_HOFF_MASK 0x03ff0000
# define TV_CC_HOFF_SHIFT 16
-/** Sets the vertical position of the CC data. Usually 21 */
+/* Sets the vertical position of the CC data. Usually 21 */
# define TV_CC_LINE_MASK 0x0000003f
# define TV_CC_LINE_SHIFT 0
#define TV_CC_DATA 0x68094
# define TV_CC_RDY (1 << 31)
-/** Second word of CC data to be transmitted. */
+/* Second word of CC data to be transmitted. */
# define TV_CC_DATA_2_MASK 0x007f0000
# define TV_CC_DATA_2_SHIFT 16
-/** First word of CC data to be transmitted. */
+/* First word of CC data to be transmitted. */
# define TV_CC_DATA_1_MASK 0x0000007f
# define TV_CC_DATA_1_SHIFT 0
@@ -3043,6 +3399,8 @@ enum punit_power_well {
#define DP_PORT_EN (1 << 31)
#define DP_PIPEB_SELECT (1 << 30)
#define DP_PIPE_MASK (1 << 30)
+#define DP_PIPE_SELECT_CHV(pipe) ((pipe) << 16)
+#define DP_PIPE_MASK_CHV (3 << 16)
/* Link training mode - select a suitable mode for each stage */
#define DP_LINK_TRAIN_PAT_1 (0 << 28)
@@ -3090,32 +3448,32 @@ enum punit_power_well {
#define DP_PLL_FREQ_160MHZ (1 << 16)
#define DP_PLL_FREQ_MASK (3 << 16)
-/** locked once port is enabled */
+/* locked once port is enabled */
#define DP_PORT_REVERSAL (1 << 15)
/* eDP */
#define DP_PLL_ENABLE (1 << 14)
-/** sends the clock on lane 15 of the PEG for debug */
+/* sends the clock on lane 15 of the PEG for debug */
#define DP_CLOCK_OUTPUT_ENABLE (1 << 13)
#define DP_SCRAMBLING_DISABLE (1 << 12)
#define DP_SCRAMBLING_DISABLE_IRONLAKE (1 << 7)
-/** limit RGB values to avoid confusing TVs */
+/* limit RGB values to avoid confusing TVs */
#define DP_COLOR_RANGE_16_235 (1 << 8)
-/** Turn on the audio link */
+/* Turn on the audio link */
#define DP_AUDIO_OUTPUT_ENABLE (1 << 6)
-/** vs and hs sync polarity */
+/* vs and hs sync polarity */
#define DP_SYNC_VS_HIGH (1 << 4)
#define DP_SYNC_HS_HIGH (1 << 3)
-/** A fantasy */
+/* A fantasy */
#define DP_DETECTED (1 << 2)
-/** The aux channel provides a way to talk to the
+/* The aux channel provides a way to talk to the
* signal sink for DDC etc. Max packet size supported
* is 20 bytes in each direction, hence the 5 fixed
* data registers
@@ -3258,6 +3616,7 @@ enum punit_power_well {
#define PIPECONF_INTERLACED_DBL_ILK (4 << 21) /* ilk/snb only */
#define PIPECONF_PFIT_PF_INTERLACED_DBL_ILK (5 << 21) /* ilk/snb only */
#define PIPECONF_INTERLACE_MODE_MASK (7 << 21)
+#define PIPECONF_EDP_RR_MODE_SWITCH (1 << 20)
#define PIPECONF_CXSR_DOWNCLOCK (1<<16)
#define PIPECONF_COLOR_RANGE_SELECT (1 << 13)
#define PIPECONF_BPC_MASK (0x7 << 5)
@@ -3276,6 +3635,7 @@ enum punit_power_well {
#define SPRITE1_FLIP_DONE_INT_EN_VLV (1UL<<30)
#define PIPE_CRC_ERROR_ENABLE (1UL<<29)
#define PIPE_CRC_DONE_ENABLE (1UL<<28)
+#define PERF_COUNTER2_INTERRUPT_EN (1UL<<27)
#define PIPE_GMBUS_EVENT_ENABLE (1UL<<27)
#define PLANE_FLIP_DONE_INT_EN_VLV (1UL<<26)
#define PIPE_HOTPLUG_INTERRUPT_ENABLE (1UL<<26)
@@ -3287,8 +3647,10 @@ enum punit_power_well {
#define PIPE_ODD_FIELD_INTERRUPT_ENABLE (1UL<<21)
#define PIPE_EVEN_FIELD_INTERRUPT_ENABLE (1UL<<20)
#define PIPE_B_PSR_INTERRUPT_ENABLE_VLV (1UL<<19)
+#define PERF_COUNTER_INTERRUPT_EN (1UL<<19)
#define PIPE_HOTPLUG_TV_INTERRUPT_ENABLE (1UL<<18) /* pre-965 */
#define PIPE_START_VBLANK_INTERRUPT_ENABLE (1UL<<18) /* 965 or later */
+#define PIPE_FRAMESTART_INTERRUPT_ENABLE (1UL<<17)
#define PIPE_VBLANK_INTERRUPT_ENABLE (1UL<<17)
#define PIPEA_HBLANK_INT_EN_VLV (1UL<<16)
#define PIPE_OVERLAY_UPDATED_ENABLE (1UL<<16)
@@ -3296,6 +3658,7 @@ enum punit_power_well {
#define SPRITE0_FLIP_DONE_INT_STATUS_VLV (1UL<<14)
#define PIPE_CRC_ERROR_INTERRUPT_STATUS (1UL<<13)
#define PIPE_CRC_DONE_INTERRUPT_STATUS (1UL<<12)
+#define PERF_COUNTER2_INTERRUPT_STATUS (1UL<<11)
#define PIPE_GMBUS_INTERRUPT_STATUS (1UL<<11)
#define PLANE_FLIP_DONE_INT_STATUS_VLV (1UL<<10)
#define PIPE_HOTPLUG_INTERRUPT_STATUS (1UL<<10)
@@ -3304,20 +3667,25 @@ enum punit_power_well {
#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)
#define PIPE_EVEN_FIELD_INTERRUPT_STATUS (1UL<<4)
#define PIPE_B_PSR_STATUS_VLV (1UL<<3)
+#define PERF_COUNTER_INTERRUPT_STATUS (1UL<<3)
#define PIPE_HOTPLUG_TV_INTERRUPT_STATUS (1UL<<2) /* pre-965 */
#define PIPE_START_VBLANK_INTERRUPT_STATUS (1UL<<2) /* 965 or later */
+#define PIPE_FRAMESTART_INTERRUPT_STATUS (1UL<<1)
#define PIPE_VBLANK_INTERRUPT_STATUS (1UL<<1)
+#define PIPE_HBLANK_INT_STATUS (1UL<<0)
#define PIPE_OVERLAY_UPDATED_STATUS (1UL<<0)
#define PIPESTAT_INT_ENABLE_MASK 0x7fff0000
#define PIPESTAT_INT_STATUS_MASK 0x0000ffff
-#define PIPE_A_OFFSET 0x70000
-#define PIPE_B_OFFSET 0x71000
-#define PIPE_C_OFFSET 0x72000
+#define PIPE_A_OFFSET 0x70000
+#define PIPE_B_OFFSET 0x71000
+#define PIPE_C_OFFSET 0x72000
+#define CHV_PIPE_C_OFFSET 0x74000
/*
* There's actually no pipe EDP. Some pipe registers have
* simply shifted from the pipe to the transcoder, while
@@ -3355,14 +3723,25 @@ enum punit_power_well {
#define SPRITED_FLIP_DONE_INT_EN (1<<26)
#define SPRITEC_FLIP_DONE_INT_EN (1<<25)
#define PLANEB_FLIP_DONE_INT_EN (1<<24)
+#define PIPE_PSR_INT_EN (1<<22)
#define PIPEA_LINE_COMPARE_INT_EN (1<<21)
#define PIPEA_HLINE_INT_EN (1<<20)
#define PIPEA_VBLANK_INT_EN (1<<19)
#define SPRITEB_FLIP_DONE_INT_EN (1<<18)
#define SPRITEA_FLIP_DONE_INT_EN (1<<17)
#define PLANEA_FLIPDONE_INT_EN (1<<16)
-
-#define DPINVGTT (VLV_DISPLAY_BASE + 0x7002c) /* VLV only */
+#define PIPEC_LINE_COMPARE_INT_EN (1<<13)
+#define PIPEC_HLINE_INT_EN (1<<12)
+#define PIPEC_VBLANK_INT_EN (1<<11)
+#define SPRITEF_FLIPDONE_INT_EN (1<<10)
+#define SPRITEE_FLIPDONE_INT_EN (1<<9)
+#define PLANEC_FLIPDONE_INT_EN (1<<8)
+
+#define DPINVGTT (VLV_DISPLAY_BASE + 0x7002c) /* VLV/CHV only */
+#define SPRITEF_INVALID_GTT_INT_EN (1<<27)
+#define SPRITEE_INVALID_GTT_INT_EN (1<<26)
+#define PLANEC_INVALID_GTT_INT_EN (1<<25)
+#define CURSORC_INVALID_GTT_INT_EN (1<<24)
#define CURSORB_INVALID_GTT_INT_EN (1<<23)
#define CURSORA_INVALID_GTT_INT_EN (1<<22)
#define SPRITED_INVALID_GTT_INT_EN (1<<21)
@@ -3372,6 +3751,11 @@ enum punit_power_well {
#define SPRITEA_INVALID_GTT_INT_EN (1<<17)
#define PLANEA_INVALID_GTT_INT_EN (1<<16)
#define DPINVGTT_EN_MASK 0xff0000
+#define DPINVGTT_EN_MASK_CHV 0xfff0000
+#define SPRITEF_INVALID_GTT_STATUS (1<<11)
+#define SPRITEE_INVALID_GTT_STATUS (1<<10)
+#define PLANEC_INVALID_GTT_STATUS (1<<9)
+#define CURSORC_INVALID_GTT_STATUS (1<<8)
#define CURSORB_INVALID_GTT_STATUS (1<<7)
#define CURSORA_INVALID_GTT_STATUS (1<<6)
#define SPRITED_INVALID_GTT_STATUS (1<<5)
@@ -3381,6 +3765,7 @@ enum punit_power_well {
#define SPRITEA_INVALID_GTT_STATUS (1<<1)
#define PLANEA_INVALID_GTT_STATUS (1<<0)
#define DPINVGTT_STATUS_MASK 0xff
+#define DPINVGTT_STATUS_MASK_CHV 0xfff
#define DSPARB 0x70030
#define DSPARB_CSTART_MASK (0x7f << 7)
@@ -3420,14 +3805,43 @@ enum punit_power_well {
#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
/* FIFO watermark sizes etc */
#define G4X_FIFO_LINE_SIZE 64
@@ -3535,12 +3949,13 @@ enum punit_power_well {
#define PIPE_PIXEL_MASK 0x00ffffff
#define PIPE_PIXEL_SHIFT 0
/* GM45+ just has to be different */
-#define _PIPEA_FRMCOUNT_GM45 (dev_priv->info.display_mmio_offset + 0x70040)
-#define _PIPEA_FLIPCOUNT_GM45 (dev_priv->info.display_mmio_offset + 0x70044)
-#define PIPE_FRMCOUNT_GM45(pipe) _PIPE(pipe, _PIPEA_FRMCOUNT_GM45, _PIPEB_FRMCOUNT_GM45)
+#define _PIPEA_FRMCOUNT_GM45 0x70040
+#define _PIPEA_FLIPCOUNT_GM45 0x70044
+#define PIPE_FRMCOUNT_GM45(pipe) _PIPE2(pipe, _PIPEA_FRMCOUNT_GM45)
+#define PIPE_FLIPCOUNT_GM45(pipe) _PIPE2(pipe, _PIPEA_FLIPCOUNT_GM45)
/* Cursor A & B regs */
-#define _CURACNTR (dev_priv->info.display_mmio_offset + 0x70080)
+#define _CURACNTR 0x70080
/* Old style CUR*CNTR flags (desktop 8xx) */
#define CURSOR_ENABLE 0x80000000
#define CURSOR_GAMMA_ENABLE 0x40000000
@@ -3567,28 +3982,34 @@ enum punit_power_well {
#define MCURSOR_PIPE_B (1 << 28)
#define MCURSOR_GAMMA_ENABLE (1 << 26)
#define CURSOR_TRICKLE_FEED_DISABLE (1 << 14)
-#define _CURABASE (dev_priv->info.display_mmio_offset + 0x70084)
-#define _CURAPOS (dev_priv->info.display_mmio_offset + 0x70088)
+#define _CURABASE 0x70084
+#define _CURAPOS 0x70088
#define CURSOR_POS_MASK 0x007FF
#define CURSOR_POS_SIGN 0x8000
#define CURSOR_X_SHIFT 0
#define CURSOR_Y_SHIFT 16
#define CURSIZE 0x700a0
-#define _CURBCNTR (dev_priv->info.display_mmio_offset + 0x700c0)
-#define _CURBBASE (dev_priv->info.display_mmio_offset + 0x700c4)
-#define _CURBPOS (dev_priv->info.display_mmio_offset + 0x700c8)
+#define _CURBCNTR 0x700c0
+#define _CURBBASE 0x700c4
+#define _CURBPOS 0x700c8
#define _CURBCNTR_IVB 0x71080
#define _CURBBASE_IVB 0x71084
#define _CURBPOS_IVB 0x71088
-#define CURCNTR(pipe) _PIPE(pipe, _CURACNTR, _CURBCNTR)
-#define CURBASE(pipe) _PIPE(pipe, _CURABASE, _CURBBASE)
-#define CURPOS(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS)
+#define _CURSOR2(pipe, reg) (dev_priv->info.cursor_offsets[(pipe)] - \
+ dev_priv->info.cursor_offsets[PIPE_A] + (reg) + \
+ dev_priv->info.display_mmio_offset)
+
+#define CURCNTR(pipe) _CURSOR2(pipe, _CURACNTR)
+#define CURBASE(pipe) _CURSOR2(pipe, _CURABASE)
+#define CURPOS(pipe) _CURSOR2(pipe, _CURAPOS)
-#define CURCNTR_IVB(pipe) _PIPE(pipe, _CURACNTR, _CURBCNTR_IVB)
-#define CURBASE_IVB(pipe) _PIPE(pipe, _CURABASE, _CURBBASE_IVB)
-#define CURPOS_IVB(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS_IVB)
+#define CURSOR_A_OFFSET 0x70080
+#define CURSOR_B_OFFSET 0x700c0
+#define CHV_CURSOR_C_OFFSET 0x700e0
+#define IVB_CURSOR_B_OFFSET 0x71080
+#define IVB_CURSOR_C_OFFSET 0x72080
/* Display A control */
#define _DSPACNTR 0x70180
@@ -4093,6 +4514,7 @@ enum punit_power_well {
#define GEN8_DE_PIPE_A_IRQ (1<<16)
#define GEN8_DE_PIPE_IRQ(pipe) (1<<(16+pipe))
#define GEN8_GT_VECS_IRQ (1<<6)
+#define GEN8_GT_PM_IRQ (1<<4)
#define GEN8_GT_VCS2_IRQ (1<<3)
#define GEN8_GT_VCS1_IRQ (1<<2)
#define GEN8_GT_BCS_IRQ (1<<1)
@@ -4120,7 +4542,7 @@ enum punit_power_well {
#define GEN8_PIPE_SPRITE_FAULT (1 << 9)
#define GEN8_PIPE_PRIMARY_FAULT (1 << 8)
#define GEN8_PIPE_SPRITE_FLIP_DONE (1 << 5)
-#define GEN8_PIPE_FLIP_DONE (1 << 4)
+#define GEN8_PIPE_PRIMARY_FLIP_DONE (1 << 4)
#define GEN8_PIPE_SCAN_LINE_EVENT (1 << 2)
#define GEN8_PIPE_VSYNC (1 << 1)
#define GEN8_PIPE_VBLANK (1 << 0)
@@ -4832,6 +5254,8 @@ enum punit_power_well {
#define PORT_TRANS_SEL_CPT(pipe) ((pipe) << 29)
#define PORT_TO_PIPE(val) (((val) & (1<<30)) >> 30)
#define PORT_TO_PIPE_CPT(val) (((val) & PORT_TRANS_SEL_MASK) >> 29)
+#define SDVO_PORT_TO_PIPE_CHV(val) (((val) & (3<<24)) >> 24)
+#define DP_PORT_TO_PIPE_CHV(val) (((val) & (3<<16)) >> 16)
#define TRANS_DP_CTL_A 0xe0300
#define TRANS_DP_CTL_B 0xe1300
@@ -4888,6 +5312,8 @@ enum punit_power_well {
#define EDP_LINK_TRAIN_VOL_EMP_MASK_IVB (0x3f<<22)
+#define VLV_PMWGICZ 0x1300a4
+
#define FORCEWAKE 0xA18C
#define FORCEWAKE_VLV 0x1300b0
#define FORCEWAKE_ACK_VLV 0x1300b4
@@ -4896,15 +5322,22 @@ enum punit_power_well {
#define FORCEWAKE_ACK_HSW 0x130044
#define FORCEWAKE_ACK 0x130090
#define VLV_GTLC_WAKE_CTRL 0x130090
+#define VLV_GTLC_RENDER_CTX_EXISTS (1 << 25)
+#define VLV_GTLC_MEDIA_CTX_EXISTS (1 << 24)
+#define VLV_GTLC_ALLOWWAKEREQ (1 << 0)
+
#define VLV_GTLC_PW_STATUS 0x130094
-#define VLV_GTLC_PW_RENDER_STATUS_MASK 0x80
-#define VLV_GTLC_PW_MEDIA_STATUS_MASK 0x20
+#define VLV_GTLC_ALLOWWAKEACK (1 << 0)
+#define VLV_GTLC_ALLOWWAKEERR (1 << 1)
+#define VLV_GTLC_PW_MEDIA_STATUS_MASK (1 << 5)
+#define VLV_GTLC_PW_RENDER_STATUS_MASK (1 << 7)
#define FORCEWAKE_MT 0xa188 /* multi-threaded */
#define FORCEWAKE_KERNEL 0x1
#define FORCEWAKE_USER 0x2
#define FORCEWAKE_MT_ACK 0x130040
#define ECOBUS 0xa180
#define FORCEWAKE_MT_ENABLE (1<<5)
+#define VLV_SPAREG2H 0xA194
#define GTFIFODBG 0x120000
#define GT_FIFO_SBDROPERR (1<<6)
@@ -4924,6 +5357,7 @@ enum punit_power_well {
#define HSW_EDRAM_PRESENT 0x120010
#define GEN6_UCGCTL1 0x9400
+# define GEN6_EU_TCUNIT_CLOCK_GATE_DISABLE (1 << 16)
# define GEN6_BLBUNIT_CLOCK_GATE_DISABLE (1 << 5)
# define GEN6_CSUNIT_CLOCK_GATE_DISABLE (1 << 7)
@@ -4934,12 +5368,19 @@ enum punit_power_well {
# define GEN6_RCPBUNIT_CLOCK_GATE_DISABLE (1 << 12)
# define GEN6_RCCUNIT_CLOCK_GATE_DISABLE (1 << 11)
+#define GEN6_UCGCTL3 0x9408
+
#define GEN7_UCGCTL4 0x940c
#define GEN7_L3BANK2X_CLOCK_GATE_DISABLE (1<<25)
+#define GEN6_RCGCTL1 0x9410
+#define GEN6_RCGCTL2 0x9414
+#define GEN6_RSTCTL 0x9420
+
#define GEN8_UCGCTL6 0x9430
#define GEN8_SDEUNIT_CLOCK_GATE_DISABLE (1<<14)
+#define GEN6_GFXPAUSE 0xA000
#define GEN6_RPNSWREQ 0xA008
#define GEN6_TURBO_DISABLE (1<<31)
#define GEN6_FREQUENCY(x) ((x)<<25)
@@ -4992,6 +5433,9 @@ enum punit_power_well {
#define GEN6_RP_UP_EI 0xA068
#define GEN6_RP_DOWN_EI 0xA06C
#define GEN6_RP_IDLE_HYSTERSIS 0xA070
+#define GEN6_RPDEUHWTC 0xA080
+#define GEN6_RPDEUC 0xA084
+#define GEN6_RPDEUCSW 0xA088
#define GEN6_RC_STATE 0xA094
#define GEN6_RC1_WAKE_RATE_LIMIT 0xA098
#define GEN6_RC6_WAKE_RATE_LIMIT 0xA09C
@@ -4999,11 +5443,15 @@ enum punit_power_well {
#define GEN6_RC_EVALUATION_INTERVAL 0xA0A8
#define GEN6_RC_IDLE_HYSTERSIS 0xA0AC
#define GEN6_RC_SLEEP 0xA0B0
+#define GEN6_RCUBMABDTMR 0xA0B0
#define GEN6_RC1e_THRESHOLD 0xA0B4
#define GEN6_RC6_THRESHOLD 0xA0B8
#define GEN6_RC6p_THRESHOLD 0xA0BC
+#define VLV_RCEDATA 0xA0BC
#define GEN6_RC6pp_THRESHOLD 0xA0C0
#define GEN6_PMINTRMSK 0xA168
+#define GEN8_PMINTR_REDIRECT_TO_NON_DISP (1<<31)
+#define VLV_PWRDWNUPCTL 0xA294
#define GEN6_PMISR 0x44020
#define GEN6_PMIMR 0x44024 /* rps_lock */
@@ -5020,6 +5468,9 @@ enum punit_power_well {
GEN6_PM_RP_DOWN_THRESHOLD | \
GEN6_PM_RP_DOWN_TIMEOUT)
+#define GEN7_GT_SCRATCH_BASE 0x4F100
+#define GEN7_GT_SCRATCH_REG_NUM 8
+
#define VLV_GTLC_SURVIVABILITY_REG 0x130098
#define VLV_GFX_CLK_STATUS_BIT (1<<3)
#define VLV_GFX_CLK_FORCE_ON_BIT (1<<2)
@@ -5030,6 +5481,9 @@ enum punit_power_well {
#define VLV_MEDIA_RC6_COUNT_EN (1<<1)
#define VLV_RENDER_RC6_COUNT_EN (1<<0)
#define GEN6_GT_GFX_RC6 0x138108
+#define VLV_GT_RENDER_RC6 0x138108
+#define VLV_GT_MEDIA_RC6 0x13810C
+
#define GEN6_GT_GFX_RC6p 0x13810C
#define GEN6_GT_GFX_RC6pp 0x138110
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index 56785e8fb2eb..043123c77a1f 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -328,8 +328,6 @@ int i915_save_state(struct drm_device *dev)
}
}
- intel_disable_gt_powersave(dev);
-
/* Cache mode state */
if (INTEL_INFO(dev)->gen < 7)
dev_priv->regfile.saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
index 9c57029f6f4b..86ce39aad0ff 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -186,7 +186,7 @@ i915_l3_write(struct file *filp, struct kobject *kobj,
struct drm_minor *dminor = dev_to_drm_minor(dev);
struct drm_device *drm_dev = dminor->dev;
struct drm_i915_private *dev_priv = drm_dev->dev_private;
- struct i915_hw_context *ctx;
+ struct intel_context *ctx;
u32 *temp = NULL; /* Just here to make handling failures easy */
int slice = (int)(uintptr_t)attr->private;
int ret;
@@ -263,6 +263,8 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev,
flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+ intel_runtime_pm_get(dev_priv);
+
mutex_lock(&dev_priv->rps.hw_lock);
if (IS_VALLEYVIEW(dev_priv->dev)) {
u32 freq;
@@ -273,6 +275,8 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev,
}
mutex_unlock(&dev_priv->rps.hw_lock);
+ intel_runtime_pm_put(dev_priv);
+
return snprintf(buf, PAGE_SIZE, "%d\n", ret);
}
diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h
index 23c26f1f8b37..f5aa0067755a 100644
--- a/drivers/gpu/drm/i915/i915_trace.h
+++ b/drivers/gpu/drm/i915/i915_trace.h
@@ -7,6 +7,7 @@
#include <drm/drmP.h>
#include "i915_drv.h"
+#include "intel_drv.h"
#include "intel_ringbuffer.h"
#undef TRACE_SYSTEM
@@ -14,6 +15,80 @@
#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM)
#define TRACE_INCLUDE_FILE i915_trace
+/* pipe updates */
+
+TRACE_EVENT(i915_pipe_update_start,
+ TP_PROTO(struct intel_crtc *crtc, u32 min, u32 max),
+ TP_ARGS(crtc, min, max),
+
+ TP_STRUCT__entry(
+ __field(enum pipe, pipe)
+ __field(u32, frame)
+ __field(u32, scanline)
+ __field(u32, min)
+ __field(u32, max)
+ ),
+
+ TP_fast_assign(
+ __entry->pipe = crtc->pipe;
+ __entry->frame = crtc->base.dev->driver->get_vblank_counter(crtc->base.dev,
+ crtc->pipe);
+ __entry->scanline = intel_get_crtc_scanline(crtc);
+ __entry->min = min;
+ __entry->max = max;
+ ),
+
+ TP_printk("pipe %c, frame=%u, scanline=%u, min=%u, max=%u",
+ pipe_name(__entry->pipe), __entry->frame,
+ __entry->scanline, __entry->min, __entry->max)
+);
+
+TRACE_EVENT(i915_pipe_update_vblank_evaded,
+ TP_PROTO(struct intel_crtc *crtc, u32 min, u32 max, u32 frame),
+ TP_ARGS(crtc, min, max, frame),
+
+ TP_STRUCT__entry(
+ __field(enum pipe, pipe)
+ __field(u32, frame)
+ __field(u32, scanline)
+ __field(u32, min)
+ __field(u32, max)
+ ),
+
+ TP_fast_assign(
+ __entry->pipe = crtc->pipe;
+ __entry->frame = frame;
+ __entry->scanline = intel_get_crtc_scanline(crtc);
+ __entry->min = min;
+ __entry->max = max;
+ ),
+
+ TP_printk("pipe %c, frame=%u, scanline=%u, min=%u, max=%u",
+ pipe_name(__entry->pipe), __entry->frame,
+ __entry->scanline, __entry->min, __entry->max)
+);
+
+TRACE_EVENT(i915_pipe_update_end,
+ TP_PROTO(struct intel_crtc *crtc, u32 frame),
+ TP_ARGS(crtc, frame),
+
+ TP_STRUCT__entry(
+ __field(enum pipe, pipe)
+ __field(u32, frame)
+ __field(u32, scanline)
+ ),
+
+ TP_fast_assign(
+ __entry->pipe = crtc->pipe;
+ __entry->frame = frame;
+ __entry->scanline = intel_get_crtc_scanline(crtc);
+ ),
+
+ TP_printk("pipe %c, frame=%u, scanline=%u",
+ pipe_name(__entry->pipe), __entry->frame,
+ __entry->scanline)
+);
+
/* object tracking */
TRACE_EVENT(i915_gem_object_create,
@@ -251,8 +326,8 @@ TRACE_EVENT(i915_gem_evict_vm,
);
TRACE_EVENT(i915_gem_ring_sync_to,
- TP_PROTO(struct intel_ring_buffer *from,
- struct intel_ring_buffer *to,
+ TP_PROTO(struct intel_engine_cs *from,
+ struct intel_engine_cs *to,
u32 seqno),
TP_ARGS(from, to, seqno),
@@ -277,7 +352,7 @@ TRACE_EVENT(i915_gem_ring_sync_to,
);
TRACE_EVENT(i915_gem_ring_dispatch,
- TP_PROTO(struct intel_ring_buffer *ring, u32 seqno, u32 flags),
+ TP_PROTO(struct intel_engine_cs *ring, u32 seqno, u32 flags),
TP_ARGS(ring, seqno, flags),
TP_STRUCT__entry(
@@ -300,7 +375,7 @@ TRACE_EVENT(i915_gem_ring_dispatch,
);
TRACE_EVENT(i915_gem_ring_flush,
- TP_PROTO(struct intel_ring_buffer *ring, u32 invalidate, u32 flush),
+ TP_PROTO(struct intel_engine_cs *ring, u32 invalidate, u32 flush),
TP_ARGS(ring, invalidate, flush),
TP_STRUCT__entry(
@@ -323,7 +398,7 @@ TRACE_EVENT(i915_gem_ring_flush,
);
DECLARE_EVENT_CLASS(i915_gem_request,
- TP_PROTO(struct intel_ring_buffer *ring, u32 seqno),
+ TP_PROTO(struct intel_engine_cs *ring, u32 seqno),
TP_ARGS(ring, seqno),
TP_STRUCT__entry(
@@ -343,12 +418,12 @@ DECLARE_EVENT_CLASS(i915_gem_request,
);
DEFINE_EVENT(i915_gem_request, i915_gem_request_add,
- TP_PROTO(struct intel_ring_buffer *ring, u32 seqno),
+ TP_PROTO(struct intel_engine_cs *ring, u32 seqno),
TP_ARGS(ring, seqno)
);
TRACE_EVENT(i915_gem_request_complete,
- TP_PROTO(struct intel_ring_buffer *ring),
+ TP_PROTO(struct intel_engine_cs *ring),
TP_ARGS(ring),
TP_STRUCT__entry(
@@ -368,12 +443,12 @@ TRACE_EVENT(i915_gem_request_complete,
);
DEFINE_EVENT(i915_gem_request, i915_gem_request_retire,
- TP_PROTO(struct intel_ring_buffer *ring, u32 seqno),
+ TP_PROTO(struct intel_engine_cs *ring, u32 seqno),
TP_ARGS(ring, seqno)
);
TRACE_EVENT(i915_gem_request_wait_begin,
- TP_PROTO(struct intel_ring_buffer *ring, u32 seqno),
+ TP_PROTO(struct intel_engine_cs *ring, u32 seqno),
TP_ARGS(ring, seqno),
TP_STRUCT__entry(
@@ -402,12 +477,12 @@ TRACE_EVENT(i915_gem_request_wait_begin,
);
DEFINE_EVENT(i915_gem_request, i915_gem_request_wait_end,
- TP_PROTO(struct intel_ring_buffer *ring, u32 seqno),
+ TP_PROTO(struct intel_engine_cs *ring, u32 seqno),
TP_ARGS(ring, seqno)
);
DECLARE_EVENT_CLASS(i915_ring,
- TP_PROTO(struct intel_ring_buffer *ring),
+ TP_PROTO(struct intel_engine_cs *ring),
TP_ARGS(ring),
TP_STRUCT__entry(
@@ -424,12 +499,12 @@ DECLARE_EVENT_CLASS(i915_ring,
);
DEFINE_EVENT(i915_ring, i915_ring_wait_begin,
- TP_PROTO(struct intel_ring_buffer *ring),
+ TP_PROTO(struct intel_engine_cs *ring),
TP_ARGS(ring)
);
DEFINE_EVENT(i915_ring, i915_ring_wait_end,
- TP_PROTO(struct intel_ring_buffer *ring),
+ TP_PROTO(struct intel_engine_cs *ring),
TP_ARGS(ring)
);
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index aff4a113cda3..1ee98f121a00 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -49,13 +49,19 @@ find_section(struct bdb_header *bdb, int section_id)
total = bdb->bdb_size;
/* walk the sections looking for section_id */
- while (index < total) {
+ while (index + 3 < total) {
current_id = *(base + index);
index++;
+
current_size = *((u16 *)(base + index));
index += 2;
+
+ if (index + current_size > total)
+ return NULL;
+
if (current_id == section_id)
return base + index;
+
index += current_size;
}
@@ -206,7 +212,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
const struct lvds_dvo_timing *panel_dvo_timing;
const struct lvds_fp_timing *fp_timing;
struct drm_display_mode *panel_fixed_mode;
- int i, downclock;
+ int i, downclock, drrs_mode;
lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
if (!lvds_options)
@@ -218,6 +224,28 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
panel_type = lvds_options->panel_type;
+ drrs_mode = (lvds_options->dps_panel_type_bits
+ >> (panel_type * 2)) & MODE_MASK;
+ /*
+ * VBT has static DRRS = 0 and seamless DRRS = 2.
+ * The below piece of code is required to adjust vbt.drrs_type
+ * to match the enum drrs_support_type.
+ */
+ switch (drrs_mode) {
+ case 0:
+ dev_priv->vbt.drrs_type = STATIC_DRRS_SUPPORT;
+ DRM_DEBUG_KMS("DRRS supported mode is static\n");
+ break;
+ case 2:
+ dev_priv->vbt.drrs_type = SEAMLESS_DRRS_SUPPORT;
+ DRM_DEBUG_KMS("DRRS supported mode is seamless\n");
+ break;
+ default:
+ dev_priv->vbt.drrs_type = DRRS_NOT_SUPPORTED;
+ DRM_DEBUG_KMS("DRRS not supported (VBT input)\n");
+ break;
+ }
+
lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
if (!lvds_lfp_data)
return;
@@ -526,6 +554,16 @@ parse_driver_features(struct drm_i915_private *dev_priv,
if (driver->dual_frequency)
dev_priv->render_reclock_avail = true;
+
+ DRM_DEBUG_KMS("DRRS State Enabled:%d\n", driver->drrs_enabled);
+ /*
+ * If DRRS is not supported, drrs_type has to be set to 0.
+ * This is because, VBT is configured in such a way that
+ * static DRRS is 0 and DRRS not supported is represented by
+ * driver->drrs_enabled=false
+ */
+ if (!driver->drrs_enabled)
+ dev_priv->vbt.drrs_type = DRRS_NOT_SUPPORTED;
}
static void
@@ -628,19 +666,221 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
}
}
+static u8 *goto_next_sequence(u8 *data, int *size)
+{
+ u16 len;
+ int tmp = *size;
+
+ if (--tmp < 0)
+ return NULL;
+
+ /* goto first element */
+ data++;
+ while (1) {
+ switch (*data) {
+ case MIPI_SEQ_ELEM_SEND_PKT:
+ /*
+ * skip by this element payload size
+ * skip elem id, command flag and data type
+ */
+ tmp -= 5;
+ if (tmp < 0)
+ return NULL;
+
+ data += 3;
+ len = *((u16 *)data);
+
+ tmp -= len;
+ if (tmp < 0)
+ return NULL;
+
+ /* skip by len */
+ data = data + 2 + len;
+ break;
+ case MIPI_SEQ_ELEM_DELAY:
+ /* skip by elem id, and delay is 4 bytes */
+ tmp -= 5;
+ if (tmp < 0)
+ return NULL;
+
+ data += 5;
+ break;
+ case MIPI_SEQ_ELEM_GPIO:
+ tmp -= 3;
+ if (tmp < 0)
+ return NULL;
+
+ data += 3;
+ break;
+ default:
+ DRM_ERROR("Unknown element\n");
+ return NULL;
+ }
+
+ /* end of sequence ? */
+ if (*data == 0)
+ break;
+ }
+
+ /* goto next sequence or end of block byte */
+ if (--tmp < 0)
+ return NULL;
+
+ data++;
+
+ /* update amount of data left for the sequence block to be parsed */
+ *size = tmp;
+ return data;
+}
+
static void
parse_mipi(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
{
- struct bdb_mipi *mipi;
+ struct bdb_mipi_config *start;
+ struct bdb_mipi_sequence *sequence;
+ struct mipi_config *config;
+ struct mipi_pps_data *pps;
+ u8 *data, *seq_data;
+ int i, panel_id, seq_size;
+ u16 block_size;
+
+ /* parse MIPI blocks only if LFP type is MIPI */
+ if (!dev_priv->vbt.has_mipi)
+ return;
+
+ /* Initialize this to undefined indicating no generic MIPI support */
+ dev_priv->vbt.dsi.panel_id = MIPI_DSI_UNDEFINED_PANEL_ID;
+
+ /* Block #40 is already parsed and panel_fixed_mode is
+ * stored in dev_priv->lfp_lvds_vbt_mode
+ * resuse this when needed
+ */
+
+ /* Parse #52 for panel index used from panel_type already
+ * parsed
+ */
+ start = find_section(bdb, BDB_MIPI_CONFIG);
+ if (!start) {
+ DRM_DEBUG_KMS("No MIPI config BDB found");
+ return;
+ }
+
+ DRM_DEBUG_DRIVER("Found MIPI Config block, panel index = %d\n",
+ panel_type);
- mipi = find_section(bdb, BDB_MIPI_CONFIG);
- if (!mipi) {
- DRM_DEBUG_KMS("No MIPI BDB found");
+ /*
+ * get hold of the correct configuration block and pps data as per
+ * the panel_type as index
+ */
+ config = &start->config[panel_type];
+ pps = &start->pps[panel_type];
+
+ /* store as of now full data. Trim when we realise all is not needed */
+ dev_priv->vbt.dsi.config = kmemdup(config, sizeof(struct mipi_config), GFP_KERNEL);
+ if (!dev_priv->vbt.dsi.config)
+ return;
+
+ dev_priv->vbt.dsi.pps = kmemdup(pps, sizeof(struct mipi_pps_data), GFP_KERNEL);
+ if (!dev_priv->vbt.dsi.pps) {
+ kfree(dev_priv->vbt.dsi.config);
return;
}
- /* XXX: add more info */
+ /* We have mandatory mipi config blocks. Initialize as generic panel */
dev_priv->vbt.dsi.panel_id = MIPI_DSI_GENERIC_PANEL_ID;
+
+ /* Check if we have sequence block as well */
+ sequence = find_section(bdb, BDB_MIPI_SEQUENCE);
+ if (!sequence) {
+ DRM_DEBUG_KMS("No MIPI Sequence found, parsing complete\n");
+ return;
+ }
+
+ DRM_DEBUG_DRIVER("Found MIPI sequence block\n");
+
+ block_size = get_blocksize(sequence);
+
+ /*
+ * parse the sequence block for individual sequences
+ */
+ dev_priv->vbt.dsi.seq_version = sequence->version;
+
+ seq_data = &sequence->data[0];
+
+ /*
+ * sequence block is variable length and hence we need to parse and
+ * get the sequence data for specific panel id
+ */
+ for (i = 0; i < MAX_MIPI_CONFIGURATIONS; i++) {
+ panel_id = *seq_data;
+ seq_size = *((u16 *) (seq_data + 1));
+ if (panel_id == panel_type)
+ break;
+
+ /* skip the sequence including seq header of 3 bytes */
+ seq_data = seq_data + 3 + seq_size;
+ if ((seq_data - &sequence->data[0]) > block_size) {
+ DRM_ERROR("Sequence start is beyond sequence block size, corrupted sequence block\n");
+ return;
+ }
+ }
+
+ if (i == MAX_MIPI_CONFIGURATIONS) {
+ DRM_ERROR("Sequence block detected but no valid configuration\n");
+ return;
+ }
+
+ /* check if found sequence is completely within the sequence block
+ * just being paranoid */
+ if (seq_size > block_size) {
+ DRM_ERROR("Corrupted sequence/size, bailing out\n");
+ return;
+ }
+
+ /* skip the panel id(1 byte) and seq size(2 bytes) */
+ dev_priv->vbt.dsi.data = kmemdup(seq_data + 3, seq_size, GFP_KERNEL);
+ if (!dev_priv->vbt.dsi.data)
+ return;
+
+ /*
+ * loop into the sequence data and split into multiple sequneces
+ * There are only 5 types of sequences as of now
+ */
+ data = dev_priv->vbt.dsi.data;
+ dev_priv->vbt.dsi.size = seq_size;
+
+ /* two consecutive 0x00 indicate end of all sequences */
+ while (1) {
+ int seq_id = *data;
+ if (MIPI_SEQ_MAX > seq_id && seq_id > MIPI_SEQ_UNDEFINED) {
+ dev_priv->vbt.dsi.sequence[seq_id] = data;
+ DRM_DEBUG_DRIVER("Found mipi sequence - %d\n", seq_id);
+ } else {
+ DRM_ERROR("undefined sequence\n");
+ goto err;
+ }
+
+ /* partial parsing to skip elements */
+ data = goto_next_sequence(data, &seq_size);
+
+ if (data == NULL) {
+ DRM_ERROR("Sequence elements going beyond block itself. Sequence block parsing failed\n");
+ goto err;
+ }
+
+ if (*data == 0)
+ break; /* end of sequence reached */
+ }
+
+ DRM_DEBUG_DRIVER("MIPI related vbt parsing complete\n");
+ return;
+err:
+ kfree(dev_priv->vbt.dsi.data);
+ dev_priv->vbt.dsi.data = NULL;
+
+ /* error during parsing so set all pointers to null
+ * because of partial parsing */
+ memset(dev_priv->vbt.dsi.sequence, 0, MIPI_SEQ_MAX);
}
static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,
@@ -823,6 +1063,15 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
/* skip the device block if device type is invalid */
continue;
}
+
+ if (p_child->common.dvo_port >= DVO_PORT_MIPIA
+ && p_child->common.dvo_port <= DVO_PORT_MIPID
+ &&p_child->common.device_type & DEVICE_TYPE_MIPI_OUTPUT) {
+ DRM_DEBUG_KMS("Found MIPI as LFP\n");
+ dev_priv->vbt.has_mipi = 1;
+ dev_priv->vbt.dsi.port = p_child->common.dvo_port;
+ }
+
child_dev_ptr = dev_priv->vbt.child_dev + count;
count++;
memcpy((void *)child_dev_ptr, (void *)p_child,
@@ -893,6 +1142,46 @@ static const struct dmi_system_id intel_no_opregion_vbt[] = {
{ }
};
+static struct bdb_header *validate_vbt(char *base, size_t size,
+ struct vbt_header *vbt,
+ const char *source)
+{
+ size_t offset;
+ struct bdb_header *bdb;
+
+ if (vbt == NULL) {
+ DRM_DEBUG_DRIVER("VBT signature missing\n");
+ return NULL;
+ }
+
+ offset = (char *)vbt - base;
+ if (offset + sizeof(struct vbt_header) > size) {
+ DRM_DEBUG_DRIVER("VBT header incomplete\n");
+ return NULL;
+ }
+
+ if (memcmp(vbt->signature, "$VBT", 4)) {
+ DRM_DEBUG_DRIVER("VBT invalid signature\n");
+ return NULL;
+ }
+
+ offset += vbt->bdb_offset;
+ if (offset + sizeof(struct bdb_header) > size) {
+ DRM_DEBUG_DRIVER("BDB header incomplete\n");
+ return NULL;
+ }
+
+ bdb = (struct bdb_header *)(base + offset);
+ if (offset + bdb->bdb_size > size) {
+ DRM_DEBUG_DRIVER("BDB incomplete\n");
+ return NULL;
+ }
+
+ DRM_DEBUG_KMS("Using VBT from %s: %20s\n",
+ source, vbt->signature);
+ return bdb;
+}
+
/**
* intel_parse_bios - find VBT and initialize settings from the BIOS
* @dev: DRM device
@@ -916,20 +1205,13 @@ intel_parse_bios(struct drm_device *dev)
init_vbt_defaults(dev_priv);
/* XXX Should this validation be moved to intel_opregion.c? */
- if (!dmi_check_system(intel_no_opregion_vbt) && dev_priv->opregion.vbt) {
- struct vbt_header *vbt = dev_priv->opregion.vbt;
- if (memcmp(vbt->signature, "$VBT", 4) == 0) {
- DRM_DEBUG_KMS("Using VBT from OpRegion: %20s\n",
- vbt->signature);
- bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset);
- } else
- dev_priv->opregion.vbt = NULL;
- }
+ if (!dmi_check_system(intel_no_opregion_vbt) && dev_priv->opregion.vbt)
+ bdb = validate_vbt((char *)dev_priv->opregion.header, OPREGION_SIZE,
+ (struct vbt_header *)dev_priv->opregion.vbt,
+ "OpRegion");
if (bdb == NULL) {
- struct vbt_header *vbt = NULL;
- size_t size;
- int i;
+ size_t i, size;
bios = pci_map_rom(pdev, &size);
if (!bios)
@@ -937,19 +1219,18 @@ intel_parse_bios(struct drm_device *dev)
/* Scour memory looking for the VBT signature */
for (i = 0; i + 4 < size; i++) {
- if (!memcmp(bios + i, "$VBT", 4)) {
- vbt = (struct vbt_header *)(bios + i);
+ if (memcmp(bios + i, "$VBT", 4) == 0) {
+ bdb = validate_vbt(bios, size,
+ (struct vbt_header *)(bios + i),
+ "PCI ROM");
break;
}
}
- if (!vbt) {
- DRM_DEBUG_DRIVER("VBT signature missing\n");
+ if (!bdb) {
pci_unmap_rom(pdev, bios);
return -1;
}
-
- bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
}
/* Grab useful general definitions */
diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
index f27f7b282465..b98667796337 100644
--- a/drivers/gpu/drm/i915/intel_bios.h
+++ b/drivers/gpu/drm/i915/intel_bios.h
@@ -282,6 +282,9 @@ struct bdb_general_definitions {
union child_device_config devices[0];
} __packed;
+/* Mask for DRRS / Panel Channel / SSC / BLT control bits extraction */
+#define MODE_MASK 0x3
+
struct bdb_lvds_options {
u8 panel_type;
u8 rsvd1;
@@ -294,6 +297,18 @@ struct bdb_lvds_options {
u8 lvds_edid:1;
u8 rsvd2:1;
u8 rsvd4;
+ /* LVDS Panel channel bits stored here */
+ u32 lvds_panel_channel_bits;
+ /* LVDS SSC (Spread Spectrum Clock) bits stored here. */
+ u16 ssc_bits;
+ u16 ssc_freq;
+ u16 ssc_ddt;
+ /* Panel color depth defined here */
+ u16 panel_color_depth;
+ /* LVDS panel type bits stored here */
+ u32 dps_panel_type_bits;
+ /* LVDS backlight control type bits stored here */
+ u32 blt_control_type_bits;
} __packed;
/* LFP pointer table contains entries to the struct below */
@@ -482,6 +497,20 @@ struct bdb_driver_features {
u8 hdmi_termination;
u8 custom_vbt_version;
+ /* Driver features data block */
+ u16 rmpm_enabled:1;
+ u16 s2ddt_enabled:1;
+ u16 dpst_enabled:1;
+ u16 bltclt_enabled:1;
+ u16 adb_enabled:1;
+ u16 drrs_enabled:1;
+ u16 grs_enabled:1;
+ u16 gpmt_enabled:1;
+ u16 tbt_enabled:1;
+ u16 psr_enabled:1;
+ u16 ips_enabled:1;
+ u16 reserved3:4;
+ u16 pc_feature_valid:1;
} __packed;
#define EDP_18BPP 0
@@ -714,6 +743,10 @@ int intel_parse_bios(struct drm_device *dev);
#define DVO_PORT_DPC 8
#define DVO_PORT_DPD 9
#define DVO_PORT_DPA 10
+#define DVO_PORT_MIPIA 21
+#define DVO_PORT_MIPIB 22
+#define DVO_PORT_MIPIC 23
+#define DVO_PORT_MIPID 24
/* Block 52 contains MIPI Panel info
* 6 such enteries will there. Index into correct
@@ -870,4 +903,35 @@ struct bdb_mipi_sequence {
u8 data[0];
};
+/* MIPI Sequnece Block definitions */
+enum mipi_seq {
+ MIPI_SEQ_UNDEFINED = 0,
+ MIPI_SEQ_ASSERT_RESET,
+ MIPI_SEQ_INIT_OTP,
+ MIPI_SEQ_DISPLAY_ON,
+ MIPI_SEQ_DISPLAY_OFF,
+ MIPI_SEQ_DEASSERT_RESET,
+ MIPI_SEQ_MAX
+};
+
+enum mipi_seq_element {
+ MIPI_SEQ_ELEM_UNDEFINED = 0,
+ MIPI_SEQ_ELEM_SEND_PKT,
+ MIPI_SEQ_ELEM_DELAY,
+ MIPI_SEQ_ELEM_GPIO,
+ MIPI_SEQ_ELEM_STATUS,
+ MIPI_SEQ_ELEM_MAX
+};
+
+enum mipi_gpio_pin_index {
+ MIPI_GPIO_UNDEFINED = 0,
+ MIPI_GPIO_PANEL_ENABLE,
+ MIPI_GPIO_BL_ENABLE,
+ MIPI_GPIO_PWM_ENABLE,
+ MIPI_GPIO_RESET_N,
+ MIPI_GPIO_PWR_DOWN_R,
+ MIPI_GPIO_STDBY_RST_N,
+ MIPI_GPIO_MAX
+};
+
#endif /* _I830_BIOS_H_ */
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index aa5a3dc43342..5a045d3bd77e 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -144,28 +144,49 @@ static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode)
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crt *crt = intel_encoder_to_crt(encoder);
- u32 temp;
+ struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
+ struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
+ u32 adpa;
+
+ if (INTEL_INFO(dev)->gen >= 5)
+ adpa = ADPA_HOTPLUG_BITS;
+ else
+ adpa = 0;
- temp = I915_READ(crt->adpa_reg);
- temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
- temp &= ~ADPA_DAC_ENABLE;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ adpa |= ADPA_HSYNC_ACTIVE_HIGH;
+ if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ adpa |= ADPA_VSYNC_ACTIVE_HIGH;
+
+ /* For CPT allow 3 pipe config, for others just use A or B */
+ if (HAS_PCH_LPT(dev))
+ ; /* Those bits don't exist here */
+ else if (HAS_PCH_CPT(dev))
+ adpa |= PORT_TRANS_SEL_CPT(crtc->pipe);
+ else if (crtc->pipe == 0)
+ adpa |= ADPA_PIPE_A_SELECT;
+ else
+ adpa |= ADPA_PIPE_B_SELECT;
+
+ if (!HAS_PCH_SPLIT(dev))
+ I915_WRITE(BCLRPAT(crtc->pipe), 0);
switch (mode) {
case DRM_MODE_DPMS_ON:
- temp |= ADPA_DAC_ENABLE;
+ adpa |= ADPA_DAC_ENABLE;
break;
case DRM_MODE_DPMS_STANDBY:
- temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
+ adpa |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
break;
case DRM_MODE_DPMS_SUSPEND:
- temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
+ adpa |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
break;
case DRM_MODE_DPMS_OFF:
- temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
+ adpa |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
break;
}
- I915_WRITE(crt->adpa_reg, temp);
+ I915_WRITE(crt->adpa_reg, adpa);
}
static void intel_disable_crt(struct intel_encoder *encoder)
@@ -274,42 +295,6 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder,
return true;
}
-static void intel_crt_mode_set(struct intel_encoder *encoder)
-{
-
- struct drm_device *dev = encoder->base.dev;
- struct intel_crt *crt = intel_encoder_to_crt(encoder);
- struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
- u32 adpa;
-
- if (INTEL_INFO(dev)->gen >= 5)
- adpa = ADPA_HOTPLUG_BITS;
- else
- adpa = 0;
-
- if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
- adpa |= ADPA_HSYNC_ACTIVE_HIGH;
- if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
- adpa |= ADPA_VSYNC_ACTIVE_HIGH;
-
- /* For CPT allow 3 pipe config, for others just use A or B */
- if (HAS_PCH_LPT(dev))
- ; /* Those bits don't exist here */
- else if (HAS_PCH_CPT(dev))
- adpa |= PORT_TRANS_SEL_CPT(crtc->pipe);
- else if (crtc->pipe == 0)
- adpa |= ADPA_PIPE_A_SELECT;
- else
- adpa |= ADPA_PIPE_B_SELECT;
-
- if (!HAS_PCH_SPLIT(dev))
- I915_WRITE(BCLRPAT(crtc->pipe), 0);
-
- I915_WRITE(crt->adpa_reg, adpa);
-}
-
static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
@@ -645,11 +630,12 @@ intel_crt_detect(struct drm_connector *connector, bool force)
enum intel_display_power_domain power_domain;
enum drm_connector_status status;
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, drm_get_connector_name(connector),
+ connector->base.id, connector->name,
force);
power_domain = intel_display_port_power_domain(intel_encoder);
@@ -688,12 +674,12 @@ intel_crt_detect(struct drm_connector *connector, bool force)
}
/* for pre-945g platforms use load detect */
- if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
+ 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);
+ intel_release_load_detect_pipe(connector, &tmp, &ctx);
} else
status = connector_status_unknown;
@@ -867,7 +853,6 @@ void intel_crt_init(struct drm_device *dev)
crt->adpa_reg = ADPA;
crt->base.compute_config = intel_crt_compute_config;
- crt->base.mode_set = intel_crt_mode_set;
crt->base.disable = intel_disable_crt;
crt->base.enable = intel_enable_crt;
if (I915_HAS_HOTPLUG(dev))
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 0ad4e9600063..b17b9c7c769f 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -364,55 +364,6 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
DRM_ERROR("FDI link training failed!\n");
}
-static void intel_ddi_mode_set(struct intel_encoder *encoder)
-{
- struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
- int port = intel_ddi_get_encoder_port(encoder);
- int pipe = crtc->pipe;
- int type = encoder->type;
- struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode;
-
- DRM_DEBUG_KMS("Preparing DDI mode on port %c, pipe %c\n",
- port_name(port), pipe_name(pipe));
-
- crtc->eld_vld = false;
- if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
- 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_EMP_400MV_0DB_HSW;
- intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
-
- if (intel_dp->has_audio) {
- DRM_DEBUG_DRIVER("DP audio on pipe %c on DDI\n",
- pipe_name(crtc->pipe));
-
- /* write eld */
- DRM_DEBUG_DRIVER("DP audio: write eld information\n");
- intel_write_eld(&encoder->base, adjusted_mode);
- }
- } else if (type == INTEL_OUTPUT_HDMI) {
- struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
-
- if (intel_hdmi->has_audio) {
- /* Proper support for digital audio needs a new logic
- * and a new set of registers, so we leave it for future
- * patch bombing.
- */
- DRM_DEBUG_DRIVER("HDMI audio on pipe %c on DDI\n",
- pipe_name(crtc->pipe));
-
- /* write eld */
- DRM_DEBUG_DRIVER("HDMI audio: write eld information\n");
- intel_write_eld(&encoder->base, adjusted_mode);
- }
-
- intel_hdmi->set_infoframes(&encoder->base, adjusted_mode);
- }
-}
-
static struct intel_encoder *
intel_ddi_get_crtc_encoder(struct drm_crtc *crtc)
{
@@ -1062,9 +1013,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
}
if (type == INTEL_OUTPUT_HDMI) {
- struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
-
- if (intel_hdmi->has_hdmi_sink)
+ if (intel_crtc->config.has_hdmi_sink)
temp |= TRANS_DDI_MODE_SELECT_HDMI;
else
temp |= TRANS_DDI_MODE_SELECT_DVI;
@@ -1293,28 +1242,48 @@ void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc)
static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder)
{
struct drm_encoder *encoder = &intel_encoder->base;
- struct drm_crtc *crtc = encoder->crtc;
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ struct intel_crtc *crtc = to_intel_crtc(encoder->crtc);
enum port port = intel_ddi_get_encoder_port(intel_encoder);
int type = intel_encoder->type;
+ if (crtc->config.has_audio) {
+ DRM_DEBUG_DRIVER("Audio on pipe %c on DDI\n",
+ pipe_name(crtc->pipe));
+
+ /* write eld */
+ DRM_DEBUG_DRIVER("DDI audio: write eld information\n");
+ intel_write_eld(encoder, &crtc->config.adjusted_mode);
+ }
+
if (type == INTEL_OUTPUT_EDP) {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
intel_edp_panel_on(intel_dp);
}
- WARN_ON(intel_crtc->ddi_pll_sel == PORT_CLK_SEL_NONE);
- I915_WRITE(PORT_CLK_SEL(port), intel_crtc->ddi_pll_sel);
+ WARN_ON(crtc->ddi_pll_sel == PORT_CLK_SEL_NONE);
+ I915_WRITE(PORT_CLK_SEL(port), crtc->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_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
intel_dp_start_link_train(intel_dp);
intel_dp_complete_link_train(intel_dp);
if (port != PORT_A)
intel_dp_stop_link_train(intel_dp);
+ } else if (type == INTEL_OUTPUT_HDMI) {
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
+ intel_hdmi->set_infoframes(encoder,
+ crtc->config.has_hdmi_sink,
+ &crtc->config.adjusted_mode);
}
}
@@ -1385,7 +1354,8 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder)
intel_edp_psr_enable(intel_dp);
}
- if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) {
+ if (intel_crtc->config.has_audio) {
+ intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO);
tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
tmp |= ((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4));
I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
@@ -1403,11 +1373,14 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder)
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t tmp;
- if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) {
+ /* We can't touch HSW_AUD_PIN_ELD_CP_VLD uncionditionally because this
+ * register is part of the power well on Haswell. */
+ if (intel_crtc->config.has_audio) {
tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) <<
(pipe * 4));
I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
+ intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO);
}
if (type == INTEL_OUTPUT_EDP) {
@@ -1580,6 +1553,7 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
switch (temp & TRANS_DDI_MODE_SELECT_MASK) {
case TRANS_DDI_MODE_SELECT_HDMI:
+ pipe_config->has_hdmi_sink = true;
case TRANS_DDI_MODE_SELECT_DVI:
case TRANS_DDI_MODE_SELECT_FDI:
break;
@@ -1592,6 +1566,12 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
break;
}
+ if (intel_display_power_enabled(dev_priv, POWER_DOMAIN_AUDIO)) {
+ temp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
+ if (temp & (AUDIO_OUTPUT_ENABLE_A << (intel_crtc->pipe * 4)))
+ pipe_config->has_audio = true;
+ }
+
if (encoder->type == INTEL_OUTPUT_EDP && dev_priv->vbt.edp_bpp &&
pipe_config->pipe_bpp > dev_priv->vbt.edp_bpp) {
/*
@@ -1708,7 +1688,6 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
DRM_MODE_ENCODER_TMDS);
intel_encoder->compute_config = intel_ddi_compute_config;
- intel_encoder->mode_set = intel_ddi_mode_set;
intel_encoder->enable = intel_enable_ddi;
intel_encoder->pre_enable = intel_ddi_pre_enable;
intel_encoder->disable = intel_disable_ddi;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 5b60e25baa32..efd3cf50cb0f 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -41,6 +41,9 @@
#include <drm/drm_crtc_helper.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; })
+
static void intel_increase_pllclock(struct drm_crtc *crtc);
static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on);
@@ -55,6 +58,15 @@ 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);
+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);
typedef struct {
int min, max;
@@ -328,6 +340,22 @@ static const intel_limit_t intel_limits_vlv = {
.p2 = { .p2_slow = 2, .p2_fast = 20 }, /* slow=min, fast=max */
};
+static const intel_limit_t intel_limits_chv = {
+ /*
+ * These are the data rate limits (measured in fast clocks)
+ * since those are the strictest limits we have. The fast
+ * clock and actual rate limits are more relaxed, so checking
+ * them would make no difference.
+ */
+ .dot = { .min = 25000 * 5, .max = 540000 * 5},
+ .vco = { .min = 4860000, .max = 6700000 },
+ .n = { .min = 1, .max = 1 },
+ .m1 = { .min = 2, .max = 2 },
+ .m2 = { .min = 24 << 22, .max = 175 << 22 },
+ .p1 = { .min = 2, .max = 4 },
+ .p2 = { .p2_slow = 1, .p2_fast = 14 },
+};
+
static void vlv_clock(int refclk, intel_clock_t *clock)
{
clock->m = clock->m1 * clock->m2;
@@ -412,6 +440,8 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk)
limit = &intel_limits_pineview_lvds;
else
limit = &intel_limits_pineview_sdvo;
+ } else if (IS_CHERRYVIEW(dev)) {
+ limit = &intel_limits_chv;
} else if (IS_VALLEYVIEW(dev)) {
limit = &intel_limits_vlv;
} else if (!IS_GEN2(dev)) {
@@ -456,6 +486,17 @@ static void i9xx_clock(int refclk, intel_clock_t *clock)
clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p);
}
+static void chv_clock(int refclk, intel_clock_t *clock)
+{
+ clock->m = clock->m1 * clock->m2;
+ clock->p = clock->p1 * clock->p2;
+ if (WARN_ON(clock->n == 0 || clock->p == 0))
+ return;
+ clock->vco = DIV_ROUND_CLOSEST_ULL((uint64_t)refclk * clock->m,
+ clock->n << 22);
+ clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p);
+}
+
#define INTELPllInvalid(s) do { /* DRM_DEBUG(s); */ return false; } while (0)
/**
* Returns whether the given set of divisors are valid for a given refclk with
@@ -731,6 +772,58 @@ vlv_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc,
return found;
}
+static bool
+chv_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *match_clock,
+ intel_clock_t *best_clock)
+{
+ struct drm_device *dev = crtc->dev;
+ intel_clock_t clock;
+ uint64_t m2;
+ int found = false;
+
+ memset(best_clock, 0, sizeof(*best_clock));
+
+ /*
+ * Based on hardware doc, the n always set to 1, and m1 always
+ * set to 2. If requires to support 200Mhz refclk, we need to
+ * revisit this because n may not 1 anymore.
+ */
+ clock.n = 1, clock.m1 = 2;
+ target *= 5; /* fast clock */
+
+ for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
+ for (clock.p2 = limit->p2.p2_fast;
+ clock.p2 >= limit->p2.p2_slow;
+ clock.p2 -= clock.p2 > 10 ? 2 : 1) {
+
+ clock.p = clock.p1 * clock.p2;
+
+ m2 = DIV_ROUND_CLOSEST_ULL(((uint64_t)target * clock.p *
+ clock.n) << 22, refclk * clock.m1);
+
+ if (m2 > INT_MAX/clock.m1)
+ continue;
+
+ clock.m2 = m2;
+
+ chv_clock(refclk, &clock);
+
+ if (!intel_PLL_is_valid(dev, limit, &clock))
+ continue;
+
+ /* based on hardware requirement, prefer bigger p
+ */
+ if (clock.p > best_clock->p) {
+ *best_clock = clock;
+ found = true;
+ }
+ }
+ }
+
+ return found;
+}
+
bool intel_crtc_active(struct drm_crtc *crtc)
{
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -765,7 +858,7 @@ 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))
- DRM_DEBUG_KMS("vblank wait timed out\n");
+ WARN(1, "vblank wait timed out\n");
}
/**
@@ -878,7 +971,7 @@ bool ibx_digital_port_connected(struct drm_i915_private *dev_priv,
u32 bit;
if (HAS_PCH_IBX(dev_priv->dev)) {
- switch(port->port) {
+ switch (port->port) {
case PORT_B:
bit = SDE_PORTB_HOTPLUG;
break;
@@ -892,7 +985,7 @@ bool ibx_digital_port_connected(struct drm_i915_private *dev_priv,
return true;
}
} else {
- switch(port->port) {
+ switch (port->port) {
case PORT_B:
bit = SDE_PORTB_HOTPLUG_CPT;
break;
@@ -1097,10 +1190,8 @@ static void assert_cursor(struct drm_i915_private *dev_priv,
if (IS_845G(dev) || IS_I865G(dev))
cur_state = I915_READ(_CURACNTR) & CURSOR_ENABLE;
- else if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev))
- cur_state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
else
- cur_state = I915_READ(CURCNTR_IVB(pipe)) & CURSOR_MODE;
+ cur_state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
WARN(cur_state != state,
"cursor on pipe %c assertion failure (expected %s, current %s)\n",
@@ -1253,6 +1344,9 @@ static bool dp_pipe_enabled(struct drm_i915_private *dev_priv,
u32 trans_dp_ctl = I915_READ(trans_dp_ctl_reg);
if ((trans_dp_ctl & TRANS_DP_PORT_SEL_MASK) != port_sel)
return false;
+ } else if (IS_CHERRYVIEW(dev_priv->dev)) {
+ if ((val & DP_PIPE_MASK_CHV) != DP_PIPE_SELECT_CHV(pipe))
+ return false;
} else {
if ((val & DP_PIPE_MASK) != (pipe << 30))
return false;
@@ -1269,6 +1363,9 @@ static bool hdmi_pipe_enabled(struct drm_i915_private *dev_priv,
if (HAS_PCH_CPT(dev_priv->dev)) {
if ((val & SDVO_PIPE_SEL_MASK_CPT) != SDVO_PIPE_SEL_CPT(pipe))
return false;
+ } else if (IS_CHERRYVIEW(dev_priv->dev)) {
+ if ((val & SDVO_PIPE_SEL_MASK_CHV) != SDVO_PIPE_SEL_CHV(pipe))
+ return false;
} else {
if ((val & SDVO_PIPE_SEL_MASK) != SDVO_PIPE_SEL(pipe))
return false;
@@ -1367,7 +1464,17 @@ static void intel_init_dpio(struct drm_device *dev)
if (!IS_VALLEYVIEW(dev))
return;
- DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO;
+ /*
+ * IOSF_PORT_DPIO is used for VLV x2 PHY (DP/HDMI B and C),
+ * CHV x1 PHY (DP/HDMI D)
+ * IOSF_PORT_DPIO_2 is used for CHV x2 PHY (DP/HDMI B and C)
+ */
+ if (IS_CHERRYVIEW(dev)) {
+ DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO_2;
+ DPIO_PHY_IOSF_PORT(DPIO_PHY1) = IOSF_PORT_DPIO;
+ } else {
+ DPIO_PHY_IOSF_PORT(DPIO_PHY0) = IOSF_PORT_DPIO;
+ }
}
static void intel_reset_dpio(struct drm_device *dev)
@@ -1377,25 +1484,48 @@ static void intel_reset_dpio(struct drm_device *dev)
if (!IS_VALLEYVIEW(dev))
return;
- /*
- * 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);
+ if (IS_CHERRYVIEW(dev)) {
+ enum dpio_phy phy;
+ u32 val;
- /*
- * 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);
+ 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)
@@ -1436,6 +1566,44 @@ static void vlv_enable_pll(struct intel_crtc *crtc)
udelay(150); /* wait for warmup */
}
+static void chv_enable_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;
+ enum dpio_channel port = vlv_pipe_to_channel(pipe);
+ u32 tmp;
+
+ assert_pipe_disabled(dev_priv, crtc->pipe);
+
+ BUG_ON(!IS_CHERRYVIEW(dev_priv->dev));
+
+ mutex_lock(&dev_priv->dpio_lock);
+
+ /* Enable back the 10bit clock to display controller */
+ tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW14(port));
+ tmp |= DPIO_DCLKP_EN;
+ vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), tmp);
+
+ /*
+ * Need to wait > 100ns between dclkp clock enable bit and PLL enable.
+ */
+ udelay(1);
+
+ /* Enable PLL */
+ I915_WRITE(DPLL(pipe), crtc->config.dpll_hw_state.dpll);
+
+ /* Check PLL is locked */
+ if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1))
+ DRM_ERROR("PLL %d failed to lock\n", pipe);
+
+ /* not sure when this should be written */
+ I915_WRITE(DPLL_MD(pipe), crtc->config.dpll_hw_state.dpll_md);
+ POSTING_READ(DPLL_MD(pipe));
+
+ mutex_unlock(&dev_priv->dpio_lock);
+}
+
static void i9xx_enable_pll(struct intel_crtc *crtc)
{
struct drm_device *dev = crtc->base.dev;
@@ -1519,45 +1687,92 @@ static void vlv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
val = DPLL_INTEGRATED_CRI_CLK_VLV | DPLL_REFA_CLK_ENABLE_VLV;
I915_WRITE(DPLL(pipe), val);
POSTING_READ(DPLL(pipe));
+
+}
+
+static void chv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
+{
+ enum dpio_channel port = vlv_pipe_to_channel(pipe);
+ u32 val;
+
+ /* Make sure the pipe isn't still relying on us */
+ assert_pipe_disabled(dev_priv, pipe);
+
+ /* Set PLL en = 0 */
+ val = DPLL_SSC_REF_CLOCK_CHV;
+ if (pipe != PIPE_A)
+ val |= DPLL_INTEGRATED_CRI_CLK_VLV;
+ I915_WRITE(DPLL(pipe), val);
+ POSTING_READ(DPLL(pipe));
+
+ mutex_lock(&dev_priv->dpio_lock);
+
+ /* Disable 10bit clock to display controller */
+ val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW14(port));
+ val &= ~DPIO_DCLKP_EN;
+ vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port), val);
+
+ mutex_unlock(&dev_priv->dpio_lock);
}
void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
struct intel_digital_port *dport)
{
u32 port_mask;
+ int dpll_reg;
switch (dport->port) {
case PORT_B:
port_mask = DPLL_PORTB_READY_MASK;
+ dpll_reg = DPLL(0);
break;
case PORT_C:
port_mask = DPLL_PORTC_READY_MASK;
+ dpll_reg = DPLL(0);
+ break;
+ case PORT_D:
+ port_mask = DPLL_PORTD_READY_MASK;
+ dpll_reg = DPIO_PHY_STATUS;
break;
default:
BUG();
}
- if (wait_for((I915_READ(DPLL(0)) & port_mask) == 0, 1000))
+ if (wait_for((I915_READ(dpll_reg) & port_mask) == 0, 1000))
WARN(1, "timed out waiting for port %c ready: 0x%08x\n",
- port_name(dport->port), I915_READ(DPLL(0)));
+ port_name(dport->port), I915_READ(dpll_reg));
+}
+
+static void intel_prepare_shared_dpll(struct intel_crtc *crtc)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
+
+ WARN_ON(!pll->refcount);
+ if (pll->active == 0) {
+ DRM_DEBUG_DRIVER("setting up %s\n", pll->name);
+ WARN_ON(pll->on);
+ assert_shared_dpll_disabled(dev_priv, pll);
+
+ pll->mode_set(dev_priv, pll);
+ }
}
/**
- * ironlake_enable_shared_dpll - enable PCH PLL
+ * intel_enable_shared_dpll - enable PCH PLL
* @dev_priv: i915 private structure
* @pipe: pipe PLL to enable
*
* The PCH PLL needs to be enabled before the PCH transcoder, since it
* drives the transcoder clock.
*/
-static void ironlake_enable_shared_dpll(struct intel_crtc *crtc)
+static void intel_enable_shared_dpll(struct intel_crtc *crtc)
{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
- /* PCH PLLs only available on ILK, SNB and IVB */
- BUG_ON(INTEL_INFO(dev)->gen < 5);
if (WARN_ON(pll == NULL))
return;
@@ -1804,16 +2019,6 @@ static void intel_enable_pipe(struct intel_crtc *crtc)
I915_WRITE(reg, val | PIPECONF_ENABLE);
POSTING_READ(reg);
-
- /*
- * There's no guarantee the pipe will really start running now. It
- * depends on the Gen, the output type and the relative order between
- * pipe and plane enabling. Avoid waiting on HSW+ since it's not
- * necessary.
- * TODO: audit the previous gens.
- */
- if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev))
- intel_wait_for_vblank(dev_priv->dev, pipe);
}
/**
@@ -1890,18 +2095,17 @@ static void intel_enable_primary_hw_plane(struct drm_i915_private *dev_priv,
/* If the pipe isn't enabled, we can't pump pixels and may hang */
assert_pipe_enabled(dev_priv, pipe);
- WARN(intel_crtc->primary_enabled, "Primary plane already enabled\n");
+ if (intel_crtc->primary_enabled)
+ return;
intel_crtc->primary_enabled = true;
reg = DSPCNTR(plane);
val = I915_READ(reg);
- if (val & DISPLAY_PLANE_ENABLE)
- return;
+ WARN_ON(val & DISPLAY_PLANE_ENABLE);
I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE);
intel_flush_primary_plane(dev_priv, plane);
- intel_wait_for_vblank(dev_priv->dev, pipe);
}
/**
@@ -1920,18 +2124,17 @@ static void intel_disable_primary_hw_plane(struct drm_i915_private *dev_priv,
int reg;
u32 val;
- WARN(!intel_crtc->primary_enabled, "Primary plane already disabled\n");
+ if (!intel_crtc->primary_enabled)
+ return;
intel_crtc->primary_enabled = false;
reg = DSPCNTR(plane);
val = I915_READ(reg);
- if ((val & DISPLAY_PLANE_ENABLE) == 0)
- return;
+ WARN_ON((val & DISPLAY_PLANE_ENABLE) == 0);
I915_WRITE(reg, val & ~DISPLAY_PLANE_ENABLE);
intel_flush_primary_plane(dev_priv, plane);
- intel_wait_for_vblank(dev_priv->dev, pipe);
}
static bool need_vtd_wa(struct drm_device *dev)
@@ -1954,7 +2157,7 @@ static int intel_align_height(struct drm_device *dev, int height, bool tiled)
int
intel_pin_and_fence_fb_obj(struct drm_device *dev,
struct drm_i915_gem_object *obj,
- struct intel_ring_buffer *pipelined)
+ struct intel_engine_cs *pipelined)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 alignment;
@@ -2134,7 +2337,7 @@ static void intel_find_plane_obj(struct intel_crtc *intel_crtc,
* Failed to alloc the obj, check to see if we should share
* an fb with another CRTC instead
*/
- list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, c) {
i = to_intel_crtc(c);
if (c == &intel_crtc->base)
@@ -2152,9 +2355,9 @@ static void intel_find_plane_obj(struct intel_crtc *intel_crtc,
}
}
-static int i9xx_update_primary_plane(struct drm_crtc *crtc,
- struct drm_framebuffer *fb,
- int x, int y)
+static void i9xx_update_primary_plane(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2166,15 +2369,6 @@ static int i9xx_update_primary_plane(struct drm_crtc *crtc,
u32 dspcntr;
u32 reg;
- switch (plane) {
- case 0:
- case 1:
- break;
- default:
- DRM_ERROR("Can't update plane %c in SAREA\n", plane_name(plane));
- return -EINVAL;
- }
-
intel_fb = to_intel_framebuffer(fb);
obj = intel_fb->obj;
@@ -2249,13 +2443,11 @@ static int i9xx_update_primary_plane(struct drm_crtc *crtc,
} else
I915_WRITE(DSPADDR(plane), i915_gem_obj_ggtt_offset(obj) + linear_offset);
POSTING_READ(reg);
-
- return 0;
}
-static int ironlake_update_primary_plane(struct drm_crtc *crtc,
- struct drm_framebuffer *fb,
- int x, int y)
+static void ironlake_update_primary_plane(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ int x, int y)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2267,16 +2459,6 @@ static int ironlake_update_primary_plane(struct drm_crtc *crtc,
u32 dspcntr;
u32 reg;
- switch (plane) {
- case 0:
- case 1:
- case 2:
- break;
- default:
- DRM_ERROR("Can't update plane %c in SAREA\n", plane_name(plane));
- return -EINVAL;
- }
-
intel_fb = to_intel_framebuffer(fb);
obj = intel_fb->obj;
@@ -2343,8 +2525,6 @@ static int ironlake_update_primary_plane(struct drm_crtc *crtc,
I915_WRITE(DSPLINOFF(plane), linear_offset);
}
POSTING_READ(reg);
-
- return 0;
}
/* Assume fb object is pinned & idle & fenced and just update base pointers */
@@ -2359,7 +2539,9 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
dev_priv->display.disable_fbc(dev);
intel_increase_pllclock(crtc);
- return dev_priv->display.update_primary_plane(crtc, fb, x, y);
+ dev_priv->display.update_primary_plane(crtc, fb, x, y);
+
+ return 0;
}
void intel_display_handle_reset(struct drm_device *dev)
@@ -2381,7 +2563,7 @@ void intel_display_handle_reset(struct drm_device *dev)
* pending_flip_queue really got woken up.
*/
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, crtc) {
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
enum plane plane = intel_crtc->plane;
@@ -2389,10 +2571,10 @@ void intel_display_handle_reset(struct drm_device *dev)
intel_finish_page_flip_plane(dev, plane);
}
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, crtc) {
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- mutex_lock(&crtc->mutex);
+ drm_modeset_lock(&crtc->mutex, NULL);
/*
* FIXME: Once we have proper support for primary planes (and
* disabling them without disabling the entire crtc) allow again
@@ -2403,7 +2585,7 @@ void intel_display_handle_reset(struct drm_device *dev)
crtc->primary->fb,
crtc->x,
crtc->y);
- mutex_unlock(&crtc->mutex);
+ drm_modeset_unlock(&crtc->mutex);
}
}
@@ -2518,14 +2700,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
intel_crtc->config.pipe_src_h = adjusted_mode->crtc_vdisplay;
}
- ret = dev_priv->display.update_primary_plane(crtc, fb, x, y);
- if (ret) {
- mutex_lock(&dev->struct_mutex);
- intel_unpin_fb_obj(to_intel_framebuffer(fb)->obj);
- mutex_unlock(&dev->struct_mutex);
- DRM_ERROR("failed to update base address\n");
- return ret;
- }
+ dev_priv->display.update_primary_plane(crtc, fb, x, y);
old_fb = crtc->primary->fb;
crtc->primary->fb = fb;
@@ -2628,12 +2803,10 @@ static void ironlake_fdi_link_train(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 plane = intel_crtc->plane;
u32 reg, temp, tries;
- /* FDI needs bits from pipe & plane first */
+ /* FDI needs bits from pipe first */
assert_pipe_enabled(dev_priv, pipe);
- assert_plane_enabled(dev_priv, plane);
/* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
for train result */
@@ -3064,9 +3237,8 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc)
udelay(100);
/* Ironlake workaround, disable clock pointer after downing FDI */
- if (HAS_PCH_IBX(dev)) {
+ if (HAS_PCH_IBX(dev))
I915_WRITE(FDI_RX_CHICKEN(pipe), FDI_RX_PHASE_SYNC_POINTER_OVR);
- }
/* still set train pattern 1 */
reg = FDI_TX_CTL(pipe);
@@ -3104,7 +3276,7 @@ bool intel_has_pending_fb_unpin(struct drm_device *dev)
* cannot claim and pin a new fb without at least acquring the
* struct_mutex and so serialising with us.
*/
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+ for_each_intel_crtc(dev, crtc) {
if (atomic_read(&crtc->unpin_work_count) == 0)
continue;
@@ -3117,7 +3289,7 @@ bool intel_has_pending_fb_unpin(struct drm_device *dev)
return false;
}
-static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
+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;
@@ -3127,8 +3299,9 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue));
- wait_event(dev_priv->pending_flip_queue,
- !intel_crtc_has_pending_flip(crtc));
+ WARN_ON(wait_event_timeout(dev_priv->pending_flip_queue,
+ !intel_crtc_has_pending_flip(crtc),
+ 60*HZ) == 0);
mutex_lock(&dev->struct_mutex);
intel_finish_fb(crtc->primary->fb);
@@ -3341,7 +3514,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
* Note that enable_shared_dpll tries to do the right thing, but
* get_shared_dpll unconditionally resets the pll - we need that to have
* the right LVDS enable sequence. */
- ironlake_enable_shared_dpll(intel_crtc);
+ intel_enable_shared_dpll(intel_crtc);
/* set transcoder timing, panel must allow it */
assert_panel_unlocked(dev_priv, pipe);
@@ -3445,6 +3618,8 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc)
DRM_DEBUG_KMS("CRTC:%d using pre-allocated %s\n",
crtc->base.base.id, pll->name);
+ WARN_ON(pll->refcount);
+
goto found;
}
@@ -3478,20 +3653,13 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc)
return NULL;
found:
+ if (pll->refcount == 0)
+ pll->hw_state = crtc->config.dpll_hw_state;
+
crtc->config.shared_dpll = i;
DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name,
pipe_name(crtc->pipe));
- if (pll->active == 0) {
- memcpy(&pll->hw_state, &crtc->config.dpll_hw_state,
- sizeof(pll->hw_state));
-
- DRM_DEBUG_DRIVER("setting up %s\n", pll->name);
- WARN_ON(pll->on);
- assert_shared_dpll_disabled(dev_priv, pll);
-
- pll->mode_set(dev_priv, pll);
- }
pll->refcount++;
return pll;
@@ -3562,17 +3730,17 @@ static void intel_disable_planes(struct drm_crtc *crtc)
void hsw_enable_ips(struct intel_crtc *crtc)
{
- struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
if (!crtc->config.ips_enabled)
return;
- /* We can only enable IPS after we enable a plane and wait for a vblank.
- * We guarantee that the plane is enabled by calling intel_enable_ips
- * only after intel_enable_plane. And intel_enable_plane already waits
- * for a vblank, so all we need to do here is to enable the IPS bit. */
+ /* We can only enable IPS after we enable a plane and wait for a vblank */
+ intel_wait_for_vblank(dev, crtc->pipe);
+
assert_plane_enabled(dev_priv, crtc->plane);
- if (IS_BROADWELL(crtc->base.dev)) {
+ if (IS_BROADWELL(dev)) {
mutex_lock(&dev_priv->rps.hw_lock);
WARN_ON(sandybridge_pcode_write(dev_priv, DISPLAY_IPS_CONTROL, 0xc0000000));
mutex_unlock(&dev_priv->rps.hw_lock);
@@ -3602,10 +3770,13 @@ void hsw_disable_ips(struct intel_crtc *crtc)
return;
assert_plane_enabled(dev_priv, crtc->plane);
- if (IS_BROADWELL(crtc->base.dev)) {
+ if (IS_BROADWELL(dev)) {
mutex_lock(&dev_priv->rps.hw_lock);
WARN_ON(sandybridge_pcode_write(dev_priv, DISPLAY_IPS_CONTROL, 0));
mutex_unlock(&dev_priv->rps.hw_lock);
+ /* wait for pcode to finish disabling IPS, which may take up to 42ms */
+ if (wait_for((I915_READ(IPS_CTL) & IPS_ENABLE) == 0, 42))
+ DRM_ERROR("Timed out waiting for IPS disable\n");
} else {
I915_WRITE(IPS_CTL, 0);
POSTING_READ(IPS_CTL);
@@ -3662,6 +3833,94 @@ static void intel_crtc_load_lut(struct drm_crtc *crtc)
hsw_enable_ips(intel_crtc);
}
+static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable)
+{
+ if (!enable && intel_crtc->overlay) {
+ struct drm_device *dev = intel_crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ mutex_lock(&dev->struct_mutex);
+ dev_priv->mm.interruptible = false;
+ (void) intel_overlay_switch_off(intel_crtc->overlay);
+ dev_priv->mm.interruptible = true;
+ mutex_unlock(&dev->struct_mutex);
+ }
+
+ /* Let userspace switch the overlay on again. In most cases userspace
+ * has to recompute where to put it anyway.
+ */
+}
+
+/**
+ * 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);
+ 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);
+
+ hsw_enable_ips(intel_crtc);
+
+ mutex_lock(&dev->struct_mutex);
+ intel_update_fbc(dev);
+ intel_edp_psr_update(dev);
+ mutex_unlock(&dev->struct_mutex);
+}
+
+static void intel_crtc_disable_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_crtc_wait_for_pending_flips(crtc);
+ drm_crtc_vblank_off(crtc);
+
+ if (dev_priv->fbc.plane == plane)
+ intel_disable_fbc(dev);
+
+ hsw_disable_ips(intel_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);
+}
+
static void ironlake_crtc_enable(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
@@ -3669,13 +3928,35 @@ 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;
- int plane = intel_crtc->plane;
+ enum plane plane = intel_crtc->plane;
WARN_ON(!crtc->enabled);
if (intel_crtc->active)
return;
+ if (intel_crtc->config.has_pch_encoder)
+ intel_prepare_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.has_pch_encoder) {
+ intel_cpu_transcoder_set_m_n(intel_crtc,
+ &intel_crtc->config.fdi_m_n);
+ }
+
+ 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);
@@ -3705,32 +3986,19 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
intel_update_watermarks(crtc);
intel_enable_pipe(intel_crtc);
- intel_enable_primary_hw_plane(dev_priv, plane, pipe);
- intel_enable_planes(crtc);
- intel_crtc_update_cursor(crtc, true);
if (intel_crtc->config.has_pch_encoder)
ironlake_pch_enable(crtc);
- mutex_lock(&dev->struct_mutex);
- intel_update_fbc(dev);
- mutex_unlock(&dev->struct_mutex);
-
for_each_encoder_on_crtc(dev, crtc, encoder)
encoder->enable(encoder);
if (HAS_PCH_CPT(dev))
cpt_verify_modeset(dev, intel_crtc->pipe);
- /*
- * There seems to be a race in PCH platform hw (at least on some
- * outputs) where an enabled pipe still completes any pageflip right
- * away (as if the pipe is off) instead of waiting for vblank. As soon
- * as the first vblank happend, everything works as expected. Hence just
- * wait for one vblank before returning to avoid strange things
- * happening.
- */
- intel_wait_for_vblank(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. */
@@ -3739,47 +4007,6 @@ static bool hsw_crtc_supports_ips(struct intel_crtc *crtc)
return HAS_IPS(crtc->base.dev) && crtc->pipe == PIPE_A;
}
-static void haswell_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);
- intel_enable_planes(crtc);
- intel_crtc_update_cursor(crtc, true);
-
- hsw_enable_ips(intel_crtc);
-
- mutex_lock(&dev->struct_mutex);
- intel_update_fbc(dev);
- mutex_unlock(&dev->struct_mutex);
-}
-
-static void haswell_crtc_disable_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_crtc_wait_for_pending_flips(crtc);
- drm_vblank_off(dev, pipe);
-
- /* FBC must be disabled before disabling the plane on HSW. */
- if (dev_priv->fbc.plane == plane)
- intel_disable_fbc(dev);
-
- hsw_disable_ips(intel_crtc);
-
- intel_crtc_update_cursor(crtc, false);
- intel_disable_planes(crtc);
- intel_disable_primary_hw_plane(dev_priv, plane, pipe);
-}
-
/*
* This implements the workaround described in the "notes" section of the mode
* set sequence documentation. When going from no pipes or single pipe to
@@ -3793,7 +4020,7 @@ static void haswell_mode_set_planes_workaround(struct intel_crtc *crtc)
/* We want to get the other_active_crtc only if there's only 1 other
* active crtc. */
- list_for_each_entry(crtc_it, &dev->mode_config.crtc_list, base.head) {
+ for_each_intel_crtc(dev, crtc_it) {
if (!crtc_it->active || crtc_it == crtc)
continue;
@@ -3816,12 +4043,34 @@ 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->config.has_dp_encoder)
+ intel_dp_set_m_n(intel_crtc);
+
+ intel_set_pipe_timings(intel_crtc);
+
+ if (intel_crtc->config.has_pch_encoder) {
+ intel_cpu_transcoder_set_m_n(intel_crtc,
+ &intel_crtc->config.fdi_m_n);
+ }
+
+ 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);
@@ -3862,7 +4111,9 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
/* If we change the relative order between pipe/planes enabling, we need
* to change the workaround. */
haswell_mode_set_planes_workaround(intel_crtc);
- haswell_crtc_enable_planes(crtc);
+ intel_crtc_enable_planes(crtc);
+
+ drm_crtc_vblank_on(crtc);
}
static void ironlake_pfit_disable(struct intel_crtc *crtc)
@@ -3887,26 +4138,16 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe;
- int plane = intel_crtc->plane;
u32 reg, temp;
-
if (!intel_crtc->active)
return;
+ intel_crtc_disable_planes(crtc);
+
for_each_encoder_on_crtc(dev, crtc, encoder)
encoder->disable(encoder);
- intel_crtc_wait_for_pending_flips(crtc);
- drm_vblank_off(dev, pipe);
-
- if (dev_priv->fbc.plane == plane)
- intel_disable_fbc(dev);
-
- intel_crtc_update_cursor(crtc, false);
- intel_disable_planes(crtc);
- intel_disable_primary_hw_plane(dev_priv, plane, pipe);
-
if (intel_crtc->config.has_pch_encoder)
intel_set_pch_fifo_underrun_reporting(dev, pipe, false);
@@ -3950,6 +4191,7 @@ 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);
}
@@ -3965,7 +4207,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
if (!intel_crtc->active)
return;
- haswell_crtc_disable_planes(crtc);
+ intel_crtc_disable_planes(crtc);
for_each_encoder_on_crtc(dev, crtc, encoder) {
intel_opregion_notify_encoder(encoder, false);
@@ -3997,6 +4239,7 @@ static void haswell_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);
}
@@ -4011,48 +4254,6 @@ static void haswell_crtc_off(struct drm_crtc *crtc)
intel_ddi_put_crtc_pll(crtc);
}
-static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable)
-{
- if (!enable && intel_crtc->overlay) {
- struct drm_device *dev = intel_crtc->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
-
- mutex_lock(&dev->struct_mutex);
- dev_priv->mm.interruptible = false;
- (void) intel_overlay_switch_off(intel_crtc->overlay);
- dev_priv->mm.interruptible = true;
- mutex_unlock(&dev->struct_mutex);
- }
-
- /* Let userspace switch the overlay on again. In most cases userspace
- * has to recompute where to put it anyway.
- */
-}
-
-/**
- * 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 i9xx_pfit_enable(struct intel_crtc *crtc)
{
struct drm_device *dev = crtc->base.dev;
@@ -4164,7 +4365,7 @@ static void modeset_update_crtc_power_domains(struct drm_device *dev)
* First get all needed power domains, then put all unneeded, to avoid
* any unnecessary toggling of the power wells.
*/
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+ for_each_intel_crtc(dev, crtc) {
enum intel_display_power_domain domain;
if (!crtc->base.enabled)
@@ -4176,7 +4377,7 @@ static void modeset_update_crtc_power_domains(struct drm_device *dev)
intel_display_power_get(dev_priv, domain);
}
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+ for_each_intel_crtc(dev, crtc) {
enum intel_display_power_domain domain;
for_each_power_domain(domain, crtc->enabled_power_domains)
@@ -4207,6 +4408,9 @@ 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;
+
if (cdclk >= 320) /* jump to highest voltage for 400MHz too */
cmd = 2;
else if (cdclk == 266)
@@ -4261,7 +4465,7 @@ static void valleyview_set_cdclk(struct drm_device *dev, int cdclk)
intel_i2c_reset(dev);
}
-static int valleyview_cur_cdclk(struct drm_i915_private *dev_priv)
+int valleyview_cur_cdclk(struct drm_i915_private *dev_priv)
{
int cur_cdclk, vco;
int divider;
@@ -4282,10 +4486,6 @@ static int valleyview_cur_cdclk(struct drm_i915_private *dev_priv)
static int valleyview_calc_cdclk(struct drm_i915_private *dev_priv,
int max_pixclk)
{
- int cur_cdclk;
-
- cur_cdclk = valleyview_cur_cdclk(dev_priv);
-
/*
* Really only a few cases to deal with, as only 4 CDclks are supported:
* 200MHz
@@ -4311,8 +4511,7 @@ static int intel_mode_max_pixclk(struct drm_i915_private *dev_priv)
struct intel_crtc *intel_crtc;
int max_pixclk = 0;
- list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, intel_crtc) {
if (intel_crtc->new_enabled)
max_pixclk = max(max_pixclk,
intel_crtc->new_config->adjusted_mode.crtc_clock);
@@ -4327,14 +4526,13 @@ static void valleyview_modeset_global_pipes(struct drm_device *dev,
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc;
int max_pixclk = intel_mode_max_pixclk(dev_priv);
- int cur_cdclk = valleyview_cur_cdclk(dev_priv);
- if (valleyview_calc_cdclk(dev_priv, max_pixclk) == cur_cdclk)
+ if (valleyview_calc_cdclk(dev_priv, max_pixclk) ==
+ dev_priv->vlv_cdclk_freq)
return;
/* disable/enable all currently active pipes while we change cdclk */
- list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
- base.head)
+ for_each_intel_crtc(dev, intel_crtc)
if (intel_crtc->base.enabled)
*prepare_pipes |= (1 << intel_crtc->pipe);
}
@@ -4343,10 +4541,9 @@ static void valleyview_modeset_global_resources(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int max_pixclk = intel_mode_max_pixclk(dev_priv);
- int cur_cdclk = valleyview_cur_cdclk(dev_priv);
int req_cdclk = valleyview_calc_cdclk(dev_priv, max_pixclk);
- if (req_cdclk != cur_cdclk)
+ if (req_cdclk != dev_priv->vlv_cdclk_freq)
valleyview_set_cdclk(dev, req_cdclk);
modeset_update_crtc_power_domains(dev);
}
@@ -4360,22 +4557,55 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
int pipe = intel_crtc->pipe;
int plane = intel_crtc->plane;
bool is_dsi;
+ u32 dspcntr;
WARN_ON(!crtc->enabled);
if (intel_crtc->active)
return;
+ vlv_prepare_pll(intel_crtc);
+
+ /* Set up the display plane register */
+ dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+ 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);
+
for_each_encoder_on_crtc(dev, crtc, encoder)
if (encoder->pre_pll_enable)
encoder->pre_pll_enable(encoder);
is_dsi = intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI);
- if (!is_dsi)
- vlv_enable_pll(intel_crtc);
+ if (!is_dsi) {
+ if (IS_CHERRYVIEW(dev))
+ chv_enable_pll(intel_crtc);
+ else
+ vlv_enable_pll(intel_crtc);
+ }
for_each_encoder_on_crtc(dev, crtc, encoder)
if (encoder->pre_enable)
@@ -4387,15 +4617,25 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
intel_update_watermarks(crtc);
intel_enable_pipe(intel_crtc);
- intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
- intel_enable_primary_hw_plane(dev_priv, plane, pipe);
- intel_enable_planes(crtc);
- intel_crtc_update_cursor(crtc, true);
-
- intel_update_fbc(dev);
for_each_encoder_on_crtc(dev, crtc, encoder)
encoder->enable(encoder);
+
+ intel_crtc_enable_planes(crtc);
+
+ drm_crtc_vblank_on(crtc);
+
+ /* Underruns don't raise interrupts, so check manually. */
+ i9xx_check_fifo_underruns(dev);
+}
+
+static void i9xx_set_pll_dividers(struct intel_crtc *crtc)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(FP0(crtc->pipe), crtc->config.dpll_hw_state.fp0);
+ I915_WRITE(FP1(crtc->pipe), crtc->config.dpll_hw_state.fp1);
}
static void i9xx_crtc_enable(struct drm_crtc *crtc)
@@ -4406,14 +4646,49 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe;
int plane = intel_crtc->plane;
+ u32 dspcntr;
WARN_ON(!crtc->enabled);
if (intel_crtc->active)
return;
+ 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))
+ intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
+
for_each_encoder_on_crtc(dev, crtc, encoder)
if (encoder->pre_enable)
encoder->pre_enable(encoder);
@@ -4426,21 +4701,26 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
intel_update_watermarks(crtc);
intel_enable_pipe(intel_crtc);
- intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
- intel_enable_primary_hw_plane(dev_priv, plane, pipe);
- 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);
-
- /* Give the overlay scaler a chance to enable if it's on this pipe */
- intel_crtc_dpms_overlay(intel_crtc, true);
-
- intel_update_fbc(dev);
for_each_encoder_on_crtc(dev, crtc, encoder)
encoder->enable(encoder);
+
+ intel_crtc_enable_planes(crtc);
+
+ /*
+ * Gen2 reports pipe underruns whenever all planes are disabled.
+ * So don't enable underrun reporting before at least some planes
+ * are enabled.
+ * FIXME: Need to fix the logic to work when we turn off all planes
+ * but leave the pipe running.
+ */
+ 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);
}
static void i9xx_pfit_disable(struct intel_crtc *crtc)
@@ -4465,27 +4745,31 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe;
- int plane = intel_crtc->plane;
if (!intel_crtc->active)
return;
- for_each_encoder_on_crtc(dev, crtc, encoder)
- encoder->disable(encoder);
+ /*
+ * Gen2 reports pipe underruns whenever all planes are disabled.
+ * So diasble underrun reporting before all the planes get disabled.
+ * FIXME: Need to fix the logic to work when we turn off all planes
+ * but leave the pipe running.
+ */
+ if (IS_GEN2(dev))
+ intel_set_cpu_fifo_underrun_reporting(dev, pipe, false);
- /* Give the overlay scaler a chance to disable if it's on this pipe */
- intel_crtc_wait_for_pending_flips(crtc);
- drm_vblank_off(dev, pipe);
+ intel_crtc_disable_planes(crtc);
- if (dev_priv->fbc.plane == plane)
- intel_disable_fbc(dev);
+ for_each_encoder_on_crtc(dev, crtc, encoder)
+ encoder->disable(encoder);
- 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);
+ /*
+ * 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.
+ */
+ if (IS_GEN2(dev))
+ intel_wait_for_vblank(dev, pipe);
- intel_set_cpu_fifo_underrun_reporting(dev, pipe, false);
intel_disable_pipe(dev_priv, pipe);
i9xx_pfit_disable(intel_crtc);
@@ -4494,15 +4778,25 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
if (encoder->post_disable)
encoder->post_disable(encoder);
- if (IS_VALLEYVIEW(dev) && !intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI))
- vlv_disable_pll(dev_priv, pipe);
- else if (!IS_VALLEYVIEW(dev))
- i9xx_disable_pll(dev_priv, pipe);
+ if (!intel_pipe_has_type(crtc, INTEL_OUTPUT_DSI)) {
+ if (IS_CHERRYVIEW(dev))
+ chv_disable_pll(dev_priv, pipe);
+ else if (IS_VALLEYVIEW(dev))
+ vlv_disable_pll(dev_priv, pipe);
+ else
+ i9xx_disable_pll(dev_priv, pipe);
+ }
+
+ if (!IS_GEN2(dev))
+ intel_set_cpu_fifo_underrun_reporting(dev, pipe, false);
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);
}
static void i9xx_crtc_off(struct drm_crtc *crtc)
@@ -4565,13 +4859,11 @@ 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 intel_crtc *intel_crtc = to_intel_crtc(crtc);
/* crtc should still be enabled when we disable it. */
WARN_ON(!crtc->enabled);
dev_priv->display.crtc_disable(crtc);
- intel_crtc->eld_vld = false;
intel_crtc_update_sarea(crtc, false);
dev_priv->display.off(crtc);
@@ -4635,7 +4927,7 @@ static void intel_connector_check_state(struct intel_connector *connector)
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.base.id,
- drm_get_connector_name(&connector->base));
+ connector->base.name);
WARN(connector->base.dpms == DRM_MODE_DPMS_OFF,
"wrong connector dpms state\n");
@@ -5039,8 +5331,6 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc,
intel_clock_t *reduced_clock)
{
struct drm_device *dev = crtc->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- int pipe = crtc->pipe;
u32 fp, fp2 = 0;
if (IS_PINEVIEW(dev)) {
@@ -5053,17 +5343,14 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc,
fp2 = i9xx_dpll_compute_fp(reduced_clock);
}
- I915_WRITE(FP0(pipe), fp);
crtc->config.dpll_hw_state.fp0 = fp;
crtc->lowfreq_avail = false;
if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) &&
reduced_clock && i915.powersave) {
- I915_WRITE(FP1(pipe), fp2);
crtc->config.dpll_hw_state.fp1 = fp2;
crtc->lowfreq_avail = true;
} else {
- I915_WRITE(FP1(pipe), fp);
crtc->config.dpll_hw_state.fp1 = fp;
}
}
@@ -5141,12 +5428,34 @@ static void intel_dp_set_m_n(struct intel_crtc *crtc)
static void vlv_update_pll(struct intel_crtc *crtc)
{
+ u32 dpll, dpll_md;
+
+ /*
+ * Enable DPIO clock input. We should never disable the reference
+ * clock for pipe B, since VGA hotplug / manual detection depends
+ * on it.
+ */
+ dpll = DPLL_EXT_BUFFER_ENABLE_VLV | DPLL_REFA_CLK_ENABLE_VLV |
+ DPLL_VGA_MODE_DIS | DPLL_INTEGRATED_CLOCK_VLV;
+ /* We should never disable this, set it here for state tracking */
+ if (crtc->pipe == PIPE_B)
+ dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
+ dpll |= DPLL_VCO_ENABLE;
+ crtc->config.dpll_hw_state.dpll = dpll;
+
+ dpll_md = (crtc->config.pixel_multiplier - 1)
+ << DPLL_MD_UDI_MULTIPLIER_SHIFT;
+ crtc->config.dpll_hw_state.dpll_md = dpll_md;
+}
+
+static void vlv_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;
- u32 dpll, mdiv;
+ u32 mdiv;
u32 bestn, bestm1, bestm2, bestp1, bestp2;
- u32 coreclk, reg_val, dpll_md;
+ u32 coreclk, reg_val;
mutex_lock(&dev_priv->dpio_lock);
@@ -5159,7 +5468,7 @@ static void vlv_update_pll(struct intel_crtc *crtc)
/* See eDP HDMI DPIO driver vbios notes doc */
/* PLL B needs special handling */
- if (pipe)
+ if (pipe == PIPE_B)
vlv_pllb_recal_opamp(dev_priv, pipe);
/* Set up Tx target for periodic Rcomp update */
@@ -5203,7 +5512,7 @@ static void vlv_update_pll(struct intel_crtc *crtc)
if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) ||
intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) {
/* Use SSC source */
- if (!pipe)
+ if (pipe == PIPE_A)
vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe),
0x0df40000);
else
@@ -5211,7 +5520,7 @@ static void vlv_update_pll(struct intel_crtc *crtc)
0x0df70000);
} else { /* HDMI or VGA */
/* Use bend source */
- if (!pipe)
+ if (pipe == PIPE_A)
vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW5(pipe),
0x0df70000);
else
@@ -5227,26 +5536,84 @@ static void vlv_update_pll(struct intel_crtc *crtc)
vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW7(pipe), coreclk);
vlv_dpio_write(dev_priv, pipe, VLV_PLL_DW11(pipe), 0x87871000);
+ mutex_unlock(&dev_priv->dpio_lock);
+}
+
+static void chv_update_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;
+ int dpll_reg = DPLL(crtc->pipe);
+ enum dpio_channel port = vlv_pipe_to_channel(pipe);
+ u32 loopfilter, intcoeff;
+ 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;
+ bestm2 = crtc->config.dpll.m2 >> 22;
+ bestp1 = crtc->config.dpll.p1;
+ bestp2 = crtc->config.dpll.p2;
/*
- * Enable DPIO clock input. We should never disable the reference
- * clock for pipe B, since VGA hotplug / manual detection depends
- * on it.
+ * Enable Refclk and SSC
*/
- dpll = DPLL_EXT_BUFFER_ENABLE_VLV | DPLL_REFA_CLK_ENABLE_VLV |
- DPLL_VGA_MODE_DIS | DPLL_INTEGRATED_CLOCK_VLV;
- /* We should never disable this, set it here for state tracking */
- if (pipe == PIPE_B)
- dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
- dpll |= DPLL_VCO_ENABLE;
- crtc->config.dpll_hw_state.dpll = dpll;
+ I915_WRITE(dpll_reg,
+ crtc->config.dpll_hw_state.dpll & ~DPLL_VCO_ENABLE);
- dpll_md = (crtc->config.pixel_multiplier - 1)
- << DPLL_MD_UDI_MULTIPLIER_SHIFT;
- crtc->config.dpll_hw_state.dpll_md = dpll_md;
+ mutex_lock(&dev_priv->dpio_lock);
+
+ /* p1 and p2 divider */
+ vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW13(port),
+ 5 << DPIO_CHV_S1_DIV_SHIFT |
+ bestp1 << DPIO_CHV_P1_DIV_SHIFT |
+ bestp2 << DPIO_CHV_P2_DIV_SHIFT |
+ 1 << DPIO_CHV_K_DIV_SHIFT);
+
+ /* Feedback post-divider - m2 */
+ vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW0(port), bestm2);
+
+ /* Feedback refclk divider - n and m1 */
+ vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW1(port),
+ DPIO_CHV_M1_DIV_BY_2 |
+ 1 << DPIO_CHV_N_DIV_SHIFT);
+
+ /* M2 fraction division */
+ vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW2(port), bestm2_frac);
+
+ /* M2 fraction division enable */
+ vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW3(port),
+ DPIO_CHV_FRAC_DIV_EN |
+ (2 << DPIO_CHV_FEEDFWD_GAIN_SHIFT));
+
+ /* Loop filter */
+ refclk = i9xx_get_refclk(&crtc->base, 0);
+ loopfilter = 5 << DPIO_CHV_PROP_COEFF_SHIFT |
+ 2 << DPIO_CHV_GAIN_CTRL_SHIFT;
+ if (refclk == 100000)
+ intcoeff = 11;
+ else if (refclk == 38400)
+ intcoeff = 10;
+ else
+ intcoeff = 9;
+ loopfilter |= intcoeff << DPIO_CHV_INT_COEFF_SHIFT;
+ vlv_dpio_write(dev_priv, pipe, CHV_PLL_DW6(port), loopfilter);
- if (crtc->config.has_dp_encoder)
- intel_dp_set_m_n(crtc);
+ /* AFC Recal */
+ vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW14(port),
+ vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW14(port)) |
+ DPIO_AFC_RECAL);
mutex_unlock(&dev_priv->dpio_lock);
}
@@ -5325,9 +5692,6 @@ static void i9xx_update_pll(struct intel_crtc *crtc,
<< DPLL_MD_UDI_MULTIPLIER_SHIFT;
crtc->config.dpll_hw_state.dpll_md = dpll_md;
}
-
- if (crtc->config.has_dp_encoder)
- intel_dp_set_m_n(crtc);
}
static void i8xx_update_pll(struct intel_crtc *crtc,
@@ -5567,16 +5931,12 @@ static int i9xx_crtc_mode_set(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;
int refclk, num_connectors = 0;
intel_clock_t clock, reduced_clock;
- u32 dspcntr;
bool ok, has_reduced_clock = false;
bool is_lvds = false, is_dsi = false;
struct intel_encoder *encoder;
const intel_limit_t *limit;
- int ret;
for_each_encoder_on_crtc(dev, crtc, encoder) {
switch (encoder->type) {
@@ -5592,7 +5952,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
}
if (is_dsi)
- goto skip_dpll;
+ return 0;
if (!intel_crtc->config.clock_set) {
refclk = i9xx_get_refclk(crtc, num_connectors);
@@ -5637,43 +5997,17 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
i8xx_update_pll(intel_crtc,
has_reduced_clock ? &reduced_clock : NULL,
num_connectors);
+ } else if (IS_CHERRYVIEW(dev)) {
+ chv_update_pll(intel_crtc);
} else if (IS_VALLEYVIEW(dev)) {
vlv_update_pll(intel_crtc);
} else {
i9xx_update_pll(intel_crtc,
has_reduced_clock ? &reduced_clock : NULL,
- num_connectors);
- }
-
-skip_dpll:
- /* Set up the display plane register */
- dspcntr = DISPPLANE_GAMMA_ENABLE;
-
- if (!IS_VALLEYVIEW(dev)) {
- if (pipe == 0)
- dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
- else
- dspcntr |= DISPPLANE_SEL_PIPE_B;
+ num_connectors);
}
- 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));
-
- ret = intel_pipe_set_base(crtc, x, y, fb);
-
- return ret;
+ return 0;
}
static void i9xx_get_pfit_config(struct intel_crtc *crtc,
@@ -5793,6 +6127,36 @@ static void i9xx_get_plane_config(struct intel_crtc *crtc,
}
+static void chv_crtc_clock_get(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;
+ int pipe = pipe_config->cpu_transcoder;
+ enum dpio_channel port = vlv_pipe_to_channel(pipe);
+ intel_clock_t clock;
+ u32 cmn_dw13, pll_dw0, pll_dw1, pll_dw2;
+ int refclk = 100000;
+
+ mutex_lock(&dev_priv->dpio_lock);
+ cmn_dw13 = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW13(port));
+ pll_dw0 = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW0(port));
+ pll_dw1 = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW1(port));
+ pll_dw2 = vlv_dpio_read(dev_priv, pipe, CHV_PLL_DW2(port));
+ mutex_unlock(&dev_priv->dpio_lock);
+
+ clock.m1 = (pll_dw1 & 0x7) == DPIO_CHV_M1_DIV_BY_2 ? 2 : 0;
+ clock.m2 = ((pll_dw0 & 0xff) << 22) | (pll_dw2 & 0x3fffff);
+ clock.n = (pll_dw1 >> DPIO_CHV_N_DIV_SHIFT) & 0xf;
+ clock.p1 = (cmn_dw13 >> DPIO_CHV_P1_DIV_SHIFT) & 0x7;
+ clock.p2 = (cmn_dw13 >> DPIO_CHV_P2_DIV_SHIFT) & 0x1f;
+
+ chv_clock(refclk, &clock);
+
+ /* clock.dot is the fast clock */
+ pipe_config->port_clock = clock.dot / 5;
+}
+
static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
struct intel_crtc_config *pipe_config)
{
@@ -5827,6 +6191,9 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
}
}
+ if (IS_VALLEYVIEW(dev) && (tmp & PIPECONF_COLOR_RANGE_SELECT))
+ pipe_config->limited_color_range = true;
+
if (INTEL_INFO(dev)->gen < 4)
pipe_config->double_wide = tmp & PIPECONF_DOUBLE_WIDE;
@@ -5862,7 +6229,9 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
DPLL_PORTB_READY_MASK);
}
- if (IS_VALLEYVIEW(dev))
+ if (IS_CHERRYVIEW(dev))
+ chv_crtc_clock_get(crtc, pipe_config);
+ else if (IS_VALLEYVIEW(dev))
vlv_crtc_clock_get(crtc, pipe_config);
else
i9xx_crtc_clock_get(crtc, pipe_config);
@@ -5983,8 +6352,7 @@ static void ironlake_init_pch_refclk(struct drm_device *dev)
if (intel_panel_use_ssc(dev_priv) && can_ssc) {
DRM_DEBUG_KMS("Using SSC on eDP\n");
val |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
- }
- else
+ } else
val |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
} else
val |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
@@ -6563,10 +6931,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
struct drm_framebuffer *fb)
{
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;
int num_connectors = 0;
intel_clock_t clock, reduced_clock;
u32 dpll = 0, fp = 0, fp2 = 0;
@@ -6574,7 +6939,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
bool is_lvds = false;
struct intel_encoder *encoder;
struct intel_shared_dpll *pll;
- int ret;
for_each_encoder_on_crtc(dev, crtc, encoder) {
switch (encoder->type) {
@@ -6624,36 +6988,18 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
pll = intel_get_shared_dpll(intel_crtc);
if (pll == NULL) {
DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n",
- pipe_name(pipe));
+ pipe_name(intel_crtc->pipe));
return -EINVAL;
}
} else
intel_put_shared_dpll(intel_crtc);
- if (intel_crtc->config.has_dp_encoder)
- intel_dp_set_m_n(intel_crtc);
-
if (is_lvds && has_reduced_clock && i915.powersave)
intel_crtc->lowfreq_avail = true;
else
intel_crtc->lowfreq_avail = false;
- intel_set_pipe_timings(intel_crtc);
-
- if (intel_crtc->config.has_pch_encoder) {
- intel_cpu_transcoder_set_m_n(intel_crtc,
- &intel_crtc->config.fdi_m_n);
- }
-
- ironlake_set_pipeconf(crtc);
-
- /* Set up the display plane register */
- I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE);
- POSTING_READ(DSPCNTR(plane));
-
- ret = intel_pipe_set_base(crtc, x, y, fb);
-
- return ret;
+ return 0;
}
static void intel_pch_transcoder_get_m_n(struct intel_crtc *crtc,
@@ -6831,6 +7177,9 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
break;
}
+ if (tmp & PIPECONF_COLOR_RANGE_SELECT)
+ pipe_config->limited_color_range = true;
+
if (I915_READ(PCH_TRANSCONF(crtc->pipe)) & TRANS_ENABLE) {
struct intel_shared_dpll *pll;
@@ -6880,10 +7229,8 @@ 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;
- unsigned long irqflags;
- uint32_t val;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head)
+ for_each_intel_crtc(dev, crtc)
WARN(crtc->active, "CRTC for pipe %c enabled\n",
pipe_name(crtc->pipe));
@@ -6902,14 +7249,29 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
"Utility pin enabled\n");
WARN(I915_READ(PCH_GTC_CTL) & PCH_GTC_ENABLE, "PCH GTC enabled\n");
- spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
- val = I915_READ(DEIMR);
- WARN((val | DE_PCH_EVENT_IVB) != 0xffffffff,
- "Unexpected DEIMR bits enabled: 0x%x\n", val);
- val = I915_READ(SDEIMR);
- WARN((val | SDE_HOTPLUG_MASK_CPT) != 0xffffffff,
- "Unexpected SDEIMR bits enabled: 0x%x\n", val);
- spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+ /*
+ * In theory we can still leave IRQs enabled, as long as only the HPD
+ * interrupts remain enabled. We used to check for that, but since it's
+ * 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");
+}
+
+static void hsw_write_dcomp(struct drm_i915_private *dev_priv, uint32_t val)
+{
+ struct drm_device *dev = dev_priv->dev;
+
+ if (IS_HASWELL(dev)) {
+ 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");
+ mutex_unlock(&dev_priv->rps.hw_lock);
+ } else {
+ I915_WRITE(D_COMP, val);
+ }
+ POSTING_READ(D_COMP);
}
/*
@@ -6949,11 +7311,7 @@ static void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
val = I915_READ(D_COMP);
val |= D_COMP_COMP_DISABLE;
- 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");
- mutex_unlock(&dev_priv->rps.hw_lock);
- POSTING_READ(D_COMP);
+ hsw_write_dcomp(dev_priv, val);
ndelay(100);
if (wait_for((I915_READ(D_COMP) & D_COMP_RCOMP_IN_PROGRESS) == 0, 1))
@@ -7008,11 +7366,7 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
val = I915_READ(D_COMP);
val |= D_COMP_COMP_FORCE;
val &= ~D_COMP_COMP_DISABLE;
- mutex_lock(&dev_priv->rps.hw_lock);
- if (sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_D_COMP, val))
- DRM_ERROR("Failed to enable D_COMP\n");
- mutex_unlock(&dev_priv->rps.hw_lock);
- POSTING_READ(D_COMP);
+ hsw_write_dcomp(dev_priv, val);
val = I915_READ(LCPLL_CTL);
val &= ~LCPLL_PLL_DISABLE;
@@ -7066,8 +7420,6 @@ void hsw_enable_pc8(struct drm_i915_private *dev_priv)
struct drm_device *dev = dev_priv->dev;
uint32_t val;
- WARN_ON(!HAS_PC8(dev));
-
DRM_DEBUG_KMS("Enabling package C8+\n");
if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) {
@@ -7077,7 +7429,6 @@ void hsw_enable_pc8(struct drm_i915_private *dev_priv)
}
lpt_disable_clkout_dp(dev);
- hsw_runtime_pm_disable_interrupts(dev);
hsw_disable_lcpll(dev_priv, true, true);
}
@@ -7086,12 +7437,9 @@ void hsw_disable_pc8(struct drm_i915_private *dev_priv)
struct drm_device *dev = dev_priv->dev;
uint32_t val;
- WARN_ON(!HAS_PC8(dev));
-
DRM_DEBUG_KMS("Disabling package C8+\n");
hsw_restore_lcpll(dev_priv);
- hsw_runtime_pm_restore_interrupts(dev);
lpt_init_pch_refclk(dev);
if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) {
@@ -7101,10 +7449,11 @@ void hsw_disable_pc8(struct drm_i915_private *dev_priv)
}
intel_prepare_ddi(dev);
- i915_gem_init_swizzling(dev);
- mutex_lock(&dev_priv->rps.hw_lock);
- gen6_update_ring_freq(dev);
- mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
+static void snb_modeset_global_resources(struct drm_device *dev)
+{
+ modeset_update_crtc_power_domains(dev);
}
static void haswell_modeset_global_resources(struct drm_device *dev)
@@ -7116,39 +7465,15 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc,
int x, int y,
struct drm_framebuffer *fb)
{
- 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 plane = intel_crtc->plane;
- int ret;
if (!intel_ddi_pll_select(intel_crtc))
return -EINVAL;
intel_ddi_pll_enable(intel_crtc);
- if (intel_crtc->config.has_dp_encoder)
- intel_dp_set_m_n(intel_crtc);
-
intel_crtc->lowfreq_avail = false;
- intel_set_pipe_timings(intel_crtc);
-
- if (intel_crtc->config.has_pch_encoder) {
- intel_cpu_transcoder_set_m_n(intel_crtc,
- &intel_crtc->config.fdi_m_n);
- }
-
- 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));
-
- ret = intel_pipe_set_base(crtc, x, y, fb);
-
- return ret;
+ return 0;
}
static bool haswell_get_pipe_config(struct intel_crtc *crtc,
@@ -7228,38 +7553,6 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
return true;
}
-static int intel_crtc_mode_set(struct drm_crtc *crtc,
- int x, int y,
- struct drm_framebuffer *fb)
-{
- struct drm_device *dev = crtc->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_encoder *encoder;
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- struct drm_display_mode *mode = &intel_crtc->config.requested_mode;
- int pipe = intel_crtc->pipe;
- int ret;
-
- drm_vblank_pre_modeset(dev, pipe);
-
- ret = dev_priv->display.crtc_mode_set(crtc, x, y, fb);
-
- drm_vblank_post_modeset(dev, pipe);
-
- if (ret != 0)
- return ret;
-
- for_each_encoder_on_crtc(dev, crtc, encoder) {
- DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n",
- encoder->base.base.id,
- drm_get_encoder_name(&encoder->base),
- mode->base.id, mode->name);
- encoder->mode_set(encoder);
- }
-
- return 0;
-}
-
static struct {
int clock;
u32 config;
@@ -7374,8 +7667,6 @@ static void haswell_write_eld(struct drm_connector *connector,
{
struct drm_i915_private *dev_priv = connector->dev->dev_private;
uint8_t *eld = connector->eld;
- struct drm_device *dev = crtc->dev;
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
uint32_t eldv;
uint32_t i;
int len;
@@ -7387,17 +7678,14 @@ static void haswell_write_eld(struct drm_connector *connector,
int aud_config = HSW_AUD_CFG(pipe);
int aud_cntrl_st2 = HSW_AUD_PIN_ELD_CP_VLD;
-
- DRM_DEBUG_DRIVER("HDMI: Haswell Audio initialize....\n");
-
/* Audio output enable */
DRM_DEBUG_DRIVER("HDMI audio: enable codec\n");
tmp = I915_READ(aud_cntrl_st2);
tmp |= (AUDIO_OUTPUT_ENABLE_A << (pipe * 4));
I915_WRITE(aud_cntrl_st2, tmp);
+ POSTING_READ(aud_cntrl_st2);
- /* Wait for 1 vertical blank */
- intel_wait_for_vblank(dev, pipe);
+ assert_pipe_disabled(dev_priv, to_intel_crtc(crtc)->pipe);
/* Set ELD valid state */
tmp = I915_READ(aud_cntrl_st2);
@@ -7417,7 +7705,6 @@ static void haswell_write_eld(struct drm_connector *connector,
DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe));
eldv = AUDIO_ELD_VALID_A << (pipe * 4);
- intel_crtc->eld_vld = true;
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
@@ -7564,9 +7851,9 @@ void intel_write_eld(struct drm_encoder *encoder,
DRM_DEBUG_DRIVER("ELD on [CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
connector->base.id,
- drm_get_connector_name(connector),
+ connector->name,
connector->encoder->base.id,
- drm_get_encoder_name(connector->encoder));
+ connector->encoder->name);
connector->eld[6] = drm_av_sync_delay(connector, mode) / 2;
@@ -7579,29 +7866,33 @@ 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);
- bool visible = base != 0;
- u32 cntl;
+ uint32_t cntl;
- if (intel_crtc->cursor_visible == visible)
- return;
-
- cntl = I915_READ(_CURACNTR);
- if (visible) {
+ 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;
+ }
+
I915_WRITE(_CURABASE, base);
+ POSTING_READ(_CURABASE);
+ }
- cntl &= ~(CURSOR_FORMAT_MASK);
- /* XXX width must be 64, stride 256 => 0x00 << 28 */
- cntl |= CURSOR_ENABLE |
+ /* XXX width must be 64, stride 256 => 0x00 << 28 */
+ cntl = 0;
+ if (base)
+ cntl = (CURSOR_ENABLE |
CURSOR_GAMMA_ENABLE |
- CURSOR_FORMAT_ARGB;
- } else
- cntl &= ~(CURSOR_ENABLE | CURSOR_GAMMA_ENABLE);
- I915_WRITE(_CURACNTR, cntl);
-
- intel_crtc->cursor_visible = visible;
+ CURSOR_FORMAT_ARGB);
+ if (intel_crtc->cursor_cntl != cntl) {
+ I915_WRITE(_CURACNTR, cntl);
+ POSTING_READ(_CURACNTR);
+ intel_crtc->cursor_cntl = cntl;
+ }
}
static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
@@ -7610,16 +7901,12 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
- bool visible = base != 0;
+ uint32_t cntl;
- if (intel_crtc->cursor_visible != visible) {
- int16_t width = intel_crtc->cursor_width;
- uint32_t cntl = I915_READ(CURCNTR(pipe));
- if (base) {
- cntl &= ~(CURSOR_MODE | MCURSOR_PIPE_SELECT);
- cntl |= MCURSOR_GAMMA_ENABLE;
-
- switch (width) {
+ cntl = 0;
+ if (base) {
+ cntl = MCURSOR_GAMMA_ENABLE;
+ switch (intel_crtc->cursor_width) {
case 64:
cntl |= CURSOR_MODE_64_ARGB_AX;
break;
@@ -7632,18 +7919,16 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
default:
WARN_ON(1);
return;
- }
- cntl |= pipe << 28; /* Connect to correct pipe */
- } else {
- cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
- cntl |= CURSOR_MODE_DISABLE;
}
+ cntl |= pipe << 28; /* Connect to correct pipe */
+ }
+ if (intel_crtc->cursor_cntl != cntl) {
I915_WRITE(CURCNTR(pipe), cntl);
-
- intel_crtc->cursor_visible = visible;
+ POSTING_READ(CURCNTR(pipe));
+ intel_crtc->cursor_cntl = cntl;
}
+
/* and commit changes on next vblank */
- POSTING_READ(CURCNTR(pipe));
I915_WRITE(CURBASE(pipe), base);
POSTING_READ(CURBASE(pipe));
}
@@ -7654,15 +7939,12 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
- bool visible = base != 0;
-
- if (intel_crtc->cursor_visible != visible) {
- int16_t width = intel_crtc->cursor_width;
- uint32_t cntl = I915_READ(CURCNTR_IVB(pipe));
- if (base) {
- cntl &= ~CURSOR_MODE;
- cntl |= MCURSOR_GAMMA_ENABLE;
- switch (width) {
+ uint32_t cntl;
+
+ cntl = 0;
+ if (base) {
+ cntl = MCURSOR_GAMMA_ENABLE;
+ switch (intel_crtc->cursor_width) {
case 64:
cntl |= CURSOR_MODE_64_ARGB_AX;
break;
@@ -7675,23 +7957,20 @@ static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
default:
WARN_ON(1);
return;
- }
- } else {
- cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
- cntl |= CURSOR_MODE_DISABLE;
- }
- if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
- cntl |= CURSOR_PIPE_CSC_ENABLE;
- cntl &= ~CURSOR_TRICKLE_FEED_DISABLE;
}
- I915_WRITE(CURCNTR_IVB(pipe), cntl);
+ }
+ if (IS_HASWELL(dev) || IS_BROADWELL(dev))
+ cntl |= CURSOR_PIPE_CSC_ENABLE;
- intel_crtc->cursor_visible = visible;
+ if (intel_crtc->cursor_cntl != cntl) {
+ I915_WRITE(CURCNTR(pipe), cntl);
+ POSTING_READ(CURCNTR(pipe));
+ intel_crtc->cursor_cntl = cntl;
}
+
/* and commit changes on next vblank */
- POSTING_READ(CURCNTR_IVB(pipe));
- I915_WRITE(CURBASE_IVB(pipe), base);
- POSTING_READ(CURBASE_IVB(pipe));
+ I915_WRITE(CURBASE(pipe), base);
+ POSTING_READ(CURBASE(pipe));
}
/* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */
@@ -7705,7 +7984,6 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
int x = intel_crtc->cursor_x;
int y = intel_crtc->cursor_y;
u32 base = 0, pos = 0;
- bool visible;
if (on)
base = intel_crtc->cursor_addr;
@@ -7734,20 +8012,18 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
}
pos |= y << CURSOR_Y_SHIFT;
- visible = base != 0;
- if (!visible && !intel_crtc->cursor_visible)
+ if (base == 0 && intel_crtc->cursor_base == 0)
return;
- if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev)) {
- I915_WRITE(CURPOS_IVB(pipe), pos);
+ I915_WRITE(CURPOS(pipe), pos);
+
+ if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev))
ivb_update_cursor(crtc, base);
- } else {
- I915_WRITE(CURPOS(pipe), pos);
- if (IS_845G(dev) || IS_I865G(dev))
- i845_update_cursor(crtc, base);
- else
- i9xx_update_cursor(crtc, base);
- }
+ else 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,
@@ -8015,7 +8291,8 @@ mode_fits_in_fbdev(struct drm_device *dev,
bool intel_get_load_detect_pipe(struct drm_connector *connector,
struct drm_display_mode *mode,
- struct intel_load_detect_pipe *old)
+ struct intel_load_detect_pipe *old,
+ struct drm_modeset_acquire_ctx *ctx)
{
struct intel_crtc *intel_crtc;
struct intel_encoder *intel_encoder =
@@ -8025,11 +8302,19 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
struct drm_crtc *crtc = NULL;
struct drm_device *dev = encoder->dev;
struct drm_framebuffer *fb;
- int i = -1;
+ struct drm_mode_config *config = &dev->mode_config;
+ int ret, i = -1;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
- connector->base.id, drm_get_connector_name(connector),
- encoder->base.id, drm_get_encoder_name(encoder));
+ 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)
+ goto fail_unlock;
/*
* Algorithm gets a little messy:
@@ -8045,7 +8330,9 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
if (encoder->crtc) {
crtc = encoder->crtc;
- mutex_lock(&crtc->mutex);
+ ret = drm_modeset_lock(&crtc->mutex, ctx);
+ if (ret)
+ goto fail_unlock;
old->dpms_mode = connector->dpms;
old->load_detect_temp = false;
@@ -8058,7 +8345,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
}
/* Find an unused one (if possible) */
- list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, possible_crtc) {
i++;
if (!(encoder->possible_crtcs & (1 << i)))
continue;
@@ -8073,10 +8360,12 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
*/
if (!crtc) {
DRM_DEBUG_KMS("no pipe available for load-detect\n");
- return false;
+ goto fail_unlock;
}
- mutex_lock(&crtc->mutex);
+ ret = drm_modeset_lock(&crtc->mutex, ctx);
+ if (ret)
+ goto fail_unlock;
intel_encoder->new_crtc = to_intel_crtc(crtc);
to_intel_connector(connector)->new_encoder = intel_encoder;
@@ -8126,12 +8415,21 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
intel_crtc->new_config = &intel_crtc->config;
else
intel_crtc->new_config = NULL;
- mutex_unlock(&crtc->mutex);
+fail_unlock:
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(ctx);
+ 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 intel_load_detect_pipe *old,
+ struct drm_modeset_acquire_ctx *ctx)
{
struct intel_encoder *intel_encoder =
intel_attached_encoder(connector);
@@ -8140,8 +8438,8 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
- connector->base.id, drm_get_connector_name(connector),
- encoder->base.id, drm_get_encoder_name(encoder));
+ connector->base.id, connector->name,
+ encoder->base.id, encoder->name);
if (old->load_detect_temp) {
to_intel_connector(connector)->new_encoder = NULL;
@@ -8155,7 +8453,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
drm_framebuffer_unreference(old->release_fb);
}
- mutex_unlock(&crtc->mutex);
+ goto unlock;
return;
}
@@ -8163,7 +8461,9 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
if (old->dpms_mode != DRM_MODE_DPMS_ON)
connector->funcs->dpms(connector, old->dpms_mode);
- mutex_unlock(&crtc->mutex);
+unlock:
+ drm_modeset_drop_locks(ctx);
+ drm_modeset_acquire_fini(ctx);
}
static int i9xx_pll_refclk(struct drm_device *dev,
@@ -8449,7 +8749,7 @@ void intel_mark_idle(struct drm_device *dev)
if (!i915.powersave)
goto out;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, crtc) {
if (!crtc->primary->fb)
continue;
@@ -8464,7 +8764,7 @@ out:
}
void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
- struct intel_ring_buffer *ring)
+ struct intel_engine_cs *ring)
{
struct drm_device *dev = obj->base.dev;
struct drm_crtc *crtc;
@@ -8472,7 +8772,7 @@ void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
if (!i915.powersave)
return;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, crtc) {
if (!crtc->primary->fb)
continue;
@@ -8560,7 +8860,7 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
if (work->event)
drm_send_vblank_event(dev, intel_crtc->pipe, work->event);
- drm_vblank_put(dev, intel_crtc->pipe);
+ drm_crtc_vblank_put(crtc);
spin_unlock_irqrestore(&dev->event_lock, flags);
@@ -8587,6 +8887,48 @@ void intel_finish_page_flip_plane(struct drm_device *dev, int plane)
do_intel_finish_page_flip(dev, crtc);
}
+/* Is 'a' after or equal to 'b'? */
+static bool g4x_flip_count_after_eq(u32 a, u32 b)
+{
+ return !((a - b) & 0x80000000);
+}
+
+static bool page_flip_finished(struct intel_crtc *crtc)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /*
+ * The relevant registers doen't exist on pre-ctg.
+ * As the flip done interrupt doesn't trigger for mmio
+ * flips on gmch platforms, a flip count check isn't
+ * really needed there. But since ctg has the registers,
+ * include it in the check anyway.
+ */
+ if (INTEL_INFO(dev)->gen < 5 && !IS_G4X(dev))
+ return true;
+
+ /*
+ * A DSPSURFLIVE check isn't enough in case the mmio and CS flips
+ * used the same base address. In that case the mmio flip might
+ * have completed, but the CS hasn't even executed the flip yet.
+ *
+ * A flip count check isn't enough as the CS might have updated
+ * the base address just after start of vblank, but before we
+ * managed to process the interrupt. This means we'd complete the
+ * CS flip too soon.
+ *
+ * Combining both checks should get us a good enough result. It may
+ * still happen that the CS flip has been executed, but has not
+ * yet actually completed. But in case the base address is the same
+ * anyway, we don't really care.
+ */
+ return (I915_READ(DSPSURFLIVE(crtc->plane)) & ~0xfff) ==
+ crtc->unpin_work->gtt_offset &&
+ g4x_flip_count_after_eq(I915_READ(PIPE_FLIPCOUNT_GM45(crtc->pipe)),
+ crtc->unpin_work->flip_count);
+}
+
void intel_prepare_page_flip(struct drm_device *dev, int plane)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -8599,12 +8941,12 @@ void intel_prepare_page_flip(struct drm_device *dev, int plane)
* is also accompanied by a spurious intel_prepare_page_flip().
*/
spin_lock_irqsave(&dev->event_lock, flags);
- if (intel_crtc->unpin_work)
+ if (intel_crtc->unpin_work && page_flip_finished(intel_crtc))
atomic_inc_not_zero(&intel_crtc->unpin_work->pending);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
-inline static void intel_mark_page_flip_active(struct intel_crtc *intel_crtc)
+static inline void intel_mark_page_flip_active(struct intel_crtc *intel_crtc)
{
/* Ensure that the work item is consistent when activating it ... */
smp_wmb();
@@ -8617,21 +8959,16 @@ static int intel_gen2_queue_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);
u32 flip_mask;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
int ret;
- ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
- if (ret)
- goto err;
-
ret = intel_ring_begin(ring, 6);
if (ret)
- goto err_unpin;
+ return ret;
/* Can't queue multiple flips, so wait for the previous
* one to finish before executing the next.
@@ -8645,38 +8982,28 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
intel_ring_emit(ring, MI_DISPLAY_FLIP |
MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
intel_ring_emit(ring, fb->pitches[0]);
- intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+ intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
intel_ring_emit(ring, 0); /* aux display base address, unused */
intel_mark_page_flip_active(intel_crtc);
__intel_ring_advance(ring);
return 0;
-
-err_unpin:
- intel_unpin_fb_obj(obj);
-err:
- return ret;
}
static int intel_gen3_queue_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);
u32 flip_mask;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
int ret;
- ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
- if (ret)
- goto err;
-
ret = intel_ring_begin(ring, 6);
if (ret)
- goto err_unpin;
+ return ret;
if (intel_crtc->plane)
flip_mask = MI_WAIT_FOR_PLANE_B_FLIP;
@@ -8687,38 +9014,29 @@ static int intel_gen3_queue_flip(struct drm_device *dev,
intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 |
MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
intel_ring_emit(ring, fb->pitches[0]);
- intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+ intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
intel_ring_emit(ring, MI_NOOP);
intel_mark_page_flip_active(intel_crtc);
__intel_ring_advance(ring);
return 0;
-
-err_unpin:
- intel_unpin_fb_obj(obj);
-err:
- return ret;
}
static int intel_gen4_queue_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);
uint32_t pf, pipesrc;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
int ret;
- ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
- if (ret)
- goto err;
-
ret = intel_ring_begin(ring, 4);
if (ret)
- goto err_unpin;
+ return ret;
/* i965+ uses the linear or tiled offsets from the
* Display Registers (which do not change across a page-flip)
@@ -8727,8 +9045,7 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
intel_ring_emit(ring, MI_DISPLAY_FLIP |
MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
intel_ring_emit(ring, fb->pitches[0]);
- intel_ring_emit(ring,
- (i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset) |
+ intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset |
obj->tiling_mode);
/* XXX Enabling the panel-fitter across page-flip is so far
@@ -8742,37 +9059,28 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
intel_mark_page_flip_active(intel_crtc);
__intel_ring_advance(ring);
return 0;
-
-err_unpin:
- intel_unpin_fb_obj(obj);
-err:
- return ret;
}
static int intel_gen6_queue_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);
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
uint32_t pf, pipesrc;
int ret;
- ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
- if (ret)
- goto err;
-
ret = intel_ring_begin(ring, 4);
if (ret)
- goto err_unpin;
+ return ret;
intel_ring_emit(ring, MI_DISPLAY_FLIP |
MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
intel_ring_emit(ring, fb->pitches[0] | obj->tiling_mode);
- intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+ intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
/* Contrary to the suggestions in the documentation,
* "Enable Panel Fitter" does not seem to be required when page
@@ -8787,34 +9095,20 @@ static int intel_gen6_queue_flip(struct drm_device *dev,
intel_mark_page_flip_active(intel_crtc);
__intel_ring_advance(ring);
return 0;
-
-err_unpin:
- intel_unpin_fb_obj(obj);
-err:
- return ret;
}
static int intel_gen7_queue_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);
- struct intel_ring_buffer *ring;
uint32_t plane_bit = 0;
int len, ret;
- ring = obj->ring;
- if (IS_VALLEYVIEW(dev) || ring == NULL || ring->id != RCS)
- ring = &dev_priv->ring[BCS];
-
- ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
- if (ret)
- goto err;
-
- switch(intel_crtc->plane) {
+ switch (intel_crtc->plane) {
case PLANE_A:
plane_bit = MI_DISPLAY_FLIP_IVB_PLANE_A;
break;
@@ -8826,13 +9120,20 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
break;
default:
WARN_ONCE(1, "unknown plane in flip command\n");
- ret = -ENODEV;
- goto err_unpin;
+ return -ENODEV;
}
len = 4;
- if (ring->id == RCS)
+ if (ring->id == RCS) {
len += 6;
+ /*
+ * On Gen 8, SRM is now taking an extra dword to accommodate
+ * 48bits addresses, and we need a NOOP for the batch size to
+ * stay even.
+ */
+ if (IS_GEN8(dev))
+ len += 2;
+ }
/*
* BSpec MI_DISPLAY_FLIP for IVB:
@@ -8846,11 +9147,11 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
*/
ret = intel_ring_cacheline_align(ring);
if (ret)
- goto err_unpin;
+ return ret;
ret = intel_ring_begin(ring, len);
if (ret)
- goto err_unpin;
+ return ret;
/* Unmask the flip-done completion message. Note that the bspec says that
* we should do this for both the BCS and RCS, and that we must not unmask
@@ -8867,31 +9168,35 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
intel_ring_emit(ring, ~(DERRMR_PIPEA_PRI_FLIP_DONE |
DERRMR_PIPEB_PRI_FLIP_DONE |
DERRMR_PIPEC_PRI_FLIP_DONE));
- intel_ring_emit(ring, MI_STORE_REGISTER_MEM(1) |
- MI_SRM_LRM_GLOBAL_GTT);
+ if (IS_GEN8(dev))
+ intel_ring_emit(ring, MI_STORE_REGISTER_MEM_GEN8(1) |
+ MI_SRM_LRM_GLOBAL_GTT);
+ else
+ intel_ring_emit(ring, MI_STORE_REGISTER_MEM(1) |
+ MI_SRM_LRM_GLOBAL_GTT);
intel_ring_emit(ring, DERRMR);
intel_ring_emit(ring, ring->scratch.gtt_offset + 256);
+ if (IS_GEN8(dev)) {
+ intel_ring_emit(ring, 0);
+ intel_ring_emit(ring, MI_NOOP);
+ }
}
intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | plane_bit);
intel_ring_emit(ring, (fb->pitches[0] | obj->tiling_mode));
- intel_ring_emit(ring, i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+ intel_ring_emit(ring, intel_crtc->unpin_work->gtt_offset);
intel_ring_emit(ring, (MI_NOOP));
intel_mark_page_flip_active(intel_crtc);
__intel_ring_advance(ring);
return 0;
-
-err_unpin:
- intel_unpin_fb_obj(obj);
-err:
- return ret;
}
static int intel_default_queue_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)
{
return -ENODEV;
@@ -8908,6 +9213,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_unpin_work *work;
+ struct intel_engine_cs *ring;
unsigned long flags;
int ret;
@@ -8936,7 +9242,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
work->old_fb_obj = to_intel_framebuffer(old_fb)->obj;
INIT_WORK(&work->work, intel_unpin_work_fn);
- ret = drm_vblank_get(dev, intel_crtc->pipe);
+ ret = drm_crtc_vblank_get(crtc);
if (ret)
goto free_work;
@@ -8945,7 +9251,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
if (intel_crtc->unpin_work) {
spin_unlock_irqrestore(&dev->event_lock, flags);
kfree(work);
- drm_vblank_put(dev, intel_crtc->pipe);
+ drm_crtc_vblank_put(crtc);
DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
return -EBUSY;
@@ -8973,10 +9279,30 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
atomic_inc(&intel_crtc->unpin_work_count);
intel_crtc->reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter);
- ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, page_flip_flags);
+ if (INTEL_INFO(dev)->gen >= 5 || IS_G4X(dev))
+ work->flip_count = I915_READ(PIPE_FLIPCOUNT_GM45(intel_crtc->pipe)) + 1;
+
+ if (IS_VALLEYVIEW(dev)) {
+ ring = &dev_priv->ring[BCS];
+ } else if (INTEL_INFO(dev)->gen >= 7) {
+ ring = obj->ring;
+ if (ring == NULL || ring->id != RCS)
+ ring = &dev_priv->ring[BCS];
+ } else {
+ ring = &dev_priv->ring[RCS];
+ }
+
+ ret = intel_pin_and_fence_fb_obj(dev, obj, ring);
if (ret)
goto cleanup_pending;
+ 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;
+
intel_disable_fbc(dev);
intel_mark_fb_busy(obj, NULL);
mutex_unlock(&dev->struct_mutex);
@@ -8985,6 +9311,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
return 0;
+cleanup_unpin:
+ intel_unpin_fb_obj(obj);
cleanup_pending:
atomic_dec(&intel_crtc->unpin_work_count);
crtc->primary->fb = old_fb;
@@ -8997,7 +9325,7 @@ cleanup:
intel_crtc->unpin_work = NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
- drm_vblank_put(dev, intel_crtc->pipe);
+ drm_crtc_vblank_put(crtc);
free_work:
kfree(work);
@@ -9040,8 +9368,7 @@ static void intel_modeset_update_staged_output_state(struct drm_device *dev)
to_intel_crtc(encoder->base.crtc);
}
- list_for_each_entry(crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, crtc) {
crtc->new_enabled = crtc->base.enabled;
if (crtc->new_enabled)
@@ -9072,21 +9399,20 @@ static void intel_modeset_commit_output_state(struct drm_device *dev)
encoder->base.crtc = &encoder->new_crtc->base;
}
- list_for_each_entry(crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, crtc) {
crtc->base.enabled = crtc->new_enabled;
}
}
static void
-connected_sink_compute_bpp(struct intel_connector * connector,
+connected_sink_compute_bpp(struct intel_connector *connector,
struct intel_crtc_config *pipe_config)
{
int bpp = pipe_config->pipe_bpp;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] checking for sink bpp constrains\n",
connector->base.base.id,
- drm_get_connector_name(&connector->base));
+ connector->base.name);
/* Don't use an invalid EDID bpc value */
if (connector->base.display_info.bpc &&
@@ -9427,8 +9753,7 @@ intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes,
}
/* Check for pipes that will be enabled/disabled ... */
- list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, intel_crtc) {
if (intel_crtc->base.enabled == intel_crtc->new_enabled)
continue;
@@ -9501,8 +9826,7 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes)
intel_modeset_commit_output_state(dev);
/* Double check state. */
- list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, intel_crtc) {
WARN_ON(intel_crtc->base.enabled != intel_crtc_in_use(&intel_crtc->base));
WARN_ON(intel_crtc->new_config &&
intel_crtc->new_config != &intel_crtc->config);
@@ -9631,6 +9955,12 @@ intel_pipe_config_compare(struct drm_device *dev,
PIPE_CONF_CHECK_I(adjusted_mode.crtc_vsync_end);
PIPE_CONF_CHECK_I(pixel_multiplier);
+ PIPE_CONF_CHECK_I(has_hdmi_sink);
+ if ((INTEL_INFO(dev)->gen < 8 && !IS_HASWELL(dev)) ||
+ IS_VALLEYVIEW(dev))
+ PIPE_CONF_CHECK_I(limited_color_range);
+
+ PIPE_CONF_CHECK_I(has_audio);
PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags,
DRM_MODE_FLAG_INTERLACE);
@@ -9728,7 +10058,7 @@ check_encoder_state(struct drm_device *dev)
DRM_DEBUG_KMS("[ENCODER:%d:%s]\n",
encoder->base.base.id,
- drm_get_encoder_name(&encoder->base));
+ encoder->base.name);
WARN(&encoder->new_crtc->base != encoder->base.crtc,
"encoder's stage crtc doesn't match current crtc\n");
@@ -9780,8 +10110,7 @@ check_crtc_state(struct drm_device *dev)
struct intel_encoder *encoder;
struct intel_crtc_config pipe_config;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, crtc) {
bool enabled = false;
bool active = false;
@@ -9870,8 +10199,7 @@ check_shared_dpll_state(struct drm_device *dev)
"pll on state mismatch (expected %i, found %i)\n",
pll->on, active);
- list_for_each_entry(crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, crtc) {
if (crtc->base.enabled && intel_crtc_to_shared_dpll(crtc) == pll)
enabled_crtcs++;
if (crtc->active && intel_crtc_to_shared_dpll(crtc) == pll)
@@ -9911,6 +10239,44 @@ void ironlake_check_encoder_dotclock(const struct intel_crtc_config *pipe_config
pipe_config->adjusted_mode.crtc_clock, dotclock);
}
+static void update_scanline_offset(struct intel_crtc *crtc)
+{
+ struct drm_device *dev = crtc->base.dev;
+
+ /*
+ * The scanline counter increments at the leading edge of hsync.
+ *
+ * On most platforms it starts counting from vtotal-1 on the
+ * first active line. That means the scanline counter value is
+ * always one less than what we would expect. Ie. just after
+ * start of vblank, which also occurs at start of hsync (on the
+ * last active line), the scanline counter will read vblank_start-1.
+ *
+ * On gen2 the scanline counter starts counting from 1 instead
+ * of vtotal-1, so we have to subtract one (or rather add vtotal-1
+ * to keep the value positive), instead of adding one.
+ *
+ * On HSW+ the behaviour of the scanline counter depends on the output
+ * type. For DP ports it behaves like most other platforms, but on HDMI
+ * there's an extra 1 line difference. So we need to add two instead of
+ * one to the value.
+ */
+ if (IS_GEN2(dev)) {
+ const struct drm_display_mode *mode = &crtc->config.adjusted_mode;
+ int vtotal;
+
+ vtotal = mode->crtc_vtotal;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ vtotal /= 2;
+
+ crtc->scanline_offset = vtotal - 1;
+ } else if (HAS_DDI(dev) &&
+ intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) {
+ crtc->scanline_offset = 2;
+ } else
+ crtc->scanline_offset = 1;
+}
+
static int __intel_set_mode(struct drm_crtc *crtc,
struct drm_display_mode *mode,
int x, int y, struct drm_framebuffer *fb)
@@ -10002,15 +10368,38 @@ static int __intel_set_mode(struct drm_crtc *crtc,
* on the DPLL.
*/
for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) {
- ret = intel_crtc_mode_set(&intel_crtc->base,
- x, y, fb);
+ struct drm_framebuffer *old_fb;
+
+ mutex_lock(&dev->struct_mutex);
+ ret = intel_pin_and_fence_fb_obj(dev,
+ to_intel_framebuffer(fb)->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);
+ mutex_unlock(&dev->struct_mutex);
+
+ crtc->primary->fb = fb;
+ crtc->x = x;
+ crtc->y = y;
+
+ ret = dev_priv->display.crtc_mode_set(&intel_crtc->base,
+ x, y, fb);
if (ret)
goto done;
}
/* Now enable the clocks, plane, pipe, and connectors that we set up. */
- for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc)
+ for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) {
+ update_scanline_offset(intel_crtc);
+
dev_priv->display.crtc_enable(&intel_crtc->base);
+ }
/* FIXME: add subpixel order */
done:
@@ -10086,7 +10475,7 @@ static int intel_set_config_save_state(struct drm_device *dev,
* restored, not the drivers personal bookkeeping.
*/
count = 0;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, crtc) {
config->save_crtc_enabled[count++] = crtc->enabled;
}
@@ -10112,7 +10501,7 @@ static void intel_set_config_restore_state(struct drm_device *dev,
int count;
count = 0;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+ for_each_intel_crtc(dev, crtc) {
crtc->new_enabled = config->save_crtc_enabled[count++];
if (crtc->new_enabled)
@@ -10236,7 +10625,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
connector->base.base.id,
- drm_get_connector_name(&connector->base));
+ connector->base.name);
}
@@ -10271,7 +10660,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
connector->base.base.id,
- drm_get_connector_name(&connector->base),
+ connector->base.name,
new_crtc->base.id);
}
@@ -10302,8 +10691,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
}
/* Now we've also updated encoder->new_crtc for all encoders. */
- list_for_each_entry(crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, crtc) {
crtc->new_enabled = false;
list_for_each_entry(encoder,
@@ -10516,7 +10904,7 @@ static void ibx_pch_dpll_disable(struct drm_i915_private *dev_priv,
struct intel_crtc *crtc;
/* Make sure no transcoder isn't still depending on us. */
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) {
+ for_each_intel_crtc(dev, crtc) {
if (intel_crtc_to_shared_dpll(crtc) == pll)
assert_pch_transcoder_disabled(dev_priv, crtc->pipe);
}
@@ -10573,16 +10961,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
- if (IS_GEN2(dev)) {
- intel_crtc->max_cursor_width = GEN2_CURSOR_WIDTH;
- intel_crtc->max_cursor_height = GEN2_CURSOR_HEIGHT;
- } else {
- intel_crtc->max_cursor_width = CURSOR_WIDTH;
- intel_crtc->max_cursor_height = CURSOR_HEIGHT;
- }
- dev->mode_config.cursor_width = intel_crtc->max_cursor_width;
- dev->mode_config.cursor_height = intel_crtc->max_cursor_height;
-
drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
for (i = 0; i < 256; i++) {
intel_crtc->lut_r[i] = i;
@@ -10601,19 +10979,27 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
intel_crtc->plane = !pipe;
}
+ intel_crtc->cursor_base = ~0;
+ intel_crtc->cursor_cntl = ~0;
+
+ init_waitqueue_head(&intel_crtc->vbl_wait);
+
BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL);
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
+
+ WARN_ON(drm_crtc_index(&intel_crtc->base) != intel_crtc->pipe);
}
enum pipe intel_get_pipe_from_connector(struct intel_connector *connector)
{
struct drm_encoder *encoder = connector->base.encoder;
+ struct drm_device *dev = connector->base.dev;
- WARN_ON(!mutex_is_locked(&connector->base.dev->mode_config.mutex));
+ WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
if (!encoder)
return INVALID_PIPE;
@@ -10709,7 +11095,7 @@ static void intel_setup_outputs(struct drm_device *dev)
intel_lvds_init(dev);
- if (!IS_ULT(dev))
+ if (!IS_ULT(dev) && !IS_CHERRYVIEW(dev) && dev_priv->vbt.int_crt_support)
intel_crt_init(dev);
if (HAS_DDI(dev)) {
@@ -10773,6 +11159,15 @@ static void intel_setup_outputs(struct drm_device *dev)
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) {
+ 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);
+ }
+ }
+
intel_dsi_init(dev);
} else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) {
bool found = false;
@@ -11002,6 +11397,8 @@ static void intel_init_display(struct drm_device *dev)
if (HAS_PCH_SPLIT(dev) || IS_G4X(dev))
dev_priv->display.find_dpll = g4x_find_best_dpll;
+ else if (IS_CHERRYVIEW(dev))
+ dev_priv->display.find_dpll = chv_find_best_dpll;
else if (IS_VALLEYVIEW(dev))
dev_priv->display.find_dpll = vlv_find_best_dpll;
else if (IS_PINEVIEW(dev))
@@ -11083,6 +11480,8 @@ static void intel_init_display(struct drm_device *dev)
} 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;
@@ -11211,9 +11610,6 @@ 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 },
-
/* Lenovo U160 cannot use SSC on LVDS */
{ 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
@@ -11287,9 +11683,7 @@ void intel_modeset_init_hw(struct drm_device *dev)
intel_reset_dpio(dev);
- mutex_lock(&dev->struct_mutex);
intel_enable_gt_powersave(dev);
- mutex_unlock(&dev->struct_mutex);
}
void intel_modeset_suspend_hw(struct drm_device *dev)
@@ -11333,6 +11727,15 @@ void intel_modeset_init(struct drm_device *dev)
dev->mode_config.max_width = 8192;
dev->mode_config.max_height = 8192;
}
+
+ if (IS_GEN2(dev)) {
+ dev->mode_config.cursor_width = GEN2_CURSOR_WIDTH;
+ dev->mode_config.cursor_height = GEN2_CURSOR_HEIGHT;
+ } else {
+ dev->mode_config.cursor_width = MAX_CURSOR_WIDTH;
+ dev->mode_config.cursor_height = MAX_CURSOR_HEIGHT;
+ }
+
dev->mode_config.fb_base = dev_priv->gtt.mappable_base;
DRM_DEBUG_KMS("%d display pipe%s available.\n",
@@ -11362,12 +11765,11 @@ void intel_modeset_init(struct drm_device *dev)
/* Just in case the BIOS is doing something questionable. */
intel_disable_fbc(dev);
- mutex_lock(&dev->mode_config.mutex);
+ drm_modeset_lock_all(dev);
intel_modeset_setup_hw_state(dev, false);
- mutex_unlock(&dev->mode_config.mutex);
+ drm_modeset_unlock_all(dev);
- list_for_each_entry(crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, crtc) {
if (!crtc->active)
continue;
@@ -11395,6 +11797,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;
/* 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
@@ -11411,8 +11814,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))
- intel_release_load_detect_pipe(crt, &load_detect_temp);
+ if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, &ctx))
+ intel_release_load_detect_pipe(crt, &load_detect_temp, &ctx);
}
@@ -11447,6 +11850,12 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
reg = PIPECONF(crtc->config.cpu_transcoder);
I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK);
+ /* restore vblank interrupts to correct state */
+ if (crtc->active)
+ drm_vblank_on(dev, crtc->pipe);
+ else
+ drm_vblank_off(dev, crtc->pipe);
+
/* We need to sanitize the plane -> pipe mapping first because this will
* disable the crtc (and hence change the state) if it is wrong. Note
* that gen4+ has a fixed plane -> pipe mapping. */
@@ -11525,16 +11934,25 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
encoder->base.crtc = NULL;
}
}
- if (crtc->active) {
+
+ if (crtc->active || IS_VALLEYVIEW(dev) || INTEL_INFO(dev)->gen < 5) {
/*
* We start out with underrun reporting disabled to avoid races.
* For correct bookkeeping mark this on active crtcs.
*
+ * Also on gmch platforms we dont have any hardware bits to
+ * disable the underrun reporting. Which means we need to start
+ * out with underrun reporting disabled also on inactive pipes,
+ * since otherwise we'll complain about the garbage we read when
+ * e.g. coming up after runtime pm.
+ *
* No protection against concurrent access is required - at
* worst a fifo underrun happens which also sets this to false.
*/
crtc->cpu_fifo_underrun_disabled = true;
crtc->pch_fifo_underrun_disabled = true;
+
+ update_scanline_offset(crtc);
}
}
@@ -11552,7 +11970,7 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
if (encoder->connectors_active && !has_active_crtc) {
DRM_DEBUG_KMS("[ENCODER:%d:%s] has active connectors but no active pipe!\n",
encoder->base.base.id,
- drm_get_encoder_name(&encoder->base));
+ encoder->base.name);
/* Connector is active, but has no active pipe. This is
* fallout from our resume register restoring. Disable
@@ -11560,7 +11978,7 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
if (encoder->base.crtc) {
DRM_DEBUG_KMS("[ENCODER:%d:%s] manually disabled\n",
encoder->base.base.id,
- drm_get_encoder_name(&encoder->base));
+ encoder->base.name);
encoder->disable(encoder);
}
encoder->base.crtc = NULL;
@@ -11611,6 +12029,16 @@ void i915_redisable_vga(struct drm_device *dev)
i915_redisable_vga_power_on(dev);
}
+static bool primary_get_hw_state(struct intel_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+
+ if (!crtc->active)
+ return false;
+
+ return I915_READ(DSPCNTR(crtc->plane)) & DISPLAY_PLANE_ENABLE;
+}
+
static void intel_modeset_readout_hw_state(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -11620,8 +12048,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
struct intel_connector *connector;
int i;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, crtc) {
memset(&crtc->config, 0, sizeof(crtc->config));
crtc->config.quirks |= PIPE_CONFIG_QUIRK_INHERITED_MODE;
@@ -11630,7 +12057,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
&crtc->config);
crtc->base.enabled = crtc->active;
- crtc->primary_enabled = crtc->active;
+ crtc->primary_enabled = primary_get_hw_state(crtc);
DRM_DEBUG_KMS("[CRTC:%d] hw state readout: %s\n",
crtc->base.base.id,
@@ -11646,8 +12073,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
pll->on = pll->get_hw_state(dev_priv, pll, &pll->hw_state);
pll->active = 0;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, crtc) {
if (crtc->active && intel_crtc_to_shared_dpll(crtc) == pll)
pll->active++;
}
@@ -11672,7 +12098,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
encoder->connectors_active = false;
DRM_DEBUG_KMS("[ENCODER:%d:%s] hw state readout: %s, pipe %c\n",
encoder->base.base.id,
- drm_get_encoder_name(&encoder->base),
+ encoder->base.name,
encoder->base.crtc ? "enabled" : "disabled",
pipe_name(pipe));
}
@@ -11689,7 +12115,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
}
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] hw state readout: %s\n",
connector->base.base.id,
- drm_get_connector_name(&connector->base),
+ connector->base.name,
connector->base.encoder ? "enabled" : "disabled");
}
}
@@ -11712,8 +12138,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
* Note that this could go away if we move to using crtc_config
* checking everywhere.
*/
- list_for_each_entry(crtc, &dev->mode_config.crtc_list,
- base.head) {
+ for_each_intel_crtc(dev, crtc) {
if (crtc->active && i915.fastboot) {
intel_mode_from_pipe_config(&crtc->base.mode, &crtc->config);
DRM_DEBUG_KMS("[CRTC:%d] found active mode: ",
@@ -11789,7 +12214,7 @@ void intel_modeset_gem_init(struct drm_device *dev)
* for this.
*/
mutex_lock(&dev->struct_mutex);
- list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, c) {
if (!c->primary->fb)
continue;
@@ -11835,7 +12260,7 @@ void intel_modeset_cleanup(struct drm_device *dev)
intel_unregister_dsm_handler();
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, crtc) {
/* Skip inactive CRTCs */
if (!crtc->primary->fb)
continue;
@@ -11933,6 +12358,7 @@ struct intel_display_error_state {
struct intel_pipe_error_state {
bool power_domain_on;
u32 source;
+ u32 stat;
} pipe[I915_MAX_PIPES];
struct intel_plane_error_state {
@@ -11990,15 +12416,9 @@ intel_display_capture_error_state(struct drm_device *dev)
if (!error->pipe[i].power_domain_on)
continue;
- if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) {
- error->cursor[i].control = I915_READ(CURCNTR(i));
- error->cursor[i].position = I915_READ(CURPOS(i));
- error->cursor[i].base = I915_READ(CURBASE(i));
- } else {
- error->cursor[i].control = I915_READ(CURCNTR_IVB(i));
- error->cursor[i].position = I915_READ(CURPOS_IVB(i));
- error->cursor[i].base = I915_READ(CURBASE_IVB(i));
- }
+ error->cursor[i].control = I915_READ(CURCNTR(i));
+ error->cursor[i].position = I915_READ(CURPOS(i));
+ error->cursor[i].base = I915_READ(CURBASE(i));
error->plane[i].control = I915_READ(DSPCNTR(i));
error->plane[i].stride = I915_READ(DSPSTRIDE(i));
@@ -12014,6 +12434,9 @@ intel_display_capture_error_state(struct drm_device *dev)
}
error->pipe[i].source = I915_READ(PIPESRC(i));
+
+ if (!HAS_PCH_SPLIT(dev))
+ error->pipe[i].stat = I915_READ(PIPESTAT(i));
}
error->num_transcoders = INTEL_INFO(dev)->num_pipes;
@@ -12064,6 +12487,7 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m,
err_printf(m, " Power: %s\n",
error->pipe[i].power_domain_on ? "on" : "off");
err_printf(m, " SRC: %08x\n", error->pipe[i].source);
+ err_printf(m, " STAT: %08x\n", error->pipe[i].stat);
err_printf(m, "Plane [%d]:\n", i);
err_printf(m, " CNTR: %08x\n", error->plane[i].control);
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 2a00cb828d20..52fda950fd2a 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -64,6 +64,24 @@ static const struct dp_link_dpll vlv_dpll[] = {
{ .p1 = 2, .p2 = 2, .n = 1, .m1 = 2, .m2 = 27 } }
};
+/*
+ * CHV supports eDP 1.4 that have more link rates.
+ * Below only provides the fixed rate but exclude variable rate.
+ */
+static const struct dp_link_dpll chv_dpll[] = {
+ /*
+ * CHV requires to program fractional division for m2.
+ * m2 is stored in fixed point format using formula below
+ * (m2_int << 22) | m2_fraction
+ */
+ { DP_LINK_BW_1_62, /* m2_int = 32, m2_fraction = 1677722 */
+ { .p1 = 4, .p2 = 2, .n = 1, .m1 = 2, .m2 = 0x819999a } },
+ { DP_LINK_BW_2_7, /* m2_int = 27, m2_fraction = 0 */
+ { .p1 = 4, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } },
+ { DP_LINK_BW_5_4, /* m2_int = 27, m2_fraction = 0 */
+ { .p1 = 2, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } }
+};
+
/**
* is_edp - is the given port attached to an eDP panel (either CPU or PCH)
* @intel_dp: DP struct
@@ -330,8 +348,12 @@ 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;
- return !dev_priv->pm.suspended &&
+ 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;
}
@@ -697,9 +719,9 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector)
DRM_DEBUG_KMS("registering %s bus for %s\n", name,
connector->base.kdev->kobj.name);
- ret = drm_dp_aux_register_i2c_bus(&intel_dp->aux);
+ ret = drm_dp_aux_register(&intel_dp->aux);
if (ret < 0) {
- DRM_ERROR("drm_dp_aux_register_i2c_bus() for %s failed (%d)\n",
+ DRM_ERROR("drm_dp_aux_register() for %s failed (%d)\n",
name, ret);
return;
}
@@ -709,7 +731,7 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector)
intel_dp->aux.ddc.dev.kobj.name);
if (ret < 0) {
DRM_ERROR("sysfs_create_link() for %s failed (%d)\n", name, ret);
- drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
+ drm_dp_aux_unregister(&intel_dp->aux);
}
}
@@ -739,6 +761,9 @@ intel_dp_set_clock(struct intel_encoder *encoder,
} else if (HAS_PCH_SPLIT(dev)) {
divisor = pch_dpll;
count = ARRAY_SIZE(pch_dpll);
+ } else if (IS_CHERRYVIEW(dev)) {
+ divisor = chv_dpll;
+ count = ARRAY_SIZE(chv_dpll);
} else if (IS_VALLEYVIEW(dev)) {
divisor = vlv_dpll;
count = ARRAY_SIZE(vlv_dpll);
@@ -755,6 +780,20 @@ 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)
@@ -780,6 +819,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
pipe_config->has_pch_encoder = true;
pipe_config->has_dp_encoder = true;
+ pipe_config->has_audio = intel_dp->has_audio;
if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) {
intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
@@ -880,6 +920,14 @@ found:
pipe_config->port_clock,
&pipe_config->dp_m_n);
+ if (intel_connector->panel.downclock_mode != NULL &&
+ intel_dp->drrs_state.type == SEAMLESS_DRRS_SUPPORT) {
+ 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);
return true;
@@ -915,7 +963,7 @@ static void ironlake_set_pll_cpu_edp(struct intel_dp *intel_dp)
udelay(500);
}
-static void intel_dp_mode_set(struct intel_encoder *encoder)
+static void intel_dp_prepare(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -950,7 +998,7 @@ static void intel_dp_mode_set(struct intel_encoder *encoder)
intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
intel_dp->DP |= DP_PORT_WIDTH(intel_dp->lane_count);
- if (intel_dp->has_audio) {
+ if (crtc->config.has_audio) {
DRM_DEBUG_DRIVER("Enabling DP audio on pipe %c\n",
pipe_name(crtc->pipe));
intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE;
@@ -983,14 +1031,15 @@ static void intel_dp_mode_set(struct intel_encoder *encoder)
if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
intel_dp->DP |= DP_ENHANCED_FRAMING;
- if (crtc->pipe == 1)
- intel_dp->DP |= DP_PIPEB_SELECT;
+ if (!IS_CHERRYVIEW(dev)) {
+ if (crtc->pipe == 1)
+ intel_dp->DP |= DP_PIPEB_SELECT;
+ } else {
+ intel_dp->DP |= DP_PIPE_SELECT_CHV(crtc->pipe);
+ }
} else {
intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
}
-
- if (port == PORT_A && !IS_VALLEYVIEW(dev))
- ironlake_set_pll_cpu_edp(intel_dp);
}
#define IDLE_ON_MASK (PP_ON | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK)
@@ -1082,7 +1131,10 @@ static u32 ironlake_get_pp_control(struct intel_dp *intel_dp)
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);
+ struct intel_encoder *intel_encoder = &intel_dig_port->base;
struct drm_i915_private *dev_priv = dev->dev_private;
+ enum intel_display_power_domain power_domain;
u32 pp;
u32 pp_stat_reg, pp_ctrl_reg;
bool need_to_disable = !intel_dp->want_panel_vdd;
@@ -1095,7 +1147,8 @@ static bool _edp_panel_vdd_on(struct intel_dp *intel_dp)
if (edp_have_panel_vdd(intel_dp))
return need_to_disable;
- intel_runtime_pm_get(dev_priv);
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
DRM_DEBUG_KMS("Turning eDP VDD on\n");
@@ -1139,9 +1192,14 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp)
u32 pp;
u32 pp_stat_reg, pp_ctrl_reg;
- WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+ WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_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;
+
DRM_DEBUG_KMS("Turning eDP VDD off\n");
pp = ironlake_get_pp_control(intel_dp);
@@ -1160,7 +1218,8 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp)
if ((pp & POWER_TARGET_ON) == 0)
intel_dp->last_power_cycle = jiffies;
- intel_runtime_pm_put(dev_priv);
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_put(dev_priv, power_domain);
}
}
@@ -1170,9 +1229,9 @@ static void edp_panel_vdd_work(struct work_struct *__work)
struct intel_dp, panel_vdd_work);
struct drm_device *dev = intel_dp_to_dev(intel_dp);
- mutex_lock(&dev->mode_config.mutex);
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
edp_panel_vdd_off_sync(intel_dp);
- mutex_unlock(&dev->mode_config.mutex);
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
}
static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
@@ -1244,8 +1303,11 @@ void intel_edp_panel_on(struct intel_dp *intel_dp)
void intel_edp_panel_off(struct intel_dp *intel_dp)
{
+ 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 = intel_dp_to_dev(intel_dp);
struct drm_i915_private *dev_priv = dev->dev_private;
+ enum intel_display_power_domain power_domain;
u32 pp;
u32 pp_ctrl_reg;
@@ -1275,7 +1337,8 @@ void intel_edp_panel_off(struct intel_dp *intel_dp)
wait_panel_off(intel_dp);
/* We got a reference when we enabled the VDD. */
- intel_runtime_pm_put(dev_priv);
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_put(dev_priv, power_domain);
}
void intel_edp_backlight_on(struct intel_dp *intel_dp)
@@ -1432,6 +1495,8 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
if (port == PORT_A && IS_GEN7(dev) && !IS_VALLEYVIEW(dev)) {
*pipe = PORT_TO_PIPE_CPT(tmp);
+ } else if (IS_CHERRYVIEW(dev)) {
+ *pipe = DP_PORT_TO_PIPE_CHV(tmp);
} else if (!HAS_PCH_CPT(dev) || port == PORT_A) {
*pipe = PORT_TO_PIPE(tmp);
} else {
@@ -1479,8 +1544,11 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
int dotclock;
+ tmp = I915_READ(intel_dp->output_reg);
+ if (tmp & DP_AUDIO_OUTPUT_ENABLE)
+ pipe_config->has_audio = true;
+
if ((port == PORT_A) || !HAS_PCH_CPT(dev)) {
- tmp = I915_READ(intel_dp->output_reg);
if (tmp & DP_SYNC_HS_HIGH)
flags |= DRM_MODE_FLAG_PHSYNC;
else
@@ -1816,17 +1884,59 @@ static void intel_disable_dp(struct intel_encoder *encoder)
intel_dp_link_down(intel_dp);
}
-static void intel_post_disable_dp(struct intel_encoder *encoder)
+static void g4x_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);
+}
+
+static void vlv_post_disable_dp(struct intel_encoder *encoder)
+{
+ struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+
+ intel_dp_link_down(intel_dp);
+}
+
+static void chv_post_disable_dp(struct intel_encoder *encoder)
+{
+ struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+ 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 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;
- if (port == PORT_A || IS_VALLEYVIEW(dev)) {
- intel_dp_link_down(intel_dp);
- if (!IS_VALLEYVIEW(dev))
- ironlake_edp_pll_off(intel_dp);
- }
+ intel_dp_link_down(intel_dp);
+
+ mutex_lock(&dev_priv->dpio_lock);
+
+ /* Propagate soft reset to data lane reset */
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
+ val |= CHV_PCS_REQ_SOFTRESET_EN;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
+ val |= CHV_PCS_REQ_SOFTRESET_EN;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
+ val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
+ val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
+
+ mutex_unlock(&dev_priv->dpio_lock);
}
static void intel_enable_dp(struct intel_encoder *encoder)
@@ -1868,8 +1978,13 @@ static void g4x_pre_enable_dp(struct intel_encoder *encoder)
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
struct intel_digital_port *dport = dp_to_dig_port(intel_dp);
- if (dport->port == PORT_A)
+ intel_dp_prepare(encoder);
+
+ /* Only ilk+ has port A */
+ if (dport->port == PORT_A) {
+ ironlake_set_pll_cpu_edp(intel_dp);
ironlake_edp_pll_on(intel_dp);
+ }
}
static void vlv_pre_enable_dp(struct intel_encoder *encoder)
@@ -1921,6 +2036,8 @@ static void vlv_dp_pre_pll_enable(struct intel_encoder *encoder)
enum dpio_channel port = vlv_dport_to_channel(dport);
int pipe = intel_crtc->pipe;
+ intel_dp_prepare(encoder);
+
/* Program Tx lane resets to default */
mutex_lock(&dev_priv->dpio_lock);
vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW0(port),
@@ -1939,6 +2056,69 @@ static void vlv_dp_pre_pll_enable(struct intel_encoder *encoder)
mutex_unlock(&dev_priv->dpio_lock);
}
+static void chv_pre_enable_dp(struct intel_encoder *encoder)
+{
+ struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+ 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);
+ int pipe = intel_crtc->pipe;
+ int data, i;
+ u32 val;
+
+ mutex_lock(&dev_priv->dpio_lock);
+
+ /* Deassert soft data lane reset*/
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
+ val |= CHV_PCS_REQ_SOFTRESET_EN;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
+ val |= CHV_PCS_REQ_SOFTRESET_EN;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
+ val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
+ val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
+
+ /* Program Tx lane latency optimal setting*/
+ for (i = 0; i < 4; i++) {
+ /* Set the latency optimal bit */
+ data = (i == 1) ? 0x0 : 0x6;
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW11(ch, i),
+ data << DPIO_FRC_LATENCY_SHFIT);
+
+ /* Set the upar bit */
+ data = (i == 1) ? 0x0 : 0x1;
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i),
+ data << DPIO_UPAR_SHIFT);
+ }
+
+ /* Data lane stagger programming */
+ /* FIXME: Fix up value only after power analysis */
+
+ 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);
+ }
+
+ intel_enable_dp(encoder);
+
+ vlv_wait_port_ready(dev_priv, dport);
+}
+
/*
* Native read with retry for link status and receiver capability reads for
* cases where the sink may still be asleep.
@@ -2163,6 +2343,166 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp)
return 0;
}
+static uint32_t intel_chv_signal_levels(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 *dport = dp_to_dig_port(intel_dp);
+ struct intel_crtc *intel_crtc = to_intel_crtc(dport->base.base.crtc);
+ u32 deemph_reg_value, margin_reg_value, val;
+ uint8_t train_set = intel_dp->train_set[0];
+ enum dpio_channel ch = vlv_dport_to_channel(dport);
+ enum pipe pipe = intel_crtc->pipe;
+ int i;
+
+ switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
+ case DP_TRAIN_PRE_EMPHASIS_0:
+ switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+ case DP_TRAIN_VOLTAGE_SWING_400:
+ deemph_reg_value = 128;
+ margin_reg_value = 52;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_600:
+ deemph_reg_value = 128;
+ margin_reg_value = 77;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_800:
+ deemph_reg_value = 128;
+ margin_reg_value = 102;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_1200:
+ deemph_reg_value = 128;
+ margin_reg_value = 154;
+ /* FIXME extra to set for 1200 */
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case DP_TRAIN_PRE_EMPHASIS_3_5:
+ switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+ case DP_TRAIN_VOLTAGE_SWING_400:
+ deemph_reg_value = 85;
+ margin_reg_value = 78;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_600:
+ deemph_reg_value = 85;
+ margin_reg_value = 116;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_800:
+ deemph_reg_value = 85;
+ margin_reg_value = 154;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case DP_TRAIN_PRE_EMPHASIS_6:
+ switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+ case DP_TRAIN_VOLTAGE_SWING_400:
+ deemph_reg_value = 64;
+ margin_reg_value = 104;
+ break;
+ case DP_TRAIN_VOLTAGE_SWING_600:
+ deemph_reg_value = 64;
+ margin_reg_value = 154;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case DP_TRAIN_PRE_EMPHASIS_9_5:
+ switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
+ case DP_TRAIN_VOLTAGE_SWING_400:
+ deemph_reg_value = 43;
+ margin_reg_value = 154;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ mutex_lock(&dev_priv->dpio_lock);
+
+ /* Clear calc init */
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
+ val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
+ val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
+
+ /* Program swing deemph */
+ for (i = 0; i < 4; i++) {
+ val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i));
+ val &= ~DPIO_SWING_DEEMPH9P5_MASK;
+ val |= deemph_reg_value << DPIO_SWING_DEEMPH9P5_SHIFT;
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW4(ch, i), val);
+ }
+
+ /* 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;
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val);
+ }
+
+ /* Disable unique transition scale */
+ for (i = 0; i < 4; i++) {
+ val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i));
+ val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN;
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val);
+ }
+
+ if (((train_set & DP_TRAIN_PRE_EMPHASIS_MASK)
+ == DP_TRAIN_PRE_EMPHASIS_0) &&
+ ((train_set & DP_TRAIN_VOLTAGE_SWING_MASK)
+ == DP_TRAIN_VOLTAGE_SWING_1200)) {
+
+ /*
+ * The document said it needs to set bit 27 for ch0 and bit 26
+ * for ch1. Might be a typo in the doc.
+ * For now, for this unique transition scale selection, set bit
+ * 27 for ch0 and ch1.
+ */
+ for (i = 0; i < 4; i++) {
+ val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i));
+ val |= DPIO_TX_UNIQ_TRANS_SCALE_EN;
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val);
+ }
+
+ for (i = 0; i < 4; i++) {
+ val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW2(ch, i));
+ val &= ~(0xff << DPIO_UNIQ_TRANS_SCALE_SHIFT);
+ val |= (0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT);
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val);
+ }
+ }
+
+ /* Start swing calculation */
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
+ val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
+ val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
+
+ /* LRC Bypass */
+ val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30);
+ val |= DPIO_LRC_BYPASS;
+ vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, val);
+
+ mutex_unlock(&dev_priv->dpio_lock);
+
+ return 0;
+}
+
static void
intel_get_adjust_train(struct intel_dp *intel_dp,
const uint8_t link_status[DP_LINK_STATUS_SIZE])
@@ -2377,6 +2717,9 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
} else if (IS_HASWELL(dev)) {
signal_levels = intel_hsw_signal_levels(train_set);
mask = DDI_BUF_EMP_MASK;
+ } else if (IS_CHERRYVIEW(dev)) {
+ signal_levels = intel_chv_signal_levels(intel_dp);
+ mask = 0;
} else if (IS_VALLEYVIEW(dev)) {
signal_levels = intel_vlv_signal_levels(intel_dp);
mask = 0;
@@ -2743,22 +3086,7 @@ intel_dp_link_down(struct intel_dp *intel_dp)
to_intel_crtc(intel_dig_port->base.base.crtc);
uint32_t DP = intel_dp->DP;
- /*
- * DDI code has a strict mode set sequence and we should try to respect
- * it, otherwise we might hang the machine in many different ways. So we
- * really should be disabling the port only on a complete crtc_disable
- * sequence. This function is just called under two conditions on DDI
- * code:
- * - Link train failed while doing crtc_enable, and on this case we
- * really should respect the mode set sequence and wait for a
- * crtc_disable.
- * - Someone turned the monitor off and intel_dp_check_link_status
- * called us. We don't need to disable the whole port on this case, so
- * when someone turns the monitor on again,
- * intel_ddi_prepare_link_retrain will take care of redoing the link
- * train.
- */
- if (HAS_DDI(dev))
+ if (WARN_ON(HAS_DDI(dev)))
return;
if (WARN_ON((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0))
@@ -2775,9 +3103,6 @@ intel_dp_link_down(struct intel_dp *intel_dp)
}
POSTING_READ(intel_dp->output_reg);
- /* We don't really know why we're doing this */
- intel_wait_for_vblank(dev, intel_crtc->pipe);
-
if (HAS_PCH_IBX(dev) &&
I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) {
struct drm_crtc *crtc = intel_dig_port->base.base.crtc;
@@ -2948,6 +3273,7 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
u8 sink_irq_vector;
u8 link_status[DP_LINK_STATUS_SIZE];
+ /* FIXME: This access isn't protected by any locks. */
if (!intel_encoder->connectors_active)
return;
@@ -2980,7 +3306,7 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
if (!drm_dp_channel_eq_ok(link_status, intel_dp->lane_count)) {
DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n",
- drm_get_encoder_name(&intel_encoder->base));
+ intel_encoder->base.name);
intel_dp_start_link_train(intel_dp);
intel_dp_complete_link_train(intel_dp);
intel_dp_stop_link_train(intel_dp);
@@ -3166,7 +3492,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)
intel_display_power_get(dev_priv, power_domain);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
- connector->base.id, drm_get_connector_name(connector));
+ connector->base.id, connector->name);
intel_dp->has_audio = false;
@@ -3374,13 +3700,13 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
struct intel_dp *intel_dp = &intel_dig_port->dp;
struct drm_device *dev = intel_dp_to_dev(intel_dp);
- drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
+ drm_dp_aux_unregister(&intel_dp->aux);
drm_encoder_cleanup(encoder);
if (is_edp(intel_dp)) {
cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
- mutex_lock(&dev->mode_config.mutex);
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
edp_panel_vdd_off_sync(intel_dp);
- mutex_unlock(&dev->mode_config.mutex);
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
}
kfree(intel_dig_port);
}
@@ -3651,6 +3977,130 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
I915_READ(pp_div_reg));
}
+void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_encoder *encoder;
+ struct intel_dp *intel_dp = NULL;
+ struct intel_crtc_config *config = NULL;
+ struct intel_crtc *intel_crtc = NULL;
+ struct intel_connector *intel_connector = dev_priv->drrs.connector;
+ u32 reg, val;
+ enum edp_drrs_refresh_rate_type index = DRRS_HIGH_RR;
+
+ if (refresh_rate <= 0) {
+ DRM_DEBUG_KMS("Refresh rate should be positive non-zero.\n");
+ return;
+ }
+
+ if (intel_connector == NULL) {
+ DRM_DEBUG_KMS("DRRS supported for eDP only.\n");
+ return;
+ }
+
+ if (INTEL_INFO(dev)->gen < 8 && intel_edp_is_psr_enabled(dev)) {
+ DRM_DEBUG_KMS("DRRS is disabled as PSR is enabled\n");
+ return;
+ }
+
+ encoder = intel_attached_encoder(&intel_connector->base);
+ intel_dp = enc_to_intel_dp(&encoder->base);
+ intel_crtc = encoder->new_crtc;
+
+ if (!intel_crtc) {
+ DRM_DEBUG_KMS("DRRS: intel_crtc not initialized\n");
+ return;
+ }
+
+ config = &intel_crtc->config;
+
+ if (intel_dp->drrs_state.type < SEAMLESS_DRRS_SUPPORT) {
+ DRM_DEBUG_KMS("Only Seamless DRRS supported.\n");
+ return;
+ }
+
+ if (intel_connector->panel.downclock_mode->vrefresh == refresh_rate)
+ index = DRRS_LOW_RR;
+
+ if (index == intel_dp->drrs_state.refresh_rate_type) {
+ DRM_DEBUG_KMS(
+ "DRRS requested for previously set RR...ignoring\n");
+ return;
+ }
+
+ if (!intel_crtc->active) {
+ DRM_DEBUG_KMS("eDP encoder disabled. CRTC not Active\n");
+ return;
+ }
+
+ if (INTEL_INFO(dev)->gen > 6 && INTEL_INFO(dev)->gen < 8) {
+ reg = PIPECONF(intel_crtc->config.cpu_transcoder);
+ 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);
+ } else {
+ val &= ~PIPECONF_EDP_RR_MODE_SWITCH;
+ }
+ I915_WRITE(reg, val);
+ }
+
+ /*
+ * mutex taken to ensure that there is no race between differnt
+ * drrs calls trying to update refresh rate. This scenario may occur
+ * in future when idleness detection based DRRS in kernel and
+ * possible calls from user space to set differnt RR are made.
+ */
+
+ mutex_lock(&intel_dp->drrs_state.mutex);
+
+ intel_dp->drrs_state.refresh_rate_type = index;
+
+ mutex_unlock(&intel_dp->drrs_state.mutex);
+
+ DRM_DEBUG_KMS("eDP Refresh Rate set to : %dHz\n", refresh_rate);
+}
+
+static struct drm_display_mode *
+intel_dp_drrs_init(struct intel_digital_port *intel_dig_port,
+ struct intel_connector *intel_connector,
+ struct drm_display_mode *fixed_mode)
+{
+ struct drm_connector *connector = &intel_connector->base;
+ 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;
+ struct drm_display_mode *downclock_mode = NULL;
+
+ if (INTEL_INFO(dev)->gen <= 6) {
+ DRM_DEBUG_KMS("DRRS supported for Gen7 and above\n");
+ return NULL;
+ }
+
+ if (dev_priv->vbt.drrs_type != SEAMLESS_DRRS_SUPPORT) {
+ DRM_INFO("VBT doesn't support DRRS\n");
+ return NULL;
+ }
+
+ downclock_mode = intel_find_panel_downclock
+ (dev, fixed_mode, connector);
+
+ if (!downclock_mode) {
+ DRM_INFO("DRRS not supported\n");
+ return NULL;
+ }
+
+ dev_priv->drrs.connector = intel_connector;
+
+ mutex_init(&intel_dp->drrs_state.mutex);
+
+ 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");
+ return downclock_mode;
+}
+
static bool intel_edp_init_connector(struct intel_dp *intel_dp,
struct intel_connector *intel_connector,
struct edp_power_seq *power_seq)
@@ -3661,10 +4111,13 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
struct drm_device *dev = intel_encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_display_mode *fixed_mode = NULL;
+ struct drm_display_mode *downclock_mode = NULL;
bool has_dpcd;
struct drm_display_mode *scan;
struct edid *edid;
+ intel_dp->drrs_state.type = DRRS_NOT_SUPPORTED;
+
if (!is_edp(intel_dp))
return true;
@@ -3715,6 +4168,9 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
list_for_each_entry(scan, &connector->probed_modes, head) {
if ((scan->type & DRM_MODE_TYPE_PREFERRED)) {
fixed_mode = drm_mode_duplicate(dev, scan);
+ downclock_mode = intel_dp_drrs_init(
+ intel_dig_port,
+ intel_connector, fixed_mode);
break;
}
}
@@ -3728,7 +4184,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
}
mutex_unlock(&dev->mode_config.mutex);
- intel_panel_init(&intel_connector->panel, fixed_mode, NULL);
+ intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
intel_panel_setup_backlight(connector);
return true;
@@ -3826,12 +4282,12 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
intel_dp->psr_setup_done = false;
if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
- drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
+ drm_dp_aux_unregister(&intel_dp->aux);
if (is_edp(intel_dp)) {
cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
- mutex_lock(&dev->mode_config.mutex);
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
edp_panel_vdd_off_sync(intel_dp);
- mutex_unlock(&dev->mode_config.mutex);
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
}
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
@@ -3877,25 +4333,36 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
DRM_MODE_ENCODER_TMDS);
intel_encoder->compute_config = intel_dp_compute_config;
- intel_encoder->mode_set = intel_dp_mode_set;
intel_encoder->disable = intel_disable_dp;
- intel_encoder->post_disable = intel_post_disable_dp;
intel_encoder->get_hw_state = intel_dp_get_hw_state;
intel_encoder->get_config = intel_dp_get_config;
- if (IS_VALLEYVIEW(dev)) {
+ if (IS_CHERRYVIEW(dev)) {
+ intel_encoder->pre_enable = chv_pre_enable_dp;
+ intel_encoder->enable = vlv_enable_dp;
+ intel_encoder->post_disable = chv_post_disable_dp;
+ } else if (IS_VALLEYVIEW(dev)) {
intel_encoder->pre_pll_enable = vlv_dp_pre_pll_enable;
intel_encoder->pre_enable = vlv_pre_enable_dp;
intel_encoder->enable = vlv_enable_dp;
+ intel_encoder->post_disable = vlv_post_disable_dp;
} else {
intel_encoder->pre_enable = g4x_pre_enable_dp;
intel_encoder->enable = g4x_enable_dp;
+ intel_encoder->post_disable = g4x_post_disable_dp;
}
intel_dig_port->port = port;
intel_dig_port->dp.output_reg = output_reg;
intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
- intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
+ if (IS_CHERRYVIEW(dev)) {
+ if (port == PORT_D)
+ intel_encoder->crtc_mask = 1 << 2;
+ else
+ intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
+ } else {
+ intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
+ }
intel_encoder->cloneable = 0;
intel_encoder->hot_plug = intel_dp_hot_plug;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 328b1a70264b..bda0ae3d80cc 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -81,8 +81,8 @@
/* Maximum cursor sizes */
#define GEN2_CURSOR_WIDTH 64
#define GEN2_CURSOR_HEIGHT 64
-#define CURSOR_WIDTH 256
-#define CURSOR_HEIGHT 256
+#define MAX_CURSOR_WIDTH 256
+#define MAX_CURSOR_HEIGHT 256
#define INTEL_I2C_BUS_DVO 1
#define INTEL_I2C_BUS_SDVO 2
@@ -106,8 +106,8 @@
#define INTEL_DVO_CHIP_TMDS 2
#define INTEL_DVO_CHIP_TVOUT 4
-#define INTEL_DSI_COMMAND_MODE 0
-#define INTEL_DSI_VIDEO_MODE 1
+#define INTEL_DSI_VIDEO_MODE 0
+#define INTEL_DSI_COMMAND_MODE 1
struct intel_framebuffer {
struct drm_framebuffer base;
@@ -273,6 +273,13 @@ struct intel_crtc_config {
* accordingly. */
bool has_dp_encoder;
+ /* Whether we should send NULL infoframes. Required for audio. */
+ bool has_hdmi_sink;
+
+ /* Audio enabled on this pipe. Only valid if either has_hdmi_sink or
+ * has_dp_encoder is set. */
+ bool has_audio;
+
/*
* Enable dithering, used when the selected pipe bpp doesn't match the
* plane bpp.
@@ -306,6 +313,9 @@ struct intel_crtc_config {
int pipe_bpp;
struct intel_link_m_n dp_m_n;
+ /* m2_n2 for eDP downclock */
+ struct intel_link_m_n dp_m2_n2;
+
/*
* Frequence the dpll for the port should run at. Differs from the
* adjusted dotclock e.g. for DP or 12bpc hdmi mode. This is also
@@ -343,6 +353,9 @@ struct intel_pipe_wm {
struct intel_wm_level wm[5];
uint32_t linetime;
bool fbc_wm_enabled;
+ bool pipe_enabled;
+ bool sprites_enabled;
+ bool sprites_scaled;
};
struct intel_crtc {
@@ -357,7 +370,6 @@ struct intel_crtc {
*/
bool active;
unsigned long enabled_power_domains;
- bool eld_vld;
bool primary_enabled; /* is the primary plane (partially) visible? */
bool lowfreq_avail;
struct intel_overlay *overlay;
@@ -374,8 +386,8 @@ struct intel_crtc {
uint32_t cursor_addr;
int16_t cursor_x, cursor_y;
int16_t cursor_width, cursor_height;
- int16_t max_cursor_width, max_cursor_height;
- bool cursor_visible;
+ uint32_t cursor_cntl;
+ uint32_t cursor_base;
struct intel_plane_config plane_config;
struct intel_crtc_config config;
@@ -396,6 +408,10 @@ struct intel_crtc {
/* watermarks currently being used */
struct intel_pipe_wm active;
} wm;
+
+ wait_queue_head_t vbl_wait;
+
+ int scanline_offset;
};
struct intel_plane_wm_parameters {
@@ -479,11 +495,23 @@ struct intel_hdmi {
enum hdmi_infoframe_type type,
const void *frame, ssize_t len);
void (*set_infoframes)(struct drm_encoder *encoder,
+ bool enable,
struct drm_display_mode *adjusted_mode);
};
#define DP_MAX_DOWNSTREAM_PORTS 0x10
+/**
+ * HIGH_RR is the highest eDP panel refresh rate read from EDID
+ * LOW_RR is the lowest eDP panel refresh rate found from EDID
+ * parsing for same resolution.
+ */
+enum edp_drrs_refresh_rate_type {
+ DRRS_HIGH_RR,
+ DRRS_LOW_RR,
+ DRRS_MAX_RR, /* RR count */
+};
+
struct intel_dp {
uint32_t output_reg;
uint32_t aux_ch_ctl_reg;
@@ -522,6 +550,12 @@ struct intel_dp {
bool has_aux_irq,
int send_bytes,
uint32_t aux_clock_divider);
+ struct {
+ enum drrs_support_type type;
+ enum edp_drrs_refresh_rate_type refresh_rate_type;
+ struct mutex mutex;
+ } drrs_state;
+
};
struct intel_digital_port {
@@ -537,6 +571,7 @@ vlv_dport_to_channel(struct intel_digital_port *dport)
{
switch (dport->port) {
case PORT_B:
+ case PORT_D:
return DPIO_CH0;
case PORT_C:
return DPIO_CH1;
@@ -545,6 +580,20 @@ vlv_dport_to_channel(struct intel_digital_port *dport)
}
}
+static inline int
+vlv_pipe_to_channel(enum pipe pipe)
+{
+ switch (pipe) {
+ case PIPE_A:
+ case PIPE_C:
+ return DPIO_CH0;
+ case PIPE_B:
+ return DPIO_CH1;
+ default:
+ BUG();
+ }
+}
+
static inline struct drm_crtc *
intel_get_crtc_for_pipe(struct drm_device *dev, int pipe)
{
@@ -569,6 +618,8 @@ struct intel_unpin_work {
#define INTEL_FLIP_INACTIVE 0
#define INTEL_FLIP_PENDING 1
#define INTEL_FLIP_COMPLETE 2
+ u32 flip_count;
+ u32 gtt_offset;
bool enable_stall_check;
};
@@ -620,8 +671,6 @@ hdmi_to_dig_port(struct intel_hdmi *intel_hdmi)
/* i915_irq.c */
bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
enum pipe pipe, bool enable);
-bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
- enum pipe pipe, bool enable);
bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
enum transcoder pch_transcoder,
bool enable);
@@ -629,8 +678,12 @@ 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 hsw_runtime_pm_disable_interrupts(struct drm_device *dev);
-void hsw_runtime_pm_restore_interrupts(struct drm_device *dev);
+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 intel_runtime_pm_disable_interrupts(struct drm_device *dev);
+void intel_runtime_pm_restore_interrupts(struct drm_device *dev);
+int intel_get_crtc_scanline(struct intel_crtc *crtc);
+void i9xx_check_fifo_underruns(struct drm_device *dev);
/* intel_crt.c */
@@ -666,9 +719,10 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
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_ring_buffer *ring);
+ struct intel_engine_cs *ring);
void intel_mark_idle(struct drm_device *dev);
void intel_crtc_restore_mode(struct drm_crtc *crtc);
void intel_crtc_update_dpms(struct drm_crtc *crtc);
@@ -695,12 +749,14 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
struct intel_digital_port *dport);
bool intel_get_load_detect_pipe(struct drm_connector *connector,
struct drm_display_mode *mode,
- struct intel_load_detect_pipe *old);
+ 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 intel_load_detect_pipe *old,
+ struct drm_modeset_acquire_ctx *ctx);
int intel_pin_and_fence_fb_obj(struct drm_device *dev,
struct drm_i915_gem_object *obj,
- struct intel_ring_buffer *pipelined);
+ struct intel_engine_cs *pipelined);
void intel_unpin_fb_obj(struct drm_i915_gem_object *obj);
struct drm_framebuffer *
__intel_framebuffer_create(struct drm_device *dev,
@@ -751,6 +807,8 @@ 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);
+
/* intel_dp.c */
void intel_dp_init(struct drm_device *dev, int output_reg, enum port port);
@@ -774,7 +832,7 @@ 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);
/* intel_dsi.c */
bool intel_dsi_init(struct drm_device *dev);
@@ -876,6 +934,7 @@ extern struct drm_display_mode *intel_find_panel_downclock(
/* intel_pm.c */
void intel_init_clock_gating(struct drm_device *dev);
void intel_suspend_hw(struct drm_device *dev);
+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,
@@ -902,6 +961,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_reset_gt_powersave(struct drm_device *dev);
void ironlake_teardown_rc6(struct drm_device *dev);
void gen6_update_ring_freq(struct drm_device *dev);
void gen6_rps_idle(struct drm_i915_private *dev_priv);
@@ -909,11 +969,13 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv);
void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv);
void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv);
void intel_runtime_pm_get(struct drm_i915_private *dev_priv);
+void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv);
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);
diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
index 33656647f8bc..02f99d768d49 100644
--- a/drivers/gpu/drm/i915/intel_dsi.c
+++ b/drivers/gpu/drm/i915/intel_dsi.c
@@ -35,6 +35,11 @@
/* the sub-encoders aka panel drivers */
static const struct intel_dsi_device intel_dsi_devices[] = {
+ {
+ .panel_id = MIPI_DSI_GENERIC_PANEL_ID,
+ .name = "vbt-generic-dsi-vid-mode-display",
+ .dev_ops = &vbt_generic_dsi_display_ops,
+ },
};
static void band_gap_reset(struct drm_i915_private *dev_priv)
@@ -59,12 +64,12 @@ static struct intel_dsi *intel_attached_dsi(struct drm_connector *connector)
static inline bool is_vid_mode(struct intel_dsi *intel_dsi)
{
- return intel_dsi->dev.type == INTEL_DSI_VIDEO_MODE;
+ return intel_dsi->operation_mode == INTEL_DSI_VIDEO_MODE;
}
static inline bool is_cmd_mode(struct intel_dsi *intel_dsi)
{
- return intel_dsi->dev.type == INTEL_DSI_COMMAND_MODE;
+ return intel_dsi->operation_mode == INTEL_DSI_COMMAND_MODE;
}
static void intel_dsi_hot_plug(struct intel_encoder *encoder)
@@ -94,13 +99,6 @@ static bool intel_dsi_compute_config(struct intel_encoder *encoder,
return true;
}
-static void intel_dsi_pre_pll_enable(struct intel_encoder *encoder)
-{
- DRM_DEBUG_KMS("\n");
-
- vlv_enable_dsi_pll(encoder);
-}
-
static void intel_dsi_device_ready(struct intel_encoder *encoder)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
@@ -110,6 +108,15 @@ static void intel_dsi_device_ready(struct intel_encoder *encoder)
DRM_DEBUG_KMS("\n");
+ mutex_lock(&dev_priv->dpio_lock);
+ /* program rcomp for compliance, reduce from 50 ohms to 45 ohms
+ * needed everytime after power gate */
+ vlv_flisdsi_write(dev_priv, 0x04, 0x0004);
+ mutex_unlock(&dev_priv->dpio_lock);
+
+ /* bandgap reset is needed after everytime we do power gate */
+ band_gap_reset(dev_priv);
+
val = I915_READ(MIPI_PORT_CTRL(pipe));
I915_WRITE(MIPI_PORT_CTRL(pipe), val | LP_OUTPUT_HOLD);
usleep_range(1000, 1500);
@@ -122,21 +129,6 @@ static void intel_dsi_device_ready(struct intel_encoder *encoder)
I915_WRITE(MIPI_DEVICE_READY(pipe), DEVICE_READY);
usleep_range(2000, 2500);
}
-static void intel_dsi_pre_enable(struct intel_encoder *encoder)
-{
- struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
-
- DRM_DEBUG_KMS("\n");
-
- if (intel_dsi->dev.dev_ops->panel_reset)
- intel_dsi->dev.dev_ops->panel_reset(&intel_dsi->dev);
-
- /* put device in ready state */
- intel_dsi_device_ready(encoder);
-
- if (intel_dsi->dev.dev_ops->send_otp_cmds)
- intel_dsi->dev.dev_ops->send_otp_cmds(&intel_dsi->dev);
-}
static void intel_dsi_enable(struct intel_encoder *encoder)
{
@@ -153,18 +145,78 @@ static void intel_dsi_enable(struct intel_encoder *encoder)
I915_WRITE(MIPI_MAX_RETURN_PKT_SIZE(pipe), 8 * 4);
else {
msleep(20); /* XXX */
- dpi_send_cmd(intel_dsi, TURN_ON);
+ dpi_send_cmd(intel_dsi, TURN_ON, DPI_LP_MODE_EN);
msleep(100);
+ if (intel_dsi->dev.dev_ops->enable)
+ intel_dsi->dev.dev_ops->enable(&intel_dsi->dev);
+
/* assert ip_tg_enable signal */
temp = I915_READ(MIPI_PORT_CTRL(pipe)) & ~LANE_CONFIGURATION_MASK;
temp = temp | intel_dsi->port_bits;
I915_WRITE(MIPI_PORT_CTRL(pipe), temp | DPI_ENABLE);
POSTING_READ(MIPI_PORT_CTRL(pipe));
}
+}
+
+static void intel_dsi_pre_enable(struct intel_encoder *encoder)
+{
+ struct drm_device *dev = encoder->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
+ enum pipe pipe = intel_crtc->pipe;
+ u32 tmp;
+
+ DRM_DEBUG_KMS("\n");
+
+ /* Disable DPOunit clock gating, can stall pipe
+ * and we need DPLL REFA always enabled */
+ tmp = I915_READ(DPLL(pipe));
+ tmp |= DPLL_REFA_CLK_ENABLE_VLV;
+ I915_WRITE(DPLL(pipe), tmp);
+
+ tmp = I915_READ(DSPCLK_GATE_D);
+ tmp |= DPOUNIT_CLOCK_GATE_DISABLE;
+ I915_WRITE(DSPCLK_GATE_D, tmp);
+
+ /* put device in ready state */
+ intel_dsi_device_ready(encoder);
+
+ msleep(intel_dsi->panel_on_delay);
+
+ if (intel_dsi->dev.dev_ops->panel_reset)
+ intel_dsi->dev.dev_ops->panel_reset(&intel_dsi->dev);
+
+ if (intel_dsi->dev.dev_ops->send_otp_cmds)
+ intel_dsi->dev.dev_ops->send_otp_cmds(&intel_dsi->dev);
- if (intel_dsi->dev.dev_ops->enable)
- intel_dsi->dev.dev_ops->enable(&intel_dsi->dev);
+ /* Enable port in pre-enable phase itself because as per hw team
+ * recommendation, port should be enabled befor plane & pipe */
+ intel_dsi_enable(encoder);
+}
+
+static void intel_dsi_enable_nop(struct intel_encoder *encoder)
+{
+ DRM_DEBUG_KMS("\n");
+
+ /* for DSI port enable has to be done before pipe
+ * and plane enable, so port enable is done in
+ * pre_enable phase itself unlike other encoders
+ */
+}
+
+static void intel_dsi_pre_disable(struct intel_encoder *encoder)
+{
+ struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+
+ DRM_DEBUG_KMS("\n");
+
+ if (is_vid_mode(intel_dsi)) {
+ /* Send Shutdown command to the panel in LP mode */
+ dpi_send_cmd(intel_dsi, SHUTDOWN, DPI_LP_MODE_EN);
+ msleep(10);
+ }
}
static void intel_dsi_disable(struct intel_encoder *encoder)
@@ -179,9 +231,6 @@ static void intel_dsi_disable(struct intel_encoder *encoder)
DRM_DEBUG_KMS("\n");
if (is_vid_mode(intel_dsi)) {
- dpi_send_cmd(intel_dsi, SHUTDOWN);
- msleep(10);
-
/* de-assert ip_tg_enable signal */
temp = I915_READ(MIPI_PORT_CTRL(pipe));
I915_WRITE(MIPI_PORT_CTRL(pipe), temp & ~DPI_ENABLE);
@@ -190,6 +239,23 @@ static void intel_dsi_disable(struct intel_encoder *encoder)
msleep(2);
}
+ /* Panel commands can be sent when clock is in LP11 */
+ I915_WRITE(MIPI_DEVICE_READY(pipe), 0x0);
+
+ 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);
+
+ I915_WRITE(MIPI_EOT_DISABLE(pipe), CLOCKSTOP);
+
+ temp = I915_READ(MIPI_DSI_FUNC_PRG(pipe));
+ temp &= ~VID_MODE_FORMAT_MASK;
+ I915_WRITE(MIPI_DSI_FUNC_PRG(pipe), temp);
+
+ I915_WRITE(MIPI_DEVICE_READY(pipe), 0x1);
+
/* if disable packets are sent before sending shutdown packet then in
* some next enable sequence send turn on packet error is observed */
if (intel_dsi->dev.dev_ops->disable)
@@ -227,16 +293,28 @@ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder)
vlv_disable_dsi_pll(encoder);
}
+
static void intel_dsi_post_disable(struct intel_encoder *encoder)
{
+ struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
+ u32 val;
DRM_DEBUG_KMS("\n");
+ intel_dsi_disable(encoder);
+
intel_dsi_clear_device_ready(encoder);
+ val = I915_READ(DSPCLK_GATE_D);
+ val &= ~DPOUNIT_CLOCK_GATE_DISABLE;
+ I915_WRITE(DSPCLK_GATE_D, val);
+
if (intel_dsi->dev.dev_ops->disable_panel_power)
intel_dsi->dev.dev_ops->disable_panel_power(&intel_dsi->dev);
+
+ msleep(intel_dsi->panel_off_delay);
+ msleep(intel_dsi->panel_pwr_cycle_delay);
}
static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
@@ -364,7 +442,7 @@ static void set_dsi_timings(struct drm_encoder *encoder,
I915_WRITE(MIPI_VBP_COUNT(pipe), vbp);
}
-static void intel_dsi_mode_set(struct intel_encoder *intel_encoder)
+static void intel_dsi_prepare(struct intel_encoder *intel_encoder)
{
struct drm_encoder *encoder = &intel_encoder->base;
struct drm_device *dev = encoder->dev;
@@ -379,9 +457,6 @@ static void intel_dsi_mode_set(struct intel_encoder *intel_encoder)
DRM_DEBUG_KMS("pipe %c\n", pipe_name(pipe));
- /* XXX: Location of the call */
- band_gap_reset(dev_priv);
-
/* escape clock divider, 20MHz, shared for A and C. device ready must be
* off when doing this! txclkesc? */
tmp = I915_READ(MIPI_CTRL(0));
@@ -452,10 +527,20 @@ static void intel_dsi_mode_set(struct intel_encoder *intel_encoder)
/* dphy stuff */
/* in terms of low power clock */
- I915_WRITE(MIPI_INIT_COUNT(pipe), txclkesc(ESCAPE_CLOCK_DIVIDER_1, 100));
+ I915_WRITE(MIPI_INIT_COUNT(pipe), txclkesc(intel_dsi->escape_clk_div, 100));
+
+ val = 0;
+ if (intel_dsi->eotp_pkt == 0)
+ val |= EOT_DISABLE;
+
+ if (intel_dsi->clock_stop)
+ val |= CLOCKSTOP;
/* recovery disables */
- I915_WRITE(MIPI_EOT_DISABLE(pipe), intel_dsi->eot_disable);
+ I915_WRITE(MIPI_EOT_DISABLE(pipe), val);
+
+ /* in terms of low power clock */
+ I915_WRITE(MIPI_INIT_COUNT(pipe), intel_dsi->init_count);
/* in terms of txbyteclkhs. actual high to low switch +
* MIPI_STOP_STATE_STALL * MIPI_LP_BYTECLK.
@@ -484,9 +569,23 @@ static void intel_dsi_mode_set(struct intel_encoder *intel_encoder)
intel_dsi->clk_hs_to_lp_count << HS_LP_PWR_SW_CNT_SHIFT);
if (is_vid_mode(intel_dsi))
+ /* Some panels might have resolution which is not a multiple of
+ * 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);
+ intel_dsi->video_mode_format |
+ IP_TG_CONFIG |
+ RANDOM_DPI_DISPLAY_RESOLUTION);
+}
+
+static void intel_dsi_pre_pll_enable(struct intel_encoder *encoder)
+{
+ DRM_DEBUG_KMS("\n");
+
+ intel_dsi_prepare(encoder);
+
+ vlv_enable_dsi_pll(encoder);
}
static enum drm_connector_status
@@ -566,11 +665,16 @@ bool intel_dsi_init(struct drm_device *dev)
struct intel_connector *intel_connector;
struct drm_connector *connector;
struct drm_display_mode *fixed_mode = NULL;
+ struct drm_i915_private *dev_priv = dev->dev_private;
const struct intel_dsi_device *dsi;
unsigned int i;
DRM_DEBUG_KMS("\n");
+ /* There is no detection method for MIPI so rely on VBT */
+ if (!dev_priv->vbt.has_mipi)
+ return false;
+
intel_dsi = kzalloc(sizeof(*intel_dsi), GFP_KERNEL);
if (!intel_dsi)
return false;
@@ -585,6 +689,13 @@ bool intel_dsi_init(struct drm_device *dev)
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);
@@ -594,9 +705,8 @@ bool intel_dsi_init(struct drm_device *dev)
intel_encoder->compute_config = intel_dsi_compute_config;
intel_encoder->pre_pll_enable = intel_dsi_pre_pll_enable;
intel_encoder->pre_enable = intel_dsi_pre_enable;
- intel_encoder->enable = intel_dsi_enable;
- intel_encoder->mode_set = intel_dsi_mode_set;
- intel_encoder->disable = intel_dsi_disable;
+ intel_encoder->enable = intel_dsi_enable_nop;
+ intel_encoder->disable = intel_dsi_pre_disable;
intel_encoder->post_disable = intel_dsi_post_disable;
intel_encoder->get_hw_state = intel_dsi_get_hw_state;
intel_encoder->get_config = intel_dsi_get_config;
diff --git a/drivers/gpu/drm/i915/intel_dsi.h b/drivers/gpu/drm/i915/intel_dsi.h
index b4a27cec882f..31db33d3e5cc 100644
--- a/drivers/gpu/drm/i915/intel_dsi.h
+++ b/drivers/gpu/drm/i915/intel_dsi.h
@@ -31,7 +31,6 @@
struct intel_dsi_device {
unsigned int panel_id;
const char *name;
- int type;
const struct intel_dsi_dev_ops *dev_ops;
void *dev_priv;
};
@@ -85,6 +84,9 @@ struct intel_dsi {
/* virtual channel */
int channel;
+ /* Video mode or command mode */
+ u16 operation_mode;
+
/* number of DSI lanes */
unsigned int lane_count;
@@ -95,8 +97,10 @@ struct intel_dsi {
u32 video_mode_format;
/* eot for MIPI_EOT_DISABLE register */
- u32 eot_disable;
+ u8 eotp_pkt;
+ u8 clock_stop;
+ u8 escape_clk_div;
u32 port_bits;
u32 bw_timer;
u32 dphy_reg;
@@ -110,6 +114,15 @@ struct intel_dsi {
u16 hs_to_lp_count;
u16 clk_lp_to_hs_count;
u16 clk_hs_to_lp_count;
+
+ u16 init_count;
+
+ /* all delays in ms */
+ u16 backlight_off_delay;
+ u16 backlight_on_delay;
+ u16 panel_on_delay;
+ u16 panel_off_delay;
+ u16 panel_pwr_cycle_delay;
};
static inline struct intel_dsi *enc_to_intel_dsi(struct drm_encoder *encoder)
@@ -120,4 +133,6 @@ 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 struct intel_dsi_dev_ops vbt_generic_dsi_display_ops;
+
#endif /* _INTEL_DSI_H */
diff --git a/drivers/gpu/drm/i915/intel_dsi_cmd.c b/drivers/gpu/drm/i915/intel_dsi_cmd.c
index 7c40f981d2c7..3eeb21b9fddf 100644
--- a/drivers/gpu/drm/i915/intel_dsi_cmd.c
+++ b/drivers/gpu/drm/i915/intel_dsi_cmd.c
@@ -389,7 +389,7 @@ int dsi_vc_generic_read(struct intel_dsi *intel_dsi, int channel,
*
* XXX: commands with data in MIPI_DPI_DATA?
*/
-int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd)
+int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd, bool hs)
{
struct drm_encoder *encoder = &intel_dsi->base.base;
struct drm_device *dev = encoder->dev;
@@ -399,7 +399,7 @@ int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd)
u32 mask;
/* XXX: pipe, hs */
- if (intel_dsi->hs)
+ if (hs)
cmd &= ~DPI_LP_MODE;
else
cmd |= DPI_LP_MODE;
diff --git a/drivers/gpu/drm/i915/intel_dsi_cmd.h b/drivers/gpu/drm/i915/intel_dsi_cmd.h
index 54c8a234a2e0..9a18cbfa5460 100644
--- a/drivers/gpu/drm/i915/intel_dsi_cmd.h
+++ b/drivers/gpu/drm/i915/intel_dsi_cmd.h
@@ -33,6 +33,9 @@
#include "intel_drv.h"
#include "intel_dsi.h"
+#define DPI_LP_MODE_EN false
+#define DPI_HS_MODE_EN true
+
void dsi_hs_mode_enable(struct intel_dsi *intel_dsi, bool enable);
int dsi_vc_dcs_write(struct intel_dsi *intel_dsi, int channel,
@@ -47,7 +50,7 @@ int dsi_vc_dcs_read(struct intel_dsi *intel_dsi, int channel, u8 dcs_cmd,
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);
+int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd, bool hs);
/* 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
new file mode 100644
index 000000000000..21a0d348cedc
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c
@@ -0,0 +1,589 @@
+/*
+ * 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.
+ *
+ * Author: Shobhit Kumar <shobhit.kumar@intel.com>
+ *
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include <drm/i915_drm.h>
+#include <linux/slab.h>
+#include <video/mipi_display.h>
+#include <asm/intel-mid.h>
+#include <video/mipi_display.h>
+#include "i915_drv.h"
+#include "intel_drv.h"
+#include "intel_dsi.h"
+#include "intel_dsi_cmd.h"
+
+#define MIPI_TRANSFER_MODE_SHIFT 0
+#define MIPI_VIRTUAL_CHANNEL_SHIFT 1
+#define MIPI_PORT_SHIFT 3
+
+#define PREPARE_CNT_MAX 0x3F
+#define EXIT_ZERO_CNT_MAX 0x3F
+#define CLK_ZERO_CNT_MAX 0xFF
+#define TRAIL_CNT_MAX 0x1F
+
+#define NS_KHZ_RATIO 1000000
+
+#define GPI0_NC_0_HV_DDI0_HPD 0x4130
+#define GPIO_NC_0_HV_DDI0_PAD 0x4138
+#define GPIO_NC_1_HV_DDI0_DDC_SDA 0x4120
+#define GPIO_NC_1_HV_DDI0_DDC_SDA_PAD 0x4128
+#define GPIO_NC_2_HV_DDI0_DDC_SCL 0x4110
+#define GPIO_NC_2_HV_DDI0_DDC_SCL_PAD 0x4118
+#define GPIO_NC_3_PANEL0_VDDEN 0x4140
+#define GPIO_NC_3_PANEL0_VDDEN_PAD 0x4148
+#define GPIO_NC_4_PANEL0_BLKEN 0x4150
+#define GPIO_NC_4_PANEL0_BLKEN_PAD 0x4158
+#define GPIO_NC_5_PANEL0_BLKCTL 0x4160
+#define GPIO_NC_5_PANEL0_BLKCTL_PAD 0x4168
+#define GPIO_NC_6_PCONF0 0x4180
+#define GPIO_NC_6_PAD 0x4188
+#define GPIO_NC_7_PCONF0 0x4190
+#define GPIO_NC_7_PAD 0x4198
+#define GPIO_NC_8_PCONF0 0x4170
+#define GPIO_NC_8_PAD 0x4178
+#define GPIO_NC_9_PCONF0 0x4100
+#define GPIO_NC_9_PAD 0x4108
+#define GPIO_NC_10_PCONF0 0x40E0
+#define GPIO_NC_10_PAD 0x40E8
+#define GPIO_NC_11_PCONF0 0x40F0
+#define GPIO_NC_11_PAD 0x40F8
+
+struct gpio_table {
+ u16 function_reg;
+ u16 pad_reg;
+ u8 init;
+};
+
+static struct gpio_table gtable[] = {
+ { GPI0_NC_0_HV_DDI0_HPD, GPIO_NC_0_HV_DDI0_PAD, 0 },
+ { GPIO_NC_1_HV_DDI0_DDC_SDA, GPIO_NC_1_HV_DDI0_DDC_SDA_PAD, 0 },
+ { GPIO_NC_2_HV_DDI0_DDC_SCL, GPIO_NC_2_HV_DDI0_DDC_SCL_PAD, 0 },
+ { GPIO_NC_3_PANEL0_VDDEN, GPIO_NC_3_PANEL0_VDDEN_PAD, 0 },
+ { GPIO_NC_4_PANEL0_BLKEN, GPIO_NC_4_PANEL0_BLKEN_PAD, 0 },
+ { GPIO_NC_5_PANEL0_BLKCTL, GPIO_NC_5_PANEL0_BLKCTL_PAD, 0 },
+ { GPIO_NC_6_PCONF0, GPIO_NC_6_PAD, 0 },
+ { GPIO_NC_7_PCONF0, GPIO_NC_7_PAD, 0 },
+ { GPIO_NC_8_PCONF0, GPIO_NC_8_PAD, 0 },
+ { GPIO_NC_9_PCONF0, GPIO_NC_9_PAD, 0 },
+ { GPIO_NC_10_PCONF0, GPIO_NC_10_PAD, 0},
+ { GPIO_NC_11_PCONF0, GPIO_NC_11_PAD, 0}
+};
+
+static u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi, u8 *data)
+{
+ u8 type, byte, mode, vc, port;
+ u16 len;
+
+ byte = *data++;
+ mode = (byte >> MIPI_TRANSFER_MODE_SHIFT) & 0x1;
+ vc = (byte >> MIPI_VIRTUAL_CHANNEL_SHIFT) & 0x3;
+ port = (byte >> MIPI_PORT_SHIFT) & 0x3;
+
+ /* LP or HS mode */
+ intel_dsi->hs = mode;
+
+ /* get packet type and increment the pointer */
+ type = *data++;
+
+ len = *((u16 *) data);
+ data += 2;
+
+ switch (type) {
+ case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+ dsi_vc_generic_write_0(intel_dsi, vc);
+ break;
+ case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+ dsi_vc_generic_write_1(intel_dsi, vc, *data);
+ break;
+ case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+ dsi_vc_generic_write_2(intel_dsi, vc, *data, *(data + 1));
+ break;
+ case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
+ case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
+ case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
+ DRM_DEBUG_DRIVER("Generic Read not yet implemented or used\n");
+ break;
+ case MIPI_DSI_GENERIC_LONG_WRITE:
+ dsi_vc_generic_write(intel_dsi, vc, data, len);
+ break;
+ case MIPI_DSI_DCS_SHORT_WRITE:
+ dsi_vc_dcs_write_0(intel_dsi, vc, *data);
+ break;
+ case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+ dsi_vc_dcs_write_1(intel_dsi, vc, *data, *(data + 1));
+ break;
+ case MIPI_DSI_DCS_READ:
+ DRM_DEBUG_DRIVER("DCS Read not yet implemented or used\n");
+ break;
+ case MIPI_DSI_DCS_LONG_WRITE:
+ dsi_vc_dcs_write(intel_dsi, vc, data, len);
+ break;
+ };
+
+ data += len;
+
+ return data;
+}
+
+static u8 *mipi_exec_delay(struct intel_dsi *intel_dsi, u8 *data)
+{
+ u32 delay = *((u32 *) data);
+
+ usleep_range(delay, delay + 10);
+ data += 4;
+
+ return data;
+}
+
+static u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, u8 *data)
+{
+ u8 gpio, action;
+ u16 function, pad;
+ u32 val;
+ struct drm_device *dev = intel_dsi->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ gpio = *data++;
+
+ /* pull up/down */
+ action = *data++;
+
+ function = gtable[gpio].function_reg;
+ pad = gtable[gpio].pad_reg;
+
+ mutex_lock(&dev_priv->dpio_lock);
+ if (!gtable[gpio].init) {
+ /* program the function */
+ /* FIXME: remove constant below */
+ vlv_gpio_nc_write(dev_priv, function, 0x2000CC00);
+ gtable[gpio].init = 1;
+ }
+
+ val = 0x4 | action;
+
+ /* pull up/down */
+ vlv_gpio_nc_write(dev_priv, pad, val);
+ mutex_unlock(&dev_priv->dpio_lock);
+
+ return data;
+}
+
+typedef u8 * (*fn_mipi_elem_exec)(struct intel_dsi *intel_dsi, u8 *data);
+static const fn_mipi_elem_exec exec_elem[] = {
+ NULL, /* reserved */
+ mipi_exec_send_packet,
+ mipi_exec_delay,
+ mipi_exec_gpio,
+ NULL, /* status read; later */
+};
+
+/*
+ * MIPI Sequence from VBT #53 parsing logic
+ * We have already separated each seqence during bios parsing
+ * Following is generic execution function for any sequence
+ */
+
+static const char * const seq_name[] = {
+ "UNDEFINED",
+ "MIPI_SEQ_ASSERT_RESET",
+ "MIPI_SEQ_INIT_OTP",
+ "MIPI_SEQ_DISPLAY_ON",
+ "MIPI_SEQ_DISPLAY_OFF",
+ "MIPI_SEQ_DEASSERT_RESET"
+};
+
+static void generic_exec_sequence(struct intel_dsi *intel_dsi, char *sequence)
+{
+ u8 *data = sequence;
+ fn_mipi_elem_exec mipi_elem_exec;
+ int index;
+
+ if (!sequence)
+ return;
+
+ DRM_DEBUG_DRIVER("Starting MIPI sequence - %s\n", seq_name[*data]);
+
+ /* go to the first element of the sequence */
+ data++;
+
+ /* parse each byte till we reach end of sequence byte - 0x00 */
+ while (1) {
+ index = *data;
+ mipi_elem_exec = exec_elem[index];
+ if (!mipi_elem_exec) {
+ DRM_ERROR("Unsupported MIPI element, skipping sequence execution\n");
+ return;
+ }
+
+ /* goto element payload */
+ data++;
+
+ /* execute the element specific rotines */
+ data = mipi_elem_exec(intel_dsi, data);
+
+ /*
+ * After processing the element, data should point to
+ * next element or end of sequence
+ * check if have we reached end of sequence
+ */
+ if (*data == 0x00)
+ break;
+ }
+}
+
+static bool generic_init(struct intel_dsi_device *dsi)
+{
+ struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+ struct drm_device *dev = intel_dsi->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct mipi_config *mipi_config = dev_priv->vbt.dsi.config;
+ struct mipi_pps_data *pps = dev_priv->vbt.dsi.pps;
+ struct drm_display_mode *mode = dev_priv->vbt.lfp_lvds_vbt_mode;
+ u32 bits_per_pixel = 24;
+ u32 tlpx_ns, extra_byte_count, bitrate, tlpx_ui;
+ u32 ui_num, ui_den;
+ u32 prepare_cnt, exit_zero_cnt, clk_zero_cnt, trail_cnt;
+ u32 ths_prepare_ns, tclk_trail_ns;
+ u32 tclk_prepare_clkzero, ths_prepare_hszero;
+ u32 lp_to_hs_switch, hs_to_lp_switch;
+
+ DRM_DEBUG_KMS("\n");
+
+ intel_dsi->eotp_pkt = mipi_config->eot_pkt_disabled ? 0 : 1;
+ intel_dsi->clock_stop = mipi_config->enable_clk_stop ? 1 : 0;
+ intel_dsi->lane_count = mipi_config->lane_cnt + 1;
+ intel_dsi->pixel_format = mipi_config->videomode_color_format << 7;
+
+ if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB666)
+ bits_per_pixel = 18;
+ 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;
+ intel_dsi->lp_rx_timeout = mipi_config->lp_rx_timeout;
+ intel_dsi->turn_arnd_val = mipi_config->turn_around_timeout;
+ 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;
+
+ switch (intel_dsi->escape_clk_div) {
+ case 0:
+ tlpx_ns = 50;
+ break;
+ case 1:
+ tlpx_ns = 100;
+ break;
+
+ case 2:
+ tlpx_ns = 200;
+ break;
+ default:
+ tlpx_ns = 50;
+ break;
+ }
+
+ switch (intel_dsi->lane_count) {
+ case 1:
+ case 2:
+ extra_byte_count = 2;
+ break;
+ case 3:
+ extra_byte_count = 4;
+ break;
+ case 4:
+ default:
+ extra_byte_count = 3;
+ break;
+ }
+
+ /*
+ * ui(s) = 1/f [f in hz]
+ * ui(ns) = 10^9 / (f*10^6) [f in Mhz] -> 10^3/f(Mhz)
+ */
+
+ /* in Kbps */
+ ui_num = NS_KHZ_RATIO;
+ ui_den = bitrate;
+
+ tclk_prepare_clkzero = mipi_config->tclk_prepare_clkzero;
+ ths_prepare_hszero = mipi_config->ths_prepare_hszero;
+
+ /*
+ * B060
+ * LP byte clock = TLPX/ (8UI)
+ */
+ intel_dsi->lp_byte_clk = DIV_ROUND_UP(tlpx_ns * ui_den, 8 * ui_num);
+
+ /* count values in UI = (ns value) * (bitrate / (2 * 10^6))
+ *
+ * Since txddrclkhs_i is 2xUI, all the count values programmed in
+ * DPHY param register are divided by 2
+ *
+ * prepare count
+ */
+ 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 */
+ exit_zero_cnt = DIV_ROUND_UP(
+ (ths_prepare_hszero - ths_prepare_ns) * ui_den,
+ ui_num * 2
+ );
+
+ /*
+ * Exit zero is unified val ths_zero and ths_exit
+ * minimum value for ths_exit = 110ns
+ * min (exit_zero_cnt * 2) = 110/UI
+ * exit_zero_cnt = 55/UI
+ */
+ if (exit_zero_cnt < (55 * ui_den / ui_num))
+ if ((55 * ui_den) % ui_num)
+ exit_zero_cnt += 1;
+
+ /* clk zero count */
+ clk_zero_cnt = DIV_ROUND_UP(
+ (tclk_prepare_clkzero - ths_prepare_ns)
+ * ui_den, 2 * ui_num);
+
+ /* trail count */
+ tclk_trail_ns = max(mipi_config->tclk_trail, mipi_config->ths_trail);
+ trail_cnt = DIV_ROUND_UP(tclk_trail_ns * ui_den, 2 * ui_num);
+
+ if (prepare_cnt > PREPARE_CNT_MAX ||
+ exit_zero_cnt > EXIT_ZERO_CNT_MAX ||
+ clk_zero_cnt > CLK_ZERO_CNT_MAX ||
+ trail_cnt > TRAIL_CNT_MAX)
+ DRM_DEBUG_DRIVER("Values crossing maximum limits, restricting to max values\n");
+
+ if (prepare_cnt > PREPARE_CNT_MAX)
+ prepare_cnt = PREPARE_CNT_MAX;
+
+ if (exit_zero_cnt > EXIT_ZERO_CNT_MAX)
+ exit_zero_cnt = EXIT_ZERO_CNT_MAX;
+
+ if (clk_zero_cnt > CLK_ZERO_CNT_MAX)
+ clk_zero_cnt = CLK_ZERO_CNT_MAX;
+
+ if (trail_cnt > TRAIL_CNT_MAX)
+ trail_cnt = TRAIL_CNT_MAX;
+
+ /* B080 */
+ intel_dsi->dphy_reg = exit_zero_cnt << 24 | trail_cnt << 16 |
+ clk_zero_cnt << 8 | prepare_cnt;
+
+ /*
+ * LP to HS switch count = 4TLPX + PREP_COUNT * 2 + EXIT_ZERO_COUNT * 2
+ * + 10UI + Extra Byte Count
+ *
+ * HS to LP switch count = THS-TRAIL + 2TLPX + Extra Byte Count
+ * Extra Byte Count is calculated according to number of lanes.
+ * High Low Switch Count is the Max of LP to HS and
+ * HS to LP switch count
+ *
+ */
+ tlpx_ui = DIV_ROUND_UP(tlpx_ns * ui_den, ui_num);
+
+ /* B044 */
+ /* FIXME:
+ * The comment above does not match with the code */
+ lp_to_hs_switch = DIV_ROUND_UP(4 * tlpx_ui + prepare_cnt * 2 +
+ exit_zero_cnt * 2 + 10, 8);
+
+ hs_to_lp_switch = DIV_ROUND_UP(mipi_config->ths_trail + 2 * tlpx_ui, 8);
+
+ intel_dsi->hs_to_lp_count = max(lp_to_hs_switch, hs_to_lp_switch);
+ intel_dsi->hs_to_lp_count += extra_byte_count;
+
+ /* B088 */
+ /* LP -> HS for clock lanes
+ * LP clk sync + LP11 + LP01 + tclk_prepare + tclk_zero +
+ * extra byte count
+ * 2TPLX + 1TLPX + 1 TPLX(in ns) + prepare_cnt * 2 + clk_zero_cnt *
+ * 2(in UI) + extra byte count
+ * In byteclks = (4TLPX + prepare_cnt * 2 + clk_zero_cnt *2 (in UI)) /
+ * 8 + extra byte count
+ */
+ intel_dsi->clk_lp_to_hs_count =
+ DIV_ROUND_UP(
+ 4 * tlpx_ui + prepare_cnt * 2 +
+ clk_zero_cnt * 2,
+ 8);
+
+ intel_dsi->clk_lp_to_hs_count += extra_byte_count;
+
+ /* HS->LP for Clock Lanes
+ * Low Power clock synchronisations + 1Tx byteclk + tclk_trail +
+ * Extra byte count
+ * 2TLPX + 8UI + (trail_count*2)(in UI) + Extra byte count
+ * In byteclks = (2*TLpx(in UI) + trail_count*2 +8)(in UI)/8 +
+ * Extra byte count
+ */
+ intel_dsi->clk_hs_to_lp_count =
+ DIV_ROUND_UP(2 * tlpx_ui + trail_cnt * 2 + 8,
+ 8);
+ intel_dsi->clk_hs_to_lp_count += extra_byte_count;
+
+ DRM_DEBUG_KMS("Eot %s\n", intel_dsi->eotp_pkt ? "enabled" : "disabled");
+ DRM_DEBUG_KMS("Clockstop %s\n", intel_dsi->clock_stop ?
+ "disabled" : "enabled");
+ DRM_DEBUG_KMS("Mode %s\n", intel_dsi->operation_mode ? "command" : "video");
+ DRM_DEBUG_KMS("Pixel Format %d\n", intel_dsi->pixel_format);
+ DRM_DEBUG_KMS("TLPX %d\n", intel_dsi->escape_clk_div);
+ DRM_DEBUG_KMS("LP RX Timeout 0x%x\n", intel_dsi->lp_rx_timeout);
+ DRM_DEBUG_KMS("Turnaround Timeout 0x%x\n", intel_dsi->turn_arnd_val);
+ DRM_DEBUG_KMS("Init Count 0x%x\n", intel_dsi->init_count);
+ DRM_DEBUG_KMS("HS to LP Count 0x%x\n", intel_dsi->hs_to_lp_count);
+ DRM_DEBUG_KMS("LP Byte Clock %d\n", intel_dsi->lp_byte_clk);
+ DRM_DEBUG_KMS("DBI BW Timer 0x%x\n", intel_dsi->bw_timer);
+ DRM_DEBUG_KMS("LP to HS Clock Count 0x%x\n", intel_dsi->clk_lp_to_hs_count);
+ DRM_DEBUG_KMS("HS to LP Clock Count 0x%x\n", intel_dsi->clk_hs_to_lp_count);
+ DRM_DEBUG_KMS("BTA %s\n",
+ intel_dsi->video_frmt_cfg_bits & DISABLE_VIDEO_BTA ?
+ "disabled" : "enabled");
+
+ /* delays in VBT are in unit of 100us, so need to convert
+ * here in ms
+ * Delay (100us) * 100 /1000 = Delay / 10 (ms) */
+ intel_dsi->backlight_off_delay = pps->bl_disable_delay / 10;
+ intel_dsi->backlight_on_delay = pps->bl_enable_delay / 10;
+ intel_dsi->panel_on_delay = pps->panel_on_delay / 10;
+ intel_dsi->panel_off_delay = pps->panel_off_delay / 10;
+ intel_dsi->panel_pwr_cycle_delay = pps->panel_power_cycle_delay / 10;
+
+ return true;
+}
+
+static int generic_mode_valid(struct intel_dsi_device *dsi,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static bool generic_mode_fixup(struct intel_dsi_device *dsi,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode) {
+ return true;
+}
+
+static void generic_panel_reset(struct intel_dsi_device *dsi)
+{
+ struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+ struct drm_device *dev = intel_dsi->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_ASSERT_RESET];
+
+ generic_exec_sequence(intel_dsi, sequence);
+}
+
+static void generic_disable_panel_power(struct intel_dsi_device *dsi)
+{
+ struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+ struct drm_device *dev = intel_dsi->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET];
+
+ generic_exec_sequence(intel_dsi, sequence);
+}
+
+static void generic_send_otp_cmds(struct intel_dsi_device *dsi)
+{
+ struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+ struct drm_device *dev = intel_dsi->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_INIT_OTP];
+
+ generic_exec_sequence(intel_dsi, sequence);
+}
+
+static void generic_enable(struct intel_dsi_device *dsi)
+{
+ struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+ struct drm_device *dev = intel_dsi->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_DISPLAY_ON];
+
+ generic_exec_sequence(intel_dsi, sequence);
+}
+
+static void generic_disable(struct intel_dsi_device *dsi)
+{
+ struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+ struct drm_device *dev = intel_dsi->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ char *sequence = dev_priv->vbt.dsi.sequence[MIPI_SEQ_DISPLAY_OFF];
+
+ generic_exec_sequence(intel_dsi, sequence);
+}
+
+static enum drm_connector_status generic_detect(struct intel_dsi_device *dsi)
+{
+ return connector_status_connected;
+}
+
+static bool generic_get_hw_state(struct intel_dsi_device *dev)
+{
+ return true;
+}
+
+static struct drm_display_mode *generic_get_modes(struct intel_dsi_device *dsi)
+{
+ struct intel_dsi *intel_dsi = container_of(dsi, struct intel_dsi, dev);
+ struct drm_device *dev = intel_dsi->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->vbt.lfp_lvds_vbt_mode->type |= DRM_MODE_TYPE_PREFERRED;
+ return dev_priv->vbt.lfp_lvds_vbt_mode;
+}
+
+static void generic_destroy(struct intel_dsi_device *dsi) { }
+
+/* Callbacks. We might not need them all. */
+struct intel_dsi_dev_ops vbt_generic_dsi_display_ops = {
+ .init = generic_init,
+ .mode_valid = generic_mode_valid,
+ .mode_fixup = generic_mode_fixup,
+ .panel_reset = generic_panel_reset,
+ .disable_panel_power = generic_disable_panel_power,
+ .send_otp_cmds = generic_send_otp_cmds,
+ .enable = generic_enable,
+ .disable = generic_disable,
+ .detect = generic_detect,
+ .get_hw_state = generic_get_hw_state,
+ .get_modes = generic_get_modes,
+ .destroy = generic_destroy,
+};
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
index 7fe3feedfe03..a3631c0a5c28 100644
--- a/drivers/gpu/drm/i915/intel_dvo.c
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -285,7 +285,7 @@ static bool intel_dvo_compute_config(struct intel_encoder *encoder,
return true;
}
-static void intel_dvo_mode_set(struct intel_encoder *encoder)
+static void intel_dvo_pre_enable(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -343,7 +343,7 @@ intel_dvo_detect(struct drm_connector *connector, bool force)
{
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
- connector->base.id, drm_get_connector_name(connector));
+ connector->base.id, connector->name);
return intel_dvo->dev.dev_ops->detect(&intel_dvo->dev);
}
@@ -475,7 +475,7 @@ void intel_dvo_init(struct drm_device *dev)
intel_encoder->get_hw_state = intel_dvo_get_hw_state;
intel_encoder->get_config = intel_dvo_get_config;
intel_encoder->compute_config = intel_dvo_compute_config;
- intel_encoder->mode_set = intel_dvo_mode_set;
+ intel_encoder->pre_enable = intel_dvo_pre_enable;
intel_connector->get_hw_state = intel_dvo_connector_get_hw_state;
intel_connector->unregister = intel_connector_unregister;
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index f73ba5e6b7a8..088fe9378a4c 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -343,15 +343,15 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
num_connectors_detected++;
if (!enabled[i]) {
- DRM_DEBUG_KMS("connector %d not enabled, skipping\n",
- connector->base.id);
+ DRM_DEBUG_KMS("connector %s not enabled, skipping\n",
+ connector->name);
continue;
}
encoder = connector->encoder;
if (!encoder || WARN_ON(!encoder->crtc)) {
- DRM_DEBUG_KMS("connector %d has no encoder or crtc, skipping\n",
- connector->base.id);
+ DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n",
+ connector->name);
enabled[i] = false;
continue;
}
@@ -373,16 +373,16 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
}
}
- DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
- fb_conn->connector->base.id);
+ DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n",
+ connector->name);
/* go for command line mode first */
modes[i] = drm_pick_cmdline_mode(fb_conn, width, height);
/* try for preferred next */
if (!modes[i]) {
- DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
- fb_conn->connector->base.id);
+ DRM_DEBUG_KMS("looking for preferred mode on connector %s\n",
+ connector->name);
modes[i] = drm_has_preferred_mode(fb_conn, width,
height);
}
@@ -390,7 +390,7 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
/* No preferred mode marked by the EDID? Are there any modes? */
if (!modes[i] && !list_empty(&connector->modes)) {
DRM_DEBUG_KMS("using first mode listed on connector %s\n",
- drm_get_connector_name(connector));
+ connector->name);
modes[i] = list_first_entry(&connector->modes,
struct drm_display_mode,
head);
@@ -409,16 +409,20 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
* since the fb helper layer wants a pointer to
* something we own.
*/
+ DRM_DEBUG_KMS("looking for current mode on connector %s\n",
+ connector->name);
intel_mode_from_pipe_config(&encoder->crtc->hwmode,
&to_intel_crtc(encoder->crtc)->config);
modes[i] = &encoder->crtc->hwmode;
}
crtcs[i] = new_crtc;
- DRM_DEBUG_KMS("connector %s on crtc %d: %s\n",
- drm_get_connector_name(connector),
+ DRM_DEBUG_KMS("connector %s on pipe %d [CRTC:%d]: %dx%d%s\n",
+ connector->name,
+ pipe_name(to_intel_crtc(encoder->crtc)->pipe),
encoder->crtc->base.id,
- modes[i]->name);
+ modes[i]->hdisplay, modes[i]->vdisplay,
+ modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" :"");
fallback = false;
}
@@ -497,7 +501,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev,
return false;
/* Find the largest fb */
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, crtc) {
intel_crtc = to_intel_crtc(crtc);
if (!intel_crtc->active || !crtc->primary->fb) {
@@ -521,7 +525,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev,
}
/* Now make sure all the pipes will fit into it */
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, crtc) {
unsigned int cur_size;
intel_crtc = to_intel_crtc(crtc);
@@ -586,7 +590,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev,
drm_framebuffer_reference(&ifbdev->fb->base);
/* Final pass to check if any active pipes don't have fbs */
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, crtc) {
intel_crtc = to_intel_crtc(crtc);
if (!intel_crtc->active)
@@ -692,11 +696,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev)
if (!dev_priv->fbdev)
return;
- drm_modeset_lock_all(dev);
-
- ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
+ ret = drm_fb_helper_restore_fbdev_mode_unlocked(&dev_priv->fbdev->helper);
if (ret)
DRM_DEBUG("failed to restore crtc mode\n");
-
- drm_modeset_unlock_all(dev);
}
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 157267aa3561..eee2bbec2958 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -418,6 +418,7 @@ intel_hdmi_set_hdmi_infoframe(struct drm_encoder *encoder,
}
static void g4x_set_infoframes(struct drm_encoder *encoder,
+ bool enable,
struct drm_display_mode *adjusted_mode)
{
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
@@ -440,7 +441,7 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
* either. */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
- if (!intel_hdmi->has_hdmi_sink) {
+ if (!enable) {
if (!(val & VIDEO_DIP_ENABLE))
return;
val &= ~VIDEO_DIP_ENABLE;
@@ -471,6 +472,7 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
}
static void ibx_set_infoframes(struct drm_encoder *encoder,
+ bool enable,
struct drm_display_mode *adjusted_mode)
{
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
@@ -486,7 +488,7 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
/* See the big comment in g4x_set_infoframes() */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
- if (!intel_hdmi->has_hdmi_sink) {
+ if (!enable) {
if (!(val & VIDEO_DIP_ENABLE))
return;
val &= ~VIDEO_DIP_ENABLE;
@@ -518,6 +520,7 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
}
static void cpt_set_infoframes(struct drm_encoder *encoder,
+ bool enable,
struct drm_display_mode *adjusted_mode)
{
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
@@ -531,7 +534,7 @@ static void cpt_set_infoframes(struct drm_encoder *encoder,
/* See the big comment in g4x_set_infoframes() */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
- if (!intel_hdmi->has_hdmi_sink) {
+ if (!enable) {
if (!(val & VIDEO_DIP_ENABLE))
return;
val &= ~(VIDEO_DIP_ENABLE | VIDEO_DIP_ENABLE_AVI);
@@ -554,20 +557,23 @@ static void cpt_set_infoframes(struct drm_encoder *encoder,
}
static void vlv_set_infoframes(struct drm_encoder *encoder,
+ bool enable,
struct drm_display_mode *adjusted_mode)
{
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+ struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe);
u32 val = I915_READ(reg);
+ u32 port = VIDEO_DIP_PORT(intel_dig_port->port);
assert_hdmi_port_disabled(intel_hdmi);
/* See the big comment in g4x_set_infoframes() */
val |= VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC;
- if (!intel_hdmi->has_hdmi_sink) {
+ if (!enable) {
if (!(val & VIDEO_DIP_ENABLE))
return;
val &= ~VIDEO_DIP_ENABLE;
@@ -576,9 +582,19 @@ static void vlv_set_infoframes(struct drm_encoder *encoder,
return;
}
+ if (port != (val & VIDEO_DIP_PORT_MASK)) {
+ if (val & VIDEO_DIP_ENABLE) {
+ val &= ~VIDEO_DIP_ENABLE;
+ I915_WRITE(reg, val);
+ POSTING_READ(reg);
+ }
+ val &= ~VIDEO_DIP_PORT_MASK;
+ val |= port;
+ }
+
val |= VIDEO_DIP_ENABLE;
- val &= ~(VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_ENABLE_GAMUT |
- VIDEO_DIP_ENABLE_GCP);
+ val &= ~(VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_ENABLE_VENDOR |
+ VIDEO_DIP_ENABLE_GAMUT | VIDEO_DIP_ENABLE_GCP);
I915_WRITE(reg, val);
POSTING_READ(reg);
@@ -589,6 +605,7 @@ static void vlv_set_infoframes(struct drm_encoder *encoder,
}
static void hsw_set_infoframes(struct drm_encoder *encoder,
+ bool enable,
struct drm_display_mode *adjusted_mode)
{
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
@@ -599,7 +616,7 @@ static void hsw_set_infoframes(struct drm_encoder *encoder,
assert_hdmi_port_disabled(intel_hdmi);
- if (!intel_hdmi->has_hdmi_sink) {
+ if (!enable) {
I915_WRITE(reg, 0);
POSTING_READ(reg);
return;
@@ -616,7 +633,7 @@ static void hsw_set_infoframes(struct drm_encoder *encoder,
intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode);
}
-static void intel_hdmi_mode_set(struct intel_encoder *encoder)
+static void intel_hdmi_prepare(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -638,27 +655,26 @@ static void intel_hdmi_mode_set(struct intel_encoder *encoder)
else
hdmi_val |= SDVO_COLOR_FORMAT_8bpc;
- /* Required on CPT */
- if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
+ if (crtc->config.has_hdmi_sink)
hdmi_val |= HDMI_MODE_SELECT_HDMI;
- if (intel_hdmi->has_audio) {
+ if (crtc->config.has_audio) {
+ WARN_ON(!crtc->config.has_hdmi_sink);
DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
pipe_name(crtc->pipe));
hdmi_val |= SDVO_AUDIO_ENABLE;
- hdmi_val |= HDMI_MODE_SELECT_HDMI;
intel_write_eld(&encoder->base, adjusted_mode);
}
if (HAS_PCH_CPT(dev))
hdmi_val |= SDVO_PIPE_SEL_CPT(crtc->pipe);
+ else if (IS_CHERRYVIEW(dev))
+ hdmi_val |= SDVO_PIPE_SEL_CHV(crtc->pipe);
else
hdmi_val |= SDVO_PIPE_SEL(crtc->pipe);
I915_WRITE(intel_hdmi->hdmi_reg, hdmi_val);
POSTING_READ(intel_hdmi->hdmi_reg);
-
- intel_hdmi->set_infoframes(&encoder->base, adjusted_mode);
}
static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
@@ -681,6 +697,8 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
if (HAS_PCH_CPT(dev))
*pipe = PORT_TO_PIPE_CPT(tmp);
+ else if (IS_CHERRYVIEW(dev))
+ *pipe = SDVO_PORT_TO_PIPE_CHV(tmp);
else
*pipe = PORT_TO_PIPE(tmp);
@@ -707,6 +725,12 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder,
else
flags |= DRM_MODE_FLAG_NVSYNC;
+ if (tmp & HDMI_MODE_SELECT_HDMI)
+ pipe_config->has_hdmi_sink = true;
+
+ if (tmp & HDMI_MODE_SELECT_HDMI)
+ pipe_config->has_audio = true;
+
pipe_config->adjusted_mode.flags |= flags;
if ((tmp & SDVO_COLOR_FORMAT_MASK) == HDMI_COLOR_FORMAT_12bpc)
@@ -729,7 +753,7 @@ static void intel_enable_hdmi(struct intel_encoder *encoder)
u32 temp;
u32 enable_bits = SDVO_ENABLE;
- if (intel_hdmi->has_audio)
+ if (intel_crtc->config.has_audio)
enable_bits |= SDVO_AUDIO_ENABLE;
temp = I915_READ(intel_hdmi->hdmi_reg);
@@ -883,9 +907,11 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
int portclock_limit = hdmi_portclock_limit(intel_hdmi, false);
int desired_bpp;
+ pipe_config->has_hdmi_sink = intel_hdmi->has_hdmi_sink;
+
if (intel_hdmi->color_range_auto) {
/* See CEA-861-E - 5.1 Default Encoding Parameters */
- if (intel_hdmi->has_hdmi_sink &&
+ if (pipe_config->has_hdmi_sink &&
drm_match_cea_mode(adjusted_mode) > 1)
intel_hdmi->color_range = HDMI_COLOR_RANGE_16_235;
else
@@ -898,13 +924,16 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
if (HAS_PCH_SPLIT(dev) && !HAS_DDI(dev))
pipe_config->has_pch_encoder = true;
+ if (pipe_config->has_hdmi_sink && intel_hdmi->has_audio)
+ pipe_config->has_audio = true;
+
/*
* HDMI is either 12 or 8, so if the display lets 10bpc sneak
* through, clamp it down. Note that g4x/vlv don't support 12bpc hdmi
* outputs. We also need to check that the higher clock still fits
* within limits.
*/
- if (pipe_config->pipe_bpp > 8*3 && intel_hdmi->has_hdmi_sink &&
+ if (pipe_config->pipe_bpp > 8*3 && pipe_config->has_hdmi_sink &&
clock_12bpc <= portclock_limit &&
hdmi_12bpc_possible(encoder->new_crtc)) {
DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n");
@@ -944,7 +973,7 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
enum drm_connector_status status = connector_status_disconnected;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
- connector->base.id, drm_get_connector_name(connector));
+ connector->base.id, connector->name);
power_domain = intel_display_port_power_domain(intel_encoder);
intel_display_power_get(dev_priv, power_domain);
@@ -1104,20 +1133,34 @@ done:
return 0;
}
+static void intel_hdmi_pre_enable(struct intel_encoder *encoder)
+{
+ struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+ struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
+ struct drm_display_mode *adjusted_mode =
+ &intel_crtc->config.adjusted_mode;
+
+ intel_hdmi_prepare(encoder);
+
+ intel_hdmi->set_infoframes(&encoder->base,
+ intel_crtc->config.has_hdmi_sink,
+ adjusted_mode);
+}
+
static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
{
struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
+ struct intel_hdmi *intel_hdmi = &dport->hdmi;
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);
+ struct drm_display_mode *adjusted_mode =
+ &intel_crtc->config.adjusted_mode;
enum dpio_channel port = vlv_dport_to_channel(dport);
int pipe = intel_crtc->pipe;
u32 val;
- if (!IS_VALLEYVIEW(dev))
- return;
-
/* Enable clock channels for this port */
mutex_lock(&dev_priv->dpio_lock);
val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW8(port));
@@ -1144,6 +1187,10 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
vlv_dpio_write(dev_priv, pipe, VLV_PCS_DW23(port), 0x00400888);
mutex_unlock(&dev_priv->dpio_lock);
+ intel_hdmi->set_infoframes(&encoder->base,
+ intel_crtc->config.has_hdmi_sink,
+ adjusted_mode);
+
intel_enable_hdmi(encoder);
vlv_wait_port_ready(dev_priv, dport);
@@ -1159,8 +1206,7 @@ static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
enum dpio_channel port = vlv_dport_to_channel(dport);
int pipe = intel_crtc->pipe;
- if (!IS_VALLEYVIEW(dev))
- return;
+ intel_hdmi_prepare(encoder);
/* Program Tx lane resets to default */
mutex_lock(&dev_priv->dpio_lock);
@@ -1199,6 +1245,152 @@ static void vlv_hdmi_post_disable(struct intel_encoder *encoder)
mutex_unlock(&dev_priv->dpio_lock);
}
+static void chv_hdmi_post_disable(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;
+
+ mutex_lock(&dev_priv->dpio_lock);
+
+ /* Propagate soft reset to data lane reset */
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
+ val |= CHV_PCS_REQ_SOFTRESET_EN;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
+ val |= CHV_PCS_REQ_SOFTRESET_EN;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
+ val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
+ val &= ~(DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
+
+ mutex_unlock(&dev_priv->dpio_lock);
+}
+
+static void chv_hdmi_pre_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);
+ int pipe = intel_crtc->pipe;
+ int data, i;
+ u32 val;
+
+ mutex_lock(&dev_priv->dpio_lock);
+
+ /* Deassert soft data lane reset*/
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW1(ch));
+ val |= CHV_PCS_REQ_SOFTRESET_EN;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW1(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW1(ch));
+ val |= CHV_PCS_REQ_SOFTRESET_EN;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW1(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW0(ch));
+ val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW0(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW0(ch));
+ val |= (DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW0(ch), val);
+
+ /* Program Tx latency optimal setting */
+ for (i = 0; i < 4; i++) {
+ /* Set the latency optimal bit */
+ data = (i == 1) ? 0x0 : 0x6;
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW11(ch, i),
+ data << DPIO_FRC_LATENCY_SHFIT);
+
+ /* Set the upar bit */
+ data = (i == 1) ? 0x0 : 0x1;
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW14(ch, i),
+ data << DPIO_UPAR_SHIFT);
+ }
+
+ /* Data lane stagger programming */
+ /* FIXME: Fix up value only after power analysis */
+
+ /* Clear calc init */
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
+ val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
+ val &= ~(DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3);
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
+
+ /* FIXME: Program the support xxx V-dB */
+ /* Use 800mV-0dB */
+ for (i = 0; i < 4; i++) {
+ val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW4(ch, i));
+ val &= ~DPIO_SWING_DEEMPH9P5_MASK;
+ val |= 128 << DPIO_SWING_DEEMPH9P5_SHIFT;
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW4(ch, i), val);
+ }
+
+ 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;
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW2(ch, i), val);
+ }
+
+ /* Disable unique transition scale */
+ for (i = 0; i < 4; i++) {
+ val = vlv_dpio_read(dev_priv, pipe, CHV_TX_DW3(ch, i));
+ val &= ~DPIO_TX_UNIQ_TRANS_SCALE_EN;
+ vlv_dpio_write(dev_priv, pipe, CHV_TX_DW3(ch, i), val);
+ }
+
+ /* Additional steps for 1200mV-0dB */
+#if 0
+ val = vlv_dpio_read(dev_priv, pipe, VLV_TX_DW3(ch));
+ if (ch)
+ val |= DPIO_TX_UNIQ_TRANS_SCALE_CH1;
+ else
+ val |= DPIO_TX_UNIQ_TRANS_SCALE_CH0;
+ vlv_dpio_write(dev_priv, pipe, VLV_TX_DW3(ch), val);
+
+ vlv_dpio_write(dev_priv, pipe, VLV_TX_DW2(ch),
+ vlv_dpio_read(dev_priv, pipe, VLV_TX_DW2(ch)) |
+ (0x9a << DPIO_UNIQ_TRANS_SCALE_SHIFT));
+#endif
+ /* Start swing calculation */
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS01_DW10(ch));
+ val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS01_DW10(ch), val);
+
+ val = vlv_dpio_read(dev_priv, pipe, VLV_PCS23_DW10(ch));
+ val |= DPIO_PCS_SWING_CALC_TX0_TX2 | DPIO_PCS_SWING_CALC_TX1_TX3;
+ vlv_dpio_write(dev_priv, pipe, VLV_PCS23_DW10(ch), val);
+
+ /* LRC Bypass */
+ val = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30);
+ val |= DPIO_LRC_BYPASS;
+ vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, val);
+
+ mutex_unlock(&dev_priv->dpio_lock);
+
+ intel_enable_hdmi(encoder);
+
+ vlv_wait_port_ready(dev_priv, dport);
+}
+
static void intel_hdmi_destroy(struct drm_connector *connector)
{
drm_connector_cleanup(connector);
@@ -1259,7 +1451,10 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
intel_encoder->hpd_pin = HPD_PORT_C;
break;
case PORT_D:
- intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
+ if (IS_CHERRYVIEW(dev))
+ intel_hdmi->ddc_bus = GMBUS_PORT_DPD_CHV;
+ else
+ intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
intel_encoder->hpd_pin = HPD_PORT_D;
break;
case PORT_A:
@@ -1329,21 +1524,32 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
DRM_MODE_ENCODER_TMDS);
intel_encoder->compute_config = intel_hdmi_compute_config;
- intel_encoder->mode_set = intel_hdmi_mode_set;
intel_encoder->disable = intel_disable_hdmi;
intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
intel_encoder->get_config = intel_hdmi_get_config;
- if (IS_VALLEYVIEW(dev)) {
+ if (IS_CHERRYVIEW(dev)) {
+ intel_encoder->pre_enable = chv_hdmi_pre_enable;
+ intel_encoder->enable = vlv_enable_hdmi;
+ intel_encoder->post_disable = chv_hdmi_post_disable;
+ } else if (IS_VALLEYVIEW(dev)) {
intel_encoder->pre_pll_enable = vlv_hdmi_pre_pll_enable;
intel_encoder->pre_enable = vlv_hdmi_pre_enable;
intel_encoder->enable = vlv_enable_hdmi;
intel_encoder->post_disable = vlv_hdmi_post_disable;
} else {
+ intel_encoder->pre_enable = intel_hdmi_pre_enable;
intel_encoder->enable = intel_enable_hdmi;
}
intel_encoder->type = INTEL_OUTPUT_HDMI;
- intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
+ if (IS_CHERRYVIEW(dev)) {
+ if (port == PORT_D)
+ intel_encoder->crtc_mask = 1 << 2;
+ else
+ intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
+ } else {
+ intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
+ }
intel_encoder->cloneable = 1 << INTEL_OUTPUT_ANALOG;
/*
* BSpec is unclear about HDMI+HDMI cloning on g4x, but it seems
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index f1ecf916474a..23126023aeba 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -111,13 +111,6 @@ static void intel_lvds_get_config(struct intel_encoder *encoder,
pipe_config->adjusted_mode.flags |= flags;
- /* gen2/3 store dither state in pfit control, needs to match */
- if (INTEL_INFO(dev)->gen < 4) {
- tmp = I915_READ(PFIT_CONTROL);
-
- pipe_config->gmch_pfit.control |= tmp & PANEL_8TO6_DITHER_ENABLE;
- }
-
dotclock = pipe_config->port_clock;
if (HAS_PCH_SPLIT(dev_priv->dev))
@@ -126,10 +119,6 @@ static void intel_lvds_get_config(struct intel_encoder *encoder,
pipe_config->adjusted_mode.crtc_clock = dotclock;
}
-/* The LVDS pin pair needs to be on before the DPLLs are enabled.
- * This is an exception to the general rule that mode_set doesn't turn
- * things on.
- */
static void intel_pre_enable_lvds(struct intel_encoder *encoder)
{
struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
@@ -331,15 +320,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
return true;
}
-static void intel_lvds_mode_set(struct intel_encoder *encoder)
-{
- /*
- * We don't do anything here, the LVDS port is fully set up in the pre
- * enable hook - the ordering constraints for enabling the lvds port vs.
- * enabling the display pll are too strict.
- */
-}
-
/**
* Detect the LVDS connection.
*
@@ -354,7 +334,7 @@ intel_lvds_detect(struct drm_connector *connector, bool force)
enum drm_connector_status status;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
- connector->base.id, drm_get_connector_name(connector));
+ connector->base.id, connector->name);
status = intel_panel_detect(dev);
if (status != connector_status_unknown)
@@ -953,7 +933,6 @@ void intel_lvds_init(struct drm_device *dev)
intel_encoder->enable = intel_enable_lvds;
intel_encoder->pre_enable = intel_pre_enable_lvds;
intel_encoder->compute_config = intel_lvds_compute_config;
- intel_encoder->mode_set = intel_lvds_mode_set;
intel_encoder->disable = intel_disable_lvds;
intel_encoder->get_hw_state = intel_lvds_get_hw_state;
intel_encoder->get_config = intel_lvds_get_config;
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index acde2945eb8a..2e2c71fcc9ed 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -410,7 +410,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
if (bclp > 255)
return ASLC_BACKLIGHT_FAILED;
- mutex_lock(&dev->mode_config.mutex);
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
/*
* Update backlight on all connectors that support backlight (usually
@@ -421,7 +421,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
intel_panel_set_backlight(intel_connector, bclp, 255);
iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv);
- mutex_unlock(&dev->mode_config.mutex);
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
return 0;
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index 129db0c7d835..daa118978eec 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -213,7 +213,7 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
{
struct drm_device *dev = overlay->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+ struct intel_engine_cs *ring = &dev_priv->ring[RCS];
int ret;
BUG_ON(overlay->last_flip_req);
@@ -236,7 +236,7 @@ static int intel_overlay_on(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+ struct intel_engine_cs *ring = &dev_priv->ring[RCS];
int ret;
BUG_ON(overlay->active);
@@ -263,7 +263,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
{
struct drm_device *dev = overlay->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+ struct intel_engine_cs *ring = &dev_priv->ring[RCS];
u32 flip_addr = overlay->flip_addr;
u32 tmp;
int ret;
@@ -320,7 +320,7 @@ static int intel_overlay_off(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+ struct intel_engine_cs *ring = &dev_priv->ring[RCS];
u32 flip_addr = overlay->flip_addr;
int ret;
@@ -363,7 +363,7 @@ static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+ struct intel_engine_cs *ring = &dev_priv->ring[RCS];
int ret;
if (overlay->last_flip_req == 0)
@@ -389,7 +389,7 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
{
struct drm_device *dev = overlay->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+ struct intel_engine_cs *ring = &dev_priv->ring[RCS];
int ret;
/* Only wait if there is actually an old frame to release to
@@ -688,7 +688,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
u32 swidth, swidthsw, sheight, ostride;
BUG_ON(!mutex_is_locked(&dev->struct_mutex));
- BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
+ BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
BUG_ON(!overlay);
ret = intel_overlay_release_old_vid(overlay);
@@ -793,7 +793,7 @@ int intel_overlay_switch_off(struct intel_overlay *overlay)
int ret;
BUG_ON(!mutex_is_locked(&dev->struct_mutex));
- BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
+ BUG_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
ret = intel_overlay_recover_from_interrupt(overlay);
if (ret != 0)
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index cb8cfb7e0974..5e6c888b4928 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -42,6 +42,59 @@ intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode,
drm_mode_set_crtcinfo(adjusted_mode, 0);
}
+/**
+ * intel_find_panel_downclock - find the reduced downclock for LVDS in EDID
+ * @dev: drm device
+ * @fixed_mode : panel native mode
+ * @connector: LVDS/eDP connector
+ *
+ * Return downclock_avail
+ * Find the reduced downclock for LVDS/eDP in EDID.
+ */
+struct drm_display_mode *
+intel_find_panel_downclock(struct drm_device *dev,
+ struct drm_display_mode *fixed_mode,
+ struct drm_connector *connector)
+{
+ struct drm_display_mode *scan, *tmp_mode;
+ int temp_downclock;
+
+ temp_downclock = fixed_mode->clock;
+ tmp_mode = NULL;
+
+ list_for_each_entry(scan, &connector->probed_modes, head) {
+ /*
+ * If one mode has the same resolution with the fixed_panel
+ * mode while they have the different refresh rate, it means
+ * that the reduced downclock is found. In such
+ * case we can set the different FPx0/1 to dynamically select
+ * between low and high frequency.
+ */
+ if (scan->hdisplay == fixed_mode->hdisplay &&
+ scan->hsync_start == fixed_mode->hsync_start &&
+ scan->hsync_end == fixed_mode->hsync_end &&
+ scan->htotal == fixed_mode->htotal &&
+ scan->vdisplay == fixed_mode->vdisplay &&
+ scan->vsync_start == fixed_mode->vsync_start &&
+ scan->vsync_end == fixed_mode->vsync_end &&
+ scan->vtotal == fixed_mode->vtotal) {
+ if (scan->clock < temp_downclock) {
+ /*
+ * The downclock is already found. But we
+ * expect to find the lower downclock.
+ */
+ temp_downclock = scan->clock;
+ tmp_mode = scan;
+ }
+ }
+ }
+
+ if (temp_downclock < fixed_mode->clock)
+ return drm_mode_duplicate(dev, tmp_mode);
+ else
+ return NULL;
+}
+
/* adjusted_mode has been preset to be the panel's fixed mode */
void
intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
@@ -308,21 +361,43 @@ void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
PFIT_FILTER_FUZZY);
+ /* Make sure pre-965 set dither correctly for 18bpp panels. */
+ if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18)
+ pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+
out:
if ((pfit_control & PFIT_ENABLE) == 0) {
pfit_control = 0;
pfit_pgm_ratios = 0;
}
- /* Make sure pre-965 set dither correctly for 18bpp panels. */
- if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18)
- pfit_control |= PANEL_8TO6_DITHER_ENABLE;
-
pipe_config->gmch_pfit.control = pfit_control;
pipe_config->gmch_pfit.pgm_ratios = pfit_pgm_ratios;
pipe_config->gmch_pfit.lvds_border_bits = border;
}
+enum drm_connector_status
+intel_panel_detect(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /* Assume that the BIOS does not lie through the OpRegion... */
+ if (!i915.panel_ignore_lid && dev_priv->opregion.lid_state) {
+ return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
+ connector_status_connected :
+ connector_status_disconnected;
+ }
+
+ switch (i915.panel_ignore_lid) {
+ case -2:
+ return connector_status_connected;
+ case -1:
+ return connector_status_disconnected;
+ default:
+ return connector_status_unknown;
+ }
+}
+
static u32 intel_panel_compute_brightness(struct intel_connector *connector,
u32 val)
{
@@ -795,40 +870,18 @@ void intel_panel_enable_backlight(struct intel_connector *connector)
spin_unlock_irqrestore(&dev_priv->backlight_lock, flags);
}
-enum drm_connector_status
-intel_panel_detect(struct drm_device *dev)
-{
- struct drm_i915_private *dev_priv = dev->dev_private;
-
- /* Assume that the BIOS does not lie through the OpRegion... */
- if (!i915.panel_ignore_lid && dev_priv->opregion.lid_state) {
- return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
- connector_status_connected :
- connector_status_disconnected;
- }
-
- switch (i915.panel_ignore_lid) {
- case -2:
- return connector_status_connected;
- case -1:
- return connector_status_disconnected;
- default:
- return connector_status_unknown;
- }
-}
-
#if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE)
static int intel_backlight_device_update_status(struct backlight_device *bd)
{
struct intel_connector *connector = bl_get_data(bd);
struct drm_device *dev = connector->base.dev;
- mutex_lock(&dev->mode_config.mutex);
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
DRM_DEBUG_KMS("updating intel_backlight, brightness=%d/%d\n",
bd->props.brightness, bd->props.max_brightness);
intel_panel_set_backlight(connector, bd->props.brightness,
bd->props.max_brightness);
- mutex_unlock(&dev->mode_config.mutex);
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
return 0;
}
@@ -840,9 +893,9 @@ static int intel_backlight_device_get_brightness(struct backlight_device *bd)
int ret;
intel_runtime_pm_get(dev_priv);
- mutex_lock(&dev->mode_config.mutex);
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
ret = intel_panel_get_backlight(connector);
- mutex_unlock(&dev->mode_config.mutex);
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
intel_runtime_pm_put(dev_priv);
return ret;
@@ -1077,7 +1130,7 @@ int intel_panel_setup_backlight(struct drm_connector *connector)
if (ret) {
DRM_DEBUG_KMS("failed to setup backlight for connector %s\n",
- drm_get_connector_name(connector));
+ connector->name);
return ret;
}
@@ -1103,59 +1156,6 @@ void intel_panel_destroy_backlight(struct drm_connector *connector)
intel_backlight_device_unregister(intel_connector);
}
-/**
- * intel_find_panel_downclock - find the reduced downclock for LVDS in EDID
- * @dev: drm device
- * @fixed_mode : panel native mode
- * @connector: LVDS/eDP connector
- *
- * Return downclock_avail
- * Find the reduced downclock for LVDS/eDP in EDID.
- */
-struct drm_display_mode *
-intel_find_panel_downclock(struct drm_device *dev,
- struct drm_display_mode *fixed_mode,
- struct drm_connector *connector)
-{
- struct drm_display_mode *scan, *tmp_mode;
- int temp_downclock;
-
- temp_downclock = fixed_mode->clock;
- tmp_mode = NULL;
-
- list_for_each_entry(scan, &connector->probed_modes, head) {
- /*
- * If one mode has the same resolution with the fixed_panel
- * mode while they have the different refresh rate, it means
- * that the reduced downclock is found. In such
- * case we can set the different FPx0/1 to dynamically select
- * between low and high frequency.
- */
- if (scan->hdisplay == fixed_mode->hdisplay &&
- scan->hsync_start == fixed_mode->hsync_start &&
- scan->hsync_end == fixed_mode->hsync_end &&
- scan->htotal == fixed_mode->htotal &&
- scan->vdisplay == fixed_mode->vdisplay &&
- scan->vsync_start == fixed_mode->vsync_start &&
- scan->vsync_end == fixed_mode->vsync_end &&
- scan->vtotal == fixed_mode->vtotal) {
- if (scan->clock < temp_downclock) {
- /*
- * The downclock is already found. But we
- * expect to find the lower downclock.
- */
- temp_downclock = scan->clock;
- tmp_mode = scan;
- }
- }
- }
-
- if (temp_downclock < fixed_mode->clock)
- return drm_mode_duplicate(dev, tmp_mode);
- else
- return NULL;
-}
-
/* Set up chip specific backlight functions */
void intel_panel_init_backlight_funcs(struct drm_device *dev)
{
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index d93dcf683e8c..d1e53abec1b5 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -487,7 +487,7 @@ void intel_update_fbc(struct drm_device *dev)
* - new fb is too large to fit in compressed buffer
* - going to an unsupported config (interlace, pixel multiply, etc.)
*/
- list_for_each_entry(tmp_crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, tmp_crtc) {
if (intel_crtc_active(tmp_crtc) &&
to_intel_crtc(tmp_crtc)->primary_enabled) {
if (crtc) {
@@ -1010,7 +1010,7 @@ static struct drm_crtc *single_enabled_crtc(struct drm_device *dev)
{
struct drm_crtc *crtc, *enabled = NULL;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ for_each_crtc(dev, crtc) {
if (intel_crtc_active(crtc)) {
if (enabled)
return NULL;
@@ -1831,6 +1831,40 @@ static unsigned int ilk_display_fifo_size(const struct drm_device *dev)
return 512;
}
+static unsigned int ilk_plane_wm_reg_max(const struct drm_device *dev,
+ int level, bool is_sprite)
+{
+ if (INTEL_INFO(dev)->gen >= 8)
+ /* BDW primary/sprite plane watermarks */
+ return level == 0 ? 255 : 2047;
+ else if (INTEL_INFO(dev)->gen >= 7)
+ /* IVB/HSW primary/sprite plane watermarks */
+ return level == 0 ? 127 : 1023;
+ else if (!is_sprite)
+ /* ILK/SNB primary plane watermarks */
+ return level == 0 ? 127 : 511;
+ else
+ /* ILK/SNB sprite plane watermarks */
+ return level == 0 ? 63 : 255;
+}
+
+static unsigned int ilk_cursor_wm_reg_max(const struct drm_device *dev,
+ int level)
+{
+ if (INTEL_INFO(dev)->gen >= 7)
+ return level == 0 ? 63 : 255;
+ else
+ return level == 0 ? 31 : 63;
+}
+
+static unsigned int ilk_fbc_wm_reg_max(const struct drm_device *dev)
+{
+ if (INTEL_INFO(dev)->gen >= 8)
+ return 31;
+ else
+ return 15;
+}
+
/* Calculate the maximum primary/sprite plane watermark */
static unsigned int ilk_plane_wm_max(const struct drm_device *dev,
int level,
@@ -1839,7 +1873,6 @@ static unsigned int ilk_plane_wm_max(const struct drm_device *dev,
bool is_sprite)
{
unsigned int fifo_size = ilk_display_fifo_size(dev);
- unsigned int max;
/* if sprites aren't enabled, sprites get nothing */
if (is_sprite && !config->sprites_enabled)
@@ -1870,19 +1903,7 @@ static unsigned int ilk_plane_wm_max(const struct drm_device *dev,
}
/* clamp to max that the registers can hold */
- if (INTEL_INFO(dev)->gen >= 8)
- max = level == 0 ? 255 : 2047;
- else if (INTEL_INFO(dev)->gen >= 7)
- /* IVB/HSW primary/sprite plane watermarks */
- max = level == 0 ? 127 : 1023;
- else if (!is_sprite)
- /* ILK/SNB primary plane watermarks */
- max = level == 0 ? 127 : 511;
- else
- /* ILK/SNB sprite plane watermarks */
- max = level == 0 ? 63 : 255;
-
- return min(fifo_size, max);
+ return min(fifo_size, ilk_plane_wm_reg_max(dev, level, is_sprite));
}
/* Calculate the maximum cursor plane watermark */
@@ -1895,20 +1916,7 @@ static unsigned int ilk_cursor_wm_max(const struct drm_device *dev,
return 64;
/* otherwise just report max that registers can hold */
- if (INTEL_INFO(dev)->gen >= 7)
- return level == 0 ? 63 : 255;
- else
- return level == 0 ? 31 : 63;
-}
-
-/* Calculate the maximum FBC watermark */
-static unsigned int ilk_fbc_wm_max(const struct drm_device *dev)
-{
- /* max that registers can hold */
- if (INTEL_INFO(dev)->gen >= 8)
- return 31;
- else
- return 15;
+ return ilk_cursor_wm_reg_max(dev, level);
}
static void ilk_compute_wm_maximums(const struct drm_device *dev,
@@ -1920,7 +1928,17 @@ static void ilk_compute_wm_maximums(const struct drm_device *dev,
max->pri = ilk_plane_wm_max(dev, level, config, ddb_partitioning, false);
max->spr = ilk_plane_wm_max(dev, level, config, ddb_partitioning, true);
max->cur = ilk_cursor_wm_max(dev, level, config);
- max->fbc = ilk_fbc_wm_max(dev);
+ max->fbc = ilk_fbc_wm_reg_max(dev);
+}
+
+static void ilk_compute_wm_reg_maximums(struct drm_device *dev,
+ int level,
+ struct ilk_wm_maximums *max)
+{
+ max->pri = ilk_plane_wm_reg_max(dev, level, false);
+ max->spr = ilk_plane_wm_reg_max(dev, level, true);
+ max->cur = ilk_cursor_wm_reg_max(dev, level);
+ max->fbc = ilk_fbc_wm_reg_max(dev);
}
static bool ilk_validate_wm_level(int level,
@@ -2059,7 +2077,7 @@ static void intel_fixup_cur_wm_latency(struct drm_device *dev, uint16_t wm[5])
wm[3] *= 2;
}
-static int ilk_wm_max_level(const struct drm_device *dev)
+int ilk_wm_max_level(const struct drm_device *dev)
{
/* how many WM levels are we expecting */
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
@@ -2155,38 +2173,52 @@ static void ilk_setup_wm_latency(struct drm_device *dev)
}
static void ilk_compute_wm_parameters(struct drm_crtc *crtc,
- struct ilk_pipe_wm_parameters *p,
- struct intel_wm_config *config)
+ struct ilk_pipe_wm_parameters *p)
{
struct drm_device *dev = crtc->dev;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
enum pipe pipe = intel_crtc->pipe;
struct drm_plane *plane;
- p->active = intel_crtc_active(crtc);
- if (p->active) {
- p->pipe_htotal = intel_crtc->config.adjusted_mode.crtc_htotal;
- p->pixel_rate = ilk_pipe_pixel_rate(dev, crtc);
- p->pri.bytes_per_pixel = crtc->primary->fb->bits_per_pixel / 8;
- p->cur.bytes_per_pixel = 4;
- p->pri.horiz_pixels = intel_crtc->config.pipe_src_w;
- p->cur.horiz_pixels = intel_crtc->cursor_width;
- /* TODO: for now, assume primary and cursor planes are always enabled. */
- p->pri.enabled = true;
- p->cur.enabled = true;
- }
+ if (!intel_crtc_active(crtc))
+ return;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
- config->num_pipes_active += intel_crtc_active(crtc);
+ p->active = true;
+ p->pipe_htotal = intel_crtc->config.adjusted_mode.crtc_htotal;
+ p->pixel_rate = ilk_pipe_pixel_rate(dev, crtc);
+ p->pri.bytes_per_pixel = crtc->primary->fb->bits_per_pixel / 8;
+ p->cur.bytes_per_pixel = 4;
+ p->pri.horiz_pixels = intel_crtc->config.pipe_src_w;
+ p->cur.horiz_pixels = intel_crtc->cursor_width;
+ /* TODO: for now, assume primary and cursor planes are always enabled. */
+ p->pri.enabled = true;
+ p->cur.enabled = true;
drm_for_each_legacy_plane(plane, &dev->mode_config.plane_list) {
struct intel_plane *intel_plane = to_intel_plane(plane);
- if (intel_plane->pipe == pipe)
+ if (intel_plane->pipe == pipe) {
p->spr = intel_plane->wm;
+ break;
+ }
+ }
+}
+
+static void ilk_compute_wm_config(struct drm_device *dev,
+ struct intel_wm_config *config)
+{
+ struct intel_crtc *intel_crtc;
- config->sprites_enabled |= intel_plane->wm.enabled;
- config->sprites_scaled |= intel_plane->wm.scaled;
+ /* Compute the currently _active_ config */
+ for_each_intel_crtc(dev, intel_crtc) {
+ const struct intel_pipe_wm *wm = &intel_crtc->wm.active;
+
+ if (!wm->pipe_enabled)
+ continue;
+
+ config->sprites_enabled |= wm->sprites_enabled;
+ config->sprites_scaled |= wm->sprites_scaled;
+ config->num_pipes_active++;
}
}
@@ -2206,8 +2238,9 @@ static bool intel_compute_pipe_wm(struct drm_crtc *crtc,
};
struct ilk_wm_maximums max;
- /* LP0 watermarks always use 1/2 DDB partitioning */
- ilk_compute_wm_maximums(dev, 0, &config, INTEL_DDB_PART_1_2, &max);
+ pipe_wm->pipe_enabled = params->active;
+ pipe_wm->sprites_enabled = params->spr.enabled;
+ pipe_wm->sprites_scaled = params->spr.scaled;
/* ILK/SNB: LP2+ watermarks only w/o sprites */
if (INTEL_INFO(dev)->gen <= 6 && params->spr.enabled)
@@ -2217,15 +2250,37 @@ static bool intel_compute_pipe_wm(struct drm_crtc *crtc,
if (params->spr.scaled)
max_level = 0;
- for (level = 0; level <= max_level; level++)
- ilk_compute_wm_level(dev_priv, level, params,
- &pipe_wm->wm[level]);
+ ilk_compute_wm_level(dev_priv, 0, params, &pipe_wm->wm[0]);
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
pipe_wm->linetime = hsw_compute_linetime_wm(dev, crtc);
+ /* LP0 watermarks always use 1/2 DDB partitioning */
+ ilk_compute_wm_maximums(dev, 0, &config, INTEL_DDB_PART_1_2, &max);
+
/* At least LP0 must be valid */
- return ilk_validate_wm_level(0, &max, &pipe_wm->wm[0]);
+ if (!ilk_validate_wm_level(0, &max, &pipe_wm->wm[0]))
+ return false;
+
+ ilk_compute_wm_reg_maximums(dev, 1, &max);
+
+ for (level = 1; level <= max_level; level++) {
+ struct intel_wm_level wm = {};
+
+ ilk_compute_wm_level(dev_priv, level, params, &wm);
+
+ /*
+ * Disable any watermark level that exceeds the
+ * register maximums since such watermarks are
+ * always invalid.
+ */
+ if (!ilk_validate_wm_level(level, &max, &wm))
+ break;
+
+ pipe_wm->wm[level] = wm;
+ }
+
+ return true;
}
/*
@@ -2237,20 +2292,28 @@ static void ilk_merge_wm_level(struct drm_device *dev,
{
const struct intel_crtc *intel_crtc;
- list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
- const struct intel_wm_level *wm =
- &intel_crtc->wm.active.wm[level];
+ ret_wm->enable = true;
+
+ for_each_intel_crtc(dev, intel_crtc) {
+ const struct intel_pipe_wm *active = &intel_crtc->wm.active;
+ const struct intel_wm_level *wm = &active->wm[level];
+
+ if (!active->pipe_enabled)
+ continue;
+ /*
+ * The watermark values may have been used in the past,
+ * so we must maintain them in the registers for some
+ * time even if the level is now disabled.
+ */
if (!wm->enable)
- return;
+ ret_wm->enable = false;
ret_wm->pri_val = max(ret_wm->pri_val, wm->pri_val);
ret_wm->spr_val = max(ret_wm->spr_val, wm->spr_val);
ret_wm->cur_val = max(ret_wm->cur_val, wm->cur_val);
ret_wm->fbc_val = max(ret_wm->fbc_val, wm->fbc_val);
}
-
- ret_wm->enable = true;
}
/*
@@ -2262,6 +2325,7 @@ static void ilk_wm_merge(struct drm_device *dev,
struct intel_pipe_wm *merged)
{
int level, max_level = ilk_wm_max_level(dev);
+ int last_enabled_level = max_level;
/* ILK/SNB/IVB: LP1+ watermarks only w/ single pipe */
if ((INTEL_INFO(dev)->gen <= 6 || IS_IVYBRIDGE(dev)) &&
@@ -2277,15 +2341,19 @@ static void ilk_wm_merge(struct drm_device *dev,
ilk_merge_wm_level(dev, level, wm);
- if (!ilk_validate_wm_level(level, max, wm))
- break;
+ if (level > last_enabled_level)
+ wm->enable = false;
+ else if (!ilk_validate_wm_level(level, max, wm))
+ /* make sure all following levels get disabled */
+ last_enabled_level = level - 1;
/*
* The spec says it is preferred to disable
* FBC WMs instead of disabling a WM level.
*/
if (wm->fbc_val > max->fbc) {
- merged->fbc_wm_enabled = false;
+ if (wm->enable)
+ merged->fbc_wm_enabled = false;
wm->fbc_val = 0;
}
}
@@ -2340,14 +2408,19 @@ static void ilk_compute_wm_results(struct drm_device *dev,
level = ilk_wm_lp_to_level(wm_lp, merged);
r = &merged->wm[level];
- if (!r->enable)
- break;
- results->wm_lp[wm_lp - 1] = WM3_LP_EN |
+ /*
+ * Maintain the watermark values even if the level is
+ * disabled. Doing otherwise could cause underruns.
+ */
+ results->wm_lp[wm_lp - 1] =
(ilk_wm_lp_latency(dev, level) << WM1_LP_LATENCY_SHIFT) |
(r->pri_val << WM1_LP_SR_SHIFT) |
r->cur_val;
+ if (r->enable)
+ results->wm_lp[wm_lp - 1] |= WM1_LP_SR_EN;
+
if (INTEL_INFO(dev)->gen >= 8)
results->wm_lp[wm_lp - 1] |=
r->fbc_val << WM1_LP_FBC_SHIFT_BDW;
@@ -2355,6 +2428,10 @@ static void ilk_compute_wm_results(struct drm_device *dev,
results->wm_lp[wm_lp - 1] |=
r->fbc_val << WM1_LP_FBC_SHIFT;
+ /*
+ * Always set WM1S_LP_EN when spr_val != 0, even if the
+ * level is disabled. Doing otherwise could cause underruns.
+ */
if (INTEL_INFO(dev)->gen <= 6 && r->spr_val) {
WARN_ON(wm_lp != 1);
results->wm_lp_spr[wm_lp - 1] = WM1S_LP_EN | r->spr_val;
@@ -2363,7 +2440,7 @@ static void ilk_compute_wm_results(struct drm_device *dev,
}
/* LP0 register values */
- list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
+ for_each_intel_crtc(dev, intel_crtc) {
enum pipe pipe = intel_crtc->pipe;
const struct intel_wm_level *r =
&intel_crtc->wm.active.wm[0];
@@ -2598,7 +2675,7 @@ static void ilk_update_wm(struct drm_crtc *crtc)
struct intel_pipe_wm lp_wm_1_2 = {}, lp_wm_5_6 = {}, *best_lp_wm;
struct intel_wm_config config = {};
- ilk_compute_wm_parameters(crtc, &params, &config);
+ ilk_compute_wm_parameters(crtc, &params);
intel_compute_pipe_wm(crtc, &params, &pipe_wm);
@@ -2607,6 +2684,8 @@ static void ilk_update_wm(struct drm_crtc *crtc)
intel_crtc->wm.active = pipe_wm;
+ ilk_compute_wm_config(dev, &config);
+
ilk_compute_wm_maximums(dev, 1, &config, INTEL_DDB_PART_1_2, &max);
ilk_wm_merge(dev, &config, &max, &lp_wm_1_2);
@@ -2673,7 +2752,9 @@ static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc)
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
hw->wm_linetime[pipe] = I915_READ(PIPE_WM_LINETIME(pipe));
- if (intel_crtc_active(crtc)) {
+ active->pipe_enabled = intel_crtc_active(crtc);
+
+ if (active->pipe_enabled) {
u32 tmp = hw->wm_pipe[pipe];
/*
@@ -2706,7 +2787,7 @@ void ilk_wm_get_hw_state(struct drm_device *dev)
struct ilk_wm_values *hw = &dev_priv->wm.hw;
struct drm_crtc *crtc;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ for_each_crtc(dev, crtc)
ilk_pipe_wm_get_hw_state(crtc);
hw->wm_lp[0] = I915_READ(WM1_LP_ILK);
@@ -2714,8 +2795,10 @@ void ilk_wm_get_hw_state(struct drm_device *dev)
hw->wm_lp[2] = I915_READ(WM3_LP_ILK);
hw->wm_lp_spr[0] = I915_READ(WM1S_LP_ILK);
- hw->wm_lp_spr[1] = I915_READ(WM2S_LP_IVB);
- hw->wm_lp_spr[2] = I915_READ(WM3S_LP_IVB);
+ if (INTEL_INFO(dev)->gen >= 7) {
+ hw->wm_lp_spr[1] = I915_READ(WM2S_LP_IVB);
+ hw->wm_lp_spr[2] = I915_READ(WM3S_LP_IVB);
+ }
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
hw->partitioning = (I915_READ(WM_MISC) & WM_MISC_DATA_PARTITION_5_6) ?
@@ -3071,6 +3154,9 @@ static u32 gen6_rps_pm_mask(struct drm_i915_private *dev_priv, u8 val)
if (INTEL_INFO(dev_priv->dev)->gen <= 7 && !IS_HASWELL(dev_priv->dev))
mask |= GEN6_PM_RP_UP_EI_EXPIRED;
+ if (IS_GEN8(dev_priv->dev))
+ mask |= GEN8_PMINTR_REDIRECT_TO_NON_DISP;
+
return ~mask;
}
@@ -3091,7 +3177,7 @@ void gen6_set_rps(struct drm_device *dev, u8 val)
if (val != dev_priv->rps.cur_freq) {
gen6_set_rps_thresholds(dev_priv, val);
- if (IS_HASWELL(dev))
+ if (IS_HASWELL(dev) || IS_BROADWELL(dev))
I915_WRITE(GEN6_RPNSWREQ,
HSW_FREQUENCY(val));
else
@@ -3134,16 +3220,7 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv)
/* Mask turbo interrupt so that they will not come in between */
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
- /* Bring up the Gfx clock */
- I915_WRITE(VLV_GTLC_SURVIVABILITY_REG,
- I915_READ(VLV_GTLC_SURVIVABILITY_REG) |
- VLV_GFX_CLK_FORCE_ON_BIT);
-
- if (wait_for(((VLV_GFX_CLK_STATUS_BIT &
- I915_READ(VLV_GTLC_SURVIVABILITY_REG)) != 0), 5)) {
- DRM_ERROR("GFX_CLK_ON request timed out\n");
- return;
- }
+ vlv_force_gfx_clock(dev_priv, true);
dev_priv->rps.cur_freq = dev_priv->rps.min_freq_softlimit;
@@ -3154,10 +3231,7 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv)
& GENFREQSTATUS) == 0, 5))
DRM_ERROR("timed out waiting for Punit\n");
- /* Release the Gfx clock */
- I915_WRITE(VLV_GTLC_SURVIVABILITY_REG,
- I915_READ(VLV_GTLC_SURVIVABILITY_REG) &
- ~VLV_GFX_CLK_FORCE_ON_BIT);
+ vlv_force_gfx_clock(dev_priv, false);
I915_WRITE(GEN6_PMINTRMSK,
gen6_rps_pm_mask(dev_priv, dev_priv->rps.cur_freq));
@@ -3215,6 +3289,26 @@ void valleyview_set_rps(struct drm_device *dev, u8 val)
trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv, val));
}
+static void gen8_disable_rps_interrupts(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(GEN6_PMINTRMSK, ~GEN8_PMINTR_REDIRECT_TO_NON_DISP);
+ I915_WRITE(GEN8_GT_IER(2), I915_READ(GEN8_GT_IER(2)) &
+ ~dev_priv->pm_rps_events);
+ /* Complete PM interrupt masking here doesn't race with the rps work
+ * item again unmasking PM interrupts because that is using a different
+ * register (GEN8_GT_IMR(2)) to mask PM interrupts. The only risk is in
+ * leaving stale bits in GEN8_GT_IIR(2) and GEN8_GT_IMR(2) which
+ * gen8_enable_rps will clean up. */
+
+ spin_lock_irq(&dev_priv->irq_lock);
+ dev_priv->rps.pm_iir = 0;
+ spin_unlock_irq(&dev_priv->irq_lock);
+
+ I915_WRITE(GEN8_GT_IIR(2), dev_priv->pm_rps_events);
+}
+
static void gen6_disable_rps_interrupts(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3241,7 +3335,10 @@ static void gen6_disable_rps(struct drm_device *dev)
I915_WRITE(GEN6_RC_CONTROL, 0);
I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
- gen6_disable_rps_interrupts(dev);
+ if (IS_BROADWELL(dev))
+ gen8_disable_rps_interrupts(dev);
+ else
+ gen6_disable_rps_interrupts(dev);
}
static void valleyview_disable_rps(struct drm_device *dev)
@@ -3255,21 +3352,44 @@ static void valleyview_disable_rps(struct drm_device *dev)
static void intel_print_rc6_info(struct drm_device *dev, u32 mode)
{
+ if (IS_VALLEYVIEW(dev)) {
+ if (mode & (GEN7_RC_CTL_TO_MODE | GEN6_RC_CTL_EI_MODE(1)))
+ mode = GEN6_RC_CTL_RC6_ENABLE;
+ 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");
}
-int intel_enable_rc6(const struct drm_device *dev)
+static int sanitize_rc6_option(const struct drm_device *dev, int enable_rc6)
{
/* No RC6 before Ironlake */
if (INTEL_INFO(dev)->gen < 5)
return 0;
+ /* RC6 is only on Ironlake mobile not on desktop */
+ if (INTEL_INFO(dev)->gen == 5 && !IS_IRONLAKE_M(dev))
+ return 0;
+
/* Respect the kernel parameter if it is set */
- if (i915.enable_rc6 >= 0)
- return i915.enable_rc6;
+ if (enable_rc6 >= 0) {
+ int mask;
+
+ if (INTEL_INFO(dev)->gen == 6 || IS_IVYBRIDGE(dev))
+ mask = INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE |
+ INTEL_RC6pp_ENABLE;
+ else
+ 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);
+
+ return enable_rc6 & mask;
+ }
/* Disable RC6 on Ironlake */
if (INTEL_INFO(dev)->gen == 5)
@@ -3281,6 +3401,22 @@ int intel_enable_rc6(const struct drm_device *dev)
return INTEL_RC6_ENABLE;
}
+int intel_enable_rc6(const struct drm_device *dev)
+{
+ return i915.enable_rc6;
+}
+
+static void gen8_enable_rps_interrupts(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ 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);
+ I915_WRITE(GEN8_GT_IIR(2), dev_priv->pm_rps_events);
+ spin_unlock_irq(&dev_priv->irq_lock);
+}
+
static void gen6_enable_rps_interrupts(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3292,10 +3428,31 @@ static void gen6_enable_rps_interrupts(struct drm_device *dev)
spin_unlock_irq(&dev_priv->irq_lock);
}
+static void parse_rp_state_cap(struct drm_i915_private *dev_priv, u32 rp_state_cap)
+{
+ /* All of these values are in units of 50MHz */
+ dev_priv->rps.cur_freq = 0;
+ /* static values from HW: RP0 < RPe < RP1 < RPn (min_freq) */
+ dev_priv->rps.rp1_freq = (rp_state_cap >> 8) & 0xff;
+ dev_priv->rps.rp0_freq = (rp_state_cap >> 0) & 0xff;
+ dev_priv->rps.min_freq = (rp_state_cap >> 16) & 0xff;
+ /* XXX: only BYT has a special efficient freq */
+ dev_priv->rps.efficient_freq = dev_priv->rps.rp1_freq;
+ /* hw_max = RP0 until we check for overclocking */
+ dev_priv->rps.max_freq = dev_priv->rps.rp0_freq;
+
+ /* 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;
+}
+
static void gen8_enable_rps(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
uint32_t rc6_mask = 0, rp_state_cap;
int unused;
@@ -3310,6 +3467,7 @@ static void gen8_enable_rps(struct drm_device *dev)
I915_WRITE(GEN6_RC_CONTROL, 0);
rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+ parse_rp_state_cap(dev_priv, rp_state_cap);
/* 2b: Program RC6 thresholds.*/
I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16);
@@ -3329,8 +3487,10 @@ static void gen8_enable_rps(struct drm_device *dev)
rc6_mask);
/* 4 Program defaults and thresholds for RPS*/
- I915_WRITE(GEN6_RPNSWREQ, HSW_FREQUENCY(10)); /* Request 500 MHz */
- I915_WRITE(GEN6_RC_VIDEO_FREQ, HSW_FREQUENCY(12)); /* Request 600 MHz */
+ I915_WRITE(GEN6_RPNSWREQ,
+ HSW_FREQUENCY(dev_priv->rps.rp1_freq));
+ I915_WRITE(GEN6_RC_VIDEO_FREQ,
+ HSW_FREQUENCY(dev_priv->rps.rp1_freq));
/* NB: Docs say 1s, and 1000000 - which aren't equivalent */
I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 100000000 / 128); /* 1 second timeout */
@@ -3346,11 +3506,15 @@ static void gen8_enable_rps(struct drm_device *dev)
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_TURBO |
GEN6_RP_MEDIA_HW_NORMAL_MODE |
- GEN6_RP_MEDIA_IS_GFX |
+ GEN6_RP_MEDIA_IS_GFX | /* WaSetMaskForGfxBusyness:chv (pre-production hw ?) */
GEN6_RP_ENABLE |
GEN6_RP_UP_BUSY_AVG |
GEN6_RP_DOWN_IDLE_AVG);
@@ -3359,7 +3523,7 @@ static void gen8_enable_rps(struct drm_device *dev)
gen6_set_rps(dev, (I915_READ(GEN6_GT_PERF_STATUS) & 0xff00) >> 8);
- gen6_enable_rps_interrupts(dev);
+ gen8_enable_rps_interrupts(dev);
gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL);
}
@@ -3367,7 +3531,7 @@ static void gen8_enable_rps(struct drm_device *dev)
static void gen6_enable_rps(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
u32 rp_state_cap;
u32 gt_perf_status;
u32 rc6vids, pcu_mbox = 0, rc6_mask = 0;
@@ -3396,23 +3560,7 @@ static void gen6_enable_rps(struct drm_device *dev)
rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
- /* All of these values are in units of 50MHz */
- dev_priv->rps.cur_freq = 0;
- /* static values from HW: RP0 < RPe < RP1 < RPn (min_freq) */
- dev_priv->rps.rp1_freq = (rp_state_cap >> 8) & 0xff;
- dev_priv->rps.rp0_freq = (rp_state_cap >> 0) & 0xff;
- dev_priv->rps.min_freq = (rp_state_cap >> 16) & 0xff;
- /* XXX: only BYT has a special efficient freq */
- dev_priv->rps.efficient_freq = dev_priv->rps.rp1_freq;
- /* hw_max = RP0 until we check for overclocking */
- dev_priv->rps.max_freq = dev_priv->rps.rp0_freq;
-
- /* 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;
+ parse_rp_state_cap(dev_priv, rp_state_cap);
/* disable the counters and set deterministic thresholds */
I915_WRITE(GEN6_RC_CONTROL, 0);
@@ -3494,7 +3642,7 @@ static void gen6_enable_rps(struct drm_device *dev)
gen6_gt_force_wake_put(dev_priv, FORCEWAKE_ALL);
}
-void gen6_update_ring_freq(struct drm_device *dev)
+static void __gen6_update_ring_freq(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int min_freq = 15;
@@ -3564,6 +3712,18 @@ void gen6_update_ring_freq(struct drm_device *dev)
}
}
+void gen6_update_ring_freq(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (INTEL_INFO(dev)->gen < 6 || IS_VALLEYVIEW(dev))
+ return;
+
+ mutex_lock(&dev_priv->rps.hw_lock);
+ __gen6_update_ring_freq(dev);
+ mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
int valleyview_rps_max_freq(struct drm_i915_private *dev_priv)
{
u32 val, rp0;
@@ -3658,10 +3818,49 @@ static void valleyview_cleanup_pctx(struct drm_device *dev)
dev_priv->vlv_pctx = NULL;
}
+static void valleyview_init_gt_powersave(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ valleyview_setup_pctx(dev);
+
+ mutex_lock(&dev_priv->rps.hw_lock);
+
+ 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",
+ vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq),
+ dev_priv->rps.max_freq);
+
+ dev_priv->rps.efficient_freq = valleyview_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.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),
+ dev_priv->rps.min_freq);
+
+ /* 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 valleyview_enable_rps(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
u32 gtfifodbg, val, rc6_mode = 0;
int i;
@@ -3724,29 +3923,6 @@ static void valleyview_enable_rps(struct drm_device *dev)
vlv_gpu_freq(dev_priv, dev_priv->rps.cur_freq),
dev_priv->rps.cur_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",
- vlv_gpu_freq(dev_priv, dev_priv->rps.max_freq),
- dev_priv->rps.max_freq);
-
- dev_priv->rps.efficient_freq = valleyview_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.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),
- dev_priv->rps.min_freq);
-
- /* 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;
-
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);
@@ -3815,7 +3991,7 @@ static int ironlake_setup_rc6(struct drm_device *dev)
static void ironlake_enable_rc6(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+ struct intel_engine_cs *ring = &dev_priv->ring[RCS];
bool was_interruptible;
int ret;
@@ -3873,7 +4049,7 @@ static void ironlake_enable_rc6(struct drm_device *dev)
I915_WRITE(PWRCTXA, i915_gem_obj_ggtt_offset(dev_priv->ips.pwrctx) | PWRCTX_EN);
I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
- intel_print_rc6_info(dev, INTEL_RC6_ENABLE);
+ intel_print_rc6_info(dev, GEN6_RC_CTL_RC6_ENABLE);
}
static unsigned long intel_pxfreq(u32 vidfreq)
@@ -4327,7 +4503,7 @@ EXPORT_SYMBOL_GPL(i915_gpu_lower);
bool i915_gpu_busy(void)
{
struct drm_i915_private *dev_priv;
- struct intel_ring_buffer *ring;
+ struct intel_engine_cs *ring;
bool ret = false;
int i;
@@ -4487,14 +4663,16 @@ static void intel_init_emon(struct drm_device *dev)
void intel_init_gt_powersave(struct drm_device *dev)
{
+ i915.enable_rc6 = sanitize_rc6_option(dev, i915.enable_rc6);
+
if (IS_VALLEYVIEW(dev))
- valleyview_setup_pctx(dev);
+ valleyview_init_gt_powersave(dev);
}
void intel_cleanup_gt_powersave(struct drm_device *dev)
{
if (IS_VALLEYVIEW(dev))
- valleyview_cleanup_pctx(dev);
+ valleyview_cleanup_gt_powersave(dev);
}
void intel_disable_gt_powersave(struct drm_device *dev)
@@ -4507,8 +4685,10 @@ void intel_disable_gt_powersave(struct drm_device *dev)
if (IS_IRONLAKE_M(dev)) {
ironlake_disable_drps(dev);
ironlake_disable_rc6(dev);
- } else if (INTEL_INFO(dev)->gen >= 6) {
- cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work);
+ } 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);
+
cancel_work_sync(&dev_priv->rps.work);
mutex_lock(&dev_priv->rps.hw_lock);
if (IS_VALLEYVIEW(dev))
@@ -4533,13 +4713,15 @@ static void intel_gen6_powersave_work(struct work_struct *work)
valleyview_enable_rps(dev);
} else if (IS_BROADWELL(dev)) {
gen8_enable_rps(dev);
- gen6_update_ring_freq(dev);
+ __gen6_update_ring_freq(dev);
} else {
gen6_enable_rps(dev);
- gen6_update_ring_freq(dev);
+ __gen6_update_ring_freq(dev);
}
dev_priv->rps.enabled = true;
mutex_unlock(&dev_priv->rps.hw_lock);
+
+ intel_runtime_pm_put(dev_priv);
}
void intel_enable_gt_powersave(struct drm_device *dev)
@@ -4547,20 +4729,38 @@ void intel_enable_gt_powersave(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
if (IS_IRONLAKE_M(dev)) {
+ mutex_lock(&dev->struct_mutex);
ironlake_enable_drps(dev);
ironlake_enable_rc6(dev);
intel_init_emon(dev);
- } else if (IS_GEN6(dev) || IS_GEN7(dev)) {
+ mutex_unlock(&dev->struct_mutex);
+ } else if (IS_GEN6(dev) || IS_GEN7(dev) || IS_BROADWELL(dev)) {
/*
* PCU communication is slow and this doesn't need to be
* done at any specific time, so do this out of our fast path
* to make resume and init faster.
+ *
+ * We depend on the HW RC6 power context save/restore
+ * mechanism when entering D3 through runtime PM suspend. So
+ * disable RPM until RPS/RC6 is properly setup. We can only
+ * get here via the driver load/system resume/runtime resume
+ * paths, so the _noresume version is enough (and in case of
+ * runtime resume it's necessary).
*/
- schedule_delayed_work(&dev_priv->rps.delayed_resume_work,
- round_jiffies_up_relative(HZ));
+ if (schedule_delayed_work(&dev_priv->rps.delayed_resume_work,
+ round_jiffies_up_relative(HZ)))
+ intel_runtime_pm_get_noresume(dev_priv);
}
}
+void intel_reset_gt_powersave(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ dev_priv->rps.enabled = false;
+ intel_enable_gt_powersave(dev);
+}
+
static void ibx_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4666,6 +4866,9 @@ static void ironlake_init_clock_gating(struct drm_device *dev)
I915_WRITE(CACHE_MODE_0,
_MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE));
+ /* WaDisable_RenderCache_OperationalFlush:ilk */
+ I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
g4x_disable_trickle_feed(dev);
ibx_init_clock_gating(dev);
@@ -4741,6 +4944,9 @@ static void gen6_init_clock_gating(struct drm_device *dev)
I915_WRITE(GEN6_GT_MODE,
_MASKED_BIT_ENABLE(GEN6_TD_FOUR_ROW_DISPATCH_DISABLE));
+ /* WaDisable_RenderCache_OperationalFlush:snb */
+ I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
/*
* BSpec recoomends 8x4 when MSAA is used,
* however in practice 16x4 seems fastest.
@@ -4909,6 +5115,10 @@ static void gen8_init_clock_gating(struct drm_device *dev)
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);
@@ -4980,6 +5190,9 @@ static void haswell_init_clock_gating(struct drm_device *dev)
I915_WRITE(GEN7_FF_THREAD_MODE,
I915_READ(GEN7_FF_THREAD_MODE) & ~GEN7_FF_VS_REF_CNT_FFME);
+ /* WaDisable_RenderCache_OperationalFlush:hsw */
+ I915_WRITE(CACHE_MODE_0_GEN7, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
/* enable HiZ Raw Stall Optimization */
I915_WRITE(CACHE_MODE_0_GEN7,
_MASKED_BIT_DISABLE(HIZ_RAW_STALL_OPT_DISABLE));
@@ -5032,6 +5245,9 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
I915_WRITE(GEN7_HALF_SLICE_CHICKEN1,
_MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE));
+ /* WaDisable_RenderCache_OperationalFlush:ivb */
+ I915_WRITE(CACHE_MODE_0_GEN7, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
/* Apply the WaDisableRHWOOptimizationForRenderHang:ivb workaround. */
I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1,
GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC);
@@ -5126,6 +5342,10 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
}
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);
/* WaDisableEarlyCull:vlv */
@@ -5143,6 +5363,9 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
_MASKED_BIT_ENABLE(GEN7_MAX_PS_THREAD_DEP |
GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE));
+ /* WaDisable_RenderCache_OperationalFlush:vlv */
+ I915_WRITE(CACHE_MODE_0_GEN7, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
/* WaForceL3Serialization:vlv */
I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) &
~L3SQ_URB_READ_CAM_MATCH_DISABLE);
@@ -5165,8 +5388,11 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
I915_WRITE(GEN6_UCGCTL2,
GEN6_RCZUNIT_CLOCK_GATE_DISABLE);
- /* WaDisableL3Bank2xClockGate:vlv */
- I915_WRITE(GEN7_UCGCTL4, GEN7_L3BANK2X_CLOCK_GATE_DISABLE);
+ /* WaDisableL3Bank2xClockGate:vlv
+ * Disabling L3 clock gating- MMIO 940c[25] = 1
+ * Set bit 25, to disable L3_BANK_2x_CLK_GATING */
+ I915_WRITE(GEN7_UCGCTL4,
+ I915_READ(GEN7_UCGCTL4) | GEN7_L3BANK2X_CLOCK_GATE_DISABLE);
I915_WRITE(MI_ARB_VLV, MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE);
@@ -5191,6 +5417,59 @@ static void valleyview_init_clock_gating(struct drm_device *dev)
I915_WRITE(VLV_GUNIT_CLOCK_GATE, GCFG_DIS);
}
+static void cherryview_init_clock_gating(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE);
+
+ 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,
+ I915_READ(GEN7_FF_THREAD_MODE) &
+ ~(GEN8_FF_DS_REF_CNT_FFME | GEN7_FF_VS_REF_CNT_FFME));
+
+ /* WaDisableSemaphoreAndSyncFlipWait:chv */
+ I915_WRITE(GEN6_RC_SLEEP_PSMI_CONTROL,
+ _MASKED_BIT_ENABLE(GEN8_RC_SEMA_IDLE_MSG_DISABLE));
+
+ /* WaDisableCSUnitClockGating:chv */
+ I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) |
+ GEN6_CSUNIT_CLOCK_GATE_DISABLE);
+
+ /* WaDisableSDEUnitClockGating:chv */
+ 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);
+
+ /* WaDisableFfDopClockGating:chv (pre-production hw) */
+ I915_WRITE(GEN6_RC_SLEEP_PSMI_CONTROL,
+ _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);
+}
+
static void g4x_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -5212,6 +5491,9 @@ static void g4x_init_clock_gating(struct drm_device *dev)
I915_WRITE(CACHE_MODE_0,
_MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE));
+ /* WaDisable_RenderCache_OperationalFlush:g4x */
+ I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
+
g4x_disable_trickle_feed(dev);
}
@@ -5226,6 +5508,9 @@ static void crestline_init_clock_gating(struct drm_device *dev)
I915_WRITE16(DEUC, 0);
I915_WRITE(MI_ARB_STATE,
_MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE));
+
+ /* WaDisable_RenderCache_OperationalFlush:gen4 */
+ I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
}
static void broadwater_init_clock_gating(struct drm_device *dev)
@@ -5240,6 +5525,9 @@ static void broadwater_init_clock_gating(struct drm_device *dev)
I915_WRITE(RENCLK_GATE_D2, 0);
I915_WRITE(MI_ARB_STATE,
_MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE));
+
+ /* WaDisable_RenderCache_OperationalFlush:gen4 */
+ I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
}
static void gen3_init_clock_gating(struct drm_device *dev)
@@ -5256,6 +5544,12 @@ static void gen3_init_clock_gating(struct drm_device *dev)
/* IIR "flip pending" means done if this bit is set */
I915_WRITE(ECOSKPD, _MASKED_BIT_DISABLE(ECO_FLIP_DONE));
+
+ /* interrupts should cause a wake up from C3 */
+ I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_AGPBUSY_INT_EN));
+
+ /* 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));
}
static void i85x_init_clock_gating(struct drm_device *dev)
@@ -5263,6 +5557,10 @@ static void i85x_init_clock_gating(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE);
+
+ /* 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));
}
static void i830_init_clock_gating(struct drm_device *dev)
@@ -5314,10 +5612,25 @@ bool intel_display_power_enabled_sw(struct drm_i915_private *dev_priv,
enum intel_display_power_domain domain)
{
struct i915_power_domains *power_domains;
+ struct i915_power_well *power_well;
+ bool is_enabled;
+ int i;
+
+ if (dev_priv->pm.suspended)
+ return false;
power_domains = &dev_priv->power_domains;
+ is_enabled = true;
+ for_each_power_well_rev(i, power_well, BIT(domain), power_domains) {
+ if (power_well->always_on)
+ continue;
- return power_domains->domain_use_count[domain];
+ if (!power_well->count) {
+ is_enabled = false;
+ break;
+ }
+ }
+ return is_enabled;
}
bool intel_display_power_enabled(struct drm_i915_private *dev_priv,
@@ -5392,33 +5705,6 @@ static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv)
}
}
-static void reset_vblank_counter(struct drm_device *dev, enum pipe pipe)
-{
- assert_spin_locked(&dev->vbl_lock);
-
- dev->vblank[pipe].last = 0;
-}
-
-static void hsw_power_well_post_disable(struct drm_i915_private *dev_priv)
-{
- struct drm_device *dev = dev_priv->dev;
- enum pipe pipe;
- unsigned long irqflags;
-
- /*
- * After this, the registers on the pipes that are part of the power
- * well will become zero, so we have to adjust our counters according to
- * that.
- *
- * FIXME: Should we do this in general in drm_vblank_post_modeset?
- */
- spin_lock_irqsave(&dev->vbl_lock, irqflags);
- for_each_pipe(pipe)
- if (pipe != PIPE_A)
- reset_vblank_counter(dev, pipe);
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
-}
-
static void hsw_set_power_well(struct drm_i915_private *dev_priv,
struct i915_power_well *power_well, bool enable)
{
@@ -5447,8 +5733,6 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv,
I915_WRITE(HSW_PWR_WELL_DRIVER, 0);
POSTING_READ(HSW_PWR_WELL_DRIVER);
DRM_DEBUG_KMS("Requesting to disable the power well\n");
-
- hsw_power_well_post_disable(dev_priv);
}
}
}
@@ -5489,13 +5773,34 @@ static bool i9xx_always_on_power_well_enabled(struct drm_i915_private *dev_priv,
return true;
}
-static void vlv_set_power_well(struct drm_i915_private *dev_priv,
- struct i915_power_well *power_well, bool enable)
+void __vlv_set_power_well(struct drm_i915_private *dev_priv,
+ enum punit_power_well power_well_id, bool enable)
{
- enum punit_power_well power_well_id = power_well->data;
+ struct drm_device *dev = dev_priv->dev;
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) :
@@ -5523,6 +5828,28 @@ static 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,
@@ -5591,11 +5918,13 @@ static void vlv_display_power_well_enable(struct drm_i915_private *dev_priv,
spin_unlock_irq(&dev_priv->irq_lock);
/*
- * During driver initialization we need to defer enabling hotplug
- * processing until fbdev is set up.
+ * During driver initialization/resume we can avoid restoring the
+ * part of the HW/SW state that will be inited anyway explicitly.
*/
- if (dev_priv->enable_hotplug_processing)
- intel_hpd_init(dev_priv->dev);
+ if (dev_priv->power_domains.initializing)
+ return;
+
+ intel_hpd_init(dev_priv->dev);
i915_redisable_vga_power_on(dev_priv->dev);
}
@@ -5603,23 +5932,12 @@ static void vlv_display_power_well_enable(struct drm_i915_private *dev_priv,
static void vlv_display_power_well_disable(struct drm_i915_private *dev_priv,
struct i915_power_well *power_well)
{
- struct drm_device *dev = dev_priv->dev;
- enum pipe pipe;
-
WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DISP2D);
spin_lock_irq(&dev_priv->irq_lock);
- for_each_pipe(pipe)
- __intel_set_cpu_fifo_underrun_reporting(dev, pipe, false);
-
valleyview_disable_display_irqs(dev_priv);
spin_unlock_irq(&dev_priv->irq_lock);
- spin_lock_irq(&dev->vbl_lock);
- for_each_pipe(pipe)
- reset_vblank_counter(dev, pipe);
- spin_unlock_irq(&dev->vbl_lock);
-
vlv_set_power_well(dev_priv, power_well, false);
}
@@ -5867,12 +6185,6 @@ static struct i915_power_well vlv_power_wells[] = {
.ops = &vlv_display_power_well_ops,
},
{
- .name = "dpio-common",
- .domains = VLV_DPIO_CMN_BC_POWER_DOMAINS,
- .data = PUNIT_POWER_WELL_DPIO_CMN_BC,
- .ops = &vlv_dpio_power_well_ops,
- },
- {
.name = "dpio-tx-b-01",
.domains = VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS |
VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS |
@@ -5908,6 +6220,12 @@ static struct i915_power_well vlv_power_wells[] = {
.ops = &vlv_dpio_power_well_ops,
.data = PUNIT_POWER_WELL_DPIO_TX_C_LANES_23,
},
+ {
+ .name = "dpio-common",
+ .domains = VLV_DPIO_CMN_BC_POWER_DOMAINS,
+ .data = PUNIT_POWER_WELL_DPIO_CMN_BC,
+ .ops = &vlv_dpio_power_well_ops,
+ },
};
#define set_power_wells(power_domains, __power_wells) ({ \
@@ -5959,9 +6277,13 @@ static void intel_power_domains_resume(struct drm_i915_private *dev_priv)
void intel_power_domains_init_hw(struct drm_i915_private *dev_priv)
{
+ struct i915_power_domains *power_domains = &dev_priv->power_domains;
+
+ power_domains->initializing = true;
/* 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);
+ power_domains->initializing = false;
}
void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv)
@@ -5986,6 +6308,18 @@ void intel_runtime_pm_get(struct drm_i915_private *dev_priv)
WARN(dev_priv->pm.suspended, "Device still suspended.\n");
}
+void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = dev_priv->dev;
+ struct device *device = &dev->pdev->dev;
+
+ if (!HAS_RUNTIME_PM(dev))
+ return;
+
+ WARN(dev_priv->pm.suspended, "Getting nosync-ref while suspended.\n");
+ pm_runtime_get_noresume(device);
+}
+
void intel_runtime_pm_put(struct drm_i915_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
@@ -6008,6 +6342,15 @@ void intel_init_runtime_pm(struct drm_i915_private *dev_priv)
pm_runtime_set_active(device);
+ /*
+ * RPM depends on RC6 to save restore the GT HW context, so make RC6 a
+ * requirement.
+ */
+ if (!intel_enable_rc6(dev)) {
+ DRM_INFO("RC6 disabled, disabling runtime PM support\n");
+ return;
+ }
+
pm_runtime_set_autosuspend_delay(device, 10000); /* 10s */
pm_runtime_mark_last_busy(device);
pm_runtime_use_autosuspend(device);
@@ -6023,6 +6366,9 @@ void intel_fini_runtime_pm(struct drm_i915_private *dev_priv)
if (!HAS_RUNTIME_PM(dev))
return;
+ if (!intel_enable_rc6(dev))
+ return;
+
/* Make sure we're not suspended first. */
pm_runtime_get_sync(device);
pm_runtime_disable(device);
@@ -6087,6 +6433,10 @@ void intel_init_pm(struct drm_device *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;
+ } else if (IS_CHERRYVIEW(dev)) {
+ dev_priv->display.update_wm = valleyview_update_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.init_clock_gating =
diff --git a/drivers/gpu/drm/i915/intel_renderstate.h b/drivers/gpu/drm/i915/intel_renderstate.h
new file mode 100644
index 000000000000..a5e783a9928a
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_renderstate.h
@@ -0,0 +1,48 @@
+/*
+ * 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_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;
+};
+
+extern const struct intel_renderstate_rodata gen6_null_state;
+extern const struct intel_renderstate_rodata gen7_null_state;
+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, \
+ }
+
+#endif /* INTEL_RENDERSTATE_H */
diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen6.c b/drivers/gpu/drm/i915/intel_renderstate_gen6.c
new file mode 100644
index 000000000000..740538ad0977
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_renderstate_gen6.c
@@ -0,0 +1,289 @@
+#include "intel_renderstate.h"
+
+static const u32 gen6_null_state_relocs[] = {
+ 0x00000020,
+ 0x00000024,
+ 0x0000002c,
+ 0x000001e0,
+ 0x000001e4,
+};
+
+static const u32 gen6_null_state_batch[] = {
+ 0x69040000,
+ 0x790d0001,
+ 0x00000000,
+ 0x00000000,
+ 0x78180000,
+ 0x00000001,
+ 0x61010008,
+ 0x00000000,
+ 0x00000001, /* reloc */
+ 0x00000001, /* reloc */
+ 0x00000000,
+ 0x00000001, /* reloc */
+ 0x00000000,
+ 0x00000001,
+ 0x00000000,
+ 0x00000001,
+ 0x61020000,
+ 0x00000000,
+ 0x78050001,
+ 0x00000018,
+ 0x00000000,
+ 0x780d1002,
+ 0x00000000,
+ 0x00000000,
+ 0x00000420,
+ 0x78150003,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78100004,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78160003,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78110005,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78120002,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78170003,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x79050005,
+ 0xe0040000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x79100000,
+ 0x00000000,
+ 0x79000002,
+ 0xffffffff,
+ 0x00000000,
+ 0x00000000,
+ 0x780e0002,
+ 0x00000441,
+ 0x00000401,
+ 0x00000401,
+ 0x78021002,
+ 0x00000000,
+ 0x00000000,
+ 0x00000400,
+ 0x78130012,
+ 0x00400810,
+ 0x00000000,
+ 0x20000000,
+ 0x04000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78140007,
+ 0x00000280,
+ 0x08080000,
+ 0x00000000,
+ 0x00060000,
+ 0x4e080002,
+ 0x00100400,
+ 0x00000000,
+ 0x00000000,
+ 0x78090005,
+ 0x02000000,
+ 0x22220000,
+ 0x02f60000,
+ 0x11330000,
+ 0x02850004,
+ 0x11220000,
+ 0x78011002,
+ 0x00000000,
+ 0x00000000,
+ 0x00000200,
+ 0x78080003,
+ 0x00002000,
+ 0x00000448, /* reloc */
+ 0x00000448, /* reloc */
+ 0x00000000,
+ 0x05000000, /* cmds end */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000220, /* state start */
+ 0x00000240,
+ 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,
+ 0x0060005a,
+ 0x204077be,
+ 0x000000c0,
+ 0x008d0040,
+ 0x0060005a,
+ 0x206077be,
+ 0x000000c0,
+ 0x008d0080,
+ 0x0060005a,
+ 0x208077be,
+ 0x000000d0,
+ 0x008d0040,
+ 0x0060005a,
+ 0x20a077be,
+ 0x000000d0,
+ 0x008d0080,
+ 0x00000201,
+ 0x20080061,
+ 0x00000000,
+ 0x00000000,
+ 0x00600001,
+ 0x20200022,
+ 0x008d0000,
+ 0x00000000,
+ 0x02800031,
+ 0x21c01cc9,
+ 0x00000020,
+ 0x0a8a0001,
+ 0x00600001,
+ 0x204003be,
+ 0x008d01c0,
+ 0x00000000,
+ 0x00600001,
+ 0x206003be,
+ 0x008d01e0,
+ 0x00000000,
+ 0x00600001,
+ 0x208003be,
+ 0x008d0200,
+ 0x00000000,
+ 0x00600001,
+ 0x20a003be,
+ 0x008d0220,
+ 0x00000000,
+ 0x00600001,
+ 0x20c003be,
+ 0x008d0240,
+ 0x00000000,
+ 0x00600001,
+ 0x20e003be,
+ 0x008d0260,
+ 0x00000000,
+ 0x00600001,
+ 0x210003be,
+ 0x008d0280,
+ 0x00000000,
+ 0x00600001,
+ 0x212003be,
+ 0x008d02a0,
+ 0x00000000,
+ 0x05800031,
+ 0x24001cc8,
+ 0x00000040,
+ 0x90019000,
+ 0x0000007e,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x0000007e,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x0000007e,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x0000007e,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x0000007e,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x0000007e,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x0000007e,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x0000007e,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x30000000,
+ 0x00000124,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0xf99a130c,
+ 0x799a130c,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x80000031,
+ 0x00000003,
+ 0x00000000, /* state end */
+};
+
+RO_RENDERSTATE(6);
diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen7.c b/drivers/gpu/drm/i915/intel_renderstate_gen7.c
new file mode 100644
index 000000000000..6fa7ff2a1298
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_renderstate_gen7.c
@@ -0,0 +1,253 @@
+#include "intel_renderstate.h"
+
+static const u32 gen7_null_state_relocs[] = {
+ 0x0000000c,
+ 0x00000010,
+ 0x00000018,
+ 0x000001ec,
+};
+
+static const u32 gen7_null_state_batch[] = {
+ 0x69040000,
+ 0x61010008,
+ 0x00000000,
+ 0x00000001, /* reloc */
+ 0x00000001, /* reloc */
+ 0x00000000,
+ 0x00000001, /* reloc */
+ 0x00000000,
+ 0x00000001,
+ 0x00000000,
+ 0x00000001,
+ 0x790d0002,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78180000,
+ 0x00000001,
+ 0x79160000,
+ 0x00000008,
+ 0x78300000,
+ 0x02010040,
+ 0x78310000,
+ 0x04000000,
+ 0x78320000,
+ 0x04000000,
+ 0x78330000,
+ 0x02000000,
+ 0x78100004,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x781b0005,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x781c0002,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x781d0004,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78110005,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78120002,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78210000,
+ 0x00000000,
+ 0x78130005,
+ 0x00000000,
+ 0x20000000,
+ 0x04000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78140001,
+ 0x20000800,
+ 0x00000000,
+ 0x781e0001,
+ 0x00000000,
+ 0x00000000,
+ 0x78050005,
+ 0xe0040000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78040001,
+ 0x00000000,
+ 0x00000000,
+ 0x78240000,
+ 0x00000240,
+ 0x78230000,
+ 0x00000260,
+ 0x782f0000,
+ 0x00000280,
+ 0x781f000c,
+ 0x00400810,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78200006,
+ 0x000002c0,
+ 0x08080000,
+ 0x00000000,
+ 0x28000402,
+ 0x00060000,
+ 0x00000000,
+ 0x00000000,
+ 0x78090005,
+ 0x02000000,
+ 0x22220000,
+ 0x02f60000,
+ 0x11230000,
+ 0x02f60004,
+ 0x11230000,
+ 0x78080003,
+ 0x00006008,
+ 0x00000340, /* reloc */
+ 0xffffffff,
+ 0x00000000,
+ 0x782a0000,
+ 0x00000360,
+ 0x79000002,
+ 0xffffffff,
+ 0x00000000,
+ 0x00000000,
+ 0x7b000005,
+ 0x0000000f,
+ 0x00000003,
+ 0x00000000,
+ 0x00000001,
+ 0x00000000,
+ 0x00000000,
+ 0x05000000, /* cmds end */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000031, /* state start */
+ 0x00000003,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0xf99a130c,
+ 0x799a130c,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000492,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x0080005a,
+ 0x2e2077bd,
+ 0x000000c0,
+ 0x008d0040,
+ 0x0080005a,
+ 0x2e6077bd,
+ 0x000000d0,
+ 0x008d0040,
+ 0x02800031,
+ 0x21801fa9,
+ 0x008d0e20,
+ 0x08840001,
+ 0x00800001,
+ 0x2e2003bd,
+ 0x008d0180,
+ 0x00000000,
+ 0x00800001,
+ 0x2e6003bd,
+ 0x008d01c0,
+ 0x00000000,
+ 0x00800001,
+ 0x2ea003bd,
+ 0x008d0200,
+ 0x00000000,
+ 0x00800001,
+ 0x2ee003bd,
+ 0x008d0240,
+ 0x00000000,
+ 0x05800031,
+ 0x20001fa8,
+ 0x008d0e20,
+ 0x90031000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000380,
+ 0x000003a0,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000, /* state end */
+};
+
+RO_RENDERSTATE(7);
diff --git a/drivers/gpu/drm/i915/intel_renderstate_gen8.c b/drivers/gpu/drm/i915/intel_renderstate_gen8.c
new file mode 100644
index 000000000000..5c875615d42a
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_renderstate_gen8.c
@@ -0,0 +1,479 @@
+#include "intel_renderstate.h"
+
+static const u32 gen8_null_state_relocs[] = {
+ 0x00000048,
+ 0x00000050,
+ 0x00000060,
+ 0x000003ec,
+};
+
+static const u32 gen8_null_state_batch[] = {
+ 0x69040000,
+ 0x61020001,
+ 0x00000000,
+ 0x00000000,
+ 0x79120000,
+ 0x00000000,
+ 0x79130000,
+ 0x00000000,
+ 0x79140000,
+ 0x00000000,
+ 0x79150000,
+ 0x00000000,
+ 0x79160000,
+ 0x00000000,
+ 0x6101000e,
+ 0x00000001,
+ 0x00000000,
+ 0x00000001,
+ 0x00000001, /* reloc */
+ 0x00000000,
+ 0x00000001, /* reloc */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000001, /* reloc */
+ 0x00000000,
+ 0xfffff001,
+ 0x00001001,
+ 0xfffff001,
+ 0x00001001,
+ 0x78230000,
+ 0x000006e0,
+ 0x78210000,
+ 0x00000700,
+ 0x78300000,
+ 0x08010040,
+ 0x78330000,
+ 0x08000000,
+ 0x78310000,
+ 0x08000000,
+ 0x78320000,
+ 0x08000000,
+ 0x78240000,
+ 0x00000641,
+ 0x780e0000,
+ 0x00000601,
+ 0x780d0000,
+ 0x00000000,
+ 0x78180000,
+ 0x00000001,
+ 0x78520003,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78190009,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x781b0007,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78270000,
+ 0x00000000,
+ 0x782c0000,
+ 0x00000000,
+ 0x781c0002,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78160009,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78110008,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78290000,
+ 0x00000000,
+ 0x782e0000,
+ 0x00000000,
+ 0x781a0009,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x781d0007,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78280000,
+ 0x00000000,
+ 0x782d0000,
+ 0x00000000,
+ 0x78260000,
+ 0x00000000,
+ 0x782b0000,
+ 0x00000000,
+ 0x78150009,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78100007,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x781e0003,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78120002,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x781f0002,
+ 0x30400820,
+ 0x00000000,
+ 0x00000000,
+ 0x78510009,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78500003,
+ 0x00210000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78130002,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x782a0000,
+ 0x00000480,
+ 0x782f0000,
+ 0x00000540,
+ 0x78140000,
+ 0x00000800,
+ 0x78170009,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x7820000a,
+ 0x00000580,
+ 0x00000000,
+ 0x08080000,
+ 0x00000000,
+ 0x00000000,
+ 0x1f000002,
+ 0x00060000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x784d0000,
+ 0x40000000,
+ 0x784f0000,
+ 0x80000100,
+ 0x780f0000,
+ 0x00000740,
+ 0x78050006,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78070003,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78060003,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x78040001,
+ 0x00000000,
+ 0x00000001,
+ 0x79000002,
+ 0xffffffff,
+ 0x00000000,
+ 0x00000000,
+ 0x78080003,
+ 0x00006000,
+ 0x000005e0, /* reloc */
+ 0x00000000,
+ 0x00000000,
+ 0x78090005,
+ 0x02000000,
+ 0x22220000,
+ 0x02f60000,
+ 0x11230000,
+ 0x02850004,
+ 0x11230000,
+ 0x784b0000,
+ 0x0000000f,
+ 0x78490001,
+ 0x00000000,
+ 0x00000000,
+ 0x7b000005,
+ 0x00000000,
+ 0x00000003,
+ 0x00000000,
+ 0x00000001,
+ 0x00000000,
+ 0x00000000,
+ 0x05000000, /* cmds end */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x000004c0, /* state start */
+ 0x00000500,
+ 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,
+ 0x00000092,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x0060005a,
+ 0x21403ae8,
+ 0x3a0000c0,
+ 0x008d0040,
+ 0x0060005a,
+ 0x21603ae8,
+ 0x3a0000c0,
+ 0x008d0080,
+ 0x0060005a,
+ 0x21803ae8,
+ 0x3a0000d0,
+ 0x008d0040,
+ 0x0060005a,
+ 0x21a03ae8,
+ 0x3a0000d0,
+ 0x008d0080,
+ 0x02800031,
+ 0x2e0022e8,
+ 0x0e000140,
+ 0x08840001,
+ 0x05800031,
+ 0x200022e0,
+ 0x0e000e00,
+ 0x90031000,
+ 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,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x06200000,
+ 0x00000002,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0xf99a130c,
+ 0x799a130c,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x3f800000,
+ 0x00000000,
+ 0x3f800000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000, /* state end */
+};
+
+RO_RENDERSTATE(8);
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 79fb4cc2137c..279488addf3f 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -33,26 +33,44 @@
#include "i915_trace.h"
#include "intel_drv.h"
-static inline int ring_space(struct intel_ring_buffer *ring)
+/* 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
+
+static inline int __ring_space(int head, int tail, int size)
{
- int space = (ring->head & HEAD_ADDR) - (ring->tail + I915_RING_FREE_SPACE);
+ int space = head - (tail + I915_RING_FREE_SPACE);
if (space < 0)
- space += ring->size;
+ space += size;
return space;
}
-void __intel_ring_advance(struct intel_ring_buffer *ring)
+static inline int ring_space(struct intel_engine_cs *ring)
+{
+ struct intel_ringbuffer *ringbuf = ring->buffer;
+ return __ring_space(ringbuf->head & HEAD_ADDR, ringbuf->tail, ringbuf->size);
+}
+
+static 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);
+}
- ring->tail &= ring->size - 1;
- if (dev_priv->gpu_error.stop_rings & intel_ring_flag(ring))
+void __intel_ring_advance(struct intel_engine_cs *ring)
+{
+ struct intel_ringbuffer *ringbuf = ring->buffer;
+ ringbuf->tail &= ringbuf->size - 1;
+ if (intel_ring_stopped(ring))
return;
- ring->write_tail(ring, ring->tail);
+ ring->write_tail(ring, ringbuf->tail);
}
static int
-gen2_render_ring_flush(struct intel_ring_buffer *ring,
+gen2_render_ring_flush(struct intel_engine_cs *ring,
u32 invalidate_domains,
u32 flush_domains)
{
@@ -78,7 +96,7 @@ gen2_render_ring_flush(struct intel_ring_buffer *ring,
}
static int
-gen4_render_ring_flush(struct intel_ring_buffer *ring,
+gen4_render_ring_flush(struct intel_engine_cs *ring,
u32 invalidate_domains,
u32 flush_domains)
{
@@ -173,9 +191,9 @@ gen4_render_ring_flush(struct intel_ring_buffer *ring,
* really our business. That leaves only stall at scoreboard.
*/
static int
-intel_emit_post_sync_nonzero_flush(struct intel_ring_buffer *ring)
+intel_emit_post_sync_nonzero_flush(struct intel_engine_cs *ring)
{
- u32 scratch_addr = ring->scratch.gtt_offset + 128;
+ u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES;
int ret;
@@ -208,11 +226,11 @@ intel_emit_post_sync_nonzero_flush(struct intel_ring_buffer *ring)
}
static int
-gen6_render_ring_flush(struct intel_ring_buffer *ring,
+gen6_render_ring_flush(struct intel_engine_cs *ring,
u32 invalidate_domains, u32 flush_domains)
{
u32 flags = 0;
- u32 scratch_addr = ring->scratch.gtt_offset + 128;
+ u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES;
int ret;
/* Force SNB workarounds for PIPE_CONTROL flushes */
@@ -260,7 +278,7 @@ gen6_render_ring_flush(struct intel_ring_buffer *ring,
}
static int
-gen7_render_ring_cs_stall_wa(struct intel_ring_buffer *ring)
+gen7_render_ring_cs_stall_wa(struct intel_engine_cs *ring)
{
int ret;
@@ -278,7 +296,7 @@ gen7_render_ring_cs_stall_wa(struct intel_ring_buffer *ring)
return 0;
}
-static int gen7_ring_fbc_flush(struct intel_ring_buffer *ring, u32 value)
+static int gen7_ring_fbc_flush(struct intel_engine_cs *ring, u32 value)
{
int ret;
@@ -302,11 +320,11 @@ static int gen7_ring_fbc_flush(struct intel_ring_buffer *ring, u32 value)
}
static int
-gen7_render_ring_flush(struct intel_ring_buffer *ring,
+gen7_render_ring_flush(struct intel_engine_cs *ring,
u32 invalidate_domains, u32 flush_domains)
{
u32 flags = 0;
- u32 scratch_addr = ring->scratch.gtt_offset + 128;
+ u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES;
int ret;
/*
@@ -363,11 +381,11 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring,
}
static int
-gen8_render_ring_flush(struct intel_ring_buffer *ring,
+gen8_render_ring_flush(struct intel_engine_cs *ring,
u32 invalidate_domains, u32 flush_domains)
{
u32 flags = 0;
- u32 scratch_addr = ring->scratch.gtt_offset + 128;
+ u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES;
int ret;
flags |= PIPE_CONTROL_CS_STALL;
@@ -403,14 +421,14 @@ gen8_render_ring_flush(struct intel_ring_buffer *ring,
}
-static void ring_write_tail(struct intel_ring_buffer *ring,
+static void ring_write_tail(struct intel_engine_cs *ring,
u32 value)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
I915_WRITE_TAIL(ring, value);
}
-u64 intel_ring_get_active_head(struct intel_ring_buffer *ring)
+u64 intel_ring_get_active_head(struct intel_engine_cs *ring)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
u64 acthd;
@@ -426,7 +444,7 @@ u64 intel_ring_get_active_head(struct intel_ring_buffer *ring)
return acthd;
}
-static void ring_setup_phys_status_page(struct intel_ring_buffer *ring)
+static void ring_setup_phys_status_page(struct intel_engine_cs *ring)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
u32 addr;
@@ -437,7 +455,7 @@ static void ring_setup_phys_status_page(struct intel_ring_buffer *ring)
I915_WRITE(HWS_PGA, addr);
}
-static bool stop_ring(struct intel_ring_buffer *ring)
+static bool stop_ring(struct intel_engine_cs *ring)
{
struct drm_i915_private *dev_priv = to_i915(ring->dev);
@@ -461,11 +479,12 @@ static bool stop_ring(struct intel_ring_buffer *ring)
return (I915_READ_HEAD(ring) & HEAD_ADDR) == 0;
}
-static int init_ring_common(struct intel_ring_buffer *ring)
+static int init_ring_common(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- struct drm_i915_gem_object *obj = ring->obj;
+ struct intel_ringbuffer *ringbuf = ring->buffer;
+ struct drm_i915_gem_object *obj = ringbuf->obj;
int ret = 0;
gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL);
@@ -504,7 +523,7 @@ static int init_ring_common(struct intel_ring_buffer *ring)
* register values. */
I915_WRITE_START(ring, i915_gem_obj_ggtt_offset(obj));
I915_WRITE_CTL(ring,
- ((ring->size - PAGE_SIZE) & RING_NR_PAGES)
+ ((ringbuf->size - PAGE_SIZE) & RING_NR_PAGES)
| RING_VALID);
/* If the head is still not zero, the ring is dead */
@@ -512,12 +531,11 @@ static int init_ring_common(struct intel_ring_buffer *ring)
I915_READ_START(ring) == i915_gem_obj_ggtt_offset(obj) &&
(I915_READ_HEAD(ring) & HEAD_ADDR) == 0, 50)) {
DRM_ERROR("%s initialization failed "
- "ctl %08x head %08x tail %08x start %08x\n",
- ring->name,
- I915_READ_CTL(ring),
- I915_READ_HEAD(ring),
- I915_READ_TAIL(ring),
- I915_READ_START(ring));
+ "ctl %08x (valid? %d) head %08x tail %08x start %08x [expected %08lx]\n",
+ ring->name,
+ I915_READ_CTL(ring), I915_READ_CTL(ring) & RING_VALID,
+ I915_READ_HEAD(ring), I915_READ_TAIL(ring),
+ I915_READ_START(ring), (unsigned long)i915_gem_obj_ggtt_offset(obj));
ret = -EIO;
goto out;
}
@@ -525,10 +543,10 @@ static int init_ring_common(struct intel_ring_buffer *ring)
if (!drm_core_check_feature(ring->dev, DRIVER_MODESET))
i915_kernel_lost_context(ring->dev);
else {
- ring->head = I915_READ_HEAD(ring);
- ring->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
- ring->space = ring_space(ring);
- ring->last_retired_head = -1;
+ ringbuf->head = I915_READ_HEAD(ring);
+ ringbuf->tail = I915_READ_TAIL(ring) & TAIL_ADDR;
+ ringbuf->space = ring_space(ring);
+ ringbuf->last_retired_head = -1;
}
memset(&ring->hangcheck, 0, sizeof(ring->hangcheck));
@@ -540,7 +558,7 @@ out:
}
static int
-init_pipe_control(struct intel_ring_buffer *ring)
+init_pipe_control(struct intel_engine_cs *ring)
{
int ret;
@@ -581,7 +599,7 @@ err:
return ret;
}
-static int init_render_ring(struct intel_ring_buffer *ring)
+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;
@@ -595,19 +613,21 @@ static int init_render_ring(struct intel_ring_buffer *ring)
* 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
+ * WaDisableAsyncFlipPerfMode:snb,ivb,hsw,vlv,bdw,chv
*/
if (INTEL_INFO(dev)->gen >= 6)
I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(ASYNC_FLIP_PERF_DISABLE));
/* Required for the hardware to program scanline values for waiting */
+ /* WaEnableFlushTlbInvalidationMode:snb */
if (INTEL_INFO(dev)->gen == 6)
I915_WRITE(GFX_MODE,
- _MASKED_BIT_ENABLE(GFX_TLB_INVALIDATE_ALWAYS));
+ _MASKED_BIT_ENABLE(GFX_TLB_INVALIDATE_EXPLICIT));
+ /* WaBCSVCSTlbInvalidationMode:ivb,vlv,hsw */
if (IS_GEN7(dev))
I915_WRITE(GFX_MODE_GEN7,
- _MASKED_BIT_DISABLE(GFX_TLB_INVALIDATE_ALWAYS) |
+ _MASKED_BIT_ENABLE(GFX_TLB_INVALIDATE_EXPLICIT) |
_MASKED_BIT_ENABLE(GFX_REPLAY_MODE));
if (INTEL_INFO(dev)->gen >= 5) {
@@ -624,13 +644,6 @@ static int init_render_ring(struct intel_ring_buffer *ring)
*/
I915_WRITE(CACHE_MODE_0,
_MASKED_BIT_DISABLE(CM0_STC_EVICT_DISABLE_LRA_SNB));
-
- /* This is not explicitly set for GEN6, so read the register.
- * see intel_ring_mi_set_context() for why we care.
- * TODO: consider explicitly setting the bit for GEN5
- */
- ring->itlb_before_ctx_switch =
- !!(I915_READ(GFX_MODE) & GFX_TLB_INVALIDATE_ALWAYS);
}
if (INTEL_INFO(dev)->gen >= 6)
@@ -642,7 +655,7 @@ static int init_render_ring(struct intel_ring_buffer *ring)
return ret;
}
-static void render_ring_cleanup(struct intel_ring_buffer *ring)
+static void render_ring_cleanup(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
@@ -658,20 +671,46 @@ static void render_ring_cleanup(struct intel_ring_buffer *ring)
ring->scratch.obj = NULL;
}
-static void
-update_mboxes(struct intel_ring_buffer *ring,
- u32 mmio_offset)
+static int gen6_signal(struct intel_engine_cs *signaller,
+ unsigned int num_dwords)
{
-/* 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.
- */
+ struct drm_device *dev = signaller->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *useless;
+ int i, ret;
+
+ /* 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
- intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
- intel_ring_emit(ring, mmio_offset);
- intel_ring_emit(ring, ring->outstanding_lazy_seqno);
- intel_ring_emit(ring, MI_NOOP);
+ if (i915_semaphore_is_enabled(dev))
+ num_dwords += ((I915_NUM_RINGS-1) * MBOX_UPDATE_DWORDS);
+ else
+ return intel_ring_begin(signaller, num_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];
+ if (mbox_reg != GEN6_NOSYNC) {
+ 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);
+ }
+ }
+
+ return 0;
}
/**
@@ -684,29 +723,14 @@ update_mboxes(struct intel_ring_buffer *ring,
* This acts like a signal in the canonical semaphore.
*/
static int
-gen6_add_request(struct intel_ring_buffer *ring)
+gen6_add_request(struct intel_engine_cs *ring)
{
- struct drm_device *dev = ring->dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *useless;
- int i, ret, num_dwords = 4;
-
- if (i915_semaphore_is_enabled(dev))
- num_dwords += ((I915_NUM_RINGS-1) * MBOX_UPDATE_DWORDS);
-#undef MBOX_UPDATE_DWORDS
+ int ret;
- ret = intel_ring_begin(ring, num_dwords);
+ ret = ring->semaphore.signal(ring, 4);
if (ret)
return ret;
- if (i915_semaphore_is_enabled(dev)) {
- for_each_ring(useless, dev_priv, i) {
- u32 mbox_reg = ring->signal_mbox[i];
- if (mbox_reg != GEN6_NOSYNC)
- update_mboxes(ring, mbox_reg);
- }
- }
-
intel_ring_emit(ring, MI_STORE_DWORD_INDEX);
intel_ring_emit(ring, I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
intel_ring_emit(ring, ring->outstanding_lazy_seqno);
@@ -731,14 +755,15 @@ static inline bool i915_gem_has_seqno_wrapped(struct drm_device *dev,
* @seqno - seqno which the waiter will block on
*/
static int
-gen6_ring_sync(struct intel_ring_buffer *waiter,
- struct intel_ring_buffer *signaller,
+gen6_ring_sync(struct intel_engine_cs *waiter,
+ struct intel_engine_cs *signaller,
u32 seqno)
{
- int ret;
u32 dw1 = MI_SEMAPHORE_MBOX |
MI_SEMAPHORE_COMPARE |
MI_SEMAPHORE_REGISTER;
+ u32 wait_mbox = signaller->semaphore.mbox.wait[waiter->id];
+ int ret;
/* Throughout all of the GEM code, seqno passed implies our current
* seqno is >= the last seqno executed. However for hardware the
@@ -746,8 +771,7 @@ gen6_ring_sync(struct intel_ring_buffer *waiter,
*/
seqno -= 1;
- WARN_ON(signaller->semaphore_register[waiter->id] ==
- MI_SEMAPHORE_SYNC_INVALID);
+ WARN_ON(wait_mbox == MI_SEMAPHORE_SYNC_INVALID);
ret = intel_ring_begin(waiter, 4);
if (ret)
@@ -755,9 +779,7 @@ gen6_ring_sync(struct intel_ring_buffer *waiter,
/* If seqno wrap happened, omit the wait with no-ops */
if (likely(!i915_gem_has_seqno_wrapped(waiter->dev, seqno))) {
- intel_ring_emit(waiter,
- dw1 |
- signaller->semaphore_register[waiter->id]);
+ intel_ring_emit(waiter, dw1 | wait_mbox);
intel_ring_emit(waiter, seqno);
intel_ring_emit(waiter, 0);
intel_ring_emit(waiter, MI_NOOP);
@@ -782,9 +804,9 @@ do { \
} while (0)
static int
-pc_render_add_request(struct intel_ring_buffer *ring)
+pc_render_add_request(struct intel_engine_cs *ring)
{
- u32 scratch_addr = ring->scratch.gtt_offset + 128;
+ u32 scratch_addr = ring->scratch.gtt_offset + 2 * CACHELINE_BYTES;
int ret;
/* For Ironlake, MI_USER_INTERRUPT was deprecated and apparently
@@ -806,15 +828,15 @@ pc_render_add_request(struct intel_ring_buffer *ring)
intel_ring_emit(ring, ring->outstanding_lazy_seqno);
intel_ring_emit(ring, 0);
PIPE_CONTROL_FLUSH(ring, scratch_addr);
- scratch_addr += 128; /* write to separate cachelines */
+ scratch_addr += 2 * CACHELINE_BYTES; /* write to separate cachelines */
PIPE_CONTROL_FLUSH(ring, scratch_addr);
- scratch_addr += 128;
+ scratch_addr += 2 * CACHELINE_BYTES;
PIPE_CONTROL_FLUSH(ring, scratch_addr);
- scratch_addr += 128;
+ scratch_addr += 2 * CACHELINE_BYTES;
PIPE_CONTROL_FLUSH(ring, scratch_addr);
- scratch_addr += 128;
+ scratch_addr += 2 * CACHELINE_BYTES;
PIPE_CONTROL_FLUSH(ring, scratch_addr);
- scratch_addr += 128;
+ scratch_addr += 2 * CACHELINE_BYTES;
PIPE_CONTROL_FLUSH(ring, scratch_addr);
intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE |
@@ -830,7 +852,7 @@ pc_render_add_request(struct intel_ring_buffer *ring)
}
static u32
-gen6_ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
+gen6_ring_get_seqno(struct intel_engine_cs *ring, bool lazy_coherency)
{
/* Workaround to force correct ordering between irq and seqno writes on
* ivb (and maybe also on snb) by reading from a CS register (like
@@ -844,31 +866,31 @@ gen6_ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
}
static u32
-ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
+ring_get_seqno(struct intel_engine_cs *ring, bool lazy_coherency)
{
return intel_read_status_page(ring, I915_GEM_HWS_INDEX);
}
static void
-ring_set_seqno(struct intel_ring_buffer *ring, u32 seqno)
+ring_set_seqno(struct intel_engine_cs *ring, u32 seqno)
{
intel_write_status_page(ring, I915_GEM_HWS_INDEX, seqno);
}
static u32
-pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
+pc_render_get_seqno(struct intel_engine_cs *ring, bool lazy_coherency)
{
return ring->scratch.cpu_page[0];
}
static void
-pc_render_set_seqno(struct intel_ring_buffer *ring, u32 seqno)
+pc_render_set_seqno(struct intel_engine_cs *ring, u32 seqno)
{
ring->scratch.cpu_page[0] = seqno;
}
static bool
-gen5_ring_get_irq(struct intel_ring_buffer *ring)
+gen5_ring_get_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -886,7 +908,7 @@ gen5_ring_get_irq(struct intel_ring_buffer *ring)
}
static void
-gen5_ring_put_irq(struct intel_ring_buffer *ring)
+gen5_ring_put_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -899,7 +921,7 @@ gen5_ring_put_irq(struct intel_ring_buffer *ring)
}
static bool
-i9xx_ring_get_irq(struct intel_ring_buffer *ring)
+i9xx_ring_get_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -920,7 +942,7 @@ i9xx_ring_get_irq(struct intel_ring_buffer *ring)
}
static void
-i9xx_ring_put_irq(struct intel_ring_buffer *ring)
+i9xx_ring_put_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -936,7 +958,7 @@ i9xx_ring_put_irq(struct intel_ring_buffer *ring)
}
static bool
-i8xx_ring_get_irq(struct intel_ring_buffer *ring)
+i8xx_ring_get_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -957,7 +979,7 @@ i8xx_ring_get_irq(struct intel_ring_buffer *ring)
}
static void
-i8xx_ring_put_irq(struct intel_ring_buffer *ring)
+i8xx_ring_put_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -972,7 +994,7 @@ i8xx_ring_put_irq(struct intel_ring_buffer *ring)
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
}
-void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
+void intel_ring_setup_status_page(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -989,6 +1011,11 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
case BCS:
mmio = BLT_HWS_PGA_GEN7;
break;
+ /*
+ * VCS2 actually doesn't exist on Gen7. Only shut up
+ * gcc switch check warning
+ */
+ case VCS2:
case VCS:
mmio = BSD_HWS_PGA_GEN7;
break;
@@ -1030,7 +1057,7 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
}
static int
-bsd_ring_flush(struct intel_ring_buffer *ring,
+bsd_ring_flush(struct intel_engine_cs *ring,
u32 invalidate_domains,
u32 flush_domains)
{
@@ -1047,7 +1074,7 @@ bsd_ring_flush(struct intel_ring_buffer *ring,
}
static int
-i9xx_add_request(struct intel_ring_buffer *ring)
+i9xx_add_request(struct intel_engine_cs *ring)
{
int ret;
@@ -1065,7 +1092,7 @@ i9xx_add_request(struct intel_ring_buffer *ring)
}
static bool
-gen6_ring_get_irq(struct intel_ring_buffer *ring)
+gen6_ring_get_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1090,7 +1117,7 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring)
}
static void
-gen6_ring_put_irq(struct intel_ring_buffer *ring)
+gen6_ring_put_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1108,7 +1135,7 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring)
}
static bool
-hsw_vebox_get_irq(struct intel_ring_buffer *ring)
+hsw_vebox_get_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1128,7 +1155,7 @@ hsw_vebox_get_irq(struct intel_ring_buffer *ring)
}
static void
-hsw_vebox_put_irq(struct intel_ring_buffer *ring)
+hsw_vebox_put_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1146,7 +1173,7 @@ hsw_vebox_put_irq(struct intel_ring_buffer *ring)
}
static bool
-gen8_ring_get_irq(struct intel_ring_buffer *ring)
+gen8_ring_get_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1172,7 +1199,7 @@ gen8_ring_get_irq(struct intel_ring_buffer *ring)
}
static void
-gen8_ring_put_irq(struct intel_ring_buffer *ring)
+gen8_ring_put_irq(struct intel_engine_cs *ring)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1192,8 +1219,8 @@ gen8_ring_put_irq(struct intel_ring_buffer *ring)
}
static int
-i965_dispatch_execbuffer(struct intel_ring_buffer *ring,
- u32 offset, u32 length,
+i965_dispatch_execbuffer(struct intel_engine_cs *ring,
+ u64 offset, u32 length,
unsigned flags)
{
int ret;
@@ -1215,8 +1242,8 @@ i965_dispatch_execbuffer(struct intel_ring_buffer *ring,
/* Just userspace ABI convention to limit the wa batch bo to a resonable size */
#define I830_BATCH_LIMIT (256*1024)
static int
-i830_dispatch_execbuffer(struct intel_ring_buffer *ring,
- u32 offset, u32 len,
+i830_dispatch_execbuffer(struct intel_engine_cs *ring,
+ u64 offset, u32 len,
unsigned flags)
{
int ret;
@@ -1266,8 +1293,8 @@ i830_dispatch_execbuffer(struct intel_ring_buffer *ring,
}
static int
-i915_dispatch_execbuffer(struct intel_ring_buffer *ring,
- u32 offset, u32 len,
+i915_dispatch_execbuffer(struct intel_engine_cs *ring,
+ u64 offset, u32 len,
unsigned flags)
{
int ret;
@@ -1283,7 +1310,7 @@ i915_dispatch_execbuffer(struct intel_ring_buffer *ring,
return 0;
}
-static void cleanup_status_page(struct intel_ring_buffer *ring)
+static void cleanup_status_page(struct intel_engine_cs *ring)
{
struct drm_i915_gem_object *obj;
@@ -1297,50 +1324,44 @@ static void cleanup_status_page(struct intel_ring_buffer *ring)
ring->status_page.obj = NULL;
}
-static int init_status_page(struct intel_ring_buffer *ring)
+static int init_status_page(struct intel_engine_cs *ring)
{
- struct drm_device *dev = ring->dev;
struct drm_i915_gem_object *obj;
- int ret;
- obj = i915_gem_alloc_object(dev, 4096);
- if (obj == NULL) {
- DRM_ERROR("Failed to allocate status page\n");
- ret = -ENOMEM;
- goto err;
- }
+ if ((obj = ring->status_page.obj) == NULL) {
+ int ret;
- ret = i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
- if (ret)
- goto err_unref;
+ obj = i915_gem_alloc_object(ring->dev, 4096);
+ if (obj == NULL) {
+ DRM_ERROR("Failed to allocate status page\n");
+ return -ENOMEM;
+ }
- ret = i915_gem_obj_ggtt_pin(obj, 4096, 0);
- if (ret)
- goto err_unref;
+ ret = i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
+ if (ret)
+ goto err_unref;
+
+ ret = i915_gem_obj_ggtt_pin(obj, 4096, 0);
+ if (ret) {
+err_unref:
+ drm_gem_object_unreference(&obj->base);
+ return ret;
+ }
+
+ ring->status_page.obj = obj;
+ }
ring->status_page.gfx_addr = i915_gem_obj_ggtt_offset(obj);
ring->status_page.page_addr = kmap(sg_page(obj->pages->sgl));
- if (ring->status_page.page_addr == NULL) {
- ret = -ENOMEM;
- goto err_unpin;
- }
- ring->status_page.obj = obj;
memset(ring->status_page.page_addr, 0, PAGE_SIZE);
DRM_DEBUG_DRIVER("%s hws offset: 0x%08x\n",
ring->name, ring->status_page.gfx_addr);
return 0;
-
-err_unpin:
- i915_gem_object_ggtt_unpin(obj);
-err_unref:
- drm_gem_object_unreference(&obj->base);
-err:
- return ret;
}
-static int init_phys_status_page(struct intel_ring_buffer *ring)
+static int init_phys_status_page(struct intel_engine_cs *ring)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -1357,44 +1378,24 @@ static int init_phys_status_page(struct intel_ring_buffer *ring)
return 0;
}
-static int intel_init_ring_buffer(struct drm_device *dev,
- struct intel_ring_buffer *ring)
+static int allocate_ring_buffer(struct intel_engine_cs *ring)
{
+ 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;
- struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
- ring->dev = dev;
- INIT_LIST_HEAD(&ring->active_list);
- INIT_LIST_HEAD(&ring->request_list);
- ring->size = 32 * PAGE_SIZE;
- memset(ring->sync_seqno, 0, sizeof(ring->sync_seqno));
-
- init_waitqueue_head(&ring->irq_queue);
-
- if (I915_NEED_GFX_HWS(dev)) {
- ret = init_status_page(ring);
- if (ret)
- return ret;
- } else {
- BUG_ON(ring->id != RCS);
- ret = init_phys_status_page(ring);
- if (ret)
- return ret;
- }
+ if (intel_ring_initialized(ring))
+ return 0;
obj = NULL;
if (!HAS_LLC(dev))
- obj = i915_gem_object_create_stolen(dev, ring->size);
+ obj = i915_gem_object_create_stolen(dev, ringbuf->size);
if (obj == NULL)
- obj = i915_gem_alloc_object(dev, ring->size);
- if (obj == NULL) {
- DRM_ERROR("Failed to allocate ringbuffer\n");
- ret = -ENOMEM;
- goto err_hws;
- }
-
- ring->obj = obj;
+ obj = i915_gem_alloc_object(dev, ringbuf->size);
+ if (obj == NULL)
+ return -ENOMEM;
ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, PIN_MAPPABLE);
if (ret)
@@ -1404,65 +1405,102 @@ static int intel_init_ring_buffer(struct drm_device *dev,
if (ret)
goto err_unpin;
- ring->virtual_start =
+ ringbuf->virtual_start =
ioremap_wc(dev_priv->gtt.mappable_base + i915_gem_obj_ggtt_offset(obj),
- ring->size);
- if (ring->virtual_start == NULL) {
- DRM_ERROR("Failed to map ringbuffer.\n");
+ ringbuf->size);
+ if (ringbuf->virtual_start == NULL) {
ret = -EINVAL;
goto err_unpin;
}
- ret = ring->init(ring);
- if (ret)
- goto err_unmap;
+ ringbuf->obj = obj;
+ return 0;
+
+err_unpin:
+ i915_gem_object_ggtt_unpin(obj);
+err_unref:
+ drm_gem_object_unreference(&obj->base);
+ return ret;
+}
+
+static int intel_init_ring_buffer(struct drm_device *dev,
+ struct intel_engine_cs *ring)
+{
+ struct intel_ringbuffer *ringbuf = ring->buffer;
+ int ret;
+
+ if (ringbuf == NULL) {
+ ringbuf = kzalloc(sizeof(*ringbuf), GFP_KERNEL);
+ if (!ringbuf)
+ return -ENOMEM;
+ ring->buffer = ringbuf;
+ }
+
+ ring->dev = dev;
+ INIT_LIST_HEAD(&ring->active_list);
+ INIT_LIST_HEAD(&ring->request_list);
+ ringbuf->size = 32 * PAGE_SIZE;
+ memset(ring->semaphore.sync_seqno, 0, sizeof(ring->semaphore.sync_seqno));
+
+ init_waitqueue_head(&ring->irq_queue);
+
+ if (I915_NEED_GFX_HWS(dev)) {
+ ret = init_status_page(ring);
+ if (ret)
+ goto error;
+ } else {
+ BUG_ON(ring->id != RCS);
+ ret = init_phys_status_page(ring);
+ if (ret)
+ goto error;
+ }
+
+ ret = allocate_ring_buffer(ring);
+ if (ret) {
+ DRM_ERROR("Failed to allocate ringbuffer %s: %d\n", ring->name, ret);
+ goto error;
+ }
/* Workaround an erratum on the i830 which causes a hang if
* the TAIL pointer points to within the last 2 cachelines
* of the buffer.
*/
- ring->effective_size = ring->size;
- if (IS_I830(ring->dev) || IS_845G(ring->dev))
- ring->effective_size -= 128;
+ ringbuf->effective_size = ringbuf->size;
+ if (IS_I830(dev) || IS_845G(dev))
+ ringbuf->effective_size -= 2 * CACHELINE_BYTES;
- i915_cmd_parser_init_ring(ring);
+ ret = i915_cmd_parser_init_ring(ring);
+ if (ret)
+ goto error;
+
+ ret = ring->init(ring);
+ if (ret)
+ goto error;
return 0;
-err_unmap:
- iounmap(ring->virtual_start);
-err_unpin:
- i915_gem_object_ggtt_unpin(obj);
-err_unref:
- drm_gem_object_unreference(&obj->base);
- ring->obj = NULL;
-err_hws:
- cleanup_status_page(ring);
+error:
+ kfree(ringbuf);
+ ring->buffer = NULL;
return ret;
}
-void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring)
+void intel_cleanup_ring_buffer(struct intel_engine_cs *ring)
{
- struct drm_i915_private *dev_priv;
- int ret;
+ struct drm_i915_private *dev_priv = to_i915(ring->dev);
+ struct intel_ringbuffer *ringbuf = ring->buffer;
- if (ring->obj == NULL)
+ if (!intel_ring_initialized(ring))
return;
- /* Disable the ring buffer. The ring must be idle at this point */
- dev_priv = ring->dev->dev_private;
- ret = intel_ring_idle(ring);
- if (ret && !i915_reset_in_progress(&dev_priv->gpu_error))
- DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n",
- ring->name, ret);
-
- I915_WRITE_CTL(ring, 0);
+ intel_stop_ring_buffer(ring);
+ WARN_ON(!IS_GEN2(ring->dev) && (I915_READ_MODE(ring) & MODE_IDLE) == 0);
- iounmap(ring->virtual_start);
+ iounmap(ringbuf->virtual_start);
- i915_gem_object_ggtt_unpin(ring->obj);
- drm_gem_object_unreference(&ring->obj->base);
- ring->obj = NULL;
+ i915_gem_object_ggtt_unpin(ringbuf->obj);
+ drm_gem_object_unreference(&ringbuf->obj->base);
+ ringbuf->obj = NULL;
ring->preallocated_lazy_request = NULL;
ring->outstanding_lazy_seqno = 0;
@@ -1470,44 +1508,34 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring)
ring->cleanup(ring);
cleanup_status_page(ring);
+
+ i915_cmd_parser_fini_ring(ring);
+
+ kfree(ringbuf);
+ ring->buffer = NULL;
}
-static int intel_ring_wait_request(struct intel_ring_buffer *ring, int n)
+static int intel_ring_wait_request(struct intel_engine_cs *ring, int n)
{
+ struct intel_ringbuffer *ringbuf = ring->buffer;
struct drm_i915_gem_request *request;
- u32 seqno = 0, tail;
+ u32 seqno = 0;
int ret;
- if (ring->last_retired_head != -1) {
- ring->head = ring->last_retired_head;
- ring->last_retired_head = -1;
+ if (ringbuf->last_retired_head != -1) {
+ ringbuf->head = ringbuf->last_retired_head;
+ ringbuf->last_retired_head = -1;
- ring->space = ring_space(ring);
- if (ring->space >= n)
+ ringbuf->space = ring_space(ring);
+ if (ringbuf->space >= n)
return 0;
}
list_for_each_entry(request, &ring->request_list, list) {
- int space;
-
- if (request->tail == -1)
- continue;
-
- space = request->tail - (ring->tail + I915_RING_FREE_SPACE);
- if (space < 0)
- space += ring->size;
- if (space >= n) {
+ if (__ring_space(request->tail, ringbuf->tail, ringbuf->size) >= n) {
seqno = request->seqno;
- tail = request->tail;
break;
}
-
- /* Consume this request in case we need more space than
- * is available and so need to prevent a race between
- * updating last_retired_head and direct reads of
- * I915_RING_HEAD. It also provides a nice sanity check.
- */
- request->tail = -1;
}
if (seqno == 0)
@@ -1517,18 +1545,19 @@ static int intel_ring_wait_request(struct intel_ring_buffer *ring, int n)
if (ret)
return ret;
- ring->head = tail;
- ring->space = ring_space(ring);
- if (WARN_ON(ring->space < n))
- return -ENOSPC;
+ i915_gem_retire_requests_ring(ring);
+ ringbuf->head = ringbuf->last_retired_head;
+ ringbuf->last_retired_head = -1;
+ ringbuf->space = ring_space(ring);
return 0;
}
-static int ring_wait_for_space(struct intel_ring_buffer *ring, int n)
+static int ring_wait_for_space(struct intel_engine_cs *ring, int n)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_ringbuffer *ringbuf = ring->buffer;
unsigned long end;
int ret;
@@ -1539,7 +1568,6 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n)
/* force the tail write in case we have been skipping them */
__intel_ring_advance(ring);
- trace_i915_ring_wait_begin(ring);
/* 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
@@ -1547,12 +1575,13 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n)
* case by choosing an insanely large timeout. */
end = jiffies + 60 * HZ;
+ trace_i915_ring_wait_begin(ring);
do {
- ring->head = I915_READ_HEAD(ring);
- ring->space = ring_space(ring);
- if (ring->space >= n) {
- trace_i915_ring_wait_end(ring);
- return 0;
+ ringbuf->head = I915_READ_HEAD(ring);
+ ringbuf->space = ring_space(ring);
+ if (ringbuf->space >= n) {
+ ret = 0;
+ break;
}
if (!drm_core_check_feature(dev, DRIVER_MODESET) &&
@@ -1564,38 +1593,49 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n)
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)
- return ret;
- } while (!time_after(jiffies, end));
+ break;
+
+ if (time_after(jiffies, end)) {
+ ret = -EBUSY;
+ break;
+ }
+ } while (1);
trace_i915_ring_wait_end(ring);
- return -EBUSY;
+ return ret;
}
-static int intel_wrap_ring_buffer(struct intel_ring_buffer *ring)
+static int intel_wrap_ring_buffer(struct intel_engine_cs *ring)
{
uint32_t __iomem *virt;
- int rem = ring->size - ring->tail;
+ struct intel_ringbuffer *ringbuf = ring->buffer;
+ int rem = ringbuf->size - ringbuf->tail;
- if (ring->space < rem) {
+ if (ringbuf->space < rem) {
int ret = ring_wait_for_space(ring, rem);
if (ret)
return ret;
}
- virt = ring->virtual_start + ring->tail;
+ virt = ringbuf->virtual_start + ringbuf->tail;
rem /= 4;
while (rem--)
iowrite32(MI_NOOP, virt++);
- ring->tail = 0;
- ring->space = ring_space(ring);
+ ringbuf->tail = 0;
+ ringbuf->space = ring_space(ring);
return 0;
}
-int intel_ring_idle(struct intel_ring_buffer *ring)
+int intel_ring_idle(struct intel_engine_cs *ring)
{
u32 seqno;
int ret;
@@ -1619,7 +1659,7 @@ int intel_ring_idle(struct intel_ring_buffer *ring)
}
static int
-intel_ring_alloc_seqno(struct intel_ring_buffer *ring)
+intel_ring_alloc_seqno(struct intel_engine_cs *ring)
{
if (ring->outstanding_lazy_seqno)
return 0;
@@ -1637,18 +1677,19 @@ intel_ring_alloc_seqno(struct intel_ring_buffer *ring)
return i915_gem_get_seqno(ring->dev, &ring->outstanding_lazy_seqno);
}
-static int __intel_ring_prepare(struct intel_ring_buffer *ring,
+static int __intel_ring_prepare(struct intel_engine_cs *ring,
int bytes)
{
+ struct intel_ringbuffer *ringbuf = ring->buffer;
int ret;
- if (unlikely(ring->tail + bytes > ring->effective_size)) {
+ if (unlikely(ringbuf->tail + bytes > ringbuf->effective_size)) {
ret = intel_wrap_ring_buffer(ring);
if (unlikely(ret))
return ret;
}
- if (unlikely(ring->space < bytes)) {
+ if (unlikely(ringbuf->space < bytes)) {
ret = ring_wait_for_space(ring, bytes);
if (unlikely(ret))
return ret;
@@ -1657,7 +1698,7 @@ static int __intel_ring_prepare(struct intel_ring_buffer *ring,
return 0;
}
-int intel_ring_begin(struct intel_ring_buffer *ring,
+int intel_ring_begin(struct intel_engine_cs *ring,
int num_dwords)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -1677,19 +1718,20 @@ int intel_ring_begin(struct intel_ring_buffer *ring,
if (ret)
return ret;
- ring->space -= num_dwords * sizeof(uint32_t);
+ ring->buffer->space -= num_dwords * sizeof(uint32_t);
return 0;
}
/* Align the ring tail to a cacheline boundary */
-int intel_ring_cacheline_align(struct intel_ring_buffer *ring)
+int intel_ring_cacheline_align(struct intel_engine_cs *ring)
{
- int num_dwords = (64 - (ring->tail & 63)) / sizeof(uint32_t);
+ int num_dwords = (ring->buffer->tail & (CACHELINE_BYTES - 1)) / sizeof(uint32_t);
int ret;
if (num_dwords == 0)
return 0;
+ num_dwords = CACHELINE_BYTES / sizeof(uint32_t) - num_dwords;
ret = intel_ring_begin(ring, num_dwords);
if (ret)
return ret;
@@ -1702,7 +1744,7 @@ int intel_ring_cacheline_align(struct intel_ring_buffer *ring)
return 0;
}
-void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno)
+void intel_ring_init_seqno(struct intel_engine_cs *ring, u32 seqno)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -1719,7 +1761,7 @@ void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno)
ring->hangcheck.seqno = seqno;
}
-static void gen6_bsd_ring_write_tail(struct intel_ring_buffer *ring,
+static void gen6_bsd_ring_write_tail(struct intel_engine_cs *ring,
u32 value)
{
struct drm_i915_private *dev_priv = ring->dev->dev_private;
@@ -1752,7 +1794,7 @@ static void gen6_bsd_ring_write_tail(struct intel_ring_buffer *ring,
_MASKED_BIT_DISABLE(GEN6_BSD_SLEEP_MSG_DISABLE));
}
-static int gen6_bsd_ring_flush(struct intel_ring_buffer *ring,
+static int gen6_bsd_ring_flush(struct intel_engine_cs *ring,
u32 invalidate, u32 flush)
{
uint32_t cmd;
@@ -1788,8 +1830,8 @@ static int gen6_bsd_ring_flush(struct intel_ring_buffer *ring,
}
static int
-gen8_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
- u32 offset, u32 len,
+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;
@@ -1803,8 +1845,8 @@ gen8_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
/* FIXME(BDW): Address space and security selectors. */
intel_ring_emit(ring, MI_BATCH_BUFFER_START_GEN8 | (ppgtt<<8));
- intel_ring_emit(ring, offset);
- intel_ring_emit(ring, 0);
+ intel_ring_emit(ring, lower_32_bits(offset));
+ intel_ring_emit(ring, upper_32_bits(offset));
intel_ring_emit(ring, MI_NOOP);
intel_ring_advance(ring);
@@ -1812,8 +1854,8 @@ gen8_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
}
static int
-hsw_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
- u32 offset, u32 len,
+hsw_ring_dispatch_execbuffer(struct intel_engine_cs *ring,
+ u64 offset, u32 len,
unsigned flags)
{
int ret;
@@ -1833,8 +1875,8 @@ hsw_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
}
static int
-gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
- u32 offset, u32 len,
+gen6_ring_dispatch_execbuffer(struct intel_engine_cs *ring,
+ u64 offset, u32 len,
unsigned flags)
{
int ret;
@@ -1855,7 +1897,7 @@ gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring,
/* Blitter support (SandyBridge+) */
-static int gen6_ring_flush(struct intel_ring_buffer *ring,
+static int gen6_ring_flush(struct intel_engine_cs *ring,
u32 invalidate, u32 flush)
{
struct drm_device *dev = ring->dev;
@@ -1898,7 +1940,7 @@ static int gen6_ring_flush(struct intel_ring_buffer *ring,
int intel_init_render_ring_buffer(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+ struct intel_engine_cs *ring = &dev_priv->ring[RCS];
ring->name = "render ring";
ring->id = RCS;
@@ -1920,15 +1962,24 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
ring->irq_enable_mask = GT_RENDER_USER_INTERRUPT;
ring->get_seqno = gen6_ring_get_seqno;
ring->set_seqno = ring_set_seqno;
- ring->sync_to = gen6_ring_sync;
- ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_RV;
- ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_RB;
- ring->semaphore_register[VECS] = MI_SEMAPHORE_SYNC_RVE;
- ring->signal_mbox[RCS] = GEN6_NOSYNC;
- ring->signal_mbox[VCS] = GEN6_VRSYNC;
- ring->signal_mbox[BCS] = GEN6_BRSYNC;
- ring->signal_mbox[VECS] = GEN6_VERSYNC;
+ 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;
@@ -1999,16 +2050,25 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
+ struct intel_engine_cs *ring = &dev_priv->ring[RCS];
+ struct intel_ringbuffer *ringbuf = ring->buffer;
int ret;
+ if (ringbuf == NULL) {
+ ringbuf = kzalloc(sizeof(*ringbuf), GFP_KERNEL);
+ if (!ringbuf)
+ return -ENOMEM;
+ ring->buffer = ringbuf;
+ }
+
ring->name = "render ring";
ring->id = RCS;
ring->mmio_base = RENDER_RING_BASE;
if (INTEL_INFO(dev)->gen >= 6) {
/* non-kms not supported on gen6+ */
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_ringbuf;
}
/* Note: gem is not supported on gen5/ilk without kms (the corresponding
@@ -2043,31 +2103,39 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
INIT_LIST_HEAD(&ring->active_list);
INIT_LIST_HEAD(&ring->request_list);
- ring->size = size;
- ring->effective_size = ring->size;
+ ringbuf->size = size;
+ ringbuf->effective_size = ringbuf->size;
if (IS_I830(ring->dev) || IS_845G(ring->dev))
- ring->effective_size -= 128;
+ ringbuf->effective_size -= 2 * CACHELINE_BYTES;
- ring->virtual_start = ioremap_wc(start, size);
- if (ring->virtual_start == NULL) {
+ ringbuf->virtual_start = ioremap_wc(start, size);
+ if (ringbuf->virtual_start == NULL) {
DRM_ERROR("can not ioremap virtual address for"
" ring buffer\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_ringbuf;
}
if (!I915_NEED_GFX_HWS(dev)) {
ret = init_phys_status_page(ring);
if (ret)
- return ret;
+ goto err_vstart;
}
return 0;
+
+err_vstart:
+ iounmap(ringbuf->virtual_start);
+err_ringbuf:
+ kfree(ringbuf);
+ ring->buffer = NULL;
+ return ret;
}
int intel_init_bsd_ring_buffer(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[VCS];
+ struct intel_engine_cs *ring = &dev_priv->ring[VCS];
ring->name = "bsd ring";
ring->id = VCS;
@@ -2096,15 +2164,24 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
ring->dispatch_execbuffer =
gen6_ring_dispatch_execbuffer;
}
- ring->sync_to = gen6_ring_sync;
- ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_VR;
- ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_VB;
- ring->semaphore_register[VECS] = MI_SEMAPHORE_SYNC_VVE;
- ring->signal_mbox[RCS] = GEN6_RVSYNC;
- ring->signal_mbox[VCS] = GEN6_NOSYNC;
- ring->signal_mbox[BCS] = GEN6_BVSYNC;
- ring->signal_mbox[VECS] = GEN6_VEVSYNC;
+ 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;
@@ -2127,10 +2204,63 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev)
return intel_init_ring_buffer(dev, ring);
}
+/**
+ * Initialize the second BSD ring for Broadwell GT3.
+ * It is noted that this only exists on Broadwell GT3.
+ */
+int intel_init_bsd2_ring_buffer(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_engine_cs *ring = &dev_priv->ring[VCS2];
+
+ if ((INTEL_INFO(dev)->gen != 8)) {
+ DRM_ERROR("No dual-BSD ring on non-BDW machine\n");
+ return -EINVAL;
+ }
+
+ ring->name = "bds2_ring";
+ ring->id = VCS2;
+
+ ring->write_tail = ring_write_tail;
+ ring->mmio_base = GEN8_BSD2_RING_BASE;
+ ring->flush = gen6_bsd_ring_flush;
+ ring->add_request = gen6_add_request;
+ ring->get_seqno = gen6_ring_get_seqno;
+ ring->set_seqno = ring_set_seqno;
+ ring->irq_enable_mask =
+ GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT;
+ ring->irq_get = gen8_ring_get_irq;
+ 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;
+
+ ring->init = init_ring_common;
+
+ return intel_init_ring_buffer(dev, ring);
+}
+
int intel_init_blt_ring_buffer(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[BCS];
+ struct intel_engine_cs *ring = &dev_priv->ring[BCS];
ring->name = "blitter ring";
ring->id = BCS;
@@ -2153,15 +2283,24 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
ring->irq_put = gen6_ring_put_irq;
ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
}
- ring->sync_to = gen6_ring_sync;
- ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_BR;
- ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_BV;
- ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->semaphore_register[VECS] = MI_SEMAPHORE_SYNC_BVE;
- ring->signal_mbox[RCS] = GEN6_RBSYNC;
- ring->signal_mbox[VCS] = GEN6_VBSYNC;
- ring->signal_mbox[BCS] = GEN6_NOSYNC;
- ring->signal_mbox[VECS] = GEN6_VEBSYNC;
+ 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);
@@ -2170,7 +2309,7 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
int intel_init_vebox_ring_buffer(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_ring_buffer *ring = &dev_priv->ring[VECS];
+ struct intel_engine_cs *ring = &dev_priv->ring[VECS];
ring->name = "video enhancement ring";
ring->id = VECS;
@@ -2194,22 +2333,25 @@ int intel_init_vebox_ring_buffer(struct drm_device *dev)
ring->irq_put = hsw_vebox_put_irq;
ring->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
}
- ring->sync_to = gen6_ring_sync;
- ring->semaphore_register[RCS] = MI_SEMAPHORE_SYNC_VER;
- ring->semaphore_register[VCS] = MI_SEMAPHORE_SYNC_VEV;
- ring->semaphore_register[BCS] = MI_SEMAPHORE_SYNC_VEB;
- ring->semaphore_register[VECS] = MI_SEMAPHORE_SYNC_INVALID;
- ring->signal_mbox[RCS] = GEN6_RVESYNC;
- ring->signal_mbox[VCS] = GEN6_VVESYNC;
- ring->signal_mbox[BCS] = GEN6_BVESYNC;
- ring->signal_mbox[VECS] = 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);
}
int
-intel_ring_flush_all_caches(struct intel_ring_buffer *ring)
+intel_ring_flush_all_caches(struct intel_engine_cs *ring)
{
int ret;
@@ -2227,7 +2369,7 @@ intel_ring_flush_all_caches(struct intel_ring_buffer *ring)
}
int
-intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring)
+intel_ring_invalidate_all_caches(struct intel_engine_cs *ring)
{
uint32_t flush_domains;
int ret;
@@ -2245,3 +2387,19 @@ intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring)
ring->gpu_caches_dirty = false;
return 0;
}
+
+void
+intel_stop_ring_buffer(struct intel_engine_cs *ring)
+{
+ 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);
+
+ stop_ring(ring);
+}
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 2b91c4b4d34b..910c83cf7d44 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -1,6 +1,10 @@
#ifndef _INTEL_RINGBUFFER_H_
#define _INTEL_RINGBUFFER_H_
+#include <linux/hashtable.h>
+
+#define I915_CMD_HASH_ORDER 9
+
/*
* 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"
@@ -54,76 +58,93 @@ struct intel_ring_hangcheck {
bool deadlock;
};
-struct intel_ring_buffer {
+struct intel_ringbuffer {
+ struct drm_i915_gem_object *obj;
+ void __iomem *virtual_start;
+
+ u32 head;
+ u32 tail;
+ int space;
+ int size;
+ int effective_size;
+
+ /** We track the position of the requests in the ring buffer, and
+ * when each is retired we increment last_retired_head as the GPU
+ * must have finished processing the request and so we know we
+ * can advance the ringbuffer up to that position.
+ *
+ * last_retired_head is set to -1 after the value is consumed so
+ * we can detect new retirements.
+ */
+ u32 last_retired_head;
+};
+
+struct intel_engine_cs {
const char *name;
enum intel_ring_id {
RCS = 0x0,
VCS,
BCS,
VECS,
+ VCS2
} id;
-#define I915_NUM_RINGS 4
+#define I915_NUM_RINGS 5
+#define LAST_USER_RING (VECS + 1)
u32 mmio_base;
- void __iomem *virtual_start;
struct drm_device *dev;
- struct drm_i915_gem_object *obj;
+ struct intel_ringbuffer *buffer;
- u32 head;
- u32 tail;
- int space;
- int size;
- int effective_size;
struct intel_hw_status_page status_page;
- /** We track the position of the requests in the ring buffer, and
- * when each is retired we increment last_retired_head as the GPU
- * must have finished processing the request and so we know we
- * can advance the ringbuffer up to that position.
- *
- * last_retired_head is set to -1 after the value is consumed so
- * we can detect new retirements.
- */
- u32 last_retired_head;
-
unsigned irq_refcount; /* protected by dev_priv->irq_lock */
u32 irq_enable_mask; /* bitmask to enable ring interrupt */
u32 trace_irq_seqno;
- u32 sync_seqno[I915_NUM_RINGS-1];
- bool __must_check (*irq_get)(struct intel_ring_buffer *ring);
- void (*irq_put)(struct intel_ring_buffer *ring);
+ bool __must_check (*irq_get)(struct intel_engine_cs *ring);
+ void (*irq_put)(struct intel_engine_cs *ring);
- int (*init)(struct intel_ring_buffer *ring);
+ int (*init)(struct intel_engine_cs *ring);
- void (*write_tail)(struct intel_ring_buffer *ring,
+ void (*write_tail)(struct intel_engine_cs *ring,
u32 value);
- int __must_check (*flush)(struct intel_ring_buffer *ring,
+ int __must_check (*flush)(struct intel_engine_cs *ring,
u32 invalidate_domains,
u32 flush_domains);
- int (*add_request)(struct intel_ring_buffer *ring);
+ int (*add_request)(struct intel_engine_cs *ring);
/* Some chipsets are not quite as coherent as advertised and need
* an expensive kick to force a true read of the up-to-date seqno.
* However, the up-to-date seqno is not always required and the last
* seen value is good enough. Note that the seqno will always be
* monotonic, even if not coherent.
*/
- u32 (*get_seqno)(struct intel_ring_buffer *ring,
+ u32 (*get_seqno)(struct intel_engine_cs *ring,
bool lazy_coherency);
- void (*set_seqno)(struct intel_ring_buffer *ring,
+ void (*set_seqno)(struct intel_engine_cs *ring,
u32 seqno);
- int (*dispatch_execbuffer)(struct intel_ring_buffer *ring,
- u32 offset, u32 length,
+ int (*dispatch_execbuffer)(struct intel_engine_cs *ring,
+ u64 offset, u32 length,
unsigned flags);
#define I915_DISPATCH_SECURE 0x1
#define I915_DISPATCH_PINNED 0x2
- void (*cleanup)(struct intel_ring_buffer *ring);
- int (*sync_to)(struct intel_ring_buffer *ring,
- struct intel_ring_buffer *to,
- u32 seqno);
+ void (*cleanup)(struct intel_engine_cs *ring);
- /* our mbox written by others */
- u32 semaphore_register[I915_NUM_RINGS];
- /* mboxes this ring signals to */
- u32 signal_mbox[I915_NUM_RINGS];
+ 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;
+
+ /* AKA wait() */
+ int (*sync_to)(struct intel_engine_cs *ring,
+ struct intel_engine_cs *to,
+ u32 seqno);
+ int (*signal)(struct intel_engine_cs *signaller,
+ /* num_dwords needed by caller */
+ unsigned int num_dwords);
+ } semaphore;
/**
* List of objects currently involved in rendering from the
@@ -153,12 +174,8 @@ struct intel_ring_buffer {
wait_queue_head_t irq_queue;
- /**
- * Do an explicit TLB flush before MI_SET_CONTEXT
- */
- bool itlb_before_ctx_switch;
- struct i915_hw_context *default_context;
- struct i915_hw_context *last_context;
+ struct intel_context *default_context;
+ struct intel_context *last_context;
struct intel_ring_hangcheck hangcheck;
@@ -168,12 +185,13 @@ struct intel_ring_buffer {
volatile u32 *cpu_page;
} scratch;
+ bool needs_cmd_parser;
+
/*
- * Tables of commands the command parser needs to know about
+ * Table of commands the command parser needs to know about
* for this ring.
*/
- const struct drm_i915_cmd_table *cmd_tables;
- int cmd_table_count;
+ DECLARE_HASHTABLE(cmd_hash, I915_CMD_HASH_ORDER);
/*
* Table of registers allowed in commands that read/write registers.
@@ -202,20 +220,20 @@ struct intel_ring_buffer {
};
static inline bool
-intel_ring_initialized(struct intel_ring_buffer *ring)
+intel_ring_initialized(struct intel_engine_cs *ring)
{
- return ring->obj != NULL;
+ return ring->buffer && ring->buffer->obj;
}
static inline unsigned
-intel_ring_flag(struct intel_ring_buffer *ring)
+intel_ring_flag(struct intel_engine_cs *ring)
{
return 1 << ring->id;
}
static inline u32
-intel_ring_sync_index(struct intel_ring_buffer *ring,
- struct intel_ring_buffer *other)
+intel_ring_sync_index(struct intel_engine_cs *ring,
+ struct intel_engine_cs *other)
{
int idx;
@@ -233,7 +251,7 @@ intel_ring_sync_index(struct intel_ring_buffer *ring,
}
static inline u32
-intel_read_status_page(struct intel_ring_buffer *ring,
+intel_read_status_page(struct intel_engine_cs *ring,
int reg)
{
/* Ensure that the compiler doesn't optimize away the load. */
@@ -242,7 +260,7 @@ intel_read_status_page(struct intel_ring_buffer *ring,
}
static inline void
-intel_write_status_page(struct intel_ring_buffer *ring,
+intel_write_status_page(struct intel_engine_cs *ring,
int reg, u32 value)
{
ring->status_page.page_addr[reg] = value;
@@ -267,47 +285,51 @@ intel_write_status_page(struct intel_ring_buffer *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_cleanup_ring_buffer(struct intel_ring_buffer *ring);
+void intel_stop_ring_buffer(struct intel_engine_cs *ring);
+void intel_cleanup_ring_buffer(struct intel_engine_cs *ring);
-int __must_check intel_ring_begin(struct intel_ring_buffer *ring, int n);
-int __must_check intel_ring_cacheline_align(struct intel_ring_buffer *ring);
-static inline void intel_ring_emit(struct intel_ring_buffer *ring,
+int __must_check intel_ring_begin(struct intel_engine_cs *ring, int n);
+int __must_check intel_ring_cacheline_align(struct intel_engine_cs *ring);
+static inline void intel_ring_emit(struct intel_engine_cs *ring,
u32 data)
{
- iowrite32(data, ring->virtual_start + ring->tail);
- ring->tail += 4;
+ struct intel_ringbuffer *ringbuf = ring->buffer;
+ iowrite32(data, ringbuf->virtual_start + ringbuf->tail);
+ ringbuf->tail += 4;
}
-static inline void intel_ring_advance(struct intel_ring_buffer *ring)
+static inline void intel_ring_advance(struct intel_engine_cs *ring)
{
- ring->tail &= ring->size - 1;
+ struct intel_ringbuffer *ringbuf = ring->buffer;
+ ringbuf->tail &= ringbuf->size - 1;
}
-void __intel_ring_advance(struct intel_ring_buffer *ring);
+void __intel_ring_advance(struct intel_engine_cs *ring);
-int __must_check intel_ring_idle(struct intel_ring_buffer *ring);
-void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno);
-int intel_ring_flush_all_caches(struct intel_ring_buffer *ring);
-int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring);
+int __must_check intel_ring_idle(struct intel_engine_cs *ring);
+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);
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);
int intel_init_blt_ring_buffer(struct drm_device *dev);
int intel_init_vebox_ring_buffer(struct drm_device *dev);
-u64 intel_ring_get_active_head(struct intel_ring_buffer *ring);
-void intel_ring_setup_status_page(struct intel_ring_buffer *ring);
+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_ring_buffer *ring)
+static inline u32 intel_ring_get_tail(struct intel_engine_cs *ring)
{
- return ring->tail;
+ return ring->buffer->tail;
}
-static inline u32 intel_ring_get_seqno(struct intel_ring_buffer *ring)
+static inline u32 intel_ring_get_seqno(struct intel_engine_cs *ring)
{
BUG_ON(ring->outstanding_lazy_seqno == 0);
return ring->outstanding_lazy_seqno;
}
-static inline void i915_trace_irq_get(struct intel_ring_buffer *ring, u32 seqno)
+static inline void i915_trace_irq_get(struct intel_engine_cs *ring, u32 seqno)
{
if (ring->trace_irq_seqno == 0 && ring->irq_get(ring))
ring->trace_irq_seqno = seqno;
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 46be00d66df3..6a4d5bc17697 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -1153,20 +1153,21 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
pipe_config->pixel_multiplier =
intel_sdvo_get_pixel_multiplier(adjusted_mode);
+ pipe_config->has_hdmi_sink = intel_sdvo->has_hdmi_monitor;
+
if (intel_sdvo->color_range_auto) {
/* See CEA-861-E - 5.1 Default Encoding Parameters */
/* FIXME: This bit is only valid when using TMDS encoding and 8
* bit per color mode. */
- if (intel_sdvo->has_hdmi_monitor &&
+ if (pipe_config->has_hdmi_sink &&
drm_match_cea_mode(adjusted_mode) > 1)
- intel_sdvo->color_range = HDMI_COLOR_RANGE_16_235;
- else
- intel_sdvo->color_range = 0;
+ pipe_config->limited_color_range = true;
+ } else {
+ if (pipe_config->has_hdmi_sink &&
+ intel_sdvo->color_range == HDMI_COLOR_RANGE_16_235)
+ pipe_config->limited_color_range = true;
}
- if (intel_sdvo->color_range)
- pipe_config->limited_color_range = true;
-
/* Clock computation needs to happen after pixel multiplier. */
if (intel_sdvo->is_tv)
i9xx_adjust_sdvo_tv_clock(pipe_config);
@@ -1174,7 +1175,7 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
return true;
}
-static void intel_sdvo_mode_set(struct intel_encoder *intel_encoder)
+static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder)
{
struct drm_device *dev = intel_encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1223,7 +1224,7 @@ static void intel_sdvo_mode_set(struct intel_encoder *intel_encoder)
if (!intel_sdvo_set_target_input(intel_sdvo))
return;
- if (intel_sdvo->has_hdmi_monitor) {
+ if (crtc->config.has_hdmi_sink) {
intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI);
intel_sdvo_set_colorimetry(intel_sdvo,
SDVO_COLORIMETRY_RGB256);
@@ -1258,8 +1259,8 @@ static void intel_sdvo_mode_set(struct intel_encoder *intel_encoder)
/* The real mode polarity is set by the SDVO commands, using
* struct intel_sdvo_dtd. */
sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH;
- if (!HAS_PCH_SPLIT(dev) && intel_sdvo->is_hdmi)
- sdvox |= intel_sdvo->color_range;
+ if (!HAS_PCH_SPLIT(dev) && crtc->config.limited_color_range)
+ sdvox |= HDMI_COLOR_RANGE_16_235;
if (INTEL_INFO(dev)->gen < 5)
sdvox |= SDVO_BORDER_ENABLE;
} else {
@@ -1349,6 +1350,8 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder,
u8 val;
bool ret;
+ sdvox = I915_READ(intel_sdvo->sdvo_reg);
+
ret = intel_sdvo_get_input_timing(intel_sdvo, &dtd);
if (!ret) {
/* Some sdvo encoders are not spec compliant and don't
@@ -1377,7 +1380,6 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder,
* other platfroms.
*/
if (IS_I915G(dev) || IS_I915GM(dev)) {
- sdvox = I915_READ(intel_sdvo->sdvo_reg);
pipe_config->pixel_multiplier =
((sdvox & SDVO_PORT_MULTIPLY_MASK)
>> SDVO_PORT_MULTIPLY_SHIFT) + 1;
@@ -1406,6 +1408,15 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder,
}
}
+ if (sdvox & HDMI_COLOR_RANGE_16_235)
+ pipe_config->limited_color_range = true;
+
+ if (intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ENCODE,
+ &val, 1)) {
+ if (val == SDVO_ENCODE_HDMI)
+ pipe_config->has_hdmi_sink = true;
+ }
+
WARN(encoder_pixel_multiplier != pipe_config->pixel_multiplier,
"SDVO pixel multiplier mismatch, port: %i, encoder: %i\n",
pipe_config->pixel_multiplier, encoder_pixel_multiplier);
@@ -1732,7 +1743,7 @@ intel_sdvo_detect(struct drm_connector *connector, bool force)
enum drm_connector_status ret;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
- connector->base.id, drm_get_connector_name(connector));
+ connector->base.id, connector->name);
if (!intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_ATTACHED_DISPLAYS,
@@ -1794,7 +1805,7 @@ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
struct edid *edid;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
- connector->base.id, drm_get_connector_name(connector));
+ connector->base.id, connector->name);
/* set the bus switch and get the modes */
edid = intel_sdvo_get_edid(connector);
@@ -1892,7 +1903,7 @@ static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
int i;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
- connector->base.id, drm_get_connector_name(connector));
+ connector->base.id, connector->name);
/* Read the list of supported input resolutions for the selected TV
* format.
@@ -1929,7 +1940,7 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
struct drm_display_mode *newmode;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
- connector->base.id, drm_get_connector_name(connector));
+ connector->base.id, connector->name);
/*
* Fetch modes from VBT. For SDVO prefer the VBT mode since some
@@ -2999,7 +3010,7 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
intel_encoder->compute_config = intel_sdvo_compute_config;
intel_encoder->disable = intel_disable_sdvo;
- intel_encoder->mode_set = intel_sdvo_mode_set;
+ intel_encoder->pre_enable = intel_sdvo_pre_enable;
intel_encoder->enable = intel_enable_sdvo;
intel_encoder->get_hw_state = intel_sdvo_get_hw_state;
intel_encoder->get_config = intel_sdvo_get_config;
diff --git a/drivers/gpu/drm/i915/intel_sideband.c b/drivers/gpu/drm/i915/intel_sideband.c
index 0954f132726e..01d841ea3140 100644
--- a/drivers/gpu/drm/i915/intel_sideband.c
+++ b/drivers/gpu/drm/i915/intel_sideband.c
@@ -29,12 +29,21 @@
* IOSF sideband, see VLV2_SidebandMsg_HAS.docx and
* VLV_VLV2_PUNIT_HAS_0.8.docx
*/
+
+/* Standard MMIO read, non-posted */
+#define SB_MRD_NP 0x00
+/* Standard MMIO write, non-posted */
+#define SB_MWR_NP 0x01
+/* Private register read, double-word addressing, non-posted */
+#define SB_CRRDDA_NP 0x06
+/* Private register write, double-word addressing, non-posted */
+#define SB_CRWRDA_NP 0x07
+
static int vlv_sideband_rw(struct drm_i915_private *dev_priv, u32 devfn,
u32 port, u32 opcode, u32 addr, u32 *val)
{
u32 cmd, be = 0xf, bar = 0;
- bool is_read = (opcode == PUNIT_OPCODE_REG_READ ||
- opcode == DPIO_OPCODE_REG_READ);
+ bool is_read = (opcode == SB_MRD_NP || opcode == SB_CRRDDA_NP);
cmd = (devfn << IOSF_DEVFN_SHIFT) | (opcode << IOSF_OPCODE_SHIFT) |
(port << IOSF_PORT_SHIFT) | (be << IOSF_BYTE_ENABLES_SHIFT) |
@@ -74,7 +83,7 @@ u32 vlv_punit_read(struct drm_i915_private *dev_priv, u8 addr)
mutex_lock(&dev_priv->dpio_lock);
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT,
- PUNIT_OPCODE_REG_READ, addr, &val);
+ SB_CRRDDA_NP, addr, &val);
mutex_unlock(&dev_priv->dpio_lock);
return val;
@@ -86,7 +95,7 @@ void vlv_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val)
mutex_lock(&dev_priv->dpio_lock);
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT,
- PUNIT_OPCODE_REG_WRITE, addr, &val);
+ SB_CRWRDA_NP, addr, &val);
mutex_unlock(&dev_priv->dpio_lock);
}
@@ -95,7 +104,7 @@ u32 vlv_bunit_read(struct drm_i915_private *dev_priv, u32 reg)
u32 val = 0;
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_BUNIT,
- PUNIT_OPCODE_REG_READ, reg, &val);
+ SB_CRRDDA_NP, reg, &val);
return val;
}
@@ -103,7 +112,7 @@ u32 vlv_bunit_read(struct drm_i915_private *dev_priv, u32 reg)
void vlv_bunit_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
{
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_BUNIT,
- PUNIT_OPCODE_REG_WRITE, reg, &val);
+ SB_CRWRDA_NP, reg, &val);
}
u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr)
@@ -114,7 +123,7 @@ u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr)
mutex_lock(&dev_priv->dpio_lock);
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_NC,
- PUNIT_OPCODE_REG_READ, addr, &val);
+ SB_CRRDDA_NP, addr, &val);
mutex_unlock(&dev_priv->dpio_lock);
return val;
@@ -124,56 +133,56 @@ u32 vlv_gpio_nc_read(struct drm_i915_private *dev_priv, u32 reg)
{
u32 val = 0;
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPIO_NC,
- PUNIT_OPCODE_REG_READ, reg, &val);
+ SB_CRRDDA_NP, reg, &val);
return val;
}
void vlv_gpio_nc_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
{
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPIO_NC,
- PUNIT_OPCODE_REG_WRITE, reg, &val);
+ SB_CRWRDA_NP, reg, &val);
}
u32 vlv_cck_read(struct drm_i915_private *dev_priv, u32 reg)
{
u32 val = 0;
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCK,
- PUNIT_OPCODE_REG_READ, reg, &val);
+ SB_CRRDDA_NP, reg, &val);
return val;
}
void vlv_cck_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
{
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCK,
- PUNIT_OPCODE_REG_WRITE, reg, &val);
+ SB_CRWRDA_NP, reg, &val);
}
u32 vlv_ccu_read(struct drm_i915_private *dev_priv, u32 reg)
{
u32 val = 0;
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCU,
- PUNIT_OPCODE_REG_READ, reg, &val);
+ SB_CRRDDA_NP, reg, &val);
return val;
}
void vlv_ccu_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
{
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_CCU,
- PUNIT_OPCODE_REG_WRITE, reg, &val);
+ SB_CRWRDA_NP, reg, &val);
}
u32 vlv_gps_core_read(struct drm_i915_private *dev_priv, u32 reg)
{
u32 val = 0;
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPS_CORE,
- PUNIT_OPCODE_REG_READ, reg, &val);
+ SB_CRRDDA_NP, reg, &val);
return val;
}
void vlv_gps_core_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
{
vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_GPS_CORE,
- PUNIT_OPCODE_REG_WRITE, reg, &val);
+ SB_CRWRDA_NP, reg, &val);
}
u32 vlv_dpio_read(struct drm_i915_private *dev_priv, enum pipe pipe, int reg)
@@ -181,14 +190,22 @@ u32 vlv_dpio_read(struct drm_i915_private *dev_priv, enum pipe pipe, int reg)
u32 val = 0;
vlv_sideband_rw(dev_priv, DPIO_DEVFN, DPIO_PHY_IOSF_PORT(DPIO_PHY(pipe)),
- DPIO_OPCODE_REG_READ, reg, &val);
+ SB_MRD_NP, reg, &val);
+
+ /*
+ * FIXME: There might be some registers where all 1's is a valid value,
+ * so ideally we should check the register offset instead...
+ */
+ WARN(val == 0xffffffff, "DPIO read pipe %c reg 0x%x == 0x%x\n",
+ pipe_name(pipe), reg, val);
+
return val;
}
void vlv_dpio_write(struct drm_i915_private *dev_priv, enum pipe pipe, int reg, u32 val)
{
vlv_sideband_rw(dev_priv, DPIO_DEVFN, DPIO_PHY_IOSF_PORT(DPIO_PHY(pipe)),
- DPIO_OPCODE_REG_WRITE, reg, &val);
+ SB_MWR_NP, reg, &val);
}
/* SBI access */
@@ -253,13 +270,13 @@ void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value,
u32 vlv_flisdsi_read(struct drm_i915_private *dev_priv, u32 reg)
{
u32 val = 0;
- vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_FLISDSI,
- DPIO_OPCODE_REG_READ, reg, &val);
+ vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_FLISDSI, SB_CRRDDA_NP,
+ reg, &val);
return val;
}
void vlv_flisdsi_write(struct drm_i915_private *dev_priv, u32 reg, u32 val)
{
- vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_FLISDSI,
- DPIO_OPCODE_REG_WRITE, reg, &val);
+ vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_FLISDSI, SB_CRWRDA_NP,
+ reg, &val);
}
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 336ae6c602f2..1b66ddcdfb33 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -37,6 +37,106 @@
#include <drm/i915_drm.h>
#include "i915_drv.h"
+static int usecs_to_scanlines(const struct drm_display_mode *mode, int usecs)
+{
+ /* paranoia */
+ if (!mode->crtc_htotal)
+ return 1;
+
+ return DIV_ROUND_UP(usecs * mode->crtc_clock, 1000 * mode->crtc_htotal);
+}
+
+static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl_count)
+{
+ struct drm_device *dev = crtc->base.dev;
+ const struct drm_display_mode *mode = &crtc->config.adjusted_mode;
+ enum pipe pipe = crtc->pipe;
+ long timeout = msecs_to_jiffies_timeout(1);
+ int scanline, min, max, vblank_start;
+ DEFINE_WAIT(wait);
+
+ WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex));
+
+ vblank_start = mode->crtc_vblank_start;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ vblank_start = DIV_ROUND_UP(vblank_start, 2);
+
+ /* FIXME needs to be calibrated sensibly */
+ min = vblank_start - usecs_to_scanlines(mode, 100);
+ max = vblank_start - 1;
+
+ if (min <= 0 || max <= 0)
+ return false;
+
+ if (WARN_ON(drm_vblank_get(dev, pipe)))
+ return false;
+
+ local_irq_disable();
+
+ trace_i915_pipe_update_start(crtc, min, max);
+
+ for (;;) {
+ /*
+ * prepare_to_wait() has a memory barrier, which guarantees
+ * other CPUs can see the task state update by the time we
+ * read the scanline.
+ */
+ prepare_to_wait(&crtc->vbl_wait, &wait, TASK_UNINTERRUPTIBLE);
+
+ scanline = intel_get_crtc_scanline(crtc);
+ if (scanline < min || scanline > max)
+ break;
+
+ if (timeout <= 0) {
+ DRM_ERROR("Potential atomic update failure on pipe %c\n",
+ pipe_name(crtc->pipe));
+ break;
+ }
+
+ local_irq_enable();
+
+ timeout = schedule_timeout(timeout);
+
+ local_irq_disable();
+ }
+
+ finish_wait(&crtc->vbl_wait, &wait);
+
+ drm_vblank_put(dev, pipe);
+
+ *start_vbl_count = dev->driver->get_vblank_counter(dev, pipe);
+
+ trace_i915_pipe_update_vblank_evaded(crtc, min, max, *start_vbl_count);
+
+ return true;
+}
+
+static void intel_pipe_update_end(struct intel_crtc *crtc, u32 start_vbl_count)
+{
+ struct drm_device *dev = crtc->base.dev;
+ enum pipe pipe = crtc->pipe;
+ u32 end_vbl_count = dev->driver->get_vblank_counter(dev, pipe);
+
+ trace_i915_pipe_update_end(crtc, end_vbl_count);
+
+ local_irq_enable();
+
+ if (start_vbl_count != end_vbl_count)
+ DRM_ERROR("Atomic update failure on pipe %c (start=%u end=%u)\n",
+ pipe_name(pipe), start_vbl_count, end_vbl_count);
+}
+
+static void intel_update_primary_plane(struct intel_crtc *crtc)
+{
+ struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+ int reg = DSPCNTR(crtc->plane);
+
+ if (crtc->primary_enabled)
+ I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE);
+ else
+ I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE);
+}
+
static void
vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
struct drm_framebuffer *fb,
@@ -48,11 +148,14 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
struct drm_device *dev = dplane->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_plane *intel_plane = to_intel_plane(dplane);
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_plane->pipe;
int plane = intel_plane->plane;
u32 sprctl;
unsigned long sprsurf_offset, linear_offset;
int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
+ u32 start_vbl_count;
+ bool atomic_update;
sprctl = I915_READ(SPCNTR(pipe, plane));
@@ -131,6 +234,10 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
fb->pitches[0]);
linear_offset -= sprsurf_offset;
+ atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+ intel_update_primary_plane(intel_crtc);
+
I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]);
I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
@@ -143,7 +250,11 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
I915_WRITE(SPCNTR(pipe, plane), sprctl);
I915_WRITE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) +
sprsurf_offset);
- POSTING_READ(SPSURF(pipe, plane));
+
+ intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+ if (atomic_update)
+ intel_pipe_update_end(intel_crtc, start_vbl_count);
}
static void
@@ -152,14 +263,25 @@ vlv_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc)
struct drm_device *dev = dplane->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_plane *intel_plane = to_intel_plane(dplane);
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_plane->pipe;
int plane = intel_plane->plane;
+ u32 start_vbl_count;
+ bool atomic_update;
+
+ atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+ intel_update_primary_plane(intel_crtc);
I915_WRITE(SPCNTR(pipe, plane), I915_READ(SPCNTR(pipe, plane)) &
~SP_ENABLE);
/* Activate double buffered register update */
I915_WRITE(SPSURF(pipe, plane), 0);
- POSTING_READ(SPSURF(pipe, plane));
+
+ intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+ if (atomic_update)
+ intel_pipe_update_end(intel_crtc, start_vbl_count);
intel_update_sprite_watermarks(dplane, crtc, 0, 0, false, false);
}
@@ -226,10 +348,13 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_device *dev = plane->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_plane *intel_plane = to_intel_plane(plane);
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_plane->pipe;
u32 sprctl, sprscale = 0;
unsigned long sprsurf_offset, linear_offset;
int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
+ u32 start_vbl_count;
+ bool atomic_update;
sprctl = I915_READ(SPRCTL(pipe));
@@ -299,6 +424,10 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
pixel_size, fb->pitches[0]);
linear_offset -= sprsurf_offset;
+ atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+ intel_update_primary_plane(intel_crtc);
+
I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);
I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
@@ -317,7 +446,11 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
I915_WRITE(SPRCTL(pipe), sprctl);
I915_WRITE(SPRSURF(pipe),
i915_gem_obj_ggtt_offset(obj) + sprsurf_offset);
- POSTING_READ(SPRSURF(pipe));
+
+ intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+ if (atomic_update)
+ intel_pipe_update_end(intel_crtc, start_vbl_count);
}
static void
@@ -326,7 +459,14 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
struct drm_device *dev = plane->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_plane *intel_plane = to_intel_plane(plane);
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_plane->pipe;
+ u32 start_vbl_count;
+ bool atomic_update;
+
+ atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+ intel_update_primary_plane(intel_crtc);
I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE);
/* Can't leave the scaler enabled... */
@@ -334,7 +474,11 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
I915_WRITE(SPRSCALE(pipe), 0);
/* Activate double buffered register update */
I915_WRITE(SPRSURF(pipe), 0);
- POSTING_READ(SPRSURF(pipe));
+
+ intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+ if (atomic_update)
+ intel_pipe_update_end(intel_crtc, start_vbl_count);
/*
* Avoid underruns when disabling the sprite.
@@ -410,10 +554,13 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_device *dev = plane->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_plane *intel_plane = to_intel_plane(plane);
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_plane->pipe;
unsigned long dvssurf_offset, linear_offset;
u32 dvscntr, dvsscale;
int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
+ u32 start_vbl_count;
+ bool atomic_update;
dvscntr = I915_READ(DVSCNTR(pipe));
@@ -478,6 +625,10 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
pixel_size, fb->pitches[0]);
linear_offset -= dvssurf_offset;
+ atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+ intel_update_primary_plane(intel_crtc);
+
I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);
I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
@@ -491,7 +642,11 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
I915_WRITE(DVSCNTR(pipe), dvscntr);
I915_WRITE(DVSSURF(pipe),
i915_gem_obj_ggtt_offset(obj) + dvssurf_offset);
- POSTING_READ(DVSSURF(pipe));
+
+ intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+ if (atomic_update)
+ intel_pipe_update_end(intel_crtc, start_vbl_count);
}
static void
@@ -500,14 +655,25 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
struct drm_device *dev = plane->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_plane *intel_plane = to_intel_plane(plane);
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_plane->pipe;
+ u32 start_vbl_count;
+ bool atomic_update;
+
+ atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
+
+ intel_update_primary_plane(intel_crtc);
I915_WRITE(DVSCNTR(pipe), I915_READ(DVSCNTR(pipe)) & ~DVS_ENABLE);
/* Disable the scaler */
I915_WRITE(DVSSCALE(pipe), 0);
/* Flush double buffered register updates */
I915_WRITE(DVSSURF(pipe), 0);
- POSTING_READ(DVSSURF(pipe));
+
+ intel_flush_primary_plane(dev_priv, intel_crtc->plane);
+
+ if (atomic_update)
+ intel_pipe_update_end(intel_crtc, start_vbl_count);
/*
* Avoid underruns when disabling the sprite.
@@ -519,20 +685,10 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
}
static void
-intel_enable_primary(struct drm_crtc *crtc)
+intel_post_enable_primary(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 reg = DSPCNTR(intel_crtc->plane);
-
- if (intel_crtc->primary_enabled)
- return;
-
- intel_crtc->primary_enabled = true;
-
- I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE);
- intel_flush_primary_plane(dev_priv, intel_crtc->plane);
/*
* FIXME IPS should be fine as long as one plane is
@@ -540,10 +696,7 @@ intel_enable_primary(struct drm_crtc *crtc)
* when going from primary only to sprite only and vice
* versa.
*/
- if (intel_crtc->config.ips_enabled) {
- intel_wait_for_vblank(dev, intel_crtc->pipe);
- hsw_enable_ips(intel_crtc);
- }
+ hsw_enable_ips(intel_crtc);
mutex_lock(&dev->struct_mutex);
intel_update_fbc(dev);
@@ -551,17 +704,11 @@ intel_enable_primary(struct drm_crtc *crtc)
}
static void
-intel_disable_primary(struct drm_crtc *crtc)
+intel_pre_disable_primary(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 reg = DSPCNTR(intel_crtc->plane);
-
- if (!intel_crtc->primary_enabled)
- return;
-
- intel_crtc->primary_enabled = false;
mutex_lock(&dev->struct_mutex);
if (dev_priv->fbc.plane == intel_crtc->plane)
@@ -575,9 +722,6 @@ intel_disable_primary(struct drm_crtc *crtc)
* versa.
*/
hsw_disable_ips(intel_crtc);
-
- I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE);
- intel_flush_primary_plane(dev_priv, intel_crtc->plane);
}
static int
@@ -671,7 +815,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_i915_gem_object *obj = intel_fb->obj;
struct drm_i915_gem_object *old_obj = intel_plane->obj;
int ret;
- bool disable_primary = false;
+ bool primary_enabled;
bool visible;
int hscale, vscale;
int max_scale, min_scale;
@@ -842,8 +986,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
* If the sprite is completely covering the primary plane,
* we can disable the primary and save power.
*/
- disable_primary = drm_rect_equals(&dst, &clip) && !colorkey_enabled(intel_plane);
- WARN_ON(disable_primary && !visible && intel_crtc->active);
+ primary_enabled = !drm_rect_equals(&dst, &clip) || colorkey_enabled(intel_plane);
+ WARN_ON(!primary_enabled && !visible && intel_crtc->active);
mutex_lock(&dev->struct_mutex);
@@ -870,12 +1014,15 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
intel_plane->obj = obj;
if (intel_crtc->active) {
- /*
- * Be sure to re-enable the primary before the sprite is no longer
- * covering it fully.
- */
- if (!disable_primary)
- intel_enable_primary(crtc);
+ bool primary_was_enabled = intel_crtc->primary_enabled;
+
+ intel_crtc->primary_enabled = primary_enabled;
+
+ if (primary_was_enabled != primary_enabled)
+ intel_crtc_wait_for_pending_flips(crtc);
+
+ if (primary_was_enabled && !primary_enabled)
+ intel_pre_disable_primary(crtc);
if (visible)
intel_plane->update_plane(plane, crtc, fb, obj,
@@ -884,8 +1031,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
else
intel_plane->disable_plane(plane, crtc);
- if (disable_primary)
- intel_disable_primary(crtc);
+ if (!primary_was_enabled && primary_enabled)
+ intel_post_enable_primary(crtc);
}
/* Unpin old obj after new one is active to avoid ugliness */
@@ -923,8 +1070,14 @@ intel_disable_plane(struct drm_plane *plane)
intel_crtc = to_intel_crtc(plane->crtc);
if (intel_crtc->active) {
- intel_enable_primary(plane->crtc);
+ bool primary_was_enabled = intel_crtc->primary_enabled;
+
+ intel_crtc->primary_enabled = true;
+
intel_plane->disable_plane(plane, plane->crtc);
+
+ if (!primary_was_enabled && intel_crtc->primary_enabled)
+ intel_post_enable_primary(plane->crtc);
}
if (intel_plane->obj) {
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index bafe92e317d5..67c6c9a2eb1c 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -934,7 +934,86 @@ intel_tv_compute_config(struct intel_encoder *encoder,
return true;
}
-static void intel_tv_mode_set(struct intel_encoder *encoder)
+static void
+set_tv_mode_timings(struct drm_i915_private *dev_priv,
+ const struct tv_mode *tv_mode,
+ bool burst_ena)
+{
+ u32 hctl1, hctl2, hctl3;
+ u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
+
+ hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
+ (tv_mode->htotal << TV_HTOTAL_SHIFT);
+
+ hctl2 = (tv_mode->hburst_start << 16) |
+ (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
+
+ if (burst_ena)
+ hctl2 |= TV_BURST_ENA;
+
+ hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
+ (tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
+
+ vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
+ (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
+ (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
+
+ vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
+ (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
+ (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
+
+ vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
+ (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
+ (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
+
+ if (tv_mode->veq_ena)
+ vctl3 |= TV_EQUAL_ENA;
+
+ vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
+ (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
+
+ vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
+ (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
+
+ vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
+ (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
+
+ vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
+ (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
+
+ I915_WRITE(TV_H_CTL_1, hctl1);
+ I915_WRITE(TV_H_CTL_2, hctl2);
+ I915_WRITE(TV_H_CTL_3, hctl3);
+ I915_WRITE(TV_V_CTL_1, vctl1);
+ I915_WRITE(TV_V_CTL_2, vctl2);
+ I915_WRITE(TV_V_CTL_3, vctl3);
+ I915_WRITE(TV_V_CTL_4, vctl4);
+ I915_WRITE(TV_V_CTL_5, vctl5);
+ I915_WRITE(TV_V_CTL_6, vctl6);
+ I915_WRITE(TV_V_CTL_7, vctl7);
+}
+
+static void set_color_conversion(struct drm_i915_private *dev_priv,
+ const struct color_conversion *color_conversion)
+{
+ if (!color_conversion)
+ return;
+
+ I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
+ color_conversion->gy);
+ I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
+ color_conversion->ay);
+ I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
+ color_conversion->gu);
+ I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
+ color_conversion->au);
+ I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
+ color_conversion->gv);
+ I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
+ color_conversion->av);
+}
+
+static void intel_tv_pre_enable(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -942,14 +1021,13 @@ static void intel_tv_mode_set(struct intel_encoder *encoder)
struct intel_tv *intel_tv = enc_to_tv(encoder);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
u32 tv_ctl;
- u32 hctl1, hctl2, hctl3;
- u32 vctl1, vctl2, vctl3, vctl4, vctl5, vctl6, vctl7;
u32 scctl1, scctl2, scctl3;
int i, j;
const struct video_levels *video_levels;
const struct color_conversion *color_conversion;
bool burst_ena;
- int pipe = intel_crtc->pipe;
+ int xpos = 0x0, ypos = 0x0;
+ unsigned int xsize, ysize;
if (!tv_mode)
return; /* can't happen (mode_prepare prevents this) */
@@ -982,44 +1060,6 @@ static void intel_tv_mode_set(struct intel_encoder *encoder)
burst_ena = tv_mode->burst_ena;
break;
}
- hctl1 = (tv_mode->hsync_end << TV_HSYNC_END_SHIFT) |
- (tv_mode->htotal << TV_HTOTAL_SHIFT);
-
- hctl2 = (tv_mode->hburst_start << 16) |
- (tv_mode->hburst_len << TV_HBURST_LEN_SHIFT);
-
- if (burst_ena)
- hctl2 |= TV_BURST_ENA;
-
- hctl3 = (tv_mode->hblank_start << TV_HBLANK_START_SHIFT) |
- (tv_mode->hblank_end << TV_HBLANK_END_SHIFT);
-
- vctl1 = (tv_mode->nbr_end << TV_NBR_END_SHIFT) |
- (tv_mode->vi_end_f1 << TV_VI_END_F1_SHIFT) |
- (tv_mode->vi_end_f2 << TV_VI_END_F2_SHIFT);
-
- vctl2 = (tv_mode->vsync_len << TV_VSYNC_LEN_SHIFT) |
- (tv_mode->vsync_start_f1 << TV_VSYNC_START_F1_SHIFT) |
- (tv_mode->vsync_start_f2 << TV_VSYNC_START_F2_SHIFT);
-
- vctl3 = (tv_mode->veq_len << TV_VEQ_LEN_SHIFT) |
- (tv_mode->veq_start_f1 << TV_VEQ_START_F1_SHIFT) |
- (tv_mode->veq_start_f2 << TV_VEQ_START_F2_SHIFT);
-
- if (tv_mode->veq_ena)
- vctl3 |= TV_EQUAL_ENA;
-
- vctl4 = (tv_mode->vburst_start_f1 << TV_VBURST_START_F1_SHIFT) |
- (tv_mode->vburst_end_f1 << TV_VBURST_END_F1_SHIFT);
-
- vctl5 = (tv_mode->vburst_start_f2 << TV_VBURST_START_F2_SHIFT) |
- (tv_mode->vburst_end_f2 << TV_VBURST_END_F2_SHIFT);
-
- vctl6 = (tv_mode->vburst_start_f3 << TV_VBURST_START_F3_SHIFT) |
- (tv_mode->vburst_end_f3 << TV_VBURST_END_F3_SHIFT);
-
- vctl7 = (tv_mode->vburst_start_f4 << TV_VBURST_START_F4_SHIFT) |
- (tv_mode->vburst_end_f4 << TV_VBURST_END_F4_SHIFT);
if (intel_crtc->pipe == 1)
tv_ctl |= TV_ENC_PIPEB_SELECT;
@@ -1051,37 +1091,16 @@ static void intel_tv_mode_set(struct intel_encoder *encoder)
tv_mode->dda3_inc << TV_SCDDA3_INC_SHIFT;
/* Enable two fixes for the chips that need them. */
- if (dev->pdev->device < 0x2772)
+ if (IS_I915GM(dev))
tv_ctl |= TV_ENC_C0_FIX | TV_ENC_SDP_FIX;
- I915_WRITE(TV_H_CTL_1, hctl1);
- I915_WRITE(TV_H_CTL_2, hctl2);
- I915_WRITE(TV_H_CTL_3, hctl3);
- I915_WRITE(TV_V_CTL_1, vctl1);
- I915_WRITE(TV_V_CTL_2, vctl2);
- I915_WRITE(TV_V_CTL_3, vctl3);
- I915_WRITE(TV_V_CTL_4, vctl4);
- I915_WRITE(TV_V_CTL_5, vctl5);
- I915_WRITE(TV_V_CTL_6, vctl6);
- I915_WRITE(TV_V_CTL_7, vctl7);
+ set_tv_mode_timings(dev_priv, tv_mode, burst_ena);
+
I915_WRITE(TV_SC_CTL_1, scctl1);
I915_WRITE(TV_SC_CTL_2, scctl2);
I915_WRITE(TV_SC_CTL_3, scctl3);
- if (color_conversion) {
- I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
- color_conversion->gy);
- I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
- color_conversion->ay);
- I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
- color_conversion->gu);
- I915_WRITE(TV_CSC_U2, (color_conversion->bu << 16) |
- color_conversion->au);
- I915_WRITE(TV_CSC_V, (color_conversion->rv << 16) |
- color_conversion->gv);
- I915_WRITE(TV_CSC_V2, (color_conversion->bv << 16) |
- color_conversion->av);
- }
+ set_color_conversion(dev_priv, color_conversion);
if (INTEL_INFO(dev)->gen >= 4)
I915_WRITE(TV_CLR_KNOBS, 0x00404000);
@@ -1092,46 +1111,25 @@ static void intel_tv_mode_set(struct intel_encoder *encoder)
I915_WRITE(TV_CLR_LEVEL,
((video_levels->black << TV_BLACK_LEVEL_SHIFT) |
(video_levels->blank << TV_BLANK_LEVEL_SHIFT)));
- {
- int pipeconf_reg = PIPECONF(pipe);
- int dspcntr_reg = DSPCNTR(intel_crtc->plane);
- int pipeconf = I915_READ(pipeconf_reg);
- int dspcntr = I915_READ(dspcntr_reg);
- int xpos = 0x0, ypos = 0x0;
- unsigned int xsize, ysize;
- /* Pipe must be off here */
- I915_WRITE(dspcntr_reg, dspcntr & ~DISPLAY_PLANE_ENABLE);
- intel_flush_primary_plane(dev_priv, intel_crtc->plane);
-
- /* Wait for vblank for the disable to take effect */
- if (IS_GEN2(dev))
- intel_wait_for_vblank(dev, intel_crtc->pipe);
-
- I915_WRITE(pipeconf_reg, pipeconf & ~PIPECONF_ENABLE);
- /* Wait for vblank for the disable to take effect. */
- intel_wait_for_pipe_off(dev, intel_crtc->pipe);
-
- /* Filter ctl must be set before TV_WIN_SIZE */
- I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
- xsize = tv_mode->hblank_start - tv_mode->hblank_end;
- if (tv_mode->progressive)
- ysize = tv_mode->nbr_end + 1;
- else
- ysize = 2*tv_mode->nbr_end + 1;
-
- xpos += intel_tv->margin[TV_MARGIN_LEFT];
- ypos += intel_tv->margin[TV_MARGIN_TOP];
- xsize -= (intel_tv->margin[TV_MARGIN_LEFT] +
- intel_tv->margin[TV_MARGIN_RIGHT]);
- ysize -= (intel_tv->margin[TV_MARGIN_TOP] +
- intel_tv->margin[TV_MARGIN_BOTTOM]);
- I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
- I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
-
- I915_WRITE(pipeconf_reg, pipeconf);
- I915_WRITE(dspcntr_reg, dspcntr);
- intel_flush_primary_plane(dev_priv, intel_crtc->plane);
- }
+
+ assert_pipe_disabled(dev_priv, intel_crtc->pipe);
+
+ /* Filter ctl must be set before TV_WIN_SIZE */
+ I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE);
+ xsize = tv_mode->hblank_start - tv_mode->hblank_end;
+ if (tv_mode->progressive)
+ ysize = tv_mode->nbr_end + 1;
+ else
+ ysize = 2*tv_mode->nbr_end + 1;
+
+ xpos += intel_tv->margin[TV_MARGIN_LEFT];
+ ypos += intel_tv->margin[TV_MARGIN_TOP];
+ xsize -= (intel_tv->margin[TV_MARGIN_LEFT] +
+ intel_tv->margin[TV_MARGIN_RIGHT]);
+ ysize -= (intel_tv->margin[TV_MARGIN_TOP] +
+ intel_tv->margin[TV_MARGIN_BOTTOM]);
+ I915_WRITE(TV_WIN_POS, (xpos<<16)|ypos);
+ I915_WRITE(TV_WIN_SIZE, (xsize<<16)|ysize);
j = 0;
for (i = 0; i < 60; i++)
@@ -1316,17 +1314,18 @@ intel_tv_detect(struct drm_connector *connector, bool force)
int type;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n",
- connector->base.id, drm_get_connector_name(connector),
+ connector->base.id, connector->name,
force);
mode = reported_modes[0];
if (force) {
struct intel_load_detect_pipe tmp;
+ struct drm_modeset_acquire_ctx ctx;
- if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
+ 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);
+ intel_release_load_detect_pipe(connector, &tmp, &ctx);
} else
return connector_status_unknown;
} else
@@ -1634,7 +1633,7 @@ intel_tv_init(struct drm_device *dev)
intel_encoder->compute_config = intel_tv_compute_config;
intel_encoder->get_config = intel_tv_get_config;
- intel_encoder->mode_set = intel_tv_mode_set;
+ intel_encoder->pre_enable = intel_tv_pre_enable;
intel_encoder->enable = intel_enable_tv;
intel_encoder->disable = intel_disable_tv;
intel_encoder->get_hw_state = intel_tv_get_hw_state;
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index d0c75779d3f6..79cba593df0d 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -255,8 +255,7 @@ static void __vlv_force_wake_put(struct drm_i915_private *dev_priv,
}
-void vlv_force_wake_get(struct drm_i915_private *dev_priv,
- int fw_engine)
+static void vlv_force_wake_get(struct drm_i915_private *dev_priv, int fw_engine)
{
unsigned long irqflags;
@@ -275,8 +274,7 @@ void vlv_force_wake_get(struct drm_i915_private *dev_priv,
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
}
-void vlv_force_wake_put(struct drm_i915_private *dev_priv,
- int fw_engine)
+static void vlv_force_wake_put(struct drm_i915_private *dev_priv, int fw_engine)
{
unsigned long irqflags;
@@ -374,7 +372,7 @@ void intel_uncore_early_sanitize(struct drm_device *dev)
if (HAS_FPGA_DBG_UNCLAIMED(dev))
__raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
- if (IS_HASWELL(dev) &&
+ if ((IS_HASWELL(dev) || IS_BROADWELL(dev)) &&
(__raw_i915_read32(dev_priv, HSW_EDRAM_PRESENT) == 1)) {
/* The docs do not explain exactly how the calculation can be
* made. It is somewhat guessable, but for now, it's always
@@ -395,26 +393,8 @@ void intel_uncore_early_sanitize(struct drm_device *dev)
void intel_uncore_sanitize(struct drm_device *dev)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
- u32 reg_val;
-
/* BIOS often leaves RC6 enabled, but disable it for hw init */
intel_disable_gt_powersave(dev);
-
- /* Turn off power gate, require especially for the BIOS less system */
- if (IS_VALLEYVIEW(dev)) {
-
- mutex_lock(&dev_priv->rps.hw_lock);
- reg_val = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS);
-
- if (reg_val & (PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_RENDER) |
- PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_MEDIA) |
- PUNIT_PWRGT_PWR_GATE(PUNIT_POWER_WELL_DISP2D)))
- vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL, 0x0);
-
- mutex_unlock(&dev_priv->rps.hw_lock);
-
- }
}
/*
@@ -488,6 +468,17 @@ 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 FORCEWAKE_VLV_MEDIA_RANGE_OFFSET(reg)\
+ (((reg) >= 0x12000 && (reg) < 0x14000) ||\
+ ((reg) >= 0x22000 && (reg) < 0x24000) ||\
+ ((reg) >= 0x30000 && (reg) < 0x40000))
+
static void
ilk_dummy_write(struct drm_i915_private *dev_priv)
{
@@ -854,12 +845,15 @@ void intel_uncore_fini(struct drm_device *dev)
intel_uncore_forcewake_reset(dev, false);
}
+#define GEN_RANGE(l, h) GENMASK(h, l)
+
static const struct register_whitelist {
uint64_t offset;
uint32_t size;
- uint32_t gen_bitmask; /* support gens, 0x10 for 4, 0x30 for 4 and 5, etc. */
+ /* supported gens, 0x10 for 4, 0x30 for 4 and 5, etc. */
+ uint32_t gen_bitmask;
} whitelist[] = {
- { RING_TIMESTAMP(RENDER_RING_BASE), 8, 0x1F0 },
+ { RING_TIMESTAMP(RENDER_RING_BASE), 8, GEN_RANGE(4, 8) },
};
int i915_reg_read_ioctl(struct drm_device *dev,
@@ -911,7 +905,7 @@ int i915_get_reset_stats_ioctl(struct drm_device *dev,
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_reset_stats *args = data;
struct i915_ctx_hang_stats *hs;
- struct i915_hw_context *ctx;
+ struct intel_context *ctx;
int ret;
if (args->flags || args->pad)
@@ -955,6 +949,9 @@ static int i965_do_reset(struct drm_device *dev)
{
int ret;
+ /* FIXME: i965g/gm need a display save/restore for gpu reset. */
+ return -ENODEV;
+
/*
* Set the domains we want to reset (GRDOM/bits 2 and 3) as
* well as the reset bit (GR/bit 0). Setting the GR bit
@@ -966,7 +963,6 @@ static int i965_do_reset(struct drm_device *dev)
if (ret)
return ret;
- /* We can't reset render&media without also resetting display ... */
pci_write_config_byte(dev->pdev, I965_GDRST,
GRDOM_MEDIA | GRDOM_RESET_ENABLE);
@@ -979,26 +975,58 @@ static int i965_do_reset(struct drm_device *dev)
return 0;
}
+static int g4x_do_reset(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret;
+
+ pci_write_config_byte(dev->pdev, I965_GDRST,
+ GRDOM_RENDER | GRDOM_RESET_ENABLE);
+ ret = wait_for(i965_reset_complete(dev), 500);
+ if (ret)
+ return ret;
+
+ /* WaVcpClkGateDisableForMediaReset:ctg,elk */
+ I915_WRITE(VDECCLK_GATE_D, I915_READ(VDECCLK_GATE_D) | VCP_UNIT_CLOCK_GATE_DISABLE);
+ POSTING_READ(VDECCLK_GATE_D);
+
+ pci_write_config_byte(dev->pdev, I965_GDRST,
+ GRDOM_MEDIA | GRDOM_RESET_ENABLE);
+ ret = wait_for(i965_reset_complete(dev), 500);
+ if (ret)
+ return ret;
+
+ /* WaVcpClkGateDisableForMediaReset:ctg,elk */
+ I915_WRITE(VDECCLK_GATE_D, I915_READ(VDECCLK_GATE_D) & ~VCP_UNIT_CLOCK_GATE_DISABLE);
+ POSTING_READ(VDECCLK_GATE_D);
+
+ pci_write_config_byte(dev->pdev, I965_GDRST, 0);
+
+ return 0;
+}
+
static int ironlake_do_reset(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- u32 gdrst;
int ret;
- gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR);
- gdrst &= ~GRDOM_MASK;
I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR,
- gdrst | GRDOM_RENDER | GRDOM_RESET_ENABLE);
- ret = wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500);
+ ILK_GRDOM_RENDER | ILK_GRDOM_RESET_ENABLE);
+ ret = wait_for((I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) &
+ ILK_GRDOM_RESET_ENABLE) == 0, 500);
if (ret)
return ret;
- /* We can't reset render&media without also resetting display ... */
- gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR);
- gdrst &= ~GRDOM_MASK;
I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR,
- gdrst | GRDOM_MEDIA | GRDOM_RESET_ENABLE);
- return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500);
+ ILK_GRDOM_MEDIA | ILK_GRDOM_RESET_ENABLE);
+ ret = wait_for((I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) &
+ ILK_GRDOM_RESET_ENABLE) == 0, 500);
+ if (ret)
+ return ret;
+
+ I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, 0);
+
+ return 0;
}
static int gen6_do_reset(struct drm_device *dev)
@@ -1029,7 +1057,11 @@ int intel_gpu_reset(struct drm_device *dev)
case 7:
case 6: return gen6_do_reset(dev);
case 5: return ironlake_do_reset(dev);
- case 4: return i965_do_reset(dev);
+ case 4:
+ if (IS_G4X(dev))
+ return g4x_do_reset(dev);
+ else
+ return i965_do_reset(dev);
default: return -ENODEV;
}
}
diff --git a/drivers/gpu/drm/mga/mga_ioc32.c b/drivers/gpu/drm/mga/mga_ioc32.c
index 86b4bb804852..729bfd56b55f 100644
--- a/drivers/gpu/drm/mga/mga_ioc32.c
+++ b/drivers/gpu/drm/mga/mga_ioc32.c
@@ -214,7 +214,7 @@ long mga_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (nr < DRM_COMMAND_BASE)
return drm_compat_ioctl(filp, cmd, arg);
- if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls))
+ if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(mga_compat_ioctls))
fn = mga_compat_ioctls[nr - DRM_COMMAND_BASE];
if (fn != NULL)
diff --git a/drivers/gpu/drm/mga/mga_state.c b/drivers/gpu/drm/mga/mga_state.c
index 314685b7f41f..792f924496fc 100644
--- a/drivers/gpu/drm/mga/mga_state.c
+++ b/drivers/gpu/drm/mga/mga_state.c
@@ -1020,7 +1020,7 @@ static int mga_getparam(struct drm_device *dev, void *data, struct drm_file *fil
switch (param->param) {
case MGA_PARAM_IRQ_NR:
- value = drm_dev_to_irq(dev);
+ value = dev->pdev->irq;
break;
case MGA_PARAM_CARD_TYPE:
value = dev_priv->chipset;
@@ -1099,4 +1099,4 @@ const struct drm_ioctl_desc mga_ioctls[] = {
DRM_IOCTL_DEF_DRV(MGA_DMA_BOOTSTRAP, mga_dma_bootstrap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
};
-int mga_max_ioctl = DRM_ARRAY_SIZE(mga_ioctls);
+int mga_max_ioctl = ARRAY_SIZE(mga_ioctls);
diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c
index 26868e5c55b0..f6b283b8375e 100644
--- a/drivers/gpu/drm/mgag200/mgag200_main.c
+++ b/drivers/gpu/drm/mgag200/mgag200_main.c
@@ -322,17 +322,13 @@ static void mgag200_bo_unref(struct mgag200_bo **bo)
tbo = &((*bo)->bo);
ttm_bo_unref(&tbo);
- if (tbo == NULL)
- *bo = NULL;
-
+ *bo = NULL;
}
void mgag200_gem_free_object(struct drm_gem_object *obj)
{
struct mgag200_bo *mgag200_bo = gem_to_mga_bo(obj);
- if (!mgag200_bo)
- return;
mgag200_bo_unref(&mgag200_bo);
}
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index b6984971ce0c..f12388967856 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -3,7 +3,7 @@ config DRM_MSM
tristate "MSM DRM"
depends on DRM
depends on MSM_IOMMU
- depends on ARCH_MSM8960 || (ARM && COMPILE_TEST)
+ depends on ARCH_QCOM || (ARM && COMPILE_TEST)
select DRM_KMS_HELPER
select SHMEM
select TMPFS
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 5e1e6b0cd8ac..93ca49c8df44 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -34,6 +34,8 @@ msm-y := \
msm_gem_submit.o \
msm_gpu.o \
msm_iommu.o \
+ msm_perf.o \
+ msm_rd.o \
msm_ringbuffer.o
msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
index f20fbde5dc49..942e09d898a8 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
@@ -207,11 +207,11 @@ static int a3xx_hw_init(struct msm_gpu *gpu)
/* Turn on performance counters: */
gpu_write(gpu, REG_A3XX_RBBM_PERFCTR_CTL, 0x01);
- /* Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS
- * we will use this to augment our hang detection:
- */
- gpu_write(gpu, REG_A3XX_SP_PERFCOUNTER7_SELECT,
- SP_FS_FULL_ALU_INSTRUCTIONS);
+ /* Enable the perfcntrs that we use.. */
+ for (i = 0; i < gpu->num_perfcntrs; i++) {
+ const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i];
+ gpu_write(gpu, perfcntr->select_reg, perfcntr->select_val);
+ }
gpu_write(gpu, REG_A3XX_RBBM_INT_0_MASK, A3XX_INT0_MASK);
@@ -465,6 +465,13 @@ static const struct adreno_gpu_funcs funcs = {
},
};
+static const struct msm_gpu_perfcntr perfcntrs[] = {
+ { REG_A3XX_SP_PERFCOUNTER6_SELECT, REG_A3XX_RBBM_PERFCTR_SP_6_LO,
+ SP_ALU_ACTIVE_CYCLES, "ALUACTIVE" },
+ { REG_A3XX_SP_PERFCOUNTER7_SELECT, REG_A3XX_RBBM_PERFCTR_SP_7_LO,
+ SP_FS_FULL_ALU_INSTRUCTIONS, "ALUFULL" },
+};
+
struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
{
struct a3xx_gpu *a3xx_gpu = NULL;
@@ -504,6 +511,9 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
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);
if (ret)
goto fail;
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
index 7dedfdd12075..e56a6196867c 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
@@ -247,36 +247,49 @@ void hdmi_connector_irq(struct drm_connector *connector)
}
}
+static enum drm_connector_status detect_reg(struct hdmi *hdmi)
+{
+ uint32_t hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
+ return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
+ connector_status_connected : connector_status_disconnected;
+}
+
+static enum drm_connector_status detect_gpio(struct hdmi *hdmi)
+{
+ const struct hdmi_platform_config *config = hdmi->config;
+ return gpio_get_value(config->hpd_gpio) ?
+ connector_status_connected :
+ connector_status_disconnected;
+}
+
static enum drm_connector_status hdmi_connector_detect(
struct drm_connector *connector, bool force)
{
struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
struct hdmi *hdmi = hdmi_connector->hdmi;
- const struct hdmi_platform_config *config = hdmi->config;
- uint32_t hpd_int_status;
+ enum drm_connector_status stat_gpio, stat_reg;
int retry = 20;
- hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
+ do {
+ stat_gpio = detect_gpio(hdmi);
+ stat_reg = detect_reg(hdmi);
- /* sense seems to in some cases be momentarily de-asserted, don't
- * let that trick us into thinking the monitor is gone:
- */
- while (retry-- && !(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED)) {
- /* hdmi debounce logic seems to get stuck sometimes,
- * read directly the gpio to get a second opinion:
- */
- if (gpio_get_value(config->hpd_gpio)) {
- DBG("gpio tells us we are connected!");
- hpd_int_status |= HDMI_HPD_INT_STATUS_CABLE_DETECTED;
+ if (stat_gpio == stat_reg)
break;
- }
+
mdelay(10);
- hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
- DBG("status=%08x", hpd_int_status);
+ } while (--retry);
+
+ /* the status we get from reading gpio seems to be more reliable,
+ * so trust that one the most if we didn't manage to get hdmi and
+ * gpio status to agree:
+ */
+ if (stat_gpio != stat_reg) {
+ DBG("HDMI_HPD_INT_STATUS tells us: %d", stat_reg);
+ DBG("hpd gpio tells us: %d", stat_gpio);
}
- return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
- connector_status_connected : connector_status_disconnected;
+ return stat_gpio;
}
static void hdmi_connector_destroy(struct drm_connector *connector)
@@ -389,7 +402,8 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)
DRM_MODE_CONNECTOR_HDMIA);
drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
- connector->polled = DRM_CONNECTOR_POLL_HPD;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
connector->interlace_allowed = 1;
connector->doublescan_allowed = 0;
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index ef9957dbac94..74cebb51e8c2 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -217,8 +217,6 @@ static void mdp4_crtc_destroy(struct drm_crtc *crtc)
{
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
- mdp4_crtc->plane->funcs->destroy(mdp4_crtc->plane);
-
drm_crtc_cleanup(crtc);
drm_flip_work_cleanup(&mdp4_crtc->unref_fb_work);
drm_flip_work_cleanup(&mdp4_crtc->unref_cursor_work);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index 6ea10bdb6e8f..ebe2e60f3ab1 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -195,8 +195,6 @@ static void mdp5_crtc_destroy(struct drm_crtc *crtc)
{
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
- mdp5_crtc->plane->funcs->destroy(mdp5_crtc->plane);
-
drm_crtc_cleanup(crtc);
drm_flip_work_cleanup(&mdp5_crtc->unref_fb_work);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index ee8446c1b5f6..42caf7fcb0b9 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -280,12 +280,22 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
goto fail;
}
- ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk") ||
- get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk") ||
- get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src") ||
- get_clk(pdev, &mdp5_kms->core_clk, "core_clk") ||
- get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk") ||
- get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk");
+ ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk");
+ if (ret)
+ goto fail;
+ ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk");
+ if (ret)
+ goto fail;
+ ret = get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src");
+ if (ret)
+ goto fail;
+ ret = get_clk(pdev, &mdp5_kms->core_clk, "core_clk");
+ if (ret)
+ goto fail;
+ ret = get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk");
+ if (ret)
+ goto fail;
+ ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk");
if (ret)
goto fail;
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index 47f7bbb9c15a..f3daec4412ad 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -85,8 +85,11 @@ static int mdp5_plane_disable(struct drm_plane *plane)
static void mdp5_plane_destroy(struct drm_plane *plane)
{
struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
+ struct msm_drm_private *priv = plane->dev->dev_private;
+
+ if (priv->kms)
+ mdp5_plane_disable(plane);
- mdp5_plane_disable(plane);
drm_plane_cleanup(plane);
kfree(mdp5_plane);
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index f9de156b9e65..0d2562fb681e 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -220,7 +220,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
* is bogus, but non-null if allocation succeeded:
*/
p = dma_alloc_attrs(dev->dev, size,
- &priv->vram.paddr, 0, &attrs);
+ &priv->vram.paddr, GFP_KERNEL, &attrs);
if (!p) {
dev_err(dev->dev, "failed to allocate VRAM\n");
priv->vram.paddr = 0;
@@ -288,7 +288,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
}
pm_runtime_get_sync(dev->dev);
- ret = drm_irq_install(dev);
+ ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
pm_runtime_put_sync(dev->dev);
if (ret < 0) {
dev_err(dev->dev, "failed to install IRQ handler\n");
@@ -299,6 +299,10 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
priv->fbdev = msm_fbdev_init(dev);
#endif
+ ret = msm_debugfs_late_init(dev);
+ if (ret)
+ goto fail;
+
drm_kms_helper_poll_init(dev);
return 0;
@@ -382,11 +386,8 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file)
static void msm_lastclose(struct drm_device *dev)
{
struct msm_drm_private *priv = dev->dev_private;
- if (priv->fbdev) {
- drm_modeset_lock_all(dev);
- drm_fb_helper_restore_fbdev_mode(priv->fbdev);
- drm_modeset_unlock_all(dev);
- }
+ if (priv->fbdev)
+ drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
}
static irqreturn_t msm_irq(int irq, void *arg)
@@ -531,6 +532,41 @@ static struct drm_info_list msm_debugfs_list[] = {
{ "fb", show_locked, 0, msm_fb_show },
};
+static int late_init_minor(struct drm_minor *minor)
+{
+ int ret;
+
+ if (!minor)
+ return 0;
+
+ ret = msm_rd_debugfs_init(minor);
+ if (ret) {
+ dev_err(minor->dev->dev, "could not install rd debugfs\n");
+ return ret;
+ }
+
+ ret = msm_perf_debugfs_init(minor);
+ if (ret) {
+ dev_err(minor->dev->dev, "could not install perf debugfs\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int msm_debugfs_late_init(struct drm_device *dev)
+{
+ int ret;
+ ret = late_init_minor(dev->primary);
+ if (ret)
+ return ret;
+ ret = late_init_minor(dev->render);
+ if (ret)
+ return ret;
+ ret = late_init_minor(dev->control);
+ return ret;
+}
+
static int msm_debugfs_init(struct drm_minor *minor)
{
struct drm_device *dev = minor->dev;
@@ -545,13 +581,17 @@ static int msm_debugfs_init(struct drm_minor *minor)
return ret;
}
- return ret;
+ return 0;
}
static void msm_debugfs_cleanup(struct drm_minor *minor)
{
drm_debugfs_remove_files(msm_debugfs_list,
ARRAY_SIZE(msm_debugfs_list), minor);
+ if (!minor->dev->dev_private)
+ return;
+ msm_rd_debugfs_cleanup(minor);
+ msm_perf_debugfs_cleanup(minor);
}
#endif
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 9d10ee0b5aac..8a2c5fd0893e 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -33,7 +33,7 @@
#include <asm/sizes.h>
-#if defined(CONFIG_COMPILE_TEST) && !defined(CONFIG_ARCH_MSM)
+#if defined(CONFIG_COMPILE_TEST) && !defined(CONFIG_ARCH_QCOM)
/* stubs we need for compile-test: */
static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
{
@@ -55,6 +55,9 @@ static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
struct msm_kms;
struct msm_gpu;
struct msm_mmu;
+struct msm_rd_state;
+struct msm_perf_state;
+struct msm_gem_submit;
#define NUM_DOMAINS 2 /* one for KMS, then one per gpu core (?) */
@@ -82,6 +85,9 @@ struct msm_drm_private {
uint32_t next_fence, completed_fence;
wait_queue_head_t fence_event;
+ struct msm_rd_state *rd;
+ struct msm_perf_state *perf;
+
/* list of GEM objects: */
struct list_head inactive_list;
@@ -204,6 +210,15 @@ void __exit hdmi_unregister(void);
void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
void msm_gem_describe_objects(struct list_head *list, struct seq_file *m);
void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m);
+int msm_debugfs_late_init(struct drm_device *dev);
+int msm_rd_debugfs_init(struct drm_minor *minor);
+void msm_rd_debugfs_cleanup(struct drm_minor *minor);
+void msm_rd_dump_submit(struct msm_gem_submit *submit);
+int msm_perf_debugfs_init(struct drm_minor *minor);
+void msm_perf_debugfs_cleanup(struct drm_minor *minor);
+#else
+static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; }
+static inline void msm_rd_dump_submit(struct msm_gem_submit *submit) {}
#endif
void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index 3246bb46c4f2..bfb052688f8e 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -90,6 +90,7 @@ struct msm_gem_submit {
uint32_t type;
uint32_t size; /* in dwords */
uint32_t iova;
+ uint32_t idx; /* cmdstream buffer idx in bos[] */
} cmd[MAX_CMDS];
struct {
uint32_t flags;
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index 1f1f4cffdaed..cd0554f68316 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -402,6 +402,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
submit->cmd[i].type = submit_cmd.type;
submit->cmd[i].size = submit_cmd.size / 4;
submit->cmd[i].iova = iova + submit_cmd.submit_offset;
+ submit->cmd[i].idx = submit_cmd.submit_idx;
if (submit->valid)
continue;
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 3e667ca1f2b9..c6322197db8c 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -320,6 +320,101 @@ static void hangcheck_handler(unsigned long data)
}
/*
+ * Performance Counters:
+ */
+
+/* called under perf_lock */
+static int update_hw_cntrs(struct msm_gpu *gpu, uint32_t ncntrs, uint32_t *cntrs)
+{
+ uint32_t current_cntrs[ARRAY_SIZE(gpu->last_cntrs)];
+ int i, n = min(ncntrs, gpu->num_perfcntrs);
+
+ /* read current values: */
+ for (i = 0; i < gpu->num_perfcntrs; i++)
+ current_cntrs[i] = gpu_read(gpu, gpu->perfcntrs[i].sample_reg);
+
+ /* update cntrs: */
+ for (i = 0; i < n; i++)
+ cntrs[i] = current_cntrs[i] - gpu->last_cntrs[i];
+
+ /* save current values: */
+ for (i = 0; i < gpu->num_perfcntrs; i++)
+ gpu->last_cntrs[i] = current_cntrs[i];
+
+ return n;
+}
+
+static void update_sw_cntrs(struct msm_gpu *gpu)
+{
+ ktime_t time;
+ uint32_t elapsed;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpu->perf_lock, flags);
+ if (!gpu->perfcntr_active)
+ goto out;
+
+ time = ktime_get();
+ elapsed = ktime_to_us(ktime_sub(time, gpu->last_sample.time));
+
+ gpu->totaltime += elapsed;
+ if (gpu->last_sample.active)
+ gpu->activetime += elapsed;
+
+ gpu->last_sample.active = msm_gpu_active(gpu);
+ gpu->last_sample.time = time;
+
+out:
+ spin_unlock_irqrestore(&gpu->perf_lock, flags);
+}
+
+void msm_gpu_perfcntr_start(struct msm_gpu *gpu)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpu->perf_lock, flags);
+ /* we could dynamically enable/disable perfcntr registers too.. */
+ gpu->last_sample.active = msm_gpu_active(gpu);
+ gpu->last_sample.time = ktime_get();
+ gpu->activetime = gpu->totaltime = 0;
+ gpu->perfcntr_active = true;
+ update_hw_cntrs(gpu, 0, NULL);
+ spin_unlock_irqrestore(&gpu->perf_lock, flags);
+}
+
+void msm_gpu_perfcntr_stop(struct msm_gpu *gpu)
+{
+ gpu->perfcntr_active = false;
+}
+
+/* returns -errno or # of cntrs sampled */
+int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
+ uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&gpu->perf_lock, flags);
+
+ if (!gpu->perfcntr_active) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ *activetime = gpu->activetime;
+ *totaltime = gpu->totaltime;
+
+ gpu->activetime = gpu->totaltime = 0;
+
+ ret = update_hw_cntrs(gpu, ncntrs, cntrs);
+
+out:
+ spin_unlock_irqrestore(&gpu->perf_lock, flags);
+
+ return ret;
+}
+
+/*
* Cmdstream submission/retirement:
*/
@@ -361,6 +456,7 @@ void msm_gpu_retire(struct msm_gpu *gpu)
{
struct msm_drm_private *priv = gpu->dev->dev_private;
queue_work(priv->wq, &gpu->retire_work);
+ update_sw_cntrs(gpu);
}
/* add bo's to gpu's ring, and kick gpu: */
@@ -377,6 +473,12 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
inactive_cancel(gpu);
+ msm_rd_dump_submit(submit);
+
+ gpu->submitted_fence = submit->fence;
+
+ update_sw_cntrs(gpu);
+
ret = gpu->funcs->submit(gpu, submit, ctx);
priv->lastctx = ctx;
@@ -429,6 +531,9 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
struct iommu_domain *iommu;
int i, ret;
+ if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs)))
+ gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs);
+
gpu->dev = drm;
gpu->funcs = funcs;
gpu->name = name;
@@ -444,6 +549,8 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
(unsigned long)gpu);
+ spin_lock_init(&gpu->perf_lock);
+
BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(gpu->grp_clks));
/* Map registers: */
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index fad27008922f..9b579b792840 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -25,6 +25,7 @@
#include "msm_ringbuffer.h"
struct msm_gem_submit;
+struct msm_gpu_perfcntr;
/* So far, with hardware that I've seen to date, we can have:
* + zero, one, or two z180 2d cores
@@ -64,6 +65,18 @@ struct msm_gpu {
struct drm_device *dev;
const struct msm_gpu_funcs *funcs;
+ /* performance counters (hw & sw): */
+ spinlock_t perf_lock;
+ bool perfcntr_active;
+ struct {
+ bool active;
+ ktime_t time;
+ } last_sample;
+ uint32_t totaltime, activetime; /* sw counters */
+ uint32_t last_cntrs[5]; /* hw counters */
+ const struct msm_gpu_perfcntr *perfcntrs;
+ uint32_t num_perfcntrs;
+
struct msm_ringbuffer *rb;
uint32_t rb_iova;
@@ -113,6 +126,19 @@ static inline bool msm_gpu_active(struct msm_gpu *gpu)
return gpu->submitted_fence > gpu->funcs->last_fence(gpu);
}
+/* Perf-Counters:
+ * The select_reg and select_val are just there for the benefit of the child
+ * class that actually enables the perf counter.. but msm_gpu base class
+ * will handle sampling/displaying the counters.
+ */
+
+struct msm_gpu_perfcntr {
+ uint32_t select_reg;
+ uint32_t sample_reg;
+ uint32_t select_val;
+ const char *name;
+};
+
static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data)
{
msm_writel(data, gpu->mmio + (reg << 2));
@@ -126,6 +152,11 @@ static inline u32 gpu_read(struct msm_gpu *gpu, u32 reg)
int msm_gpu_pm_suspend(struct msm_gpu *gpu);
int msm_gpu_pm_resume(struct msm_gpu *gpu);
+void msm_gpu_perfcntr_start(struct msm_gpu *gpu);
+void msm_gpu_perfcntr_stop(struct msm_gpu *gpu);
+int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
+ uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs);
+
void msm_gpu_retire(struct msm_gpu *gpu);
int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
struct msm_file_private *ctx);
diff --git a/drivers/gpu/drm/msm/msm_perf.c b/drivers/gpu/drm/msm/msm_perf.c
new file mode 100644
index 000000000000..830857c47c86
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_perf.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2013 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/>.
+ */
+
+/* For profiling, userspace can:
+ *
+ * tail -f /sys/kernel/debug/dri/<minor>/gpu
+ *
+ * This will enable performance counters/profiling to track the busy time
+ * and any gpu specific performance counters that are supported.
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+
+#include "msm_drv.h"
+#include "msm_gpu.h"
+
+struct msm_perf_state {
+ struct drm_device *dev;
+
+ bool open;
+ int cnt;
+ struct mutex read_lock;
+
+ char buf[256];
+ int buftot, bufpos;
+
+ unsigned long next_jiffies;
+
+ struct dentry *ent;
+ struct drm_info_node *node;
+};
+
+#define SAMPLE_TIME (HZ/4)
+
+/* wait for next sample time: */
+static int wait_sample(struct msm_perf_state *perf)
+{
+ unsigned long start_jiffies = jiffies;
+
+ if (time_after(perf->next_jiffies, start_jiffies)) {
+ unsigned long remaining_jiffies =
+ perf->next_jiffies - start_jiffies;
+ int ret = schedule_timeout_interruptible(remaining_jiffies);
+ if (ret > 0) {
+ /* interrupted */
+ return -ERESTARTSYS;
+ }
+ }
+ perf->next_jiffies += SAMPLE_TIME;
+ return 0;
+}
+
+static int refill_buf(struct msm_perf_state *perf)
+{
+ struct msm_drm_private *priv = perf->dev->dev_private;
+ struct msm_gpu *gpu = priv->gpu;
+ char *ptr = perf->buf;
+ int rem = sizeof(perf->buf);
+ int i, n;
+
+ if ((perf->cnt++ % 32) == 0) {
+ /* Header line: */
+ n = snprintf(ptr, rem, "%%BUSY");
+ ptr += n;
+ rem -= n;
+
+ for (i = 0; i < gpu->num_perfcntrs; i++) {
+ const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i];
+ n = snprintf(ptr, rem, "\t%s", perfcntr->name);
+ ptr += n;
+ rem -= n;
+ }
+ } else {
+ /* Sample line: */
+ uint32_t activetime = 0, totaltime = 0;
+ uint32_t cntrs[5];
+ uint32_t val;
+ int ret;
+
+ /* sleep until next sample time: */
+ ret = wait_sample(perf);
+ if (ret)
+ return ret;
+
+ ret = msm_gpu_perfcntr_sample(gpu, &activetime, &totaltime,
+ ARRAY_SIZE(cntrs), cntrs);
+ if (ret < 0)
+ return ret;
+
+ val = totaltime ? 1000 * activetime / totaltime : 0;
+ n = snprintf(ptr, rem, "%3d.%d%%", val / 10, val % 10);
+ ptr += n;
+ rem -= n;
+
+ for (i = 0; i < ret; i++) {
+ /* cycle counters (I think).. convert to MHz.. */
+ val = cntrs[i] / 10000;
+ n = snprintf(ptr, rem, "\t%5d.%02d",
+ val / 100, val % 100);
+ ptr += n;
+ rem -= n;
+ }
+ }
+
+ n = snprintf(ptr, rem, "\n");
+ ptr += n;
+ rem -= n;
+
+ perf->bufpos = 0;
+ perf->buftot = ptr - perf->buf;
+
+ return 0;
+}
+
+static ssize_t perf_read(struct file *file, char __user *buf,
+ size_t sz, loff_t *ppos)
+{
+ struct msm_perf_state *perf = file->private_data;
+ int n = 0, ret;
+
+ mutex_lock(&perf->read_lock);
+
+ if (perf->bufpos >= perf->buftot) {
+ ret = refill_buf(perf);
+ if (ret)
+ goto out;
+ }
+
+ n = min((int)sz, perf->buftot - perf->bufpos);
+ ret = copy_to_user(buf, &perf->buf[perf->bufpos], n);
+ if (ret)
+ goto out;
+
+ perf->bufpos += n;
+ *ppos += n;
+
+out:
+ mutex_unlock(&perf->read_lock);
+ if (ret)
+ return ret;
+ return n;
+}
+
+static int perf_open(struct inode *inode, struct file *file)
+{
+ struct msm_perf_state *perf = inode->i_private;
+ struct drm_device *dev = perf->dev;
+ struct msm_drm_private *priv = dev->dev_private;
+ struct msm_gpu *gpu = priv->gpu;
+ int ret = 0;
+
+ mutex_lock(&dev->struct_mutex);
+
+ if (perf->open || !gpu) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ file->private_data = perf;
+ perf->open = true;
+ perf->cnt = 0;
+ perf->buftot = 0;
+ perf->bufpos = 0;
+ msm_gpu_perfcntr_start(gpu);
+ perf->next_jiffies = jiffies + SAMPLE_TIME;
+
+out:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+static int perf_release(struct inode *inode, struct file *file)
+{
+ struct msm_perf_state *perf = inode->i_private;
+ struct msm_drm_private *priv = perf->dev->dev_private;
+ msm_gpu_perfcntr_stop(priv->gpu);
+ perf->open = false;
+ return 0;
+}
+
+
+static const struct file_operations perf_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = perf_open,
+ .read = perf_read,
+ .llseek = no_llseek,
+ .release = perf_release,
+};
+
+int msm_perf_debugfs_init(struct drm_minor *minor)
+{
+ struct msm_drm_private *priv = minor->dev->dev_private;
+ struct msm_perf_state *perf;
+
+ /* only create on first minor: */
+ if (priv->perf)
+ return 0;
+
+ perf = kzalloc(sizeof(*perf), GFP_KERNEL);
+ if (!perf)
+ return -ENOMEM;
+
+ perf->dev = minor->dev;
+
+ mutex_init(&perf->read_lock);
+ priv->perf = perf;
+
+ perf->node = kzalloc(sizeof(*perf->node), GFP_KERNEL);
+ if (!perf->node)
+ goto fail;
+
+ perf->ent = debugfs_create_file("perf", S_IFREG | S_IRUGO,
+ minor->debugfs_root, perf, &perf_debugfs_fops);
+ if (!perf->ent) {
+ DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/perf\n",
+ minor->debugfs_root->d_name.name);
+ goto fail;
+ }
+
+ perf->node->minor = minor;
+ perf->node->dent = perf->ent;
+ perf->node->info_ent = NULL;
+
+ mutex_lock(&minor->debugfs_lock);
+ list_add(&perf->node->list, &minor->debugfs_list);
+ mutex_unlock(&minor->debugfs_lock);
+
+ return 0;
+
+fail:
+ msm_perf_debugfs_cleanup(minor);
+ return -1;
+}
+
+void msm_perf_debugfs_cleanup(struct drm_minor *minor)
+{
+ struct msm_drm_private *priv = minor->dev->dev_private;
+ struct msm_perf_state *perf = priv->perf;
+
+ if (!perf)
+ return;
+
+ priv->perf = NULL;
+
+ debugfs_remove(perf->ent);
+
+ if (perf->node) {
+ mutex_lock(&minor->debugfs_lock);
+ list_del(&perf->node->list);
+ mutex_unlock(&minor->debugfs_lock);
+ kfree(perf->node);
+ }
+
+ mutex_destroy(&perf->read_lock);
+
+ kfree(perf);
+}
+
+#endif
diff --git a/drivers/gpu/drm/msm/msm_rd.c b/drivers/gpu/drm/msm/msm_rd.c
new file mode 100644
index 000000000000..9a78c48817c6
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_rd.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2013 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/>.
+ */
+
+/* For debugging crashes, userspace can:
+ *
+ * tail -f /sys/kernel/debug/dri/<minor>/rd > logfile.rd
+ *
+ * To log the cmdstream in a format that is understood by freedreno/cffdump
+ * utility. By comparing the last successfully completed fence #, to the
+ * cmdstream for the next fence, you can narrow down which process and submit
+ * caused the gpu crash/lockup.
+ *
+ * This bypasses drm_debugfs_create_files() mainly because we need to use
+ * our own fops for a bit more control. In particular, we don't want to
+ * do anything if userspace doesn't have the debugfs file open.
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/kfifo.h>
+#include <linux/debugfs.h>
+#include <linux/circ_buf.h>
+#include <linux/wait.h>
+
+#include "msm_drv.h"
+#include "msm_gpu.h"
+#include "msm_gem.h"
+
+enum rd_sect_type {
+ RD_NONE,
+ RD_TEST, /* ascii text */
+ RD_CMD, /* ascii text */
+ RD_GPUADDR, /* u32 gpuaddr, u32 size */
+ RD_CONTEXT, /* raw dump */
+ RD_CMDSTREAM, /* raw dump */
+ RD_CMDSTREAM_ADDR, /* gpu addr of cmdstream */
+ RD_PARAM, /* u32 param_type, u32 param_val, u32 bitlen */
+ RD_FLUSH, /* empty, clear previous params */
+ RD_PROGRAM, /* shader program, raw dump */
+ RD_VERT_SHADER,
+ RD_FRAG_SHADER,
+ RD_BUFFER_CONTENTS,
+ RD_GPU_ID,
+};
+
+#define BUF_SZ 512 /* should be power of 2 */
+
+/* space used: */
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, BUF_SZ))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, BUF_SZ))
+/* space available: */
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, BUF_SZ))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, BUF_SZ))
+
+struct msm_rd_state {
+ struct drm_device *dev;
+
+ bool open;
+
+ struct dentry *ent;
+ struct drm_info_node *node;
+
+ /* current submit to read out: */
+ struct msm_gem_submit *submit;
+
+ /* fifo access is synchronized on the producer side by
+ * struct_mutex held by submit code (otherwise we could
+ * end up w/ cmds logged in different order than they
+ * were executed). And read_lock synchronizes the reads
+ */
+ struct mutex read_lock;
+
+ wait_queue_head_t fifo_event;
+ struct circ_buf fifo;
+
+ char buf[BUF_SZ];
+};
+
+static void rd_write(struct msm_rd_state *rd, const void *buf, int sz)
+{
+ struct circ_buf *fifo = &rd->fifo;
+ const char *ptr = buf;
+
+ while (sz > 0) {
+ char *fptr = &fifo->buf[fifo->head];
+ int n;
+
+ wait_event(rd->fifo_event, circ_space(&rd->fifo) > 0);
+
+ n = min(sz, circ_space_to_end(&rd->fifo));
+ memcpy(fptr, ptr, n);
+
+ fifo->head = (fifo->head + n) & (BUF_SZ - 1);
+ sz -= n;
+ ptr += n;
+
+ wake_up_all(&rd->fifo_event);
+ }
+}
+
+static void rd_write_section(struct msm_rd_state *rd,
+ enum rd_sect_type type, const void *buf, int sz)
+{
+ rd_write(rd, &type, 4);
+ rd_write(rd, &sz, 4);
+ rd_write(rd, buf, sz);
+}
+
+static ssize_t rd_read(struct file *file, char __user *buf,
+ size_t sz, loff_t *ppos)
+{
+ struct msm_rd_state *rd = file->private_data;
+ struct circ_buf *fifo = &rd->fifo;
+ const char *fptr = &fifo->buf[fifo->tail];
+ int n = 0, ret = 0;
+
+ mutex_lock(&rd->read_lock);
+
+ ret = wait_event_interruptible(rd->fifo_event,
+ circ_count(&rd->fifo) > 0);
+ if (ret)
+ goto out;
+
+ n = min_t(int, sz, circ_count_to_end(&rd->fifo));
+ ret = copy_to_user(buf, fptr, n);
+ if (ret)
+ goto out;
+
+ fifo->tail = (fifo->tail + n) & (BUF_SZ - 1);
+ *ppos += n;
+
+ wake_up_all(&rd->fifo_event);
+
+out:
+ mutex_unlock(&rd->read_lock);
+ if (ret)
+ return ret;
+ return n;
+}
+
+static int rd_open(struct inode *inode, struct file *file)
+{
+ struct msm_rd_state *rd = inode->i_private;
+ struct drm_device *dev = rd->dev;
+ struct msm_drm_private *priv = dev->dev_private;
+ struct msm_gpu *gpu = priv->gpu;
+ uint64_t val;
+ uint32_t gpu_id;
+ int ret = 0;
+
+ mutex_lock(&dev->struct_mutex);
+
+ if (rd->open || !gpu) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ file->private_data = rd;
+ rd->open = true;
+
+ /* the parsing tools need to know gpu-id to know which
+ * register database to load.
+ */
+ gpu->funcs->get_param(gpu, MSM_PARAM_GPU_ID, &val);
+ gpu_id = val;
+
+ rd_write_section(rd, RD_GPU_ID, &gpu_id, sizeof(gpu_id));
+
+out:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+static int rd_release(struct inode *inode, struct file *file)
+{
+ struct msm_rd_state *rd = inode->i_private;
+ rd->open = false;
+ return 0;
+}
+
+
+static const struct file_operations rd_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .open = rd_open,
+ .read = rd_read,
+ .llseek = no_llseek,
+ .release = rd_release,
+};
+
+int msm_rd_debugfs_init(struct drm_minor *minor)
+{
+ struct msm_drm_private *priv = minor->dev->dev_private;
+ struct msm_rd_state *rd;
+
+ /* only create on first minor: */
+ if (priv->rd)
+ return 0;
+
+ rd = kzalloc(sizeof(*rd), GFP_KERNEL);
+ if (!rd)
+ return -ENOMEM;
+
+ rd->dev = minor->dev;
+ rd->fifo.buf = rd->buf;
+
+ mutex_init(&rd->read_lock);
+ priv->rd = rd;
+
+ init_waitqueue_head(&rd->fifo_event);
+
+ rd->node = kzalloc(sizeof(*rd->node), GFP_KERNEL);
+ if (!rd->node)
+ goto fail;
+
+ rd->ent = debugfs_create_file("rd", S_IFREG | S_IRUGO,
+ minor->debugfs_root, rd, &rd_debugfs_fops);
+ if (!rd->ent) {
+ DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/rd\n",
+ minor->debugfs_root->d_name.name);
+ goto fail;
+ }
+
+ rd->node->minor = minor;
+ rd->node->dent = rd->ent;
+ rd->node->info_ent = NULL;
+
+ mutex_lock(&minor->debugfs_lock);
+ list_add(&rd->node->list, &minor->debugfs_list);
+ mutex_unlock(&minor->debugfs_lock);
+
+ return 0;
+
+fail:
+ msm_rd_debugfs_cleanup(minor);
+ return -1;
+}
+
+void msm_rd_debugfs_cleanup(struct drm_minor *minor)
+{
+ struct msm_drm_private *priv = minor->dev->dev_private;
+ struct msm_rd_state *rd = priv->rd;
+
+ if (!rd)
+ return;
+
+ priv->rd = NULL;
+
+ debugfs_remove(rd->ent);
+
+ if (rd->node) {
+ mutex_lock(&minor->debugfs_lock);
+ list_del(&rd->node->list);
+ mutex_unlock(&minor->debugfs_lock);
+ kfree(rd->node);
+ }
+
+ mutex_destroy(&rd->read_lock);
+
+ kfree(rd);
+}
+
+/* called under struct_mutex */
+void msm_rd_dump_submit(struct msm_gem_submit *submit)
+{
+ struct drm_device *dev = submit->dev;
+ struct msm_drm_private *priv = dev->dev_private;
+ struct msm_rd_state *rd = priv->rd;
+ char msg[128];
+ int i, n;
+
+ if (!rd->open)
+ return;
+
+ /* writing into fifo is serialized by caller, and
+ * rd->read_lock is used to serialize the reads
+ */
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
+ n = snprintf(msg, sizeof(msg), "%.*s/%d: fence=%u",
+ TASK_COMM_LEN, current->comm, task_pid_nr(current),
+ submit->fence);
+
+ rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4));
+
+ /* could be nice to have an option (module-param?) to snapshot
+ * all the bo's associated with the submit. Handy to see vtx
+ * buffers, etc. For now just the cmdstream bo's is enough.
+ */
+
+ for (i = 0; i < submit->nr_cmds; i++) {
+ uint32_t idx = submit->cmd[i].idx;
+ uint32_t iova = submit->cmd[i].iova;
+ uint32_t szd = submit->cmd[i].size; /* in dwords */
+ struct msm_gem_object *obj = submit->bos[idx].obj;
+ const char *buf = msm_gem_vaddr_locked(&obj->base);
+
+ buf += iova - submit->bos[idx].iova;
+
+ rd_write_section(rd, RD_GPUADDR,
+ (uint32_t[2]){ iova, szd * 4 }, 8);
+ rd_write_section(rd, RD_BUFFER_CONTENTS,
+ buf, szd * 4);
+
+ switch (submit->cmd[i].type) {
+ case MSM_SUBMIT_CMD_IB_TARGET_BUF:
+ /* ignore IB-targets, we've logged the buffer, the
+ * parser tool will follow the IB based on the logged
+ * buffer/gpuaddr, so nothing more to do.
+ */
+ break;
+ case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
+ case MSM_SUBMIT_CMD_BUF:
+ rd_write_section(rd, RD_CMDSTREAM_ADDR,
+ (uint32_t[2]){ iova, szd }, 8);
+ break;
+ }
+ }
+}
+#endif
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index b7d216264775..2b6156d0e4b5 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -102,6 +102,7 @@ nouveau-y += core/subdev/fb/nvaa.o
nouveau-y += core/subdev/fb/nvaf.o
nouveau-y += core/subdev/fb/nvc0.o
nouveau-y += core/subdev/fb/nve0.o
+nouveau-y += core/subdev/fb/gk20a.o
nouveau-y += core/subdev/fb/gm107.o
nouveau-y += core/subdev/fb/ramnv04.o
nouveau-y += core/subdev/fb/ramnv10.o
@@ -117,25 +118,32 @@ nouveau-y += core/subdev/fb/ramnva3.o
nouveau-y += core/subdev/fb/ramnvaa.o
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/sddr3.o
nouveau-y += core/subdev/fb/gddr5.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/nvd0.o
nouveau-y += core/subdev/gpio/nve0.o
nouveau-y += core/subdev/i2c/base.o
nouveau-y += core/subdev/i2c/anx9805.o
nouveau-y += core/subdev/i2c/aux.o
nouveau-y += core/subdev/i2c/bit.o
+nouveau-y += core/subdev/i2c/pad.o
+nouveau-y += core/subdev/i2c/padnv04.o
+nouveau-y += core/subdev/i2c/padnv94.o
nouveau-y += core/subdev/i2c/nv04.o
nouveau-y += core/subdev/i2c/nv4e.o
nouveau-y += core/subdev/i2c/nv50.o
nouveau-y += core/subdev/i2c/nv94.o
nouveau-y += core/subdev/i2c/nvd0.o
+nouveau-y += core/subdev/i2c/nve0.o
nouveau-y += core/subdev/ibus/nvc0.o
nouveau-y += core/subdev/ibus/nve0.o
+nouveau-y += core/subdev/ibus/gk20a.o
nouveau-y += core/subdev/instmem/base.o
nouveau-y += core/subdev/instmem/nv04.o
nouveau-y += core/subdev/instmem/nv40.o
@@ -214,6 +222,9 @@ nouveau-y += core/engine/device/nvc0.o
nouveau-y += core/engine/device/nve0.o
nouveau-y += core/engine/device/gm100.o
nouveau-y += core/engine/disp/base.o
+nouveau-y += core/engine/disp/conn.o
+nouveau-y += core/engine/disp/outp.o
+nouveau-y += core/engine/disp/outpdp.o
nouveau-y += core/engine/disp/nv04.o
nouveau-y += core/engine/disp/nv50.o
nouveau-y += core/engine/disp/nv84.o
@@ -245,6 +256,7 @@ nouveau-y += core/engine/fifo/nv50.o
nouveau-y += core/engine/fifo/nv84.o
nouveau-y += core/engine/fifo/nvc0.o
nouveau-y += core/engine/fifo/nve0.o
+nouveau-y += core/engine/fifo/gk20a.o
nouveau-y += core/engine/fifo/nv108.o
nouveau-y += core/engine/graph/ctxnv40.o
nouveau-y += core/engine/graph/ctxnv50.o
@@ -255,6 +267,7 @@ nouveau-y += core/engine/graph/ctxnvc8.o
nouveau-y += core/engine/graph/ctxnvd7.o
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/ctxnv108.o
nouveau-y += core/engine/graph/ctxgm107.o
@@ -275,6 +288,7 @@ nouveau-y += core/engine/graph/nvc8.o
nouveau-y += core/engine/graph/nvd7.o
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/nv108.o
nouveau-y += core/engine/graph/gm107.o
diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c
index 3f3c76581a9e..ae81d3b5d8b7 100644
--- a/drivers/gpu/drm/nouveau/core/core/event.c
+++ b/drivers/gpu/drm/nouveau/core/core/event.c
@@ -28,14 +28,20 @@ nouveau_event_put(struct nouveau_eventh *handler)
{
struct nouveau_event *event = handler->event;
unsigned long flags;
- if (__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
- spin_lock_irqsave(&event->refs_lock, flags);
- if (!--event->index[handler->index].refs) {
+ 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, handler->index);
+ event->disable(event, 1 << t, handler->index);
}
- spin_unlock_irqrestore(&event->refs_lock, flags);
+
}
+ spin_unlock_irqrestore(&event->refs_lock, flags);
}
void
@@ -43,14 +49,20 @@ nouveau_event_get(struct nouveau_eventh *handler)
{
struct nouveau_event *event = handler->event;
unsigned long flags;
- if (!__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
- spin_lock_irqsave(&event->refs_lock, flags);
- if (!event->index[handler->index].refs++) {
+ 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, handler->index);
+ event->enable(event, 1 << t, handler->index);
}
- spin_unlock_irqrestore(&event->refs_lock, flags);
+
}
+ spin_unlock_irqrestore(&event->refs_lock, flags);
}
static void
@@ -65,38 +77,47 @@ nouveau_event_fini(struct nouveau_eventh *handler)
}
static int
-nouveau_event_init(struct nouveau_event *event, int index,
- int (*func)(void *, int), void *priv,
+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->index[index].list);
+ 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, int index,
- int (*func)(void *, int), void *priv,
+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, index, func, priv, handler);
+ ret = nouveau_event_init(event, types, index, func, priv, handler);
if (ret)
kfree(handler);
}
@@ -116,7 +137,7 @@ nouveau_event_ref(struct nouveau_eventh *handler, struct nouveau_eventh **ref)
}
void
-nouveau_event_trigger(struct nouveau_event *event, int index)
+nouveau_event_trigger(struct nouveau_event *event, u32 types, int index)
{
struct nouveau_eventh *handler;
unsigned long flags;
@@ -125,10 +146,15 @@ nouveau_event_trigger(struct nouveau_event *event, int index)
return;
spin_lock_irqsave(&event->list_lock, flags);
- list_for_each_entry(handler, &event->index[index].list, head) {
- if (test_bit(NVKM_EVENT_ENABLE, &handler->flags) &&
- handler->func(handler->priv, index) == NVKM_EVENT_DROP)
- nouveau_event_put(handler);
+ 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);
}
spin_unlock_irqrestore(&event->list_lock, flags);
}
@@ -144,20 +170,27 @@ nouveau_event_destroy(struct nouveau_event **pevent)
}
int
-nouveau_event_create(int index_nr, struct nouveau_event **pevent)
+nouveau_event_create(int types_nr, int index_nr, struct nouveau_event **pevent)
{
struct nouveau_event *event;
int i;
- event = *pevent = kzalloc(sizeof(*event) + index_nr *
- sizeof(event->index[0]), GFP_KERNEL);
+ 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);
+ 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->index[i].list);
+ INIT_LIST_HEAD(&event->list[i]);
+ event->types_nr = types_nr;
event->index_nr = index_nr;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/core/object.c b/drivers/gpu/drm/nouveau/core/core/object.c
index 7f48e288215f..124538555904 100644
--- a/drivers/gpu/drm/nouveau/core/core/object.c
+++ b/drivers/gpu/drm/nouveau/core/core/object.c
@@ -156,7 +156,7 @@ nouveau_object_ctor(struct nouveau_object *parent,
}
if (ret == 0) {
- nv_debug(object, "created\n");
+ nv_trace(object, "created\n");
atomic_set(&object->refcount, 1);
}
@@ -166,7 +166,7 @@ nouveau_object_ctor(struct nouveau_object *parent,
static void
nouveau_object_dtor(struct nouveau_object *object)
{
- nv_debug(object, "destroying\n");
+ nv_trace(object, "destroying\n");
nv_ofuncs(object)->dtor(object);
}
@@ -337,7 +337,7 @@ nouveau_object_inc(struct nouveau_object *object)
goto fail_self;
}
- nv_debug(object, "initialised\n");
+ nv_trace(object, "initialised\n");
return 0;
fail_self:
@@ -375,7 +375,7 @@ nouveau_object_decf(struct nouveau_object *object)
if (object->parent)
nouveau_object_dec(object->parent, false);
- nv_debug(object, "stopped\n");
+ nv_trace(object, "stopped\n");
return 0;
}
@@ -411,7 +411,7 @@ nouveau_object_decs(struct nouveau_object *object)
}
}
- nv_debug(object, "suspended\n");
+ nv_trace(object, "suspended\n");
return 0;
fail_parent:
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/gm100.c b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c
index d258c21c4a22..a520029e25d9 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/gm100.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/gm100.c
@@ -60,8 +60,8 @@ gm100_identify(struct nouveau_device *device)
case 0x117:
device->cname = "GM107";
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_GPIO ] = nve0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass;
#if 0
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv04.c b/drivers/gpu/drm/nouveau/core/engine/device/nv04.c
index 0a51ff4e9e00..40b29d0214cb 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv04.c
@@ -47,7 +47,7 @@ nv04_identify(struct nouveau_device *device)
case 0x04:
device->cname = "NV04";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv04_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -65,7 +65,7 @@ nv04_identify(struct nouveau_device *device)
case 0x05:
device->cname = "NV05";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv05_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv10.c b/drivers/gpu/drm/nouveau/core/engine/device/nv10.c
index e008de8b51b0..5f7c25ff523d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv10.c
@@ -48,8 +48,8 @@ nv10_identify(struct nouveau_device *device)
case 0x10:
device->cname = "NV10";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -65,8 +65,8 @@ nv10_identify(struct nouveau_device *device)
case 0x15:
device->cname = "NV15";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -84,8 +84,8 @@ nv10_identify(struct nouveau_device *device)
case 0x16:
device->cname = "NV16";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -103,8 +103,8 @@ nv10_identify(struct nouveau_device *device)
case 0x1a:
device->cname = "nForce";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -122,8 +122,8 @@ nv10_identify(struct nouveau_device *device)
case 0x11:
device->cname = "NV11";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -141,8 +141,8 @@ nv10_identify(struct nouveau_device *device)
case 0x17:
device->cname = "NV17";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -160,8 +160,8 @@ nv10_identify(struct nouveau_device *device)
case 0x1f:
device->cname = "nForce2";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -179,8 +179,8 @@ nv10_identify(struct nouveau_device *device)
case 0x18:
device->cname = "NV18";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv20.c b/drivers/gpu/drm/nouveau/core/engine/device/nv20.c
index 7b629a3aed05..75fed11bba0a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv20.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv20.c
@@ -49,8 +49,8 @@ nv20_identify(struct nouveau_device *device)
case 0x20:
device->cname = "NV20";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -68,8 +68,8 @@ nv20_identify(struct nouveau_device *device)
case 0x25:
device->cname = "NV25";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -87,8 +87,8 @@ nv20_identify(struct nouveau_device *device)
case 0x28:
device->cname = "NV28";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -106,8 +106,8 @@ nv20_identify(struct nouveau_device *device)
case 0x2a:
device->cname = "NV2A";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv30.c b/drivers/gpu/drm/nouveau/core/engine/device/nv30.c
index 7dfddd5a1908..36919d7db7cc 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv30.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv30.c
@@ -49,8 +49,8 @@ nv30_identify(struct nouveau_device *device)
case 0x30:
device->cname = "NV30";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -68,8 +68,8 @@ nv30_identify(struct nouveau_device *device)
case 0x35:
device->cname = "NV35";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -87,8 +87,8 @@ nv30_identify(struct nouveau_device *device)
case 0x31:
device->cname = "NV31";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -107,8 +107,8 @@ nv30_identify(struct nouveau_device *device)
case 0x36:
device->cname = "NV36";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv20_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
@@ -127,8 +127,8 @@ nv30_identify(struct nouveau_device *device)
case 0x34:
device->cname = "NV34";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv04_clock_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv10_devinit_oclass;
device->oclass[NVDEV_SUBDEV_MC ] = nv04_mc_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv40.c b/drivers/gpu/drm/nouveau/core/engine/device/nv40.c
index 7c1ce6cf4f1f..1130a62be2c7 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv40.c
@@ -53,8 +53,8 @@ nv40_identify(struct nouveau_device *device)
case 0x40:
device->cname = "NV40";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -76,8 +76,8 @@ nv40_identify(struct nouveau_device *device)
case 0x41:
device->cname = "NV41";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -99,8 +99,8 @@ nv40_identify(struct nouveau_device *device)
case 0x42:
device->cname = "NV42";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -122,8 +122,8 @@ nv40_identify(struct nouveau_device *device)
case 0x43:
device->cname = "NV43";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -145,8 +145,8 @@ nv40_identify(struct nouveau_device *device)
case 0x45:
device->cname = "NV45";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -168,8 +168,8 @@ nv40_identify(struct nouveau_device *device)
case 0x47:
device->cname = "G70";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -191,8 +191,8 @@ nv40_identify(struct nouveau_device *device)
case 0x49:
device->cname = "G71";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -214,8 +214,8 @@ nv40_identify(struct nouveau_device *device)
case 0x4b:
device->cname = "G73";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -237,8 +237,8 @@ nv40_identify(struct nouveau_device *device)
case 0x44:
device->cname = "NV44";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -260,8 +260,8 @@ nv40_identify(struct nouveau_device *device)
case 0x46:
device->cname = "G72";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -283,8 +283,8 @@ nv40_identify(struct nouveau_device *device)
case 0x4a:
device->cname = "NV44A";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -306,8 +306,8 @@ nv40_identify(struct nouveau_device *device)
case 0x4c:
device->cname = "C61";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -329,8 +329,8 @@ nv40_identify(struct nouveau_device *device)
case 0x4e:
device->cname = "C51";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv4e_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv4e_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -352,8 +352,8 @@ nv40_identify(struct nouveau_device *device)
case 0x63:
device->cname = "C73";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -375,8 +375,8 @@ nv40_identify(struct nouveau_device *device)
case 0x67:
device->cname = "C67";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
@@ -398,8 +398,8 @@ nv40_identify(struct nouveau_device *device)
case 0x68:
device->cname = "C68";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv10_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv04_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv10_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv04_i2c_oclass;
device->oclass[NVDEV_SUBDEV_CLOCK ] = &nv40_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nv40_therm_oclass;
device->oclass[NVDEV_SUBDEV_DEVINIT] = nv1a_devinit_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c
index 66499fa0f758..ef0b0bde1a91 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c
@@ -60,8 +60,8 @@ nv50_identify(struct nouveau_device *device)
case 0x50:
device->cname = "G80";
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_GPIO ] = nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_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;
@@ -85,8 +85,8 @@ nv50_identify(struct nouveau_device *device)
case 0x84:
device->cname = "G84";
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_GPIO ] = nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_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;
@@ -113,8 +113,8 @@ nv50_identify(struct nouveau_device *device)
case 0x86:
device->cname = "G86";
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_GPIO ] = nv50_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_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;
@@ -141,8 +141,8 @@ nv50_identify(struct nouveau_device *device)
case 0x92:
device->cname = "G92";
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_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_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;
@@ -169,8 +169,8 @@ nv50_identify(struct nouveau_device *device)
case 0x94:
device->cname = "G94";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -197,8 +197,8 @@ nv50_identify(struct nouveau_device *device)
case 0x96:
device->cname = "G96";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -225,8 +225,8 @@ nv50_identify(struct nouveau_device *device)
case 0x98:
device->cname = "G98";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -253,8 +253,8 @@ nv50_identify(struct nouveau_device *device)
case 0xa0:
device->cname = "G200";
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_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv50_i2c_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;
@@ -281,8 +281,8 @@ 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 ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -309,8 +309,8 @@ 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 ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -337,8 +337,8 @@ nv50_identify(struct nouveau_device *device)
case 0xa3:
device->cname = "GT215";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -367,8 +367,8 @@ nv50_identify(struct nouveau_device *device)
case 0xa5:
device->cname = "GT216";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -396,8 +396,8 @@ nv50_identify(struct nouveau_device *device)
case 0xa8:
device->cname = "GT218";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -425,8 +425,8 @@ nv50_identify(struct nouveau_device *device)
case 0xaf:
device->cname = "MCP89";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
index 2075b3027052..f199957995fa 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
@@ -60,8 +60,8 @@ nvc0_identify(struct nouveau_device *device)
case 0xc0:
device->cname = "GF100";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -92,8 +92,8 @@ nvc0_identify(struct nouveau_device *device)
case 0xc4:
device->cname = "GF104";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -124,8 +124,8 @@ nvc0_identify(struct nouveau_device *device)
case 0xc3:
device->cname = "GF106";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -155,8 +155,8 @@ nvc0_identify(struct nouveau_device *device)
case 0xce:
device->cname = "GF114";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -187,8 +187,8 @@ nvc0_identify(struct nouveau_device *device)
case 0xcf:
device->cname = "GF116";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -219,8 +219,8 @@ nvc0_identify(struct nouveau_device *device)
case 0xc1:
device->cname = "GF108";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -250,8 +250,8 @@ nvc0_identify(struct nouveau_device *device)
case 0xc8:
device->cname = "GF110";
device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass;
- device->oclass[NVDEV_SUBDEV_GPIO ] = &nv50_gpio_oclass;
- device->oclass[NVDEV_SUBDEV_I2C ] = &nv94_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = nv92_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nv94_i2c_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;
@@ -282,8 +282,8 @@ nvc0_identify(struct nouveau_device *device)
case 0xd9:
device->cname = "GF119";
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_GPIO ] = nvd0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_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;
@@ -313,8 +313,8 @@ nvc0_identify(struct nouveau_device *device)
case 0xd7:
device->cname = "GF117";
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_GPIO ] = nvd0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nvd0_i2c_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;
diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c
index 9784cbf8a9d2..2d1e97d4264f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c
@@ -60,8 +60,8 @@ nve0_identify(struct nouveau_device *device)
case 0xe4:
device->cname = "GK104";
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_GPIO ] = nve0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_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;
@@ -93,8 +93,8 @@ nve0_identify(struct nouveau_device *device)
case 0xe7:
device->cname = "GK107";
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_GPIO ] = nve0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_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;
@@ -126,8 +126,8 @@ nve0_identify(struct nouveau_device *device)
case 0xe6:
device->cname = "GK106";
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_GPIO ] = nve0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_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;
@@ -156,11 +156,61 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = &nve0_perfmon_oclass;
break;
+ case 0xea:
+ device->cname = "GK20A";
+ device->oclass[NVDEV_SUBDEV_MC ] = nvc3_mc_oclass;
+ device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass;
+ device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass;
+ device->oclass[NVDEV_SUBDEV_FB ] = gk20a_fb_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_ENGINE_FIFO ] = gk20a_fifo_oclass;
+ device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass;
+ device->oclass[NVDEV_ENGINE_GR ] = gk20a_graph_oclass;
+ device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass;
+ device->oclass[NVDEV_ENGINE_PERFMON] = &nve0_perfmon_oclass;
+ break;
case 0xf0:
device->cname = "GK110";
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_GPIO ] = nve0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_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_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_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_VOLT ] = &nv40_volt_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_DISP ] = nvf0_disp_oclass;
+ device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass;
+ device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass;
+ device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass;
+ device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass;
+ device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass;
+ device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
+ device->oclass[NVDEV_ENGINE_PERFMON] = &nvf0_perfmon_oclass;
+ break;
+ case 0xf1:
+ device->cname = "GK110B";
+ 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_CLOCK ] = &nve0_clock_oclass;
device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass;
device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
@@ -184,18 +234,16 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass;
device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass;
-#if 0
device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass;
device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
-#endif
device->oclass[NVDEV_ENGINE_PERFMON] = &nvf0_perfmon_oclass;
break;
case 0x108:
device->cname = "GK208";
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_GPIO ] = nve0_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_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,11 +267,9 @@ nve0_identify(struct nouveau_device *device)
device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass;
device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass;
device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass;
-#if 0
device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass;
device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
-#endif
break;
default:
nv_fatal(device, "unknown Kepler chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/base.c b/drivers/gpu/drm/nouveau/core/engine/disp/base.c
index 7a5cae42834f..c41f656abe64 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/base.c
@@ -22,13 +22,87 @@
* Authors: Ben Skeggs
*/
-#include <engine/disp.h>
+#include "priv.h"
+#include "outp.h"
+#include "conn.h"
+
+static int
+nouveau_disp_hpd_check(struct nouveau_event *event, u32 types, int index)
+{
+ struct nouveau_disp *disp = event->priv;
+ 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;
+ }
+ }
+ return -ENOSYS;
+}
+
+int
+_nouveau_disp_fini(struct nouveau_object *object, bool suspend)
+{
+ struct nouveau_disp *disp = (void *)object;
+ struct nvkm_output *outp;
+ int ret;
+
+ list_for_each_entry(outp, &disp->outp, head) {
+ ret = nv_ofuncs(outp)->fini(nv_object(outp), suspend);
+ if (ret && suspend)
+ goto fail_outp;
+ }
+
+ return nouveau_engine_fini(&disp->base, suspend);
+
+fail_outp:
+ list_for_each_entry_continue_reverse(outp, &disp->outp, head) {
+ nv_ofuncs(outp)->init(nv_object(outp));
+ }
+
+ return ret;
+}
+
+int
+_nouveau_disp_init(struct nouveau_object *object)
+{
+ struct nouveau_disp *disp = (void *)object;
+ struct nvkm_output *outp;
+ int ret;
+
+ ret = nouveau_engine_init(&disp->base);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(outp, &disp->outp, head) {
+ ret = nv_ofuncs(outp)->init(nv_object(outp));
+ if (ret)
+ goto fail_outp;
+ }
+
+ return ret;
+
+fail_outp:
+ list_for_each_entry_continue_reverse(outp, &disp->outp, head) {
+ nv_ofuncs(outp)->fini(nv_object(outp), false);
+ }
+
+ return ret;
+}
void
_nouveau_disp_dtor(struct nouveau_object *object)
{
struct nouveau_disp *disp = (void *)object;
+ struct nvkm_output *outp, *outt;
+
nouveau_event_destroy(&disp->vblank);
+
+ list_for_each_entry_safe(outp, outt, &disp->outp, head) {
+ nouveau_object_ref(NULL, (struct nouveau_object **)&outp);
+ }
+
nouveau_engine_destroy(&disp->base);
}
@@ -39,8 +113,15 @@ nouveau_disp_create_(struct nouveau_object *parent,
const char *intname, const char *extname,
int length, void **pobject)
{
+ struct nouveau_disp_impl *impl = (void *)oclass;
+ struct nouveau_bios *bios = nouveau_bios(parent);
struct nouveau_disp *disp;
- int ret;
+ struct nouveau_oclass **sclass;
+ struct nouveau_object *object;
+ struct dcb_output dcbE;
+ u8 hpd = 0, ver, hdr;
+ u32 data;
+ int ret, i;
ret = nouveau_engine_create_(parent, engine, oclass, true,
intname, extname, length, pobject);
@@ -48,5 +129,42 @@ nouveau_disp_create_(struct nouveau_object *parent,
if (ret)
return ret;
- return nouveau_event_create(heads, &disp->vblank);
+ INIT_LIST_HEAD(&disp->outp);
+
+ /* create output objects for each display path in the vbios */
+ i = -1;
+ while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) {
+ if (dcbE.type == DCB_OUTPUT_UNUSED)
+ continue;
+ if (dcbE.type == DCB_OUTPUT_EOL)
+ break;
+ data = dcbE.location << 4 | dcbE.type;
+
+ oclass = nvkm_output_oclass;
+ sclass = impl->outp;
+ while (sclass && sclass[0]) {
+ if (sclass[0]->handle == data) {
+ oclass = sclass[0];
+ break;
+ }
+ sclass++;
+ }
+
+ nouveau_object_ctor(*pobject, *pobject, oclass,
+ &dcbE, i, &object);
+ hpd = max(hpd, (u8)(dcbE.connector + 1));
+ }
+
+ ret = nouveau_event_create(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);
+ if (ret)
+ return ret;
+
+ return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/conn.c b/drivers/gpu/drm/nouveau/core/engine/disp/conn.c
new file mode 100644
index 000000000000..4ffbc70ecf5a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/conn.c
@@ -0,0 +1,172 @@
+/*
+ * 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 <subdev/gpio.h>
+
+#include "conn.h"
+#include "outp.h"
+
+static void
+nvkm_connector_hpd_work(struct work_struct *w)
+{
+ struct nvkm_connector *conn = container_of(w, typeof(*conn), hpd.work);
+ 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);
+}
+
+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;
+}
+
+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);
+ return nouveau_object_fini(&conn->base, suspend);
+}
+
+int
+_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);
+ }
+ return ret;
+}
+
+void
+_nvkm_connector_dtor(struct nouveau_object *object)
+{
+ struct nvkm_connector *conn = (void *)object;
+ nouveau_event_ref(NULL, &conn->hpd.event);
+ nouveau_object_destroy(&conn->base);
+}
+
+int
+nvkm_connector_create_(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass,
+ struct nvbios_connE *info, int index,
+ int length, void **pobject)
+{
+ static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 };
+ struct nouveau_gpio *gpio = nouveau_gpio(parent);
+ struct nouveau_disp *disp = (void *)engine;
+ struct nvkm_connector *conn;
+ struct nvkm_output *outp;
+ struct dcb_gpio_func func;
+ int ret;
+
+ list_for_each_entry(outp, &disp->outp, head) {
+ if (outp->conn && outp->conn->index == index) {
+ atomic_inc(&nv_object(outp->conn)->refcount);
+ *pobject = outp->conn;
+ return 1;
+ }
+ }
+
+ ret = nouveau_object_create_(parent, engine, oclass, 0, length, pobject);
+ conn = *pobject;
+ if (ret)
+ return ret;
+
+ conn->info = *info;
+ conn->index = index;
+
+ DBG("type %02x loc %d hpd %02x dp %x di %x sr %x lcdid %x\n",
+ info->type, info->location, info->hpd, info->dp,
+ info->di, info->sr, info->lcdid);
+
+ if ((info->hpd = ffs(info->hpd))) {
+ if (--info->hpd >= ARRAY_SIZE(hpd)) {
+ ERR("hpd %02x unknown\n", info->hpd);
+ goto done;
+ }
+ 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;
+ }
+
+ ret = nouveau_event_new(gpio->events, NVKM_GPIO_TOGGLED,
+ func.line, nvkm_connector_hpd,
+ conn, &conn->hpd.event);
+ if (ret) {
+ ERR("func %02x failed, %d\n", info->hpd, ret);
+ } else {
+ DBG("func %02x (HPD)\n", info->hpd);
+ }
+ }
+
+done:
+ INIT_WORK(&conn->hpd.work, nvkm_connector_hpd_work);
+ return 0;
+}
+
+int
+_nvkm_connector_ctor(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *info, u32 index,
+ struct nouveau_object **pobject)
+{
+ struct nvkm_connector *conn;
+ int ret;
+
+ ret = nvkm_connector_create(parent, engine, oclass, info, index, &conn);
+ *pobject = nv_object(conn);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nouveau_oclass *
+nvkm_connector_oclass = &(struct nvkm_connector_impl) {
+ .base = {
+ .handle = 0,
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nvkm_connector_ctor,
+ .dtor = _nvkm_connector_dtor,
+ .init = _nvkm_connector_init,
+ .fini = _nvkm_connector_fini,
+ },
+ },
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/conn.h b/drivers/gpu/drm/nouveau/core/engine/disp/conn.h
new file mode 100644
index 000000000000..035ebeacbb1c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/conn.h
@@ -0,0 +1,59 @@
+#ifndef __NVKM_DISP_CONN_H__
+#define __NVKM_DISP_CONN_H__
+
+#include "priv.h"
+
+struct nvkm_connector {
+ struct nouveau_object base;
+ struct list_head head;
+
+ struct nvbios_connE info;
+ int index;
+
+ struct {
+ struct nouveau_eventh *event;
+ struct work_struct work;
+ } hpd;
+};
+
+#define nvkm_connector_create(p,e,c,b,i,d) \
+ nvkm_connector_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d)
+#define nvkm_connector_destroy(d) ({ \
+ struct nvkm_connector *disp = (d); \
+ _nvkm_connector_dtor(nv_object(disp)); \
+})
+#define nvkm_connector_init(d) ({ \
+ struct nvkm_connector *disp = (d); \
+ _nvkm_connector_init(nv_object(disp)); \
+})
+#define nvkm_connector_fini(d,s) ({ \
+ struct nvkm_connector *disp = (d); \
+ _nvkm_connector_fini(nv_object(disp), (s)); \
+})
+
+int nvkm_connector_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, struct nvbios_connE *,
+ int, int, void **);
+
+int _nvkm_connector_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+void _nvkm_connector_dtor(struct nouveau_object *);
+int _nvkm_connector_init(struct nouveau_object *);
+int _nvkm_connector_fini(struct nouveau_object *, bool);
+
+struct nvkm_connector_impl {
+ struct nouveau_oclass base;
+};
+
+#ifndef MSG
+#define MSG(l,f,a...) do { \
+ struct nvkm_connector *_conn = (void *)conn; \
+ nv_##l(nv_object(conn)->engine, "%02x:%02x%02x: "f, _conn->index, \
+ _conn->info.location, _conn->info.type, ##a); \
+} while(0)
+#define DBG(f,a...) MSG(debug, f, ##a)
+#define ERR(f,a...) MSG(error, f, ##a)
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
index 3ca2d25b7f5e..39562d48101d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
@@ -30,42 +30,38 @@
#include <engine/disp.h>
-#include "dport.h"
+#include <core/class.h>
-#define DBG(fmt, args...) nv_debug(dp->disp, "DP:%04x:%04x: " fmt, \
- dp->outp->hasht, dp->outp->hashm, ##args)
-#define ERR(fmt, args...) nv_error(dp->disp, "DP:%04x:%04x: " fmt, \
- dp->outp->hasht, dp->outp->hashm, ##args)
+#include "dport.h"
+#include "outpdp.h"
/******************************************************************************
* link training
*****************************************************************************/
struct dp_state {
- const struct nouveau_dp_func *func;
- struct nouveau_disp *disp;
- struct dcb_output *outp;
- struct nvbios_dpout info;
- u8 version;
- struct nouveau_i2c_port *aux;
- int head;
- u8 dpcd[4];
+ struct nvkm_output_dp *outp;
int link_nr;
u32 link_bw;
u8 stat[6];
u8 conf[4];
+ bool pc2;
+ u8 pc2stat;
+ u8 pc2conf[2];
};
static int
dp_set_link_config(struct dp_state *dp)
{
- struct nouveau_disp *disp = dp->disp;
+ struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
+ struct nvkm_output_dp *outp = dp->outp;
+ struct nouveau_disp *disp = nouveau_disp(outp);
struct nouveau_bios *bios = nouveau_bios(disp);
struct nvbios_init init = {
- .subdev = nv_subdev(dp->disp),
+ .subdev = nv_subdev(disp),
.bios = bios,
.offset = 0x0000,
- .outp = dp->outp,
- .crtc = dp->head,
+ .outp = &outp->base.info,
+ .crtc = -1,
.execute = 1,
};
u32 lnkcmp;
@@ -75,8 +71,8 @@ dp_set_link_config(struct dp_state *dp)
DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
/* set desired link configuration on the source */
- if ((lnkcmp = dp->info.lnkcmp)) {
- if (dp->version < 0x30) {
+ if ((lnkcmp = dp->outp->info.lnkcmp)) {
+ if (outp->version < 0x30) {
while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp))
lnkcmp += 4;
init.offset = nv_ro16(bios, lnkcmp + 2);
@@ -89,73 +85,112 @@ dp_set_link_config(struct dp_state *dp)
nvbios_exec(&init);
}
- ret = dp->func->lnk_ctl(dp->disp, dp->outp, dp->head,
- dp->link_nr, dp->link_bw / 27000,
- dp->dpcd[DPCD_RC02] &
- DPCD_RC02_ENHANCED_FRAME_CAP);
+ ret = impl->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000,
+ outp->dpcd[DPCD_RC02] &
+ DPCD_RC02_ENHANCED_FRAME_CAP);
if (ret) {
- ERR("lnk_ctl failed with %d\n", ret);
+ if (ret < 0)
+ ERR("lnk_ctl failed with %d\n", ret);
return ret;
}
+ impl->lnk_pwr(outp, dp->link_nr);
+
/* set desired link configuration on the sink */
sink[0] = dp->link_bw / 27000;
sink[1] = dp->link_nr;
- if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
+ if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
- return nv_wraux(dp->aux, DPCD_LC00, sink, 2);
+ return nv_wraux(outp->base.edid, DPCD_LC00_LINK_BW_SET, sink, 2);
}
static void
dp_set_training_pattern(struct dp_state *dp, u8 pattern)
{
+ struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
+ struct nvkm_output_dp *outp = dp->outp;
u8 sink_tp;
DBG("training pattern %d\n", pattern);
- dp->func->pattern(dp->disp, dp->outp, dp->head, pattern);
+ impl->pattern(outp, pattern);
- nv_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1);
+ nv_rdaux(outp->base.edid, DPCD_LC02, &sink_tp, 1);
sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
sink_tp |= pattern;
- nv_wraux(dp->aux, DPCD_LC02, &sink_tp, 1);
+ nv_wraux(outp->base.edid, DPCD_LC02, &sink_tp, 1);
}
static int
-dp_link_train_commit(struct dp_state *dp)
+dp_link_train_commit(struct dp_state *dp, bool pc)
{
- int i;
+ struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
+ struct nvkm_output_dp *outp = dp->outp;
+ int ret, i;
for (i = 0; i < dp->link_nr; i++) {
u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
+ u8 lpc2 = (dp->pc2stat >> (i * 2)) & 0x3;
u8 lpre = (lane & 0x0c) >> 2;
u8 lvsw = (lane & 0x03) >> 0;
+ u8 hivs = 3 - lpre;
+ u8 hipe = 3;
+ u8 hipc = 3;
+
+ if (lpc2 >= hipc)
+ lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED;
+ if (lpre >= hipe) {
+ lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */
+ lvsw = hivs = 3 - (lpre & 3);
+ } else
+ if (lvsw >= hivs) {
+ lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED;
+ }
dp->conf[i] = (lpre << 3) | lvsw;
- if (lvsw == 3)
- dp->conf[i] |= DPCD_LC03_MAX_SWING_REACHED;
- if (lpre == 3)
- dp->conf[i] |= DPCD_LC03_MAX_PRE_EMPHASIS_REACHED;
+ dp->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4);
+
+ DBG("config lane %d %02x %02x\n", i, dp->conf[i], lpc2);
+ impl->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3);
+ }
+
+ ret = nv_wraux(outp->base.edid, DPCD_LC03(0), dp->conf, 4);
+ if (ret)
+ return ret;
- DBG("config lane %d %02x\n", i, dp->conf[i]);
- dp->func->drv_ctl(dp->disp, dp->outp, dp->head, i, lvsw, lpre);
+ if (pc) {
+ ret = nv_wraux(outp->base.edid, DPCD_LC0F, dp->pc2conf, 2);
+ if (ret)
+ return ret;
}
- return nv_wraux(dp->aux, DPCD_LC03(0), dp->conf, 4);
+ return 0;
}
static int
-dp_link_train_update(struct dp_state *dp, u32 delay)
+dp_link_train_update(struct dp_state *dp, bool pc, u32 delay)
{
+ struct nvkm_output_dp *outp = dp->outp;
int ret;
- udelay(delay);
+ if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
+ mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
+ else
+ udelay(delay);
- ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6);
+ ret = nv_rdaux(outp->base.edid, DPCD_LS02, dp->stat, 6);
if (ret)
return ret;
- DBG("status %6ph\n", dp->stat);
+ if (pc) {
+ ret = nv_rdaux(outp->base.edid, DPCD_LS0C, &dp->pc2stat, 1);
+ if (ret)
+ dp->pc2stat = 0x00;
+ DBG("status %6ph pc2 %02x\n", dp->stat, dp->pc2stat);
+ } else {
+ DBG("status %6ph\n", dp->stat);
+ }
+
return 0;
}
@@ -169,8 +204,8 @@ dp_link_train_cr(struct dp_state *dp)
dp_set_training_pattern(dp, 1);
do {
- if (dp_link_train_commit(dp) ||
- dp_link_train_update(dp, 100))
+ if (dp_link_train_commit(dp, false) ||
+ dp_link_train_update(dp, false, 100))
break;
cr_done = true;
@@ -196,13 +231,17 @@ dp_link_train_cr(struct dp_state *dp)
static int
dp_link_train_eq(struct dp_state *dp)
{
+ struct nvkm_output_dp *outp = dp->outp;
bool eq_done = false, cr_done = true;
int tries = 0, i;
- dp_set_training_pattern(dp, 2);
+ if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED)
+ dp_set_training_pattern(dp, 3);
+ else
+ dp_set_training_pattern(dp, 2);
do {
- if (dp_link_train_update(dp, 400))
+ if (dp_link_train_update(dp, dp->pc2, 400))
break;
eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE);
@@ -215,7 +254,7 @@ dp_link_train_eq(struct dp_state *dp)
eq_done = false;
}
- if (dp_link_train_commit(dp))
+ if (dp_link_train_commit(dp, dp->pc2))
break;
} while (!eq_done && cr_done && ++tries <= 5);
@@ -225,121 +264,109 @@ dp_link_train_eq(struct dp_state *dp)
static void
dp_link_train_init(struct dp_state *dp, bool spread)
{
+ struct nvkm_output_dp *outp = dp->outp;
+ struct nouveau_disp *disp = nouveau_disp(outp);
+ struct nouveau_bios *bios = nouveau_bios(disp);
struct nvbios_init init = {
- .subdev = nv_subdev(dp->disp),
- .bios = nouveau_bios(dp->disp),
- .outp = dp->outp,
- .crtc = dp->head,
+ .subdev = nv_subdev(disp),
+ .bios = bios,
+ .outp = &outp->base.info,
+ .crtc = -1,
.execute = 1,
};
/* set desired spread */
if (spread)
- init.offset = dp->info.script[2];
+ init.offset = outp->info.script[2];
else
- init.offset = dp->info.script[3];
+ init.offset = outp->info.script[3];
nvbios_exec(&init);
/* pre-train script */
- init.offset = dp->info.script[0];
+ init.offset = outp->info.script[0];
nvbios_exec(&init);
}
static void
dp_link_train_fini(struct dp_state *dp)
{
+ struct nvkm_output_dp *outp = dp->outp;
+ struct nouveau_disp *disp = nouveau_disp(outp);
+ struct nouveau_bios *bios = nouveau_bios(disp);
struct nvbios_init init = {
- .subdev = nv_subdev(dp->disp),
- .bios = nouveau_bios(dp->disp),
- .outp = dp->outp,
- .crtc = dp->head,
+ .subdev = nv_subdev(disp),
+ .bios = bios,
+ .outp = &outp->base.info,
+ .crtc = -1,
.execute = 1,
};
/* post-train script */
- init.offset = dp->info.script[1],
+ init.offset = outp->info.script[1],
nvbios_exec(&init);
}
-int
-nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
- struct dcb_output *outp, int head, u32 datarate)
+static const struct dp_rates {
+ u32 rate;
+ u8 bw;
+ u8 nr;
+} nouveau_dp_rates[] = {
+ { 2160000, 0x14, 4 },
+ { 1080000, 0x0a, 4 },
+ { 1080000, 0x14, 2 },
+ { 648000, 0x06, 4 },
+ { 540000, 0x0a, 2 },
+ { 540000, 0x14, 1 },
+ { 324000, 0x06, 2 },
+ { 270000, 0x0a, 1 },
+ { 162000, 0x06, 1 },
+ {}
+};
+
+void
+nouveau_dp_train(struct work_struct *w)
{
- struct nouveau_bios *bios = nouveau_bios(disp);
- struct nouveau_i2c *i2c = nouveau_i2c(disp);
+ struct nvkm_output_dp *outp = container_of(w, typeof(*outp), lt.work);
+ struct nouveau_disp *disp = nouveau_disp(outp);
+ const struct dp_rates *cfg = nouveau_dp_rates;
struct dp_state _dp = {
- .disp = disp,
- .func = func,
.outp = outp,
- .head = head,
}, *dp = &_dp;
- const u32 bw_list[] = { 540000, 270000, 162000, 0 };
- const u32 *link_bw = bw_list;
- u8 hdr, cnt, len;
- u32 data;
+ u32 datarate = 0;
int ret;
- /* find the bios displayport data relevant to this output */
- data = nvbios_dpout_match(bios, outp->hasht, outp->hashm, &dp->version,
- &hdr, &cnt, &len, &dp->info);
- if (!data) {
- ERR("bios data not found\n");
- return -EINVAL;
- }
-
- /* acquire the aux channel and fetch some info about the display */
- if (outp->location)
- dp->aux = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
- else
- dp->aux = i2c->find(i2c, NV_I2C_TYPE_DCBI2C(outp->i2c_index));
- if (!dp->aux) {
- ERR("no aux channel?!\n");
- return -ENODEV;
- }
-
- ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd));
- if (ret) {
- /* it's possible the display has been unplugged before we
- * get here. we still need to execute the full set of
- * vbios scripts, and program the OR at a high enough
- * frequency to satisfy the target mode. failure to do
- * so results at best in an UPDATE hanging, and at worst
- * with PDISP running away to join the circus.
- */
- dp->dpcd[1] = link_bw[0] / 27000;
- dp->dpcd[2] = 4;
- dp->dpcd[3] = 0x00;
- ERR("failed to read DPCD\n");
- }
-
/* bring capabilities within encoder limits */
- if ((dp->dpcd[2] & 0x1f) > dp->outp->dpconf.link_nr) {
- dp->dpcd[2] &= ~0x1f;
- dp->dpcd[2] |= dp->outp->dpconf.link_nr;
+ if (nv_mclass(disp) < NVD0_DISP_CLASS)
+ 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;
+ outp->dpcd[2] |= outp->base.info.dpconf.link_nr;
+ }
+ if (outp->dpcd[1] > outp->base.info.dpconf.link_bw)
+ outp->dpcd[1] = outp->base.info.dpconf.link_bw;
+ dp->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED;
+
+ /* restrict link config to the lowest required rate, if requested */
+ if (datarate) {
+ datarate = (datarate / 8) * 10; /* 8B/10B coding overhead */
+ while (cfg[1].rate >= datarate)
+ cfg++;
}
- if (dp->dpcd[1] > dp->outp->dpconf.link_bw)
- dp->dpcd[1] = dp->outp->dpconf.link_bw;
+ cfg--;
- /* adjust required bandwidth for 8B/10B coding overhead */
- datarate = (datarate / 8) * 10;
+ /* disable link interrupt handling during link training */
+ nouveau_event_put(outp->irq);
/* enable down-spreading and execute pre-train script from vbios */
- dp_link_train_init(dp, dp->dpcd[3] & 0x01);
+ dp_link_train_init(dp, outp->dpcd[3] & 0x01);
- /* start off at highest link rate supported by encoder and display */
- while (*link_bw > (dp->dpcd[1] * 27000))
- link_bw++;
-
- while ((ret = -EIO) && link_bw[0]) {
- /* find minimum required lane count at this link rate */
- dp->link_nr = dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT;
- while ((dp->link_nr >> 1) * link_bw[0] > datarate)
- dp->link_nr >>= 1;
-
- /* drop link rate to minimum with this lane count */
- while ((link_bw[1] * dp->link_nr) > datarate)
- link_bw++;
- dp->link_bw = link_bw[0];
+ while (ret = -EIO, (++cfg)->rate) {
+ /* select next configuration supported by encoder and sink */
+ while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) ||
+ cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE]))
+ cfg++;
+ dp->link_bw = cfg->bw * 27000;
+ dp->link_nr = cfg->nr;
/* program selected link configuration */
ret = dp_set_link_config(dp);
@@ -356,17 +383,18 @@ nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
*/
break;
}
-
- /* retry at lower rate */
- link_bw++;
}
- /* finish link training */
+ /* finish link training and execute post-train script from vbios */
dp_set_training_pattern(dp, 0);
if (ret < 0)
ERR("link training failed\n");
- /* execute post-train script from vbios */
dp_link_train_fini(dp);
- return (ret < 0) ? false : true;
+
+ /* signal completion and enable link interrupt handling */
+ DBG("training complete\n");
+ atomic_set(&outp->lt.done, 1);
+ wake_up(&outp->lt.wait);
+ nouveau_event_get(outp->irq);
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.h b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h
index 0e1bbd18ff6c..5628d2d5ec71 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/dport.h
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.h
@@ -2,19 +2,18 @@
#define __NVKM_DISP_DPORT_H__
/* DPCD Receiver Capabilities */
-#define DPCD_RC00 0x00000
-#define DPCD_RC00_DPCD_REV 0xff
-#define DPCD_RC01 0x00001
-#define DPCD_RC01_MAX_LINK_RATE 0xff
+#define DPCD_RC00_DPCD_REV 0x00000
+#define DPCD_RC01_MAX_LINK_RATE 0x00001
#define DPCD_RC02 0x00002
#define DPCD_RC02_ENHANCED_FRAME_CAP 0x80
+#define DPCD_RC02_TPS3_SUPPORTED 0x40
#define DPCD_RC02_MAX_LANE_COUNT 0x1f
#define DPCD_RC03 0x00003
#define DPCD_RC03_MAX_DOWNSPREAD 0x01
+#define DPCD_RC0E_AUX_RD_INTERVAL 0x0000e
/* DPCD Link Configuration */
-#define DPCD_LC00 0x00100
-#define DPCD_LC00_LINK_BW_SET 0xff
+#define DPCD_LC00_LINK_BW_SET 0x00100
#define DPCD_LC01 0x00101
#define DPCD_LC01_ENHANCED_FRAME_EN 0x80
#define DPCD_LC01_LANE_COUNT_SET 0x1f
@@ -25,6 +24,16 @@
#define DPCD_LC03_PRE_EMPHASIS_SET 0x18
#define DPCD_LC03_MAX_SWING_REACHED 0x04
#define DPCD_LC03_VOLTAGE_SWING_SET 0x03
+#define DPCD_LC0F 0x0010f
+#define DPCD_LC0F_LANE1_MAX_POST_CURSOR2_REACHED 0x40
+#define DPCD_LC0F_LANE1_POST_CURSOR2_SET 0x30
+#define DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED 0x04
+#define DPCD_LC0F_LANE0_POST_CURSOR2_SET 0x03
+#define DPCD_LC10 0x00110
+#define DPCD_LC10_LANE3_MAX_POST_CURSOR2_REACHED 0x40
+#define DPCD_LC10_LANE3_POST_CURSOR2_SET 0x30
+#define DPCD_LC10_LANE2_MAX_POST_CURSOR2_REACHED 0x04
+#define DPCD_LC10_LANE2_POST_CURSOR2_SET 0x03
/* DPCD Link/Sink Status */
#define DPCD_LS02 0x00202
@@ -55,24 +64,12 @@
#define DPCD_LS07_LANE3_VOLTAGE_SWING 0x30
#define DPCD_LS07_LANE2_PRE_EMPHASIS 0x0c
#define DPCD_LS07_LANE2_VOLTAGE_SWING 0x03
+#define DPCD_LS0C 0x0020c
+#define DPCD_LS0C_LANE3_POST_CURSOR2 0xc0
+#define DPCD_LS0C_LANE2_POST_CURSOR2 0x30
+#define DPCD_LS0C_LANE1_POST_CURSOR2 0x0c
+#define DPCD_LS0C_LANE0_POST_CURSOR2 0x03
-struct nouveau_disp;
-struct dcb_output;
-
-struct nouveau_dp_func {
- int (*pattern)(struct nouveau_disp *, struct dcb_output *,
- int head, int pattern);
- int (*lnk_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
- int link_nr, int link_bw, bool enh_frame);
- int (*drv_ctl)(struct nouveau_disp *, struct dcb_output *, int head,
- int lane, int swing, int preem);
-};
-
-extern const struct nouveau_dp_func nv94_sor_dp_func;
-extern const struct nouveau_dp_func nvd0_sor_dp_func;
-extern const struct nouveau_dp_func nv50_pior_dp_func;
-
-int nouveau_dp_train(struct nouveau_disp *, const struct nouveau_dp_func *,
- struct dcb_output *, int, u32);
+void nouveau_dp_train(struct work_struct *);
#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c
index cf6f59677b74..9fc7447fec90 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/gm107.c
@@ -81,7 +81,6 @@ gm107_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld;
priv->sor.hdmi = nvd0_hdmi_ctrl;
- priv->sor.dp = &nvd0_sor_dp_func;
return 0;
}
@@ -94,6 +93,7 @@ gm107_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .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,
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
index 6c89af792889..a32666ed0c47 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv04.c
@@ -51,6 +51,14 @@ nv04_disp_scanoutpos(struct nouveau_object *object, u32 mthd,
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());
@@ -78,13 +86,13 @@ nv04_disp_sclass[] = {
******************************************************************************/
static void
-nv04_disp_vblank_enable(struct nouveau_event *event, int head)
+nv04_disp_vblank_enable(struct nouveau_event *event, int type, int head)
{
nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000001);
}
static void
-nv04_disp_vblank_disable(struct nouveau_event *event, int head)
+nv04_disp_vblank_disable(struct nouveau_event *event, int type, int head)
{
nv_wr32(event->priv, 0x600140 + (head * 0x2000) , 0x00000000);
}
@@ -98,12 +106,12 @@ nv04_disp_intr(struct nouveau_subdev *subdev)
u32 pvideo;
if (crtc0 & 0x00000001) {
- nouveau_event_trigger(priv->base.vblank, 0);
+ nouveau_event_trigger(priv->base.vblank, 1, 0);
nv_wr32(priv, 0x600100, 0x00000001);
}
if (crtc1 & 0x00000001) {
- nouveau_event_trigger(priv->base.vblank, 1);
+ nouveau_event_trigger(priv->base.vblank, 1, 1);
nv_wr32(priv, 0x602100, 0x00000001);
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
index 9a0cab9c3adb..1e85f36c705f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
@@ -829,13 +829,13 @@ nv50_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd,
}
static void
-nv50_disp_base_vblank_enable(struct nouveau_event *event, int head)
+nv50_disp_base_vblank_enable(struct nouveau_event *event, int type, int head)
{
nv_mask(event->priv, 0x61002c, (4 << head), (4 << head));
}
static void
-nv50_disp_base_vblank_disable(struct nouveau_event *event, int head)
+nv50_disp_base_vblank_disable(struct nouveau_event *event, int type, int head)
{
nv_mask(event->priv, 0x61002c, (4 << head), 0);
}
@@ -1114,19 +1114,20 @@ nv50_disp_intr_error(struct nv50_disp_priv *priv, int chid)
nv_wr32(priv, 0x610080 + (chid * 0x08), 0x90000000);
}
-static u16
-exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
- struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+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,
struct nvbios_outp *info)
{
struct nouveau_bios *bios = nouveau_bios(priv);
- u16 mask, type, data;
+ struct nvkm_output *outp;
+ u16 mask, type;
- if (outp < 4) {
+ if (or < 4) {
type = DCB_OUTPUT_ANALOG;
mask = 0;
} else
- if (outp < 8) {
+ if (or < 8) {
switch (ctrl & 0x00000f00) {
case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
@@ -1136,45 +1137,48 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
default:
nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
- return 0x0000;
+ return NULL;
}
- outp -= 4;
+ or -= 4;
} else {
- outp = outp - 8;
+ or = or - 8;
type = 0x0010;
mask = 0;
switch (ctrl & 0x00000f00) {
- case 0x00000000: type |= priv->pior.type[outp]; break;
+ case 0x00000000: type |= priv->pior.type[or]; break;
default:
nv_error(priv, "unknown PIOR mc 0x%08x\n", ctrl);
- return 0x0000;
+ return NULL;
}
}
mask = 0x00c0 & (mask << 6);
- mask |= 0x0001 << outp;
+ mask |= 0x0001 << or;
mask |= 0x0100 << head;
- data = dcb_outp_match(bios, type, mask, ver, hdr, dcb);
- if (!data)
- return 0x0000;
-
- /* off-chip encoders require matching the exact encoder type */
- if (dcb->location != 0)
- type |= dcb->extdev << 8;
+ list_for_each_entry(outp, &priv->base.outp, head) {
+ if ((outp->info.hasht & 0xff) == type &&
+ (outp->info.hashm & mask) == mask) {
+ *data = nvbios_outp_match(bios, outp->info.hasht,
+ outp->info.hashm,
+ ver, hdr, cnt, len, info);
+ if (!*data)
+ return NULL;
+ return outp;
+ }
+ }
- return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
+ return NULL;
}
-static bool
+static struct nvkm_output *
exec_script(struct nv50_disp_priv *priv, int head, int id)
{
struct nouveau_bios *bios = nouveau_bios(priv);
+ struct nvkm_output *outp;
struct nvbios_outp info;
- struct dcb_output dcb;
u8 ver, hdr, cnt, len;
- u16 data;
- u32 ctrl = 0x00000000;
+ u32 data, ctrl = 0;
u32 reg;
int i;
@@ -1204,36 +1208,35 @@ exec_script(struct nv50_disp_priv *priv, int head, int id)
}
if (!(ctrl & (1 << head)))
- return false;
+ return NULL;
i--;
- data = exec_lookup(priv, head, i, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);
- if (data) {
+ outp = exec_lookup(priv, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
+ if (outp) {
struct nvbios_init init = {
.subdev = nv_subdev(priv),
.bios = bios,
.offset = info.script[id],
- .outp = &dcb,
+ .outp = &outp->info,
.crtc = head,
.execute = 1,
};
- return nvbios_exec(&init) == 0;
+ nvbios_exec(&init);
}
- return false;
+ return outp;
}
-static u32
-exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
- struct dcb_output *outp)
+static struct nvkm_output *
+exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, u32 *conf)
{
struct nouveau_bios *bios = nouveau_bios(priv);
+ struct nvkm_output *outp;
struct nvbios_outp info1;
struct nvbios_ocfg info2;
u8 ver, hdr, cnt, len;
- u32 ctrl = 0x00000000;
- u32 data, conf = ~0;
+ u32 data, ctrl = 0;
u32 reg;
int i;
@@ -1263,37 +1266,37 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
}
if (!(ctrl & (1 << head)))
- return conf;
+ return NULL;
i--;
- data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1);
+ outp = exec_lookup(priv, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
if (!data)
- return conf;
+ return NULL;
- if (outp->location == 0) {
- switch (outp->type) {
+ if (outp->info.location == 0) {
+ switch (outp->info.type) {
case DCB_OUTPUT_TMDS:
- conf = (ctrl & 0x00000f00) >> 8;
+ *conf = (ctrl & 0x00000f00) >> 8;
if (pclk >= 165000)
- conf |= 0x0100;
+ *conf |= 0x0100;
break;
case DCB_OUTPUT_LVDS:
- conf = priv->sor.lvdsconf;
+ *conf = priv->sor.lvdsconf;
break;
case DCB_OUTPUT_DP:
- conf = (ctrl & 0x00000f00) >> 8;
+ *conf = (ctrl & 0x00000f00) >> 8;
break;
case DCB_OUTPUT_ANALOG:
default:
- conf = 0x00ff;
+ *conf = 0x00ff;
break;
}
} else {
- conf = (ctrl & 0x00000f00) >> 8;
+ *conf = (ctrl & 0x00000f00) >> 8;
pclk = pclk / 2;
}
- data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
+ data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2);
if (data && id < 0xff) {
data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
if (data) {
@@ -1301,7 +1304,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
.subdev = nv_subdev(priv),
.bios = bios,
.offset = data,
- .outp = outp,
+ .outp = &outp->info,
.crtc = head,
.execute = 1,
};
@@ -1310,7 +1313,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
}
}
- return conf;
+ return outp;
}
static void
@@ -1322,7 +1325,35 @@ nv50_disp_intr_unk10_0(struct nv50_disp_priv *priv, int head)
static void
nv50_disp_intr_unk20_0(struct nv50_disp_priv *priv, int head)
{
- exec_script(priv, head, 2);
+ struct nvkm_output *outp = exec_script(priv, head, 2);
+
+ /* the binary driver does this outside of the supervisor handling
+ * (after the third supervisor from a detach). we (currently?)
+ * allow both detach/attach to happen in the same set of
+ * supervisor interrupts, so it would make sense to execute this
+ * (full power down?) script after all the detach phases of the
+ * supervisor handling. like with training if needed from the
+ * second supervisor, nvidia doesn't do this, so who knows if it's
+ * entirely safe, but it does appear to work..
+ *
+ * without this script being run, on some configurations i've
+ * seen, switching from DP to TMDS on a DP connector may result
+ * in a blank screen (SOR_PWR off/on can restore it)
+ */
+ if (outp && outp->info.type == DCB_OUTPUT_DP) {
+ struct nvkm_output_dp *outpdp = (void *)outp;
+ struct nvbios_init init = {
+ .subdev = nv_subdev(priv),
+ .bios = nouveau_bios(priv),
+ .outp = &outp->info,
+ .crtc = head,
+ .offset = outpdp->info.script[4],
+ .execute = 1,
+ };
+
+ nvbios_exec(&init);
+ atomic_set(&outpdp->lt.done, 0);
+ }
}
static void
@@ -1444,56 +1475,83 @@ nv50_disp_intr_unk20_2_dp(struct nv50_disp_priv *priv,
static void
nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head)
{
- struct dcb_output outp;
+ struct nvkm_output *outp;
u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
u32 hval, hreg = 0x614200 + (head * 0x800);
u32 oval, oreg;
- u32 mask;
- u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
- if (conf != ~0) {
- if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) {
- u32 soff = (ffs(outp.or) - 1) * 0x08;
- u32 ctrl = nv_rd32(priv, 0x610794 + soff);
- u32 datarate;
-
- switch ((ctrl & 0x000f0000) >> 16) {
- case 6: datarate = pclk * 30 / 8; break;
- case 5: datarate = pclk * 24 / 8; break;
- case 2:
- default:
- datarate = pclk * 18 / 8;
- break;
- }
+ u32 mask, conf;
- nouveau_dp_train(&priv->base, priv->sor.dp,
- &outp, head, datarate);
- }
+ outp = exec_clkcmp(priv, head, 0xff, pclk, &conf);
+ if (!outp)
+ return;
+
+ /* we allow both encoder attach and detach operations to occur
+ * within a single supervisor (ie. modeset) sequence. the
+ * encoder detach scripts quite often switch off power to the
+ * lanes, which requires the link to be re-trained.
+ *
+ * this is not generally an issue as the sink "must" (heh)
+ * signal an irq when it's lost sync so the driver can
+ * re-train.
+ *
+ * however, on some boards, if one does not configure at least
+ * the gpu side of the link *before* attaching, then various
+ * things can go horribly wrong (PDISP disappearing from mmio,
+ * third supervisor never happens, etc).
+ *
+ * the solution is simply to retrain here, if necessary. last
+ * i checked, the binary driver userspace does not appear to
+ * trigger this situation (it forces an UPDATE between steps).
+ */
+ if (outp->info.type == DCB_OUTPUT_DP) {
+ u32 soff = (ffs(outp->info.or) - 1) * 0x08;
+ u32 ctrl, datarate;
- exec_clkcmp(priv, head, 0, pclk, &outp);
-
- if (!outp.location && outp.type == DCB_OUTPUT_ANALOG) {
- oreg = 0x614280 + (ffs(outp.or) - 1) * 0x800;
- oval = 0x00000000;
- hval = 0x00000000;
- mask = 0xffffffff;
- } else
- if (!outp.location) {
- if (outp.type == DCB_OUTPUT_DP)
- nv50_disp_intr_unk20_2_dp(priv, &outp, pclk);
- oreg = 0x614300 + (ffs(outp.or) - 1) * 0x800;
- oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
- hval = 0x00000000;
- mask = 0x00000707;
+ if (outp->info.location == 0) {
+ ctrl = nv_rd32(priv, 0x610794 + soff);
+ soff = 1;
} else {
- oreg = 0x614380 + (ffs(outp.or) - 1) * 0x800;
- oval = 0x00000001;
- hval = 0x00000001;
- mask = 0x00000707;
+ ctrl = nv_rd32(priv, 0x610b80 + soff);
+ soff = 2;
}
- nv_mask(priv, hreg, 0x0000000f, hval);
- nv_mask(priv, oreg, mask, oval);
+ switch ((ctrl & 0x000f0000) >> 16) {
+ case 6: datarate = pclk * 30 / 8; break;
+ case 5: datarate = pclk * 24 / 8; break;
+ case 2:
+ default:
+ datarate = pclk * 18 / 8;
+ break;
+ }
+
+ if (nvkm_output_dp_train(outp, datarate / soff, true))
+ ERR("link not trained before attach\n");
}
+
+ exec_clkcmp(priv, head, 0, pclk, &conf);
+
+ if (!outp->info.location && outp->info.type == DCB_OUTPUT_ANALOG) {
+ oreg = 0x614280 + (ffs(outp->info.or) - 1) * 0x800;
+ oval = 0x00000000;
+ hval = 0x00000000;
+ mask = 0xffffffff;
+ } else
+ if (!outp->info.location) {
+ if (outp->info.type == DCB_OUTPUT_DP)
+ nv50_disp_intr_unk20_2_dp(priv, &outp->info, pclk);
+ oreg = 0x614300 + (ffs(outp->info.or) - 1) * 0x800;
+ oval = (conf & 0x0100) ? 0x00000101 : 0x00000000;
+ hval = 0x00000000;
+ mask = 0x00000707;
+ } else {
+ oreg = 0x614380 + (ffs(outp->info.or) - 1) * 0x800;
+ oval = 0x00000001;
+ hval = 0x00000001;
+ mask = 0x00000707;
+ }
+
+ nv_mask(priv, hreg, 0x0000000f, hval);
+ nv_mask(priv, oreg, mask, oval);
}
/* If programming a TMDS output on a SOR that can also be configured for
@@ -1521,30 +1579,16 @@ nv50_disp_intr_unk40_0_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp
static void
nv50_disp_intr_unk40_0(struct nv50_disp_priv *priv, int head)
{
- struct dcb_output outp;
+ struct nvkm_output *outp;
u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
- if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) {
- if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS)
- nv50_disp_intr_unk40_0_tmds(priv, &outp);
- else
- if (outp.location == 1 && outp.type == DCB_OUTPUT_DP) {
- u32 soff = (ffs(outp.or) - 1) * 0x08;
- u32 ctrl = nv_rd32(priv, 0x610b84 + soff);
- u32 datarate;
-
- switch ((ctrl & 0x000f0000) >> 16) {
- case 6: datarate = pclk * 30 / 8; break;
- case 5: datarate = pclk * 24 / 8; break;
- case 2:
- default:
- datarate = pclk * 18 / 8;
- break;
- }
+ u32 conf;
- nouveau_dp_train(&priv->base, priv->pior.dp,
- &outp, head, datarate);
- }
- }
+ outp = exec_clkcmp(priv, head, 1, pclk, &conf);
+ if (!outp)
+ return;
+
+ if (outp->info.location == 0 && outp->info.type == DCB_OUTPUT_TMDS)
+ nv50_disp_intr_unk40_0_tmds(priv, &outp->info);
}
void
@@ -1610,13 +1654,13 @@ nv50_disp_intr(struct nouveau_subdev *subdev)
}
if (intr1 & 0x00000004) {
- nouveau_event_trigger(priv->base.vblank, 0);
+ nouveau_event_trigger(priv->base.vblank, 1, 0);
nv_wr32(priv, 0x610024, 0x00000004);
intr1 &= ~0x00000004;
}
if (intr1 & 0x00000008) {
- nouveau_event_trigger(priv->base.vblank, 1);
+ nouveau_event_trigger(priv->base.vblank, 1, 1);
nv_wr32(priv, 0x610024, 0x00000008);
intr1 &= ~0x00000008;
}
@@ -1656,11 +1700,16 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power;
priv->pior.power = nv50_pior_power;
- priv->pior.dp = &nv50_pior_dp_func;
return 0;
}
struct nouveau_oclass *
+nv50_disp_outp_sclass[] = {
+ &nv50_pior_dp_impl.base.base,
+ NULL
+};
+
+struct nouveau_oclass *
nv50_disp_oclass = &(struct nv50_disp_impl) {
.base.base.handle = NV_ENGINE(DISP, 0x50),
.base.base.ofuncs = &(struct nouveau_ofuncs) {
@@ -1669,6 +1718,7 @@ nv50_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .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,
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
index 48d59db47f0d..1a886472b6f5 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
@@ -11,6 +11,8 @@
#include "dport.h"
#include "priv.h"
+#include "outp.h"
+#include "outpdp.h"
struct nv50_disp_impl {
struct nouveau_disp_impl base;
@@ -43,13 +45,11 @@ struct nv50_disp_priv {
int (*hda_eld)(struct nv50_disp_priv *, int sor, u8 *, u32);
int (*hdmi)(struct nv50_disp_priv *, int head, int sor, u32);
u32 lvdsconf;
- const struct nouveau_dp_func *dp;
} sor;
struct {
int nr;
int (*power)(struct nv50_disp_priv *, int ext, u32 data);
u8 type[3];
- const struct nouveau_dp_func *dp;
} pior;
};
@@ -199,4 +199,14 @@ void nvd0_disp_intr(struct nouveau_subdev *);
extern const struct nv50_disp_mthd_chan nve0_disp_mast_mthd_chan;
extern const struct nv50_disp_mthd_chan nve0_disp_ovly_mthd_chan;
+extern struct nvkm_output_dp_impl nv50_pior_dp_impl;
+extern struct nouveau_oclass *nv50_disp_outp_sclass[];
+
+extern struct nvkm_output_dp_impl nv94_sor_dp_impl;
+int nv94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int);
+extern struct nouveau_oclass *nv94_disp_outp_sclass[];
+
+extern struct nvkm_output_dp_impl nvd0_sor_dp_impl;
+extern struct nouveau_oclass *nvd0_disp_outp_sclass[];
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
index 98c5b19bc2b0..1cc62e434683 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
@@ -264,7 +264,6 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->sor.power = nv50_sor_power;
priv->sor.hdmi = nv84_hdmi_ctrl;
priv->pior.power = nv50_pior_power;
- priv->pior.dp = &nv50_pior_dp_func;
return 0;
}
@@ -277,6 +276,7 @@ nv84_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .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,
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
index 6844061c7e04..4f718a9f5aef 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
@@ -77,6 +77,7 @@ nv94_disp_base_omthds[] = {
{ 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 },
@@ -122,13 +123,18 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->dac.sense = nv50_dac_sense;
priv->sor.power = nv50_sor_power;
priv->sor.hdmi = nv84_hdmi_ctrl;
- priv->sor.dp = &nv94_sor_dp_func;
priv->pior.power = nv50_pior_power;
- priv->pior.dp = &nv50_pior_dp_func;
return 0;
}
struct nouveau_oclass *
+nv94_disp_outp_sclass[] = {
+ &nv50_pior_dp_impl.base.base,
+ &nv94_sor_dp_impl.base.base,
+ NULL
+};
+
+struct nouveau_oclass *
nv94_disp_oclass = &(struct nv50_disp_impl) {
.base.base.handle = NV_ENGINE(DISP, 0x88),
.base.base.ofuncs = &(struct nouveau_ofuncs) {
@@ -137,6 +143,7 @@ nv94_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .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,
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
index 88c96241c02a..6237a9a36f70 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c
@@ -126,7 +126,6 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->sor.power = nv50_sor_power;
priv->sor.hdmi = nv84_hdmi_ctrl;
priv->pior.power = nv50_pior_power;
- priv->pior.dp = &nv50_pior_dp_func;
return 0;
}
@@ -139,6 +138,7 @@ nva0_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .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,
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
index 46cb2ce0e82a..019124d4782b 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
@@ -50,6 +50,7 @@ nva3_disp_base_omthds[] = {
{ 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 },
@@ -96,9 +97,7 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nva3_hda_eld;
priv->sor.hdmi = nva3_hdmi_ctrl;
- priv->sor.dp = &nv94_sor_dp_func;
priv->pior.power = nv50_pior_power;
- priv->pior.dp = &nv50_pior_dp_func;
return 0;
}
@@ -111,6 +110,7 @@ nva3_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .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,
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
index 876de9ac3793..48aa38a87e3f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
@@ -748,13 +748,13 @@ nvd0_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd,
}
static void
-nvd0_disp_base_vblank_enable(struct nouveau_event *event, int head)
+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 head)
+nvd0_disp_base_vblank_disable(struct nouveau_event *event, int type, int head)
{
nv_mask(event->priv, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000);
}
@@ -887,6 +887,7 @@ nvd0_disp_base_omthds[] = {
{ 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 },
@@ -915,19 +916,20 @@ nvd0_disp_sclass[] = {
* Display engine implementation
******************************************************************************/
-static u16
-exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
- struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+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,
struct nvbios_outp *info)
{
struct nouveau_bios *bios = nouveau_bios(priv);
- u16 mask, type, data;
+ struct nvkm_output *outp;
+ u16 mask, type;
- if (outp < 4) {
+ if (or < 4) {
type = DCB_OUTPUT_ANALOG;
mask = 0;
} else {
- outp -= 4;
+ or -= 4;
switch (ctrl & 0x00000f00) {
case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
@@ -939,101 +941,106 @@ exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
return 0x0000;
}
- dcb->sorconf.link = mask;
}
mask = 0x00c0 & (mask << 6);
- mask |= 0x0001 << outp;
+ mask |= 0x0001 << or;
mask |= 0x0100 << head;
- data = dcb_outp_match(bios, type, mask, ver, hdr, dcb);
- if (!data)
- return 0x0000;
+ list_for_each_entry(outp, &priv->base.outp, head) {
+ if ((outp->info.hasht & 0xff) == type &&
+ (outp->info.hashm & mask) == mask) {
+ *data = nvbios_outp_match(bios, outp->info.hasht,
+ outp->info.hashm,
+ ver, hdr, cnt, len, info);
+ if (!*data)
+ return NULL;
+ return outp;
+ }
+ }
- return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
+ return NULL;
}
-static bool
+static struct nvkm_output *
exec_script(struct nv50_disp_priv *priv, int head, int id)
{
struct nouveau_bios *bios = nouveau_bios(priv);
+ struct nvkm_output *outp;
struct nvbios_outp info;
- struct dcb_output dcb;
u8 ver, hdr, cnt, len;
- u32 ctrl = 0x00000000;
- u16 data;
- int outp;
+ u32 data, ctrl = 0;
+ int or;
- for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
- ctrl = nv_rd32(priv, 0x640180 + (outp * 0x20));
+ for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) {
+ ctrl = nv_rd32(priv, 0x640180 + (or * 0x20));
if (ctrl & (1 << head))
break;
}
- if (outp == 8)
- return false;
+ if (or == 8)
+ return NULL;
- data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);
- if (data) {
+ outp = exec_lookup(priv, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
+ if (outp) {
struct nvbios_init init = {
.subdev = nv_subdev(priv),
.bios = bios,
.offset = info.script[id],
- .outp = &dcb,
+ .outp = &outp->info,
.crtc = head,
.execute = 1,
};
- return nvbios_exec(&init) == 0;
+ nvbios_exec(&init);
}
- return false;
+ return outp;
}
-static u32
-exec_clkcmp(struct nv50_disp_priv *priv, int head, int id,
- u32 pclk, struct dcb_output *dcb)
+static struct nvkm_output *
+exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, u32 *conf)
{
struct nouveau_bios *bios = nouveau_bios(priv);
+ struct nvkm_output *outp;
struct nvbios_outp info1;
struct nvbios_ocfg info2;
u8 ver, hdr, cnt, len;
- u32 ctrl = 0x00000000;
- u32 data, conf = ~0;
- int outp;
+ u32 data, ctrl = 0;
+ int or;
- for (outp = 0; !(ctrl & (1 << head)) && outp < 8; outp++) {
- ctrl = nv_rd32(priv, 0x660180 + (outp * 0x20));
+ for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) {
+ ctrl = nv_rd32(priv, 0x660180 + (or * 0x20));
if (ctrl & (1 << head))
break;
}
- if (outp == 8)
- return conf;
+ if (or == 8)
+ return NULL;
- data = exec_lookup(priv, head, outp, ctrl, dcb, &ver, &hdr, &cnt, &len, &info1);
- if (data == 0x0000)
- return conf;
+ outp = exec_lookup(priv, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
+ if (!outp)
+ return NULL;
- switch (dcb->type) {
+ switch (outp->info.type) {
case DCB_OUTPUT_TMDS:
- conf = (ctrl & 0x00000f00) >> 8;
+ *conf = (ctrl & 0x00000f00) >> 8;
if (pclk >= 165000)
- conf |= 0x0100;
+ *conf |= 0x0100;
break;
case DCB_OUTPUT_LVDS:
- conf = priv->sor.lvdsconf;
+ *conf = priv->sor.lvdsconf;
break;
case DCB_OUTPUT_DP:
- conf = (ctrl & 0x00000f00) >> 8;
+ *conf = (ctrl & 0x00000f00) >> 8;
break;
case DCB_OUTPUT_ANALOG:
default:
- conf = 0x00ff;
+ *conf = 0x00ff;
break;
}
- data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
+ data = nvbios_ocfg_match(bios, data, *conf, &ver, &hdr, &cnt, &len, &info2);
if (data && id < 0xff) {
data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
if (data) {
@@ -1041,7 +1048,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id,
.subdev = nv_subdev(priv),
.bios = bios,
.offset = data,
- .outp = dcb,
+ .outp = &outp->info,
.crtc = head,
.execute = 1,
};
@@ -1050,7 +1057,7 @@ exec_clkcmp(struct nv50_disp_priv *priv, int head, int id,
}
}
- return conf;
+ return outp;
}
static void
@@ -1062,7 +1069,23 @@ nvd0_disp_intr_unk1_0(struct nv50_disp_priv *priv, int head)
static void
nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head)
{
- exec_script(priv, head, 2);
+ struct nvkm_output *outp = exec_script(priv, head, 2);
+
+ /* see note in nv50_disp_intr_unk20_0() */
+ if (outp && outp->info.type == DCB_OUTPUT_DP) {
+ struct nvkm_output_dp *outpdp = (void *)outp;
+ struct nvbios_init init = {
+ .subdev = nv_subdev(priv),
+ .bios = nouveau_bios(priv),
+ .outp = &outp->info,
+ .crtc = head,
+ .offset = outpdp->info.script[4],
+ .execute = 1,
+ };
+
+ nvbios_exec(&init);
+ atomic_set(&outpdp->lt.done, 0);
+ }
}
static void
@@ -1124,49 +1147,52 @@ nvd0_disp_intr_unk2_2_tu(struct nv50_disp_priv *priv, int head,
static void
nvd0_disp_intr_unk2_2(struct nv50_disp_priv *priv, int head)
{
- struct dcb_output outp;
+ struct nvkm_output *outp;
u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
- u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
- if (conf != ~0) {
- u32 addr, data;
-
- if (outp.type == DCB_OUTPUT_DP) {
- u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300));
- switch ((sync & 0x000003c0) >> 6) {
- case 6: pclk = pclk * 30 / 8; break;
- case 5: pclk = pclk * 24 / 8; break;
- case 2:
- default:
- pclk = pclk * 18 / 8;
- break;
- }
-
- nouveau_dp_train(&priv->base, priv->sor.dp,
- &outp, head, pclk);
+ u32 conf, addr, data;
+
+ outp = exec_clkcmp(priv, head, 0xff, pclk, &conf);
+ if (!outp)
+ return;
+
+ /* see note in nv50_disp_intr_unk20_2() */
+ if (outp->info.type == DCB_OUTPUT_DP) {
+ u32 sync = nv_rd32(priv, 0x660404 + (head * 0x300));
+ switch ((sync & 0x000003c0) >> 6) {
+ case 6: pclk = pclk * 30 / 8; break;
+ case 5: pclk = pclk * 24 / 8; break;
+ case 2:
+ default:
+ pclk = pclk * 18 / 8;
+ break;
}
- exec_clkcmp(priv, head, 0, pclk, &outp);
+ if (nvkm_output_dp_train(outp, pclk, true))
+ ERR("link not trained before attach\n");
+ }
- if (outp.type == DCB_OUTPUT_ANALOG) {
- addr = 0x612280 + (ffs(outp.or) - 1) * 0x800;
- data = 0x00000000;
- } else {
- if (outp.type == DCB_OUTPUT_DP)
- nvd0_disp_intr_unk2_2_tu(priv, head, &outp);
- addr = 0x612300 + (ffs(outp.or) - 1) * 0x800;
- data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
- }
+ exec_clkcmp(priv, head, 0, pclk, &conf);
- nv_mask(priv, addr, 0x00000707, data);
+ if (outp->info.type == DCB_OUTPUT_ANALOG) {
+ addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800;
+ data = 0x00000000;
+ } else {
+ if (outp->info.type == DCB_OUTPUT_DP)
+ nvd0_disp_intr_unk2_2_tu(priv, head, &outp->info);
+ addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800;
+ data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
}
+
+ nv_mask(priv, addr, 0x00000707, data);
}
static void
nvd0_disp_intr_unk4_0(struct nv50_disp_priv *priv, int head)
{
- struct dcb_output outp;
u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000;
- exec_clkcmp(priv, head, 1, pclk, &outp);
+ u32 conf;
+
+ exec_clkcmp(priv, head, 1, pclk, &conf);
}
void
@@ -1240,7 +1266,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid)
chid, (mthd & 0x0000ffc), data, mthd, unkn);
if (chid == 0) {
- switch (mthd) {
+ switch (mthd & 0xffc) {
case 0x0080:
nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 0,
impl->mthd.core);
@@ -1250,7 +1276,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid)
}
} else
if (chid <= 4) {
- switch (mthd) {
+ switch (mthd & 0xffc) {
case 0x0080:
nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 1,
impl->mthd.base);
@@ -1260,7 +1286,7 @@ nvd0_disp_intr_error(struct nv50_disp_priv *priv, int chid)
}
} else
if (chid <= 8) {
- switch (mthd) {
+ switch (mthd & 0xffc) {
case 0x0080:
nv50_disp_mthd_chan(priv, NV_DBG_ERROR, chid - 5,
impl->mthd.ovly);
@@ -1317,7 +1343,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, i);
+ nouveau_event_trigger(priv->base.vblank, 1, i);
nv_mask(priv, 0x6100bc + (i * 0x800), 0, 0);
nv_rd32(priv, 0x6100c0 + (i * 0x800));
}
@@ -1352,11 +1378,16 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld;
priv->sor.hdmi = nvd0_hdmi_ctrl;
- priv->sor.dp = &nvd0_sor_dp_func;
return 0;
}
struct nouveau_oclass *
+nvd0_disp_outp_sclass[] = {
+ &nvd0_sor_dp_impl.base.base,
+ NULL
+};
+
+struct nouveau_oclass *
nvd0_disp_oclass = &(struct nv50_disp_impl) {
.base.base.handle = NV_ENGINE(DISP, 0x90),
.base.base.ofuncs = &(struct nouveau_ofuncs) {
@@ -1365,6 +1396,7 @@ nvd0_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .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,
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
index 44e0b8f34c1a..11328e3f5df1 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c
@@ -246,7 +246,6 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld;
priv->sor.hdmi = nvd0_hdmi_ctrl;
- priv->sor.dp = &nvd0_sor_dp_func;
return 0;
}
@@ -259,6 +258,7 @@ nve0_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .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,
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c
index 482585d375fa..104388081d73 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c
@@ -81,7 +81,6 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->sor.power = nv50_sor_power;
priv->sor.hda_eld = nvd0_hda_eld;
priv->sor.hdmi = nvd0_hdmi_ctrl;
- priv->sor.dp = &nvd0_sor_dp_func;
return 0;
}
@@ -94,6 +93,7 @@ nvf0_disp_oclass = &(struct nv50_disp_impl) {
.init = _nouveau_disp_init,
.fini = _nouveau_disp_fini,
},
+ .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,
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outp.c
new file mode 100644
index 000000000000..ad9ba7ccec7f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/outp.c
@@ -0,0 +1,137 @@
+/*
+ * 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 <subdev/i2c.h>
+#include <subdev/bios.h>
+#include <subdev/bios/conn.h>
+
+#include "outp.h"
+
+int
+_nvkm_output_fini(struct nouveau_object *object, bool suspend)
+{
+ struct nvkm_output *outp = (void *)object;
+ nv_ofuncs(outp->conn)->fini(nv_object(outp->conn), suspend);
+ return nouveau_object_fini(&outp->base, suspend);
+}
+
+int
+_nvkm_output_init(struct nouveau_object *object)
+{
+ struct nvkm_output *outp = (void *)object;
+ int ret = nouveau_object_init(&outp->base);
+ if (ret == 0)
+ nv_ofuncs(outp->conn)->init(nv_object(outp->conn));
+ return 0;
+}
+
+void
+_nvkm_output_dtor(struct nouveau_object *object)
+{
+ struct nvkm_output *outp = (void *)object;
+ list_del(&outp->head);
+ nouveau_object_ref(NULL, (void *)&outp->conn);
+ nouveau_object_destroy(&outp->base);
+}
+
+int
+nvkm_output_create_(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass,
+ struct dcb_output *dcbE, int index,
+ int length, void **pobject)
+{
+ struct nouveau_bios *bios = nouveau_bios(engine);
+ struct nouveau_i2c *i2c = nouveau_i2c(parent);
+ struct nouveau_disp *disp = (void *)engine;
+ struct nvbios_connE connE;
+ struct nvkm_output *outp;
+ u8 ver, hdr;
+ u32 data;
+ int ret;
+
+ ret = nouveau_object_create_(parent, engine, oclass, 0, length, pobject);
+ outp = *pobject;
+ if (ret)
+ return ret;
+
+ outp->info = *dcbE;
+ outp->index = index;
+
+ 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 ?
+ dcbE->sorconf.link : 0, dcbE->connector, dcbE->i2c_index,
+ dcbE->bus, dcbE->heads);
+
+ outp->port = i2c->find(i2c, outp->info.i2c_index);
+ outp->edid = outp->port;
+
+ data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, &connE);
+ if (!data) {
+ DBG("vbios connector data not found\n");
+ memset(&connE, 0x00, sizeof(connE));
+ connE.type = DCB_CONNECTOR_NONE;
+ }
+
+ ret = nouveau_object_ctor(parent, engine, nvkm_connector_oclass,
+ &connE, outp->info.connector,
+ (struct nouveau_object **)&outp->conn);
+ if (ret < 0) {
+ ERR("error %d creating connector, disabling\n", ret);
+ return ret;
+ }
+
+ list_add_tail(&outp->head, &disp->outp);
+ return 0;
+}
+
+int
+_nvkm_output_ctor(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *dcbE, u32 index,
+ struct nouveau_object **pobject)
+{
+ struct nvkm_output *outp;
+ int ret;
+
+ ret = nvkm_output_create(parent, engine, oclass, dcbE, index, &outp);
+ *pobject = nv_object(outp);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nouveau_oclass *
+nvkm_output_oclass = &(struct nvkm_output_impl) {
+ .base = {
+ .handle = 0,
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nvkm_output_ctor,
+ .dtor = _nvkm_output_dtor,
+ .init = _nvkm_output_init,
+ .fini = _nvkm_output_fini,
+ },
+ },
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.h b/drivers/gpu/drm/nouveau/core/engine/disp/outp.h
new file mode 100644
index 000000000000..bc76fbf85710
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/outp.h
@@ -0,0 +1,59 @@
+#ifndef __NVKM_DISP_OUTP_H__
+#define __NVKM_DISP_OUTP_H__
+
+#include "priv.h"
+
+struct nvkm_output {
+ struct nouveau_object base;
+ struct list_head head;
+
+ struct dcb_output info;
+ int index;
+
+ struct nouveau_i2c_port *port;
+ struct nouveau_i2c_port *edid;
+
+ struct nvkm_connector *conn;
+};
+
+#define nvkm_output_create(p,e,c,b,i,d) \
+ nvkm_output_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d)
+#define nvkm_output_destroy(d) ({ \
+ struct nvkm_output *_outp = (d); \
+ _nvkm_output_dtor(nv_object(_outp)); \
+})
+#define nvkm_output_init(d) ({ \
+ struct nvkm_output *_outp = (d); \
+ _nvkm_output_init(nv_object(_outp)); \
+})
+#define nvkm_output_fini(d,s) ({ \
+ struct nvkm_output *_outp = (d); \
+ _nvkm_output_fini(nv_object(_outp), (s)); \
+})
+
+int nvkm_output_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, struct dcb_output *,
+ int, int, void **);
+
+int _nvkm_output_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+void _nvkm_output_dtor(struct nouveau_object *);
+int _nvkm_output_init(struct nouveau_object *);
+int _nvkm_output_fini(struct nouveau_object *, bool);
+
+struct nvkm_output_impl {
+ struct nouveau_oclass base;
+};
+
+#ifndef MSG
+#define MSG(l,f,a...) do { \
+ struct nvkm_output *_outp = (void *)outp; \
+ nv_##l(nv_object(outp)->engine, "%02x:%04x:%04x: "f, _outp->index, \
+ _outp->info.hasht, _outp->info.hashm, ##a); \
+} while(0)
+#define DBG(f,a...) MSG(debug, f, ##a)
+#define ERR(f,a...) MSG(error, f, ##a)
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c
new file mode 100644
index 000000000000..52c299c3d300
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.c
@@ -0,0 +1,276 @@
+/*
+ * 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 <subdev/i2c.h>
+
+#include "outpdp.h"
+#include "conn.h"
+#include "dport.h"
+
+int
+nvkm_output_dp_train(struct nvkm_output *base, u32 datarate, bool wait)
+{
+ struct nvkm_output_dp *outp = (void *)base;
+ bool retrain = true;
+ u8 link[2], stat[3];
+ u32 rate;
+ int ret, i;
+
+ /* check that the link is trained at a high enough rate */
+ ret = nv_rdaux(outp->base.edid, DPCD_LC00_LINK_BW_SET, link, 2);
+ if (ret) {
+ DBG("failed to read link config, assuming no sink\n");
+ goto done;
+ }
+
+ rate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET);
+ if (rate < ((datarate / 8) * 10)) {
+ DBG("link not trained at sufficient rate\n");
+ goto done;
+ }
+
+ /* check that link is still trained */
+ ret = nv_rdaux(outp->base.edid, DPCD_LS02, stat, 3);
+ if (ret) {
+ DBG("failed to read link status, assuming no sink\n");
+ goto done;
+ }
+
+ if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) {
+ for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); i++) {
+ u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f;
+ if (!(lane & DPCD_LS02_LANE0_CR_DONE) ||
+ !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
+ !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) {
+ DBG("lane %d not equalised\n", lane);
+ goto done;
+ }
+ }
+ retrain = false;
+ } else {
+ DBG("no inter-lane alignment\n");
+ }
+
+done:
+ if (retrain || !atomic_read(&outp->lt.done)) {
+ /* no sink, but still need to configure source */
+ if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) {
+ outp->dpcd[DPCD_RC01_MAX_LINK_RATE] =
+ outp->base.info.dpconf.link_bw;
+ outp->dpcd[DPCD_RC02] =
+ outp->base.info.dpconf.link_nr;
+ }
+ atomic_set(&outp->lt.done, 0);
+ schedule_work(&outp->lt.work);
+ } else {
+ nouveau_event_get(outp->irq);
+ }
+
+ if (wait) {
+ if (!wait_event_timeout(outp->lt.wait,
+ atomic_read(&outp->lt.done),
+ msecs_to_jiffies(2000)))
+ ret = -ETIMEDOUT;
+ }
+
+ return ret;
+}
+
+static void
+nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool present)
+{
+ struct nouveau_i2c_port *port = outp->base.edid;
+ if (present) {
+ if (!outp->present) {
+ nouveau_i2c(port)->acquire_pad(port, 0);
+ DBG("aux power -> always\n");
+ outp->present = true;
+ }
+ nvkm_output_dp_train(&outp->base, 0, true);
+ } else {
+ if (outp->present) {
+ nouveau_i2c(port)->release_pad(port);
+ DBG("aux power -> demand\n");
+ outp->present = false;
+ }
+ atomic_set(&outp->lt.done, 0);
+ }
+}
+
+static void
+nvkm_output_dp_detect(struct nvkm_output_dp *outp)
+{
+ struct nouveau_i2c_port *port = outp->base.edid;
+ int ret = nouveau_i2c(port)->acquire_pad(port, 0);
+ if (ret == 0) {
+ ret = nv_rdaux(outp->base.edid, DPCD_RC00_DPCD_REV,
+ outp->dpcd, sizeof(outp->dpcd));
+ nvkm_output_dp_enable(outp, ret == 0);
+ nouveau_i2c(port)->release_pad(port);
+ }
+}
+
+static void
+nvkm_output_dp_service_work(struct work_struct *work)
+{
+ 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;
+ }
+
+ nouveau_event_trigger(disp->hpd, send, outp->base.info.connector);
+}
+
+static int
+nvkm_output_dp_service(void *data, u32 type, int index)
+{
+ struct nvkm_output_dp *outp = data;
+ DBG("HPD: %d\n", type);
+ atomic_or(type, &outp->pending);
+ schedule_work(&outp->work);
+ return NVKM_EVENT_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_output_dp_enable(outp, false);
+ return nvkm_output_fini(&outp->base, suspend);
+}
+
+int
+_nvkm_output_dp_init(struct nouveau_object *object)
+{
+ struct nvkm_output_dp *outp = (void *)object;
+ nvkm_output_dp_detect(outp);
+ return nvkm_output_init(&outp->base);
+}
+
+void
+_nvkm_output_dp_dtor(struct nouveau_object *object)
+{
+ struct nvkm_output_dp *outp = (void *)object;
+ nouveau_event_ref(NULL, &outp->irq);
+ nvkm_output_destroy(&outp->base);
+}
+
+int
+nvkm_output_dp_create_(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass,
+ struct dcb_output *info, int index,
+ int length, void **pobject)
+{
+ struct nouveau_bios *bios = nouveau_bios(parent);
+ struct nouveau_i2c *i2c = nouveau_i2c(parent);
+ struct nvkm_output_dp *outp;
+ u8 hdr, cnt, len;
+ u32 data;
+ int ret;
+
+ ret = nvkm_output_create_(parent, engine, oclass, info, index,
+ length, pobject);
+ outp = *pobject;
+ if (ret)
+ return ret;
+
+ nouveau_event_ref(NULL, &outp->base.conn->hpd.event);
+
+ /* access to the aux channel is not optional... */
+ if (!outp->base.edid) {
+ ERR("aux channel not found\n");
+ return -ENODEV;
+ }
+
+ /* nor is the bios data for this output... */
+ data = nvbios_dpout_match(bios, outp->base.info.hasht,
+ outp->base.info.hashm, &outp->version,
+ &hdr, &cnt, &len, &outp->info);
+ if (!data) {
+ ERR("no bios dp data\n");
+ return -ENODEV;
+ }
+
+ DBG("bios dp %02x %02x %02x %02x\n", outp->version, hdr, cnt, len);
+
+ /* link training */
+ INIT_WORK(&outp->lt.work, nouveau_dp_train);
+ init_waitqueue_head(&outp->lt.wait);
+ 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);
+ 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);
+ if (ret) {
+ ERR("error monitoring aux hpd events: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+_nvkm_output_dp_ctor(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *info, u32 index,
+ struct nouveau_object **pobject)
+{
+ struct nvkm_output_dp *outp;
+ int ret;
+
+ ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp);
+ *pobject = nv_object(outp);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h
new file mode 100644
index 000000000000..ff33ba12cb67
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/outpdp.h
@@ -0,0 +1,65 @@
+#ifndef __NVKM_DISP_OUTP_DP_H__
+#define __NVKM_DISP_OUTP_DP_H__
+
+#include <subdev/bios.h>
+#include <subdev/bios/dp.h>
+
+#include "outp.h"
+
+struct nvkm_output_dp {
+ struct nvkm_output base;
+
+ struct nvbios_dpout info;
+ u8 version;
+
+ struct nouveau_eventh *irq;
+ struct nouveau_eventh *hpd;
+ struct work_struct work;
+ atomic_t pending;
+ bool present;
+ u8 dpcd[16];
+
+ struct {
+ struct work_struct work;
+ wait_queue_head_t wait;
+ atomic_t done;
+ } lt;
+};
+
+#define nvkm_output_dp_create(p,e,c,b,i,d) \
+ nvkm_output_dp_create_((p), (e), (c), (b), (i), sizeof(**d), (void **)d)
+#define nvkm_output_dp_destroy(d) ({ \
+ struct nvkm_output_dp *_outp = (d); \
+ _nvkm_output_dp_dtor(nv_object(_outp)); \
+})
+#define nvkm_output_dp_init(d) ({ \
+ struct nvkm_output_dp *_outp = (d); \
+ _nvkm_output_dp_init(nv_object(_outp)); \
+})
+#define nvkm_output_dp_fini(d,s) ({ \
+ struct nvkm_output_dp *_outp = (d); \
+ _nvkm_output_dp_fini(nv_object(_outp), (s)); \
+})
+
+int nvkm_output_dp_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, struct dcb_output *,
+ int, int, void **);
+
+int _nvkm_output_dp_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+void _nvkm_output_dp_dtor(struct nouveau_object *);
+int _nvkm_output_dp_init(struct nouveau_object *);
+int _nvkm_output_dp_fini(struct nouveau_object *, bool);
+
+struct nvkm_output_dp_impl {
+ struct nvkm_output_impl base;
+ int (*pattern)(struct nvkm_output_dp *, int);
+ int (*lnk_pwr)(struct nvkm_output_dp *, int nr);
+ int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef);
+ int (*drv_ctl)(struct nvkm_output_dp *, int ln, int vs, int pe, int pc);
+};
+
+int nvkm_output_dp_train(struct nvkm_output *, u32 rate, bool wait);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c
index 2c8ce351b52d..fe0f256f11bf 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/piornv50.c
@@ -33,68 +33,107 @@
#include "nv50.h"
/******************************************************************************
- * DisplayPort
+ * TMDS
*****************************************************************************/
-static struct nouveau_i2c_port *
-nv50_pior_dp_find(struct nouveau_disp *disp, struct dcb_output *outp)
+
+static int
+nv50_pior_tmds_ctor(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *info, u32 index,
+ struct nouveau_object **pobject)
{
- struct nouveau_i2c *i2c = nouveau_i2c(disp);
- return i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
+ struct nouveau_i2c *i2c = nouveau_i2c(parent);
+ struct nvkm_output *outp;
+ int ret;
+
+ ret = nvkm_output_create(parent, engine, oclass, info, index, &outp);
+ *pobject = nv_object(outp);
+ if (ret)
+ return ret;
+
+ outp->edid = i2c->find_type(i2c, NV_I2C_TYPE_EXTDDC(outp->info.extdev));
+ return 0;
}
+struct nvkm_output_impl
+nv50_pior_tmds_impl = {
+ .base.handle = DCB_OUTPUT_TMDS | 0x0100,
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nv50_pior_tmds_ctor,
+ .dtor = _nvkm_output_dtor,
+ .init = _nvkm_output_init,
+ .fini = _nvkm_output_fini,
+ },
+};
+
+/******************************************************************************
+ * DisplayPort
+ *****************************************************************************/
+
static int
-nv50_pior_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
- int head, int pattern)
+nv50_pior_dp_pattern(struct nvkm_output_dp *outp, int pattern)
{
- struct nouveau_i2c_port *port;
- int ret = -EINVAL;
-
- port = nv50_pior_dp_find(disp, outp);
- if (port) {
- if (port->func->pattern)
- ret = port->func->pattern(port, pattern);
- else
- ret = 0;
- }
-
- return ret;
+ struct nouveau_i2c_port *port = outp->base.edid;
+ if (port && port->func->pattern)
+ return port->func->pattern(port, pattern);
+ return port ? 0 : -ENODEV;
}
static int
-nv50_pior_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
- int head, int lane_nr, int link_bw, bool enh)
+nv50_pior_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
{
- struct nouveau_i2c_port *port;
- int ret = -EINVAL;
+ return 0;
+}
- port = nv50_pior_dp_find(disp, outp);
+static int
+nv50_pior_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
+{
+ struct nouveau_i2c_port *port = outp->base.edid;
if (port && port->func->lnk_ctl)
- ret = port->func->lnk_ctl(port, lane_nr, link_bw, enh);
+ return port->func->lnk_ctl(port, nr, bw, ef);
+ return port ? 0 : -ENODEV;
+}
- return ret;
+static int
+nv50_pior_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
+{
+ struct nouveau_i2c_port *port = outp->base.edid;
+ if (port && port->func->drv_ctl)
+ return port->func->drv_ctl(port, ln, vs, pe);
+ return port ? 0 : -ENODEV;
}
static int
-nv50_pior_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
- int head, int lane, int vsw, int pre)
+nv50_pior_dp_ctor(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *info, u32 index,
+ struct nouveau_object **pobject)
{
- struct nouveau_i2c_port *port;
- int ret = -EINVAL;
-
- port = nv50_pior_dp_find(disp, outp);
- if (port) {
- if (port->func->drv_ctl)
- ret = port->func->drv_ctl(port, lane, vsw, pre);
- else
- ret = 0;
- }
+ struct nouveau_i2c *i2c = nouveau_i2c(parent);
+ struct nvkm_output_dp *outp;
+ int ret;
- return ret;
+ ret = nvkm_output_dp_create(parent, engine, oclass, info, index, &outp);
+ *pobject = nv_object(outp);
+ if (ret)
+ return ret;
+
+ outp->base.edid = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(
+ outp->base.info.extdev));
+ return 0;
}
-const struct nouveau_dp_func
-nv50_pior_dp_func = {
+struct nvkm_output_dp_impl
+nv50_pior_dp_impl = {
+ .base.base.handle = DCB_OUTPUT_DP | 0x0010,
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nv50_pior_dp_ctor,
+ .dtor = _nvkm_output_dp_dtor,
+ .init = _nvkm_output_dp_init,
+ .fini = _nvkm_output_dp_fini,
+ },
.pattern = nv50_pior_dp_pattern,
+ .lnk_pwr = nv50_pior_dp_lnk_pwr,
.lnk_ctl = nv50_pior_dp_lnk_ctl,
.drv_ctl = nv50_pior_dp_drv_ctl,
};
@@ -102,6 +141,7 @@ nv50_pior_dp_func = {
/******************************************************************************
* General PIOR handling
*****************************************************************************/
+
int
nv50_pior_power(struct nv50_disp_priv *priv, int or, u32 data)
{
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/priv.h b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h
index cc3c7a4ca747..26e9a42569c7 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/priv.h
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/priv.h
@@ -1,10 +1,42 @@
#ifndef __NVKM_DISP_PRIV_H__
#define __NVKM_DISP_PRIV_H__
+#include <subdev/bios.h>
+#include <subdev/bios/dcb.h>
+#include <subdev/bios/conn.h>
+
#include <engine/disp.h>
struct nouveau_disp_impl {
struct nouveau_oclass base;
+ struct nouveau_oclass **outp;
+ struct nouveau_oclass **conn;
};
+#define nouveau_disp_create(p,e,c,h,i,x,d) \
+ nouveau_disp_create_((p), (e), (c), (h), (i), (x), \
+ sizeof(**d), (void **)d)
+#define nouveau_disp_destroy(d) ({ \
+ struct nouveau_disp *disp = (d); \
+ _nouveau_disp_dtor(nv_object(disp)); \
+})
+#define nouveau_disp_init(d) ({ \
+ struct nouveau_disp *disp = (d); \
+ _nouveau_disp_init(nv_object(disp)); \
+})
+#define nouveau_disp_fini(d,s) ({ \
+ struct nouveau_disp *disp = (d); \
+ _nouveau_disp_fini(nv_object(disp), (s)); \
+})
+
+int nouveau_disp_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, int heads,
+ const char *, const char *, int, void **);
+void _nouveau_disp_dtor(struct nouveau_object *);
+int _nouveau_disp_init(struct nouveau_object *);
+int _nouveau_disp_fini(struct nouveau_object *, bool);
+
+extern struct nouveau_oclass *nvkm_output_oclass;
+extern struct nouveau_oclass *nvkm_connector_oclass;
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
index 526b75242899..e1832778e8b6 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c
@@ -47,8 +47,12 @@ 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;
@@ -56,6 +60,13 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
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:
@@ -71,6 +82,23 @@ nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
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:
+ ((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);
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
index eea3ef59693d..05487cda84a8 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c
@@ -29,19 +29,21 @@
#include <subdev/bios/dcb.h>
#include <subdev/bios/dp.h>
#include <subdev/bios/init.h>
+#include <subdev/timer.h>
#include "nv50.h"
+#include "outpdp.h"
static inline u32
-nv94_sor_soff(struct dcb_output *outp)
+nv94_sor_soff(struct nvkm_output_dp *outp)
{
- return (ffs(outp->or) - 1) * 0x800;
+ return (ffs(outp->base.info.or) - 1) * 0x800;
}
static inline u32
-nv94_sor_loff(struct dcb_output *outp)
+nv94_sor_loff(struct nvkm_output_dp *outp)
{
- return nv94_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80;
+ return nv94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
}
static inline u32
@@ -55,77 +57,96 @@ nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
}
static int
-nv94_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
- int head, int pattern)
+nv94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
{
- struct nv50_disp_priv *priv = (void *)disp;
+ struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
const u32 loff = nv94_sor_loff(outp);
nv_mask(priv, 0x61c10c + loff, 0x0f000000, pattern << 24);
return 0;
}
+int
+nv94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
+{
+ struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
+ const u32 soff = nv94_sor_soff(outp);
+ const u32 loff = nv94_sor_loff(outp);
+ u32 mask = 0, i;
+
+ for (i = 0; i < nr; i++)
+ mask |= 1 << (nv94_sor_dp_lane_map(priv, i) >> 3);
+
+ nv_mask(priv, 0x61c130 + loff, 0x0000000f, mask);
+ nv_mask(priv, 0x61c034 + soff, 0x80000000, 0x80000000);
+ nv_wait(priv, 0x61c034 + soff, 0x80000000, 0x00000000);
+ return 0;
+}
+
static int
-nv94_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
- int head, int link_nr, int link_bw, bool enh_frame)
+nv94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
{
- struct nv50_disp_priv *priv = (void *)disp;
+ struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
const u32 soff = nv94_sor_soff(outp);
const u32 loff = nv94_sor_loff(outp);
u32 dpctrl = 0x00000000;
u32 clksor = 0x00000000;
- u32 lane = 0;
- int i;
- dpctrl |= ((1 << link_nr) - 1) << 16;
- if (enh_frame)
+ dpctrl |= ((1 << nr) - 1) << 16;
+ if (ef)
dpctrl |= 0x00004000;
- if (link_bw > 0x06)
+ if (bw > 0x06)
clksor |= 0x00040000;
- for (i = 0; i < link_nr; i++)
- lane |= 1 << (nv94_sor_dp_lane_map(priv, i) >> 3);
-
nv_mask(priv, 0x614300 + soff, 0x000c0000, clksor);
nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl);
- nv_mask(priv, 0x61c130 + loff, 0x0000000f, lane);
return 0;
}
static int
-nv94_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
- int head, int lane, int swing, int preem)
+nv94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
{
- struct nouveau_bios *bios = nouveau_bios(disp);
- struct nv50_disp_priv *priv = (void *)disp;
- const u32 shift = nv94_sor_dp_lane_map(priv, lane);
+ struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
+ struct nouveau_bios *bios = nouveau_bios(priv);
+ const u32 shift = nv94_sor_dp_lane_map(priv, ln);
const u32 loff = nv94_sor_loff(outp);
u32 addr, data[3];
u8 ver, hdr, cnt, len;
struct nvbios_dpout info;
struct nvbios_dpcfg ocfg;
- addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm,
+ addr = nvbios_dpout_match(bios, outp->base.info.hasht,
+ outp->base.info.hashm,
&ver, &hdr, &cnt, &len, &info);
if (!addr)
return -ENODEV;
- addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem,
+ addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
&ver, &hdr, &cnt, &len, &ocfg);
if (!addr)
return -EINVAL;
data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift);
data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift);
- data[2] = nv_rd32(priv, 0x61c130 + loff) & ~(0x0000ff00);
- nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.drv << shift));
- nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pre << shift));
- nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.unk << 8));
+ data[2] = nv_rd32(priv, 0x61c130 + loff);
+ if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
+ data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
+ nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
+ nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
+ nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.tx_pu << 8));
return 0;
}
-const struct nouveau_dp_func
-nv94_sor_dp_func = {
+struct nvkm_output_dp_impl
+nv94_sor_dp_impl = {
+ .base.base.handle = DCB_OUTPUT_DP,
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nvkm_output_dp_ctor,
+ .dtor = _nvkm_output_dp_dtor,
+ .init = _nvkm_output_dp_init,
+ .fini = _nvkm_output_dp_fini,
+ },
.pattern = nv94_sor_dp_pattern,
+ .lnk_pwr = nv94_sor_dp_lnk_pwr,
.lnk_ctl = nv94_sor_dp_lnk_ctl,
.drv_ctl = nv94_sor_dp_drv_ctl,
};
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
index d2df572f16a3..97f0e9cd3d40 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c
@@ -29,19 +29,20 @@
#include <subdev/bios/dcb.h>
#include <subdev/bios/dp.h>
#include <subdev/bios/init.h>
+#include <subdev/timer.h>
#include "nv50.h"
static inline u32
-nvd0_sor_soff(struct dcb_output *outp)
+nvd0_sor_soff(struct nvkm_output_dp *outp)
{
- return (ffs(outp->or) - 1) * 0x800;
+ return (ffs(outp->base.info.or) - 1) * 0x800;
}
static inline u32
-nvd0_sor_loff(struct dcb_output *outp)
+nvd0_sor_loff(struct nvkm_output_dp *outp)
{
- return nvd0_sor_soff(outp) + !(outp->sorconf.link & 1) * 0x80;
+ return nvd0_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
}
static inline u32
@@ -52,77 +53,80 @@ nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane)
}
static int
-nvd0_sor_dp_pattern(struct nouveau_disp *disp, struct dcb_output *outp,
- int head, int pattern)
+nvd0_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
{
- struct nv50_disp_priv *priv = (void *)disp;
+ struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
const u32 loff = nvd0_sor_loff(outp);
nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern);
return 0;
}
static int
-nvd0_sor_dp_lnk_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
- int head, int link_nr, int link_bw, bool enh_frame)
+nvd0_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
{
- struct nv50_disp_priv *priv = (void *)disp;
+ struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
const u32 soff = nvd0_sor_soff(outp);
const u32 loff = nvd0_sor_loff(outp);
u32 dpctrl = 0x00000000;
u32 clksor = 0x00000000;
- u32 lane = 0;
- int i;
- clksor |= link_bw << 18;
- dpctrl |= ((1 << link_nr) - 1) << 16;
- if (enh_frame)
+ clksor |= bw << 18;
+ dpctrl |= ((1 << nr) - 1) << 16;
+ if (ef)
dpctrl |= 0x00004000;
- for (i = 0; i < link_nr; i++)
- lane |= 1 << (nvd0_sor_dp_lane_map(priv, i) >> 3);
-
nv_mask(priv, 0x612300 + soff, 0x007c0000, clksor);
nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl);
- nv_mask(priv, 0x61c130 + loff, 0x0000000f, lane);
return 0;
}
static int
-nvd0_sor_dp_drv_ctl(struct nouveau_disp *disp, struct dcb_output *outp,
- int head, int lane, int swing, int preem)
+nvd0_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
{
- struct nouveau_bios *bios = nouveau_bios(disp);
- struct nv50_disp_priv *priv = (void *)disp;
- const u32 shift = nvd0_sor_dp_lane_map(priv, lane);
+ struct nv50_disp_priv *priv = (void *)nouveau_disp(outp);
+ struct nouveau_bios *bios = nouveau_bios(priv);
+ const u32 shift = nvd0_sor_dp_lane_map(priv, ln);
const u32 loff = nvd0_sor_loff(outp);
- u32 addr, data[3];
+ u32 addr, data[4];
u8 ver, hdr, cnt, len;
struct nvbios_dpout info;
struct nvbios_dpcfg ocfg;
- addr = nvbios_dpout_match(bios, outp->hasht, outp->hashm,
+ addr = nvbios_dpout_match(bios, outp->base.info.hasht,
+ outp->base.info.hashm,
&ver, &hdr, &cnt, &len, &info);
if (!addr)
return -ENODEV;
- addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem,
+ addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe,
&ver, &hdr, &cnt, &len, &ocfg);
if (!addr)
return -EINVAL;
data[0] = nv_rd32(priv, 0x61c118 + loff) & ~(0x000000ff << shift);
data[1] = nv_rd32(priv, 0x61c120 + loff) & ~(0x000000ff << shift);
- data[2] = nv_rd32(priv, 0x61c130 + loff) & ~(0x0000ff00);
- nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.drv << shift));
- nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pre << shift));
- nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.unk << 8));
- nv_mask(priv, 0x61c13c + loff, 0x00000000, 0x00000000);
+ data[2] = nv_rd32(priv, 0x61c130 + loff);
+ if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
+ data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
+ nv_wr32(priv, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
+ nv_wr32(priv, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
+ nv_wr32(priv, 0x61c130 + loff, data[2] | (ocfg.tx_pu << 8));
+ data[3] = nv_rd32(priv, 0x61c13c + loff) & ~(0x000000ff << shift);
+ nv_wr32(priv, 0x61c13c + loff, data[3] | (ocfg.pc << shift));
return 0;
}
-const struct nouveau_dp_func
-nvd0_sor_dp_func = {
+struct nvkm_output_dp_impl
+nvd0_sor_dp_impl = {
+ .base.base.handle = DCB_OUTPUT_DP,
+ .base.base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nvkm_output_dp_ctor,
+ .dtor = _nvkm_output_dp_dtor,
+ .init = _nvkm_output_dp_init,
+ .fini = _nvkm_output_dp_fini,
+ },
.pattern = nvd0_sor_dp_pattern,
+ .lnk_pwr = nv94_sor_dp_lnk_pwr,
.lnk_ctl = nvd0_sor_dp_lnk_ctl,
.drv_ctl = nvd0_sor_dp_drv_ctl,
};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
index 6f9041ced9a2..56ed3d73bf8e 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
@@ -91,7 +91,7 @@ nouveau_fifo_channel_create_(struct nouveau_object *parent,
if (!chan->user)
return -EFAULT;
- nouveau_event_trigger(priv->cevent, 0);
+ nouveau_event_trigger(priv->cevent, 1, 0);
chan->size = size;
return 0;
@@ -194,11 +194,11 @@ nouveau_fifo_create_(struct nouveau_object *parent,
if (!priv->channel)
return -ENOMEM;
- ret = nouveau_event_create(1, &priv->cevent);
+ ret = nouveau_event_create(1, 1, &priv->cevent);
if (ret)
return ret;
- ret = nouveau_event_create(1, &priv->uevent);
+ ret = nouveau_event_create(1, 1, &priv->uevent);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/gk20a.c b/drivers/gpu/drm/nouveau/core/engine/fifo/gk20a.c
new file mode 100644
index 000000000000..327456eae963
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/gk20a.c
@@ -0,0 +1,35 @@
+/*
+ * 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 "nve0.h"
+
+struct nouveau_oclass *
+gk20a_fifo_oclass = &(struct nve0_fifo_impl) {
+ .base.handle = NV_ENGINE(FIFO, 0xea),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nve0_fifo_ctor,
+ .dtor = nve0_fifo_dtor,
+ .init = nve0_fifo_init,
+ .fini = nve0_fifo_fini,
+ },
+ .channels = 128,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
index 54f26cc801c7..c61b16a63884 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
@@ -539,7 +539,7 @@ nv04_fifo_intr(struct nouveau_subdev *subdev)
}
if (status & 0x40000000) {
- nouveau_event_trigger(priv->base.uevent, 0);
+ nouveau_event_trigger(priv->base.uevent, 1, 0);
nv_wr32(priv, 0x002100, 0x40000000);
status &= ~0x40000000;
}
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
index fe0f41e65d9b..6e5ac16e5460 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
@@ -389,14 +389,14 @@ nv84_fifo_cclass = {
******************************************************************************/
static void
-nv84_fifo_uevent_enable(struct nouveau_event *event, int index)
+nv84_fifo_uevent_enable(struct nouveau_event *event, int type, int index)
{
struct nv84_fifo_priv *priv = event->priv;
nv_mask(priv, 0x002140, 0x40000000, 0x40000000);
}
static void
-nv84_fifo_uevent_disable(struct nouveau_event *event, int index)
+nv84_fifo_uevent_disable(struct nouveau_event *event, int type, int index)
{
struct nv84_fifo_priv *priv = event->priv;
nv_mask(priv, 0x002140, 0x40000000, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
index fa1e719872b7..ae4a4dc5642a 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
@@ -730,7 +730,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, 0);
+ nouveau_event_trigger(priv->base.uevent, 1, 0);
ints &= ~1;
}
if (ints) {
@@ -827,14 +827,14 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev)
}
static void
-nvc0_fifo_uevent_enable(struct nouveau_event *event, int index)
+nvc0_fifo_uevent_enable(struct nouveau_event *event, int type, int index)
{
struct nvc0_fifo_priv *priv = event->priv;
nv_mask(priv, 0x002140, 0x80000000, 0x80000000);
}
static void
-nvc0_fifo_uevent_disable(struct nouveau_event *event, int index)
+nvc0_fifo_uevent_disable(struct nouveau_event *event, int type, int index)
{
struct nvc0_fifo_priv *priv = event->priv;
nv_mask(priv, 0x002140, 0x80000000, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
index a9a1a9c9f9f2..298063edb92d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
@@ -859,7 +859,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, 0);
+ nouveau_event_trigger(priv->base.uevent, 1, 0);
}
static void
@@ -952,14 +952,14 @@ nve0_fifo_intr(struct nouveau_subdev *subdev)
}
static void
-nve0_fifo_uevent_enable(struct nouveau_event *event, int index)
+nve0_fifo_uevent_enable(struct nouveau_event *event, int type, int index)
{
struct nve0_fifo_priv *priv = event->priv;
nv_mask(priv, 0x002140, 0x80000000, 0x80000000);
}
static void
-nve0_fifo_uevent_disable(struct nouveau_event *event, int index)
+nve0_fifo_uevent_disable(struct nouveau_event *event, int type, int index)
{
struct nve0_fifo_priv *priv = event->priv;
nv_mask(priv, 0x002140, 0x80000000, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.h b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.h
index 014344ebee66..e96b32bb1bbc 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.h
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.h
@@ -8,6 +8,7 @@ int nve0_fifo_ctor(struct nouveau_object *, struct nouveau_object *,
struct nouveau_object **);
void nve0_fifo_dtor(struct nouveau_object *);
int nve0_fifo_init(struct nouveau_object *);
+int nve0_fifo_fini(struct nouveau_object *, bool);
struct nve0_fifo_impl {
struct nouveau_oclass base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c
new file mode 100644
index 000000000000..224ee0287ab7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxgk20a.c
@@ -0,0 +1,53 @@
+/*
+ * 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 "ctxnvc0.h"
+
+static const struct nvc0_graph_pack
+gk20a_grctx_pack_mthd[] = {
+ { nve4_grctx_init_a097_0, 0xa297 },
+ { nvc0_grctx_init_902d_0, 0x902d },
+ {}
+};
+
+struct nouveau_oclass *
+gk20a_grctx_oclass = &(struct nvc0_grctx_oclass) {
+ .base.handle = NV_ENGCTX(GR, 0xea),
+ .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,
+ .mods = nve4_grctx_generate_mods,
+ .unkn = nve4_grctx_generate_unkn,
+ .hub = nve4_grctx_pack_hub,
+ .gpc = nve4_grctx_pack_gpc,
+ .zcull = nvc0_grctx_pack_zcull,
+ .tpc = nve4_grctx_pack_tpc,
+ .ppc = nve4_grctx_pack_ppc,
+ .icmd = nve4_grctx_pack_icmd,
+ .mthd = gk20a_grctx_pack_mthd,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c
index 48351b4d6d6b..8de4a4291548 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv108.c
@@ -545,10 +545,12 @@ nv108_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
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);
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h
index 9c815d1f99ef..8da8b627b9d0 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.h
@@ -69,7 +69,9 @@ extern struct nouveau_oclass *nvd7_grctx_oclass;
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_unkn(struct nvc0_graph_priv *);
void nve4_grctx_generate_r418bb8(struct nvc0_graph_priv *);
@@ -151,6 +153,13 @@ extern const struct nvc0_graph_init nve4_grctx_init_gpm_0[];
extern const struct nvc0_graph_init nve4_grctx_init_pes_0[];
+extern const struct nvc0_graph_pack nve4_grctx_pack_hub[];
+extern const struct nvc0_graph_pack nve4_grctx_pack_gpc[];
+extern const struct nvc0_graph_pack nve4_grctx_pack_tpc[];
+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_mthd[];
extern const struct nvc0_graph_init nvf0_grctx_init_pri_0[];
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c
index 49a14b116a5f..c5b249238587 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c
@@ -272,13 +272,13 @@ nve4_grctx_init_icmd_0[] = {
{}
};
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
nve4_grctx_pack_icmd[] = {
{ nve4_grctx_init_icmd_0 },
{}
};
-static const struct nvc0_graph_init
+const struct nvc0_graph_init
nve4_grctx_init_a097_0[] = {
{ 0x000800, 8, 0x40, 0x00000000 },
{ 0x000804, 8, 0x40, 0x00000000 },
@@ -697,7 +697,7 @@ nve4_grctx_init_be_0[] = {
{}
};
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
nve4_grctx_pack_hub[] = {
{ nvc0_grctx_init_main_0 },
{ nve4_grctx_init_fe_0 },
@@ -737,7 +737,7 @@ nve4_grctx_init_gpm_0[] = {
{}
};
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
nve4_grctx_pack_gpc[] = {
{ nvc0_grctx_init_gpc_unk_0 },
{ nvd9_grctx_init_prop_0 },
@@ -802,7 +802,7 @@ nve4_grctx_init_sm_0[] = {
{}
};
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
nve4_grctx_pack_tpc[] = {
{ nvd7_grctx_init_pe_0 },
{ nve4_grctx_init_tex_0 },
@@ -826,7 +826,7 @@ nve4_grctx_init_cbm_0[] = {
{}
};
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
nve4_grctx_pack_ppc[] = {
{ nve4_grctx_init_pes_0 },
{ nve4_grctx_init_cbm_0 },
@@ -838,7 +838,7 @@ nve4_grctx_pack_ppc[] = {
* PGRAPH context implementation
******************************************************************************/
-static void
+void
nve4_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
{
u32 magic[GPC_MAX][2];
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c
index 0fab95e49f53..dec03f04114d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c
@@ -842,7 +842,7 @@ nvf0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info)
u16 magic3 = 0x0648;
magic[gpc][0] = 0x10000000 | (magic0 << 16) | offset;
magic[gpc][1] = 0x00000000 | (magic1 << 16);
- offset += 0x0324 * (priv->tpc_nr[gpc] - 1);;
+ offset += 0x0324 * (priv->tpc_nr[gpc] - 1);
magic[gpc][2] = 0x10000000 | (magic2 << 16) | offset;
magic[gpc][3] = 0x00000000 | (magic3 << 16);
offset += 0x0324;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c b/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c
new file mode 100644
index 000000000000..83048a56430d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/gk20a.c
@@ -0,0 +1,47 @@
+/*
+ * 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 "nvc0.h"
+#include "ctxnvc0.h"
+
+static struct nouveau_oclass
+gk20a_graph_sclass[] = {
+ { 0x902d, &nouveau_object_ofuncs },
+ { 0xa040, &nouveau_object_ofuncs },
+ { 0xa297, &nouveau_object_ofuncs },
+ { 0xa0c0, &nouveau_object_ofuncs },
+ {}
+};
+
+struct nouveau_oclass *
+gk20a_graph_oclass = &(struct nvc0_graph_oclass) {
+ .base.handle = NV_ENGINE(GR, 0xea),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nvc0_graph_ctor,
+ .dtor = nvc0_graph_dtor,
+ .init = nve4_graph_init,
+ .fini = nve4_graph_fini,
+ },
+ .cclass = &gk20a_grctx_oclass,
+ .sclass = gk20a_graph_sclass,
+ .mmio = nve4_graph_pack_mmio,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
index 2c7809e1a09b..1a2d56493cf6 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c
@@ -901,7 +901,7 @@ nv50_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
nv_engine(priv)->sclass = nvaf_graph_sclass;
break;
- };
+ }
/* unfortunate hw bug workaround... */
if (nv_device(priv)->chipset != 0x50 &&
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
index f3c7329da0a0..bf7bdb1f291e 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c
@@ -894,6 +894,10 @@ nvc0_graph_init_fw(struct nvc0_graph_priv *priv, u32 fuc_base,
nv_wr32(priv, fuc_base + 0x0188, i >> 6);
nv_wr32(priv, fuc_base + 0x0184, code->data[i]);
}
+
+ /* code must be padded to 0x40 words */
+ for (; i & 0x3f; i++)
+ nv_wr32(priv, fuc_base + 0x0184, 0);
}
static void
@@ -1259,10 +1263,14 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nvc0_graph_oclass *oclass = (void *)bclass;
struct nouveau_device *device = nv_device(parent);
struct nvc0_graph_priv *priv;
+ bool use_ext_fw, enable;
int ret, i;
- ret = nouveau_graph_create(parent, engine, bclass,
- (oclass->fecs.ucode != NULL), &priv);
+ use_ext_fw = nouveau_boolopt(device->cfgopt, "NvGrUseFW",
+ oclass->fecs.ucode == NULL);
+ enable = use_ext_fw || oclass->fecs.ucode != NULL;
+
+ ret = nouveau_graph_create(parent, engine, bclass, enable, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
@@ -1272,7 +1280,7 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
priv->base.units = nvc0_graph_units;
- if (nouveau_boolopt(device->cfgopt, "NvGrUseFW", false)) {
+ if (use_ext_fw) {
nv_info(priv, "using external firmware\n");
if (nvc0_graph_ctor_fw(priv, "fuc409c", &priv->fuc409c) ||
nvc0_graph_ctor_fw(priv, "fuc409d", &priv->fuc409d) ||
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h
index 90d44616c876..75203a99d902 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h
@@ -116,6 +116,7 @@ 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 *);
+int nve4_graph_fini(struct nouveau_object *, bool);
int nve4_graph_init(struct nouveau_object *);
extern struct nouveau_oclass nvc0_graph_sclass[];
@@ -217,6 +218,7 @@ extern const struct nvc0_graph_init nve4_graph_init_main_0[];
extern const struct nvc0_graph_init nve4_graph_init_tpccs_0[];
extern const struct nvc0_graph_init nve4_graph_init_pe_0[];
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_sked_0[];
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c
index f7c011217175..51e0c075ad34 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c
@@ -151,7 +151,7 @@ nve4_graph_init_be_0[] = {
{}
};
-static const struct nvc0_graph_pack
+const struct nvc0_graph_pack
nve4_graph_pack_mmio[] = {
{ nve4_graph_init_main_0 },
{ nvc0_graph_init_fe_0 },
@@ -189,7 +189,7 @@ nve4_graph_pack_mmio[] = {
* PGRAPH engine/subdev functions
******************************************************************************/
-static int
+int
nve4_graph_fini(struct nouveau_object *object, bool suspend)
{
struct nvc0_graph_priv *priv = (void *)object;
diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c
index 5ce686ee729e..f3b4d9dbf23c 100644
--- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c
@@ -124,7 +124,7 @@ nv50_software_sclass[] = {
******************************************************************************/
static int
-nv50_software_vblsem_release(void *data, int head)
+nv50_software_vblsem_release(void *data, u32 type, int head)
{
struct nv50_software_chan *chan = data;
struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
@@ -183,7 +183,7 @@ nv50_software_context_ctor(struct nouveau_object *parent,
return -ENOMEM;
for (i = 0; i < chan->vblank.nr_event; i++) {
- ret = nouveau_event_new(pdisp->vblank, i, pclass->vblank,
+ ret = nouveau_event_new(pdisp->vblank, 1, i, pclass->vblank,
chan, &chan->vblank.event[i]);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.h b/drivers/gpu/drm/nouveau/core/engine/software/nv50.h
index 2de370c21279..bb49a7a20857 100644
--- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.h
+++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.h
@@ -19,7 +19,7 @@ int nv50_software_ctor(struct nouveau_object *, struct nouveau_object *,
struct nv50_software_cclass {
struct nouveau_oclass base;
- int (*vblank)(void *, int);
+ int (*vblank)(void *, u32, int);
};
struct nv50_software_chan {
diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
index f9430c1bf3e5..135c20f38356 100644
--- a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c
@@ -104,7 +104,7 @@ nvc0_software_sclass[] = {
******************************************************************************/
static int
-nvc0_software_vblsem_release(void *data, int head)
+nvc0_software_vblsem_release(void *data, u32 type, int head)
{
struct nv50_software_chan *chan = data;
struct nv50_software_priv *priv = (void *)nv_object(chan)->engine;
diff --git a/drivers/gpu/drm/nouveau/core/include/core/class.h b/drivers/gpu/drm/nouveau/core/include/core/class.h
index 9c0cd73462d9..e0c812bc884f 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/class.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/class.h
@@ -295,6 +295,10 @@ struct nv04_display_scanoutpos {
#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
diff --git a/drivers/gpu/drm/nouveau/core/include/core/event.h b/drivers/gpu/drm/nouveau/core/include/core/event.h
index 5d539ebff3ed..ba3f1a76a815 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/event.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/event.h
@@ -12,32 +12,33 @@ struct nouveau_eventh {
struct nouveau_event *event;
struct list_head head;
unsigned long flags;
+ u32 types;
int index;
- int (*func)(void *, int);
+ int (*func)(void *, u32, int);
void *priv;
};
struct nouveau_event {
- spinlock_t list_lock;
- spinlock_t refs_lock;
-
void *priv;
- void (*enable)(struct nouveau_event *, int index);
- void (*disable)(struct nouveau_event *, int index);
+ 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);
+ int types_nr;
int index_nr;
- struct {
- struct list_head list;
- int refs;
- } index[];
+
+ spinlock_t list_lock;
+ struct list_head *list;
+ spinlock_t refs_lock;
+ int refs[];
};
-int nouveau_event_create(int index_nr, struct nouveau_event **);
+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 *, int index);
+void nouveau_event_trigger(struct nouveau_event *, u32 types, int index);
-int nouveau_event_new(struct nouveau_event *, int index,
- int (*func)(void *, int), void *,
+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 *);
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/disp.h b/drivers/gpu/drm/nouveau/core/include/engine/disp.h
index fd0c68804de3..fde842896806 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/disp.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/disp.h
@@ -6,8 +6,19 @@
#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;
};
@@ -17,25 +28,6 @@ nouveau_disp(void *obj)
return (void *)nv_device(obj)->subdev[NVDEV_ENGINE_DISP];
}
-#define nouveau_disp_create(p,e,c,h,i,x,d) \
- nouveau_disp_create_((p), (e), (c), (h), (i), (x), \
- sizeof(**d), (void **)d)
-#define nouveau_disp_destroy(d) ({ \
- struct nouveau_disp *disp = (d); \
- _nouveau_disp_dtor(nv_object(disp)); \
-})
-#define nouveau_disp_init(d) \
- nouveau_engine_init(&(d)->base)
-#define nouveau_disp_fini(d,s) \
- nouveau_engine_fini(&(d)->base, (s))
-
-int nouveau_disp_create_(struct nouveau_object *, struct nouveau_object *,
- struct nouveau_oclass *, int heads,
- const char *, const char *, int, void **);
-void _nouveau_disp_dtor(struct nouveau_object *);
-#define _nouveau_disp_init _nouveau_engine_init
-#define _nouveau_disp_fini _nouveau_engine_fini
-
extern struct nouveau_oclass *nv04_disp_oclass;
extern struct nouveau_oclass *nv50_disp_oclass;
extern struct nouveau_oclass *nv84_disp_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h
index 26b6b2bb1112..b639eb2c74ff 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h
@@ -109,6 +109,7 @@ extern struct nouveau_oclass *nv50_fifo_oclass;
extern struct nouveau_oclass *nv84_fifo_oclass;
extern struct nouveau_oclass *nvc0_fifo_oclass;
extern struct nouveau_oclass *nve0_fifo_oclass;
+extern struct nouveau_oclass *gk20a_fifo_oclass;
extern struct nouveau_oclass *nv108_fifo_oclass;
void nv04_fifo_intr(struct nouveau_subdev *);
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/graph.h b/drivers/gpu/drm/nouveau/core/include/engine/graph.h
index 871edfdf3d5b..8c1d4772da0c 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/graph.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/graph.h
@@ -68,6 +68,7 @@ extern struct nouveau_oclass *nvc8_graph_oclass;
extern struct nouveau_oclass *nvd7_graph_oclass;
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 *nv108_graph_oclass;
extern struct nouveau_oclass *gm107_graph_oclass;
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h
index a32feb3f3fb6..f3930c27cb7a 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/conn.h
@@ -22,7 +22,25 @@ enum dcb_connector_type {
DCB_CONNECTOR_NONE = 0xff
};
-u16 dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
-u16 dcb_conn(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len);
+struct nvbios_connT {
+};
+
+u32 nvbios_connTe(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len);
+u32 nvbios_connTp(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_connT *info);
+
+struct nvbios_connE {
+ u8 type;
+ u8 location;
+ u8 hpd;
+ u8 dp;
+ u8 di;
+ u8 sr;
+ u8 lcdid;
+};
+
+u32 nvbios_connEe(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *hdr);
+u32 nvbios_connEp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *hdr,
+ struct nvbios_connE *info);
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h
index 6e54218b55fc..728206e21777 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h
@@ -17,9 +17,10 @@ u16 nvbios_dpout_match(struct nouveau_bios *, u16 type, u16 mask,
struct nvbios_dpout *);
struct nvbios_dpcfg {
- u8 drv;
- u8 pre;
- u8 unk;
+ u8 pc;
+ u8 dc;
+ u8 pe;
+ u8 tx_pu;
};
u16
@@ -27,7 +28,7 @@ nvbios_dpcfg_parse(struct nouveau_bios *, u16 outp, u8 idx,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_dpcfg *);
u16
-nvbios_dpcfg_match(struct nouveau_bios *, u16 outp, u8 un, u8 vs, u8 pe,
+nvbios_dpcfg_match(struct nouveau_bios *, u16 outp, u8 pc, u8 vs, u8 pe,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_dpcfg *);
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h
index 8f4ced75444a..c01e29c9f89a 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h
@@ -77,6 +77,8 @@ struct nouveau_clock {
int tstate; /* thermal adjustment (max-) */
int dstate; /* display adjustment (min+) */
+ bool allow_reclock;
+
int (*read)(struct nouveau_clock *, enum nv_clk_src);
int (*calc)(struct nouveau_clock *, struct nouveau_cstate *);
int (*prog)(struct nouveau_clock *);
@@ -106,8 +108,8 @@ struct nouveau_clocks {
int mdiv;
};
-#define nouveau_clock_create(p,e,o,i,d) \
- nouveau_clock_create_((p), (e), (o), (i), sizeof(**d), (void **)d)
+#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_destroy(p) ({ \
struct nouveau_clock *clk = (p); \
_nouveau_clock_dtor(nv_object(clk)); \
@@ -121,7 +123,7 @@ struct nouveau_clocks {
int nouveau_clock_create_(struct nouveau_object *, struct nouveau_object *,
struct nouveau_oclass *,
- struct nouveau_clocks *, int, void **);
+ struct nouveau_clocks *, bool, int, void **);
void _nouveau_clock_dtor(struct nouveau_object *);
int _nouveau_clock_init(struct nouveau_object *);
#define _nouveau_clock_fini _nouveau_subdev_fini
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h
index 58c7ccdebb01..871e73914b24 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h
@@ -105,6 +105,7 @@ extern struct nouveau_oclass *nvaa_fb_oclass;
extern struct nouveau_oclass *nvaf_fb_oclass;
extern struct nouveau_oclass *nvc0_fb_oclass;
extern struct nouveau_oclass *nve0_fb_oclass;
+extern struct nouveau_oclass *gk20a_fb_oclass;
extern struct nouveau_oclass *gm107_fb_oclass;
#include <subdev/bios/ramcfg.h>
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h
index c85b9f1579ad..612d82ab683d 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/gpio.h
@@ -8,17 +8,18 @@
#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 nouveau_gpio {
struct nouveau_subdev base;
struct nouveau_event *events;
- /* hardware interfaces */
void (*reset)(struct nouveau_gpio *, u8 func);
- int (*drive)(struct nouveau_gpio *, int line, int dir, int out);
- int (*sense)(struct nouveau_gpio *, int line);
-
- /* software interfaces */
int (*find)(struct nouveau_gpio *, int idx, u8 tag, u8 line,
struct dcb_gpio_func *);
int (*set)(struct nouveau_gpio *, int idx, u8 tag, u8 line, int state);
@@ -31,23 +32,10 @@ nouveau_gpio(void *obj)
return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_GPIO];
}
-#define nouveau_gpio_create(p,e,o,l,d) \
- nouveau_gpio_create_((p), (e), (o), (l), sizeof(**d), (void **)d)
-#define nouveau_gpio_destroy(p) ({ \
- struct nouveau_gpio *gpio = (p); \
- _nouveau_gpio_dtor(nv_object(gpio)); \
-})
-#define nouveau_gpio_fini(p,s) \
- nouveau_subdev_fini(&(p)->base, (s))
-
-int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *,
- struct nouveau_oclass *, int, int, void **);
-void _nouveau_gpio_dtor(struct nouveau_object *);
-int nouveau_gpio_init(struct nouveau_gpio *);
-
-extern struct nouveau_oclass nv10_gpio_oclass;
-extern struct nouveau_oclass nv50_gpio_oclass;
-extern struct nouveau_oclass nvd0_gpio_oclass;
-extern struct nouveau_oclass nve0_gpio_oclass;
+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 *nvd0_gpio_oclass;
+extern struct nouveau_oclass *nve0_gpio_oclass;
#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
index 7f50a858b16f..db1b39d08013 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/i2c.h
@@ -14,52 +14,41 @@
#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 nouveau_i2c_port {
struct nouveau_object base;
struct i2c_adapter adapter;
+ struct mutex mutex;
struct list_head head;
u8 index;
+ int aux;
const struct nouveau_i2c_func *func;
};
struct nouveau_i2c_func {
- void (*acquire)(struct nouveau_i2c_port *);
- void (*release)(struct nouveau_i2c_port *);
-
void (*drive_scl)(struct nouveau_i2c_port *, int);
void (*drive_sda)(struct nouveau_i2c_port *, int);
int (*sense_scl)(struct nouveau_i2c_port *);
int (*sense_sda)(struct nouveau_i2c_port *);
- int (*aux)(struct nouveau_i2c_port *, u8, u32, u8 *, u8);
+ int (*aux)(struct nouveau_i2c_port *, bool, u8, u32, u8 *, u8);
int (*pattern)(struct nouveau_i2c_port *, int pattern);
int (*lnk_ctl)(struct nouveau_i2c_port *, int nr, int bw, bool enh);
int (*drv_ctl)(struct nouveau_i2c_port *, int lane, int sw, int pe);
};
-#define nouveau_i2c_port_create(p,e,o,i,a,f,d) \
- nouveau_i2c_port_create_((p), (e), (o), (i), (a), (f), \
- sizeof(**d), (void **)d)
-#define nouveau_i2c_port_destroy(p) ({ \
- struct nouveau_i2c_port *port = (p); \
- _nouveau_i2c_port_dtor(nv_object(i2c)); \
-})
-#define nouveau_i2c_port_init(p) \
- nouveau_object_init(&(p)->base)
-#define nouveau_i2c_port_fini(p,s) \
- nouveau_object_fini(&(p)->base, (s))
-
-int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *,
- struct nouveau_oclass *, u8,
- const struct i2c_algorithm *,
- const struct nouveau_i2c_func *,
- int, void **);
-void _nouveau_i2c_port_dtor(struct nouveau_object *);
-#define _nouveau_i2c_port_init nouveau_object_init
-#define _nouveau_i2c_port_fini nouveau_object_fini
-
struct nouveau_i2c_board_info {
struct i2c_board_info dev;
u8 udelay; /* set to 0 to use the standard delay */
@@ -67,13 +56,20 @@ struct nouveau_i2c_board_info {
struct nouveau_i2c {
struct nouveau_subdev base;
+ struct nouveau_event *ntfy;
struct nouveau_i2c_port *(*find)(struct nouveau_i2c *, u8 index);
struct nouveau_i2c_port *(*find_type)(struct nouveau_i2c *, u16 type);
+ int (*acquire_pad)(struct nouveau_i2c_port *, unsigned long timeout);
+ void (*release_pad)(struct nouveau_i2c_port *);
+ int (*acquire)(struct nouveau_i2c_port *, unsigned long timeout);
+ void (*release)(struct nouveau_i2c_port *);
int (*identify)(struct nouveau_i2c *, int index,
const char *what, struct nouveau_i2c_board_info *,
bool (*match)(struct nouveau_i2c_port *,
struct i2c_board_info *, void *), void *);
+
+ wait_queue_head_t wait;
struct list_head ports;
};
@@ -83,37 +79,12 @@ nouveau_i2c(void *obj)
return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_I2C];
}
-#define nouveau_i2c_create(p,e,o,s,d) \
- nouveau_i2c_create_((p), (e), (o), (s), sizeof(**d), (void **)d)
-#define nouveau_i2c_destroy(p) ({ \
- struct nouveau_i2c *i2c = (p); \
- _nouveau_i2c_dtor(nv_object(i2c)); \
-})
-#define nouveau_i2c_init(p) ({ \
- struct nouveau_i2c *i2c = (p); \
- _nouveau_i2c_init(nv_object(i2c)); \
-})
-#define nouveau_i2c_fini(p,s) ({ \
- struct nouveau_i2c *i2c = (p); \
- _nouveau_i2c_fini(nv_object(i2c), (s)); \
-})
-
-int nouveau_i2c_create_(struct nouveau_object *, struct nouveau_object *,
- struct nouveau_oclass *, struct nouveau_oclass *,
- int, void **);
-void _nouveau_i2c_dtor(struct nouveau_object *);
-int _nouveau_i2c_init(struct nouveau_object *);
-int _nouveau_i2c_fini(struct nouveau_object *, bool);
-
-extern struct nouveau_oclass nv04_i2c_oclass;
-extern struct nouveau_oclass nv4e_i2c_oclass;
-extern struct nouveau_oclass nv50_i2c_oclass;
-extern struct nouveau_oclass nv94_i2c_oclass;
-extern struct nouveau_oclass nvd0_i2c_oclass;
-extern struct nouveau_oclass nouveau_anx9805_sclass[];
-
-extern const struct i2c_algorithm nouveau_i2c_bit_algo;
-extern const struct i2c_algorithm nouveau_i2c_aux_algo;
+extern struct nouveau_oclass *nv04_i2c_oclass;
+extern struct nouveau_oclass *nv4e_i2c_oclass;
+extern struct nouveau_oclass *nv50_i2c_oclass;
+extern struct nouveau_oclass *nv94_i2c_oclass;
+extern struct nouveau_oclass *nvd0_i2c_oclass;
+extern struct nouveau_oclass *nve0_i2c_oclass;
static inline int
nv_rdi2cr(struct nouveau_i2c_port *port, u8 addr, u8 reg)
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/ibus.h b/drivers/gpu/drm/nouveau/core/include/subdev/ibus.h
index 88814f159d89..31df634c0fdc 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/ibus.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/ibus.h
@@ -30,5 +30,6 @@ nouveau_ibus(void *obj)
extern struct nouveau_oclass nvc0_ibus_oclass;
extern struct nouveau_oclass nve0_ibus_oclass;
+extern struct nouveau_oclass gk20a_ibus_oclass;
#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c
index bdf594116f3f..73b1ed20c8d5 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c
@@ -118,8 +118,10 @@ nouveau_bar_create_(struct nouveau_object *parent,
if (ret)
return ret;
- bar->iomem = ioremap(nv_device_resource_start(device, 3),
- nv_device_resource_len(device, 3));
+ 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;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c
index 3f30db62e656..ca8139b9ab27 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c
@@ -30,14 +30,16 @@
#include "priv.h"
+struct nvc0_bar_priv_vm {
+ struct nouveau_gpuobj *mem;
+ struct nouveau_gpuobj *pgd;
+ struct nouveau_vm *vm;
+};
+
struct nvc0_bar_priv {
struct nouveau_bar base;
spinlock_t lock;
- struct {
- struct nouveau_gpuobj *mem;
- struct nouveau_gpuobj *pgd;
- struct nouveau_vm *vm;
- } bar[2];
+ struct nvc0_bar_priv_vm bar[2];
};
static int
@@ -79,87 +81,87 @@ nvc0_bar_unmap(struct nouveau_bar *bar, struct nouveau_vma *vma)
}
static int
-nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
+nvc0_bar_init_vm(struct nvc0_bar_priv *priv, struct nvc0_bar_priv_vm *bar_vm,
+ int bar_nr)
{
- struct nouveau_device *device = nv_device(parent);
- struct nvc0_bar_priv *priv;
- struct nouveau_gpuobj *mem;
+ struct nouveau_device *device = nv_device(&priv->base);
struct nouveau_vm *vm;
+ resource_size_t bar_len;
int ret;
- ret = nouveau_bar_create(parent, engine, oclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- /* BAR3 */
ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0, 0,
- &priv->bar[0].mem);
- mem = priv->bar[0].mem;
+ &bar_vm->mem);
if (ret)
return ret;
ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0, 0,
- &priv->bar[0].pgd);
+ &bar_vm->pgd);
if (ret)
return ret;
- ret = nouveau_vm_new(device, 0, nv_device_resource_len(device, 3), 0, &vm);
+ bar_len = nv_device_resource_len(device, bar_nr);
+
+ ret = nouveau_vm_new(device, 0, bar_len, 0, &vm);
if (ret)
return ret;
atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
- ret = nouveau_gpuobj_new(nv_object(priv), NULL,
- (nv_device_resource_len(device, 3) >> 12) * 8,
- 0x1000, NVOBJ_FLAG_ZERO_ALLOC,
- &vm->pgt[0].obj[0]);
- vm->pgt[0].refcount[0] = 1;
- if (ret)
- return ret;
+ /*
+ * Bootstrap page table lookup.
+ */
+ if (bar_nr == 3) {
+ ret = nouveau_gpuobj_new(nv_object(priv), NULL,
+ (bar_len >> 12) * 8, 0x1000,
+ NVOBJ_FLAG_ZERO_ALLOC,
+ &vm->pgt[0].obj[0]);
+ vm->pgt[0].refcount[0] = 1;
+ if (ret)
+ return ret;
+ }
- ret = nouveau_vm_ref(vm, &priv->bar[0].vm, priv->bar[0].pgd);
+ ret = nouveau_vm_ref(vm, &bar_vm->vm, bar_vm->pgd);
nouveau_vm_ref(NULL, &vm, NULL);
if (ret)
return ret;
- nv_wo32(mem, 0x0200, lower_32_bits(priv->bar[0].pgd->addr));
- nv_wo32(mem, 0x0204, upper_32_bits(priv->bar[0].pgd->addr));
- nv_wo32(mem, 0x0208, lower_32_bits(nv_device_resource_len(device, 3) - 1));
- nv_wo32(mem, 0x020c, upper_32_bits(nv_device_resource_len(device, 3) - 1));
+ nv_wo32(bar_vm->mem, 0x0200, lower_32_bits(bar_vm->pgd->addr));
+ nv_wo32(bar_vm->mem, 0x0204, upper_32_bits(bar_vm->pgd->addr));
+ nv_wo32(bar_vm->mem, 0x0208, lower_32_bits(bar_len - 1));
+ nv_wo32(bar_vm->mem, 0x020c, upper_32_bits(bar_len - 1));
- /* BAR1 */
- ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0, 0,
- &priv->bar[1].mem);
- mem = priv->bar[1].mem;
- if (ret)
- return ret;
+ return 0;
+}
- ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0, 0,
- &priv->bar[1].pgd);
- if (ret)
- return ret;
+static int
+nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct nouveau_device *device = nv_device(parent);
+ struct nvc0_bar_priv *priv;
+ bool has_bar3 = nv_device_resource_len(device, 3) != 0;
+ int ret;
- ret = nouveau_vm_new(device, 0, nv_device_resource_len(device, 1), 0, &vm);
+ ret = nouveau_bar_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
if (ret)
return ret;
- atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]);
+ /* BAR3 */
+ if (has_bar3) {
+ ret = nvc0_bar_init_vm(priv, &priv->bar[0], 3);
+ if (ret)
+ return ret;
+ priv->base.alloc = nouveau_bar_alloc;
+ priv->base.kmap = nvc0_bar_kmap;
+ }
- ret = nouveau_vm_ref(vm, &priv->bar[1].vm, priv->bar[1].pgd);
- nouveau_vm_ref(NULL, &vm, NULL);
+ /* BAR1 */
+ ret = nvc0_bar_init_vm(priv, &priv->bar[1], 1);
if (ret)
return ret;
- nv_wo32(mem, 0x0200, lower_32_bits(priv->bar[1].pgd->addr));
- nv_wo32(mem, 0x0204, upper_32_bits(priv->bar[1].pgd->addr));
- nv_wo32(mem, 0x0208, lower_32_bits(nv_device_resource_len(device, 1) - 1));
- nv_wo32(mem, 0x020c, upper_32_bits(nv_device_resource_len(device, 1) - 1));
-
- priv->base.alloc = nouveau_bar_alloc;
- priv->base.kmap = nvc0_bar_kmap;
priv->base.umap = nvc0_bar_umap;
priv->base.unmap = nvc0_bar_unmap;
priv->base.flush = nv84_bar_flush;
@@ -201,7 +203,9 @@ nvc0_bar_init(struct nouveau_object *object)
nv_mask(priv, 0x100c80, 0x00000001, 0x00000000);
nv_wr32(priv, 0x001704, 0x80000000 | priv->bar[1].mem->addr >> 12);
- nv_wr32(priv, 0x001714, 0xc0000000 | priv->bar[0].mem->addr >> 12);
+ if (priv->bar[0].mem)
+ nv_wr32(priv, 0x001714,
+ 0xc0000000 | priv->bar[0].mem->addr >> 12);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c
index 222e8ebb669d..d45704a2c2df 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c
@@ -183,10 +183,11 @@ nouveau_bios_shadow_prom(struct nouveau_bios *bios)
goto out;
bios->data = kmalloc(bios->size, GFP_KERNEL);
- if (bios->data) {
- for (i = 0; i < bios->size; i += 4)
- ((u32 *)bios->data)[i/4] = nv_rd32(bios, 0x300000 + i);
- }
+ if (!bios->data)
+ goto out;
+
+ for (i = 0; i < bios->size; i += 4)
+ ((u32 *)bios->data)[i/4] = nv_rd32(bios, 0x300000 + i);
/* check the PCI record header */
pcir = nv_ro16(bios, 0x0018);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c b/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c
index 5ac010efd959..2ede3bcd96a1 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/conn.c
@@ -28,12 +28,12 @@
#include <subdev/bios/dcb.h>
#include <subdev/bios/conn.h>
-u16
-dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+u32
+nvbios_connTe(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
{
- u16 dcb = dcb_table(bios, ver, hdr, cnt, len);
+ u32 dcb = dcb_table(bios, ver, hdr, cnt, len);
if (dcb && *ver >= 0x30 && *hdr >= 0x16) {
- u16 data = nv_ro16(bios, dcb + 0x14);
+ u32 data = nv_ro16(bios, dcb + 0x14);
if (data) {
*ver = nv_ro08(bios, data + 0);
*hdr = nv_ro08(bios, data + 1);
@@ -42,15 +42,59 @@ dcb_conntab(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
return data;
}
}
- return 0x0000;
+ return 0x00000000;
}
-u16
-dcb_conn(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len)
+u32
+nvbios_connTp(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
+ struct nvbios_connT *info)
+{
+ u32 data = nvbios_connTe(bios, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x30:
+ case 0x40:
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
+}
+
+u32
+nvbios_connEe(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len)
{
u8 hdr, cnt;
- u16 data = dcb_conntab(bios, ver, &hdr, &cnt, len);
+ u32 data = nvbios_connTe(bios, ver, &hdr, &cnt, len);
if (data && idx < cnt)
return data + hdr + (idx * *len);
- return 0x0000;
+ return 0x00000000;
+}
+
+u32
+nvbios_connEp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len,
+ struct nvbios_connE *info)
+{
+ u32 data = nvbios_connEe(bios, idx, ver, len);
+ memset(info, 0x00, sizeof(*info));
+ switch (!!data * *ver) {
+ case 0x30:
+ case 0x40:
+ info->type = nv_ro08(bios, data + 0x00);
+ info->location = nv_ro08(bios, data + 0x01) & 0x0f;
+ info->hpd = (nv_ro08(bios, data + 0x01) & 0x30) >> 4;
+ info->dp = (nv_ro08(bios, data + 0x01) & 0xc0) >> 6;
+ if (*len < 4)
+ return data;
+ info->hpd |= (nv_ro08(bios, data + 0x02) & 0x03) << 2;
+ info->dp |= nv_ro08(bios, data + 0x02) & 0x0c;
+ info->di = (nv_ro08(bios, data + 0x02) & 0xf0) >> 4;
+ info->hpd |= (nv_ro08(bios, data + 0x03) & 0x07) << 4;
+ info->sr = (nv_ro08(bios, data + 0x03) & 0x08) >> 3;
+ info->lcdid = (nv_ro08(bios, data + 0x03) & 0x70) >> 4;
+ return data;
+ default:
+ break;
+ }
+ return 0x00000000;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c b/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c
index 7628fe759220..f309dd657250 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c
@@ -162,18 +162,20 @@ nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx,
struct nvbios_dpcfg *info)
{
u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len);
+ memset(info, 0x00, sizeof(*info));
if (data) {
switch (*ver) {
case 0x21:
- info->drv = nv_ro08(bios, data + 0x02);
- info->pre = nv_ro08(bios, data + 0x03);
- info->unk = nv_ro08(bios, data + 0x04);
+ info->dc = nv_ro08(bios, data + 0x02);
+ info->pe = nv_ro08(bios, data + 0x03);
+ info->tx_pu = nv_ro08(bios, data + 0x04);
break;
case 0x30:
case 0x40:
- info->drv = nv_ro08(bios, data + 0x01);
- info->pre = nv_ro08(bios, data + 0x02);
- info->unk = nv_ro08(bios, data + 0x03);
+ info->pc = nv_ro08(bios, data + 0x00);
+ info->dc = nv_ro08(bios, data + 0x01);
+ info->pe = nv_ro08(bios, data + 0x02);
+ info->tx_pu = nv_ro08(bios, data + 0x03);
break;
default:
data = 0x0000;
@@ -184,7 +186,7 @@ nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx,
}
u16
-nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 un, u8 vs, u8 pe,
+nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe,
u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
struct nvbios_dpcfg *info)
{
@@ -193,16 +195,15 @@ nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 un, u8 vs, u8 pe,
if (*ver >= 0x30) {
const u8 vsoff[] = { 0, 4, 7, 9 };
- idx = (un * 10) + vsoff[vs] + pe;
+ idx = (pc * 10) + vsoff[vs] + pe;
} else {
- while ((data = nvbios_dpcfg_entry(bios, outp, idx,
+ while ((data = nvbios_dpcfg_entry(bios, outp, ++idx,
ver, hdr, cnt, len))) {
if (nv_ro08(bios, data + 0x00) == vs &&
nv_ro08(bios, data + 0x01) == pe)
break;
- idx++;
}
}
- return nvbios_dpcfg_parse(bios, outp, pe, ver, hdr, cnt, len, info);
+ return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info);
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
index acaeaf79e3f0..626380f9e4c0 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c
@@ -98,15 +98,16 @@ static u8
init_conn(struct nvbios_init *init)
{
struct nouveau_bios *bios = init->bios;
- u8 ver, len;
- u16 conn;
+ struct nvbios_connE connE;
+ u8 ver, hdr;
+ u32 conn;
if (init_exec(init)) {
if (init->outp) {
conn = init->outp->connector;
- conn = dcb_conn(bios, conn, &ver, &len);
+ conn = nvbios_connEp(bios, conn, &ver, &hdr, &connE);
if (conn)
- return nv_ro08(bios, conn);
+ return connE.type;
}
error("script needs connector type\n");
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/base.c b/drivers/gpu/drm/nouveau/core/subdev/clock/base.c
index dd62baead39c..22351f594d2a 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/base.c
@@ -346,8 +346,8 @@ nouveau_clock_ustate_update(struct nouveau_clock *clk, int req)
struct nouveau_pstate *pstate;
int i = 0;
- /* YKW repellant */
- return -ENOSYS;
+ if (!clk->allow_reclock)
+ return -ENOSYS;
if (req != -1 && req != -2) {
list_for_each_entry(pstate, &clk->states, head) {
@@ -456,6 +456,7 @@ nouveau_clock_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass,
struct nouveau_clocks *clocks,
+ bool allow_reclock,
int length, void **object)
{
struct nouveau_device *device = nv_device(parent);
@@ -478,6 +479,8 @@ nouveau_clock_create_(struct nouveau_object *parent,
ret = nouveau_pstate_new(clk, idx++);
} while (ret == 0);
+ clk->allow_reclock = allow_reclock;
+
mode = nouveau_stropt(device->cfgopt, "NvClkMode", &arglen);
if (mode) {
if (!strncasecmpz(mode, "disabled", arglen)) {
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c
index b74db6cfc4e2..eb2d4425a49e 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c
@@ -82,7 +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, &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nv04_domain, 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 db7346f79080..8a9e16839791 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c
@@ -213,7 +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, &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nv40_domain, 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 250a6d96016b..8c132772ba9e 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,
- &priv);
+ 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 4f5a1373f002..9fb58354a80b 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c
@@ -302,7 +302,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, &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nva3_domain, false,
+ &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c
index 7a723b4f564d..6a65fc9e9663 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvaa.c
@@ -421,7 +421,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, &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nvaa_domains, 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 c3105720ed24..dbf8517f54da 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c
@@ -437,7 +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, &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nvc0_domain, 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 d3c37c96f0e7..4ac1aa30ea11 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nve0.c
@@ -473,7 +473,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, &priv);
+ ret = nouveau_clock_create(parent, engine, oclass, nve0_domain, true,
+ &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c
new file mode 100644
index 000000000000..a16024a74771
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c
@@ -0,0 +1,56 @@
+/*
+ * 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 "nvc0.h"
+
+struct gk20a_fb_priv {
+ struct nouveau_fb base;
+};
+
+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)
+{
+ struct gk20a_fb_priv *priv;
+ int ret;
+
+ ret = nouveau_fb_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct nouveau_oclass *
+gk20a_fb_oclass = &(struct nouveau_fb_impl) {
+ .base.handle = NV_SUBDEV(FB, 0xea),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gk20a_fb_ctor,
+ .dtor = _nouveau_fb_dtor,
+ .init = _nouveau_fb_init,
+ .fini = _nouveau_fb_fini,
+ },
+ .memtype = nvc0_fb_memtype_valid,
+ .ram = &gk20a_ram_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
index da74c889aed4..82273f832e42 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h
@@ -32,6 +32,7 @@ extern struct nouveau_oclass nva3_ram_oclass;
extern struct nouveau_oclass nvaa_ram_oclass;
extern struct nouveau_oclass nvc0_ram_oclass;
extern struct nouveau_oclass nve0_ram_oclass;
+extern struct nouveau_oclass gk20a_ram_oclass;
extern struct nouveau_oclass gm107_ram_oclass;
int nouveau_sddr3_calc(struct nouveau_ram *ram);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c
new file mode 100644
index 000000000000..4d77d75e4673
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramgk20a.c
@@ -0,0 +1,152 @@
+/*
+ * 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 "priv.h"
+
+#include <subdev/fb.h>
+
+struct gk20a_mem {
+ struct nouveau_mem base;
+ void *cpuaddr;
+ dma_addr_t handle;
+};
+#define to_gk20a_mem(m) container_of(m, struct gk20a_mem, base)
+
+static void
+gk20a_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem)
+{
+ struct device *dev = nv_device_base(nv_device(pfb));
+ struct gk20a_mem *mem = to_gk20a_mem(*pmem);
+
+ *pmem = NULL;
+ if (unlikely(mem == NULL))
+ return;
+
+ if (likely(mem->cpuaddr))
+ dma_free_coherent(dev, mem->base.size << PAGE_SHIFT,
+ mem->cpuaddr, mem->handle);
+
+ kfree(mem->base.pages);
+ kfree(mem);
+}
+
+static int
+gk20a_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
+ u32 memtype, struct nouveau_mem **pmem)
+{
+ struct device *dev = nv_device_base(nv_device(pfb));
+ struct gk20a_mem *mem;
+ u32 type = memtype & 0xff;
+ u32 npages, order;
+ int i;
+
+ nv_debug(pfb, "%s: size: %llx align: %x, ncmin: %x\n", __func__, size,
+ align, ncmin);
+
+ npages = size >> PAGE_SHIFT;
+ if (npages == 0)
+ npages = 1;
+
+ if (align == 0)
+ align = PAGE_SIZE;
+ align >>= PAGE_SHIFT;
+
+ /* round alignment to the next power of 2, if needed */
+ order = fls(align);
+ if ((align & (align - 1)) == 0)
+ order--;
+ align = BIT(order);
+
+ /* ensure returned address is correctly aligned */
+ npages = max(align, npages);
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ mem->base.size = npages;
+ mem->base.memtype = type;
+
+ mem->base.pages = kzalloc(sizeof(dma_addr_t) * npages, GFP_KERNEL);
+ if (!mem->base.pages) {
+ kfree(mem);
+ return -ENOMEM;
+ }
+
+ *pmem = &mem->base;
+
+ mem->cpuaddr = dma_alloc_coherent(dev, npages << PAGE_SHIFT,
+ &mem->handle, GFP_KERNEL);
+ if (!mem->cpuaddr) {
+ nv_error(pfb, "%s: cannot allocate memory!\n", __func__);
+ gk20a_ram_put(pfb, pmem);
+ return -ENOMEM;
+ }
+
+ align <<= PAGE_SHIFT;
+
+ /* alignment check */
+ if (unlikely(mem->handle & (align - 1)))
+ nv_warn(pfb, "memory not aligned as requested: %pad (0x%x)\n",
+ &mem->handle, align);
+
+ nv_debug(pfb, "alloc size: 0x%x, align: 0x%x, paddr: %pad, vaddr: %p\n",
+ npages << PAGE_SHIFT, align, &mem->handle, mem->cpuaddr);
+
+ for (i = 0; i < npages; i++)
+ mem->base.pages[i] = mem->handle + (PAGE_SIZE * i);
+
+ mem->base.offset = (u64)mem->base.pages[0];
+
+ return 0;
+}
+
+static int
+gk20a_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 datasize,
+ struct nouveau_object **pobject)
+{
+ struct nouveau_ram *ram;
+ int ret;
+
+ ret = nouveau_ram_create(parent, engine, oclass, &ram);
+ *pobject = nv_object(ram);
+ if (ret)
+ return ret;
+ ram->type = NV_MEM_TYPE_STOLEN;
+ ram->size = get_num_physpages() << PAGE_SHIFT;
+
+ ram->get = gk20a_ram_get;
+ ram->put = gk20a_ram_put;
+
+ return 0;
+}
+
+struct nouveau_oclass
+gk20a_ram_oclass = {
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gk20a_ram_ctor,
+ .dtor = _nouveau_ram_dtor,
+ .init = _nouveau_ram_init,
+ .fini = _nouveau_ram_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
index ef91b6e893af..e5d12c24cc43 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c
@@ -211,7 +211,7 @@ nv50_ram_prog(struct nouveau_fb *pfb)
struct nv50_ram *ram = (void *)pfb->ram;
struct nv50_ramseq *hwsq = &ram->hwsq;
- ram_exec(hwsq, nouveau_boolopt(device->cfgopt, "NvMemExec", false));
+ ram_exec(hwsq, nouveau_boolopt(device->cfgopt, "NvMemExec", true));
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c
index 6eb97f16fbda..8076fb195dd5 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnva3.c
@@ -309,7 +309,7 @@ nva3_ram_prog(struct nouveau_fb *pfb)
struct nouveau_device *device = nv_device(pfb);
struct nva3_ram *ram = (void *)pfb->ram;
struct nva3_ramfuc *fuc = &ram->fuc;
- ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false));
+ ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true));
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
index 8edc92224c84..5a6a5027f749 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c
@@ -408,7 +408,7 @@ nvc0_ram_prog(struct nouveau_fb *pfb)
struct nouveau_device *device = nv_device(pfb);
struct nvc0_ram *ram = (void *)pfb->ram;
struct nvc0_ramfuc *fuc = &ram->fuc;
- ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", false));
+ ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true));
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c
index 16752192cf87..84c7efbc4f38 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnve0.c
@@ -1111,7 +1111,7 @@ 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", false));
+ ram_exec(fuc, nouveau_boolopt(device->cfgopt, "NvMemExec", true));
return (ram->base.next == &ram->base.xition);
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
index f572c2804c32..45e0202f3151 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/base.c
@@ -22,21 +22,24 @@
* Authors: Ben Skeggs
*/
-#include <subdev/gpio.h>
#include <subdev/bios.h>
#include <subdev/bios/gpio.h>
+#include "priv.h"
+
static int
nouveau_gpio_drive(struct nouveau_gpio *gpio,
int idx, int line, int dir, int out)
{
- return gpio->drive ? gpio->drive(gpio, line, dir, out) : -ENODEV;
+ const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+ return impl->drive ? impl->drive(gpio, line, dir, out) : -ENODEV;
}
static int
nouveau_gpio_sense(struct nouveau_gpio *gpio, int idx, int line)
{
- return gpio->sense ? gpio->sense(gpio, line) : -ENODEV;
+ const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+ return impl->sense ? impl->sense(gpio, line) : -ENODEV;
}
static int
@@ -102,6 +105,80 @@ nouveau_gpio_get(struct nouveau_gpio *gpio, int idx, u8 tag, u8 line)
return ret;
}
+static void
+nouveau_gpio_intr_disable(struct nouveau_event *event, int type, int index)
+{
+ struct nouveau_gpio *gpio = nouveau_gpio(event->priv);
+ 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)
+{
+ struct nouveau_gpio *gpio = nouveau_gpio(event->priv);
+ const struct nouveau_gpio_impl *impl = (void *)nv_object(gpio)->oclass;
+ impl->intr_mask(gpio, type, 1 << index, 1 << index);
+}
+
+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;
+
+ 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);
+ }
+}
+
+int
+_nouveau_gpio_fini(struct nouveau_object *object, bool suspend)
+{
+ const struct nouveau_gpio_impl *impl = (void *)object->oclass;
+ struct nouveau_gpio *gpio = nouveau_gpio(object);
+ u32 mask = (1 << impl->lines) - 1;
+
+ impl->intr_mask(gpio, NVKM_GPIO_TOGGLED, mask, 0);
+ impl->intr_stat(gpio, &mask, &mask);
+
+ return nouveau_subdev_fini(&gpio->base, suspend);
+}
+
+static struct dmi_system_id gpio_reset_ids[] = {
+ {
+ .ident = "Apple Macbook 10,1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
+ }
+ },
+ { }
+};
+
+int
+_nouveau_gpio_init(struct nouveau_object *object)
+{
+ struct nouveau_gpio *gpio = nouveau_gpio(object);
+ int ret;
+
+ ret = nouveau_subdev_init(&gpio->base);
+ if (ret)
+ return ret;
+
+ if (gpio->reset && dmi_check_system(gpio_reset_ids))
+ gpio->reset(gpio, DCB_GPIO_UNUSED);
+
+ return ret;
+}
+
void
_nouveau_gpio_dtor(struct nouveau_object *object)
{
@@ -113,9 +190,10 @@ _nouveau_gpio_dtor(struct nouveau_object *object)
int
nouveau_gpio_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
- struct nouveau_oclass *oclass, int lines,
+ struct nouveau_oclass *oclass,
int length, void **pobject)
{
+ const struct nouveau_gpio_impl *impl = (void *)oclass;
struct nouveau_gpio *gpio;
int ret;
@@ -125,34 +203,34 @@ nouveau_gpio_create_(struct nouveau_object *parent,
if (ret)
return ret;
- ret = nouveau_event_create(lines, &gpio->events);
- if (ret)
- return ret;
-
gpio->find = nouveau_gpio_find;
gpio->set = nouveau_gpio_set;
gpio->get = nouveau_gpio_get;
+ gpio->reset = impl->reset;
+
+ ret = nouveau_event_create(2, impl->lines, &gpio->events);
+ 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;
}
-static struct dmi_system_id gpio_reset_ids[] = {
- {
- .ident = "Apple Macbook 10,1",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
- }
- },
- { }
-};
-
int
-nouveau_gpio_init(struct nouveau_gpio *gpio)
+_nouveau_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
{
- int ret = nouveau_subdev_init(&gpio->base);
- if (ret == 0 && gpio->reset) {
- if (dmi_check_system(gpio_reset_ids))
- gpio->reset(gpio, DCB_GPIO_UNUSED);
- }
- return ret;
+ struct nouveau_gpio *gpio;
+ int ret;
+
+ ret = nouveau_gpio_create(parent, engine, oclass, &gpio);
+ *pobject = nv_object(gpio);
+ if (ret)
+ return ret;
+
+ return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c
index 76d5d5465ddd..27ad23eaf185 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv10.c
@@ -26,10 +26,6 @@
#include "priv.h"
-struct nv10_gpio_priv {
- struct nouveau_gpio base;
-};
-
static int
nv10_gpio_sense(struct nouveau_gpio *gpio, int line)
{
@@ -83,95 +79,38 @@ nv10_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
}
static void
-nv10_gpio_intr(struct nouveau_subdev *subdev)
-{
- struct nv10_gpio_priv *priv = (void *)subdev;
- u32 intr = nv_rd32(priv, 0x001104);
- u32 hi = (intr & 0x0000ffff) >> 0;
- u32 lo = (intr & 0xffff0000) >> 16;
- int i;
-
- for (i = 0; (hi | lo) && i < 32; i++) {
- if ((hi | lo) & (1 << i))
- nouveau_event_trigger(priv->base.events, i);
- }
-
- nv_wr32(priv, 0x001104, intr);
-}
-
-static void
-nv10_gpio_intr_enable(struct nouveau_event *event, int line)
-{
- nv_wr32(event->priv, 0x001104, 0x00010001 << line);
- nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00010001 << line);
-}
-
-static void
-nv10_gpio_intr_disable(struct nouveau_event *event, int line)
-{
- nv_wr32(event->priv, 0x001104, 0x00010001 << line);
- nv_mask(event->priv, 0x001144, 0x00010001 << line, 0x00000000);
-}
-
-static int
-nv10_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
+nv10_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
{
- struct nv10_gpio_priv *priv;
- int ret;
-
- ret = nouveau_gpio_create(parent, engine, oclass, 16, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- priv->base.drive = nv10_gpio_drive;
- priv->base.sense = nv10_gpio_sense;
- priv->base.events->priv = priv;
- priv->base.events->enable = nv10_gpio_intr_enable;
- priv->base.events->disable = nv10_gpio_intr_disable;
- nv_subdev(priv)->intr = nv10_gpio_intr;
- return 0;
+ u32 intr = nv_rd32(gpio, 0x001104);
+ u32 stat = nv_rd32(gpio, 0x001144) & intr;
+ *lo = (stat & 0xffff0000) >> 16;
+ *hi = (stat & 0x0000ffff);
+ nv_wr32(gpio, 0x001104, intr);
}
static void
-nv10_gpio_dtor(struct nouveau_object *object)
-{
- struct nv10_gpio_priv *priv = (void *)object;
- nouveau_gpio_destroy(&priv->base);
-}
-
-static int
-nv10_gpio_init(struct nouveau_object *object)
-{
- struct nv10_gpio_priv *priv = (void *)object;
- int ret;
-
- ret = nouveau_gpio_init(&priv->base);
- if (ret)
- return ret;
-
- nv_wr32(priv, 0x001144, 0x00000000);
- nv_wr32(priv, 0x001104, 0xffffffff);
- return 0;
-}
-
-static int
-nv10_gpio_fini(struct nouveau_object *object, bool suspend)
+nv10_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
{
- struct nv10_gpio_priv *priv = (void *)object;
- nv_wr32(priv, 0x001144, 0x00000000);
- return nouveau_gpio_fini(&priv->base, suspend);
+ u32 inte = nv_rd32(gpio, 0x001144);
+ if (type & NVKM_GPIO_LO)
+ inte = (inte & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte = (inte & ~mask) | data;
+ nv_wr32(gpio, 0x001144, inte);
}
-struct nouveau_oclass
-nv10_gpio_oclass = {
- .handle = NV_SUBDEV(GPIO, 0x10),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nv10_gpio_ctor,
- .dtor = nv10_gpio_dtor,
- .init = nv10_gpio_init,
- .fini = nv10_gpio_fini,
+struct nouveau_oclass *
+nv10_gpio_oclass = &(struct nouveau_gpio_impl) {
+ .base.handle = NV_SUBDEV(GPIO, 0x10),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_gpio_ctor,
+ .dtor = _nouveau_gpio_dtor,
+ .init = _nouveau_gpio_init,
+ .fini = _nouveau_gpio_fini,
},
-};
+ .lines = 16,
+ .intr_stat = nv10_gpio_intr_stat,
+ .intr_mask = nv10_gpio_intr_mask,
+ .drive = nv10_gpio_drive,
+ .sense = nv10_gpio_sense,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c
index 2ef774731629..1864fa98e6b1 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv50.c
@@ -24,15 +24,10 @@
#include "priv.h"
-struct nv50_gpio_priv {
- struct nouveau_gpio base;
-};
-
-static void
+void
nv50_gpio_reset(struct nouveau_gpio *gpio, u8 match)
{
struct nouveau_bios *bios = nouveau_bios(gpio);
- struct nv50_gpio_priv *priv = (void *)gpio;
u8 ver, len;
u16 entry;
int ent = -1;
@@ -55,7 +50,7 @@ nv50_gpio_reset(struct nouveau_gpio *gpio, u8 match)
gpio->set(gpio, 0, func, line, defs);
- nv_mask(priv, reg, 0x00010001 << lsh, val << lsh);
+ nv_mask(gpio, reg, 0x00010001 << lsh, val << lsh);
}
}
@@ -72,7 +67,7 @@ nv50_gpio_location(int line, u32 *reg, u32 *shift)
return 0;
}
-static int
+int
nv50_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
{
u32 reg, shift;
@@ -84,7 +79,7 @@ nv50_gpio_drive(struct nouveau_gpio *gpio, int line, int dir, int out)
return 0;
}
-static int
+int
nv50_gpio_sense(struct nouveau_gpio *gpio, int line)
{
u32 reg, shift;
@@ -95,119 +90,40 @@ nv50_gpio_sense(struct nouveau_gpio *gpio, int line)
return !!(nv_rd32(gpio, reg) & (4 << shift));
}
-void
-nv50_gpio_intr(struct nouveau_subdev *subdev)
-{
- struct nv50_gpio_priv *priv = (void *)subdev;
- u32 intr0, intr1 = 0;
- u32 hi, lo;
- int i;
-
- intr0 = nv_rd32(priv, 0xe054) & nv_rd32(priv, 0xe050);
- if (nv_device(priv)->chipset > 0x92)
- intr1 = nv_rd32(priv, 0xe074) & nv_rd32(priv, 0xe070);
-
- hi = (intr0 & 0x0000ffff) | (intr1 << 16);
- lo = (intr0 >> 16) | (intr1 & 0xffff0000);
-
- for (i = 0; (hi | lo) && i < 32; i++) {
- if ((hi | lo) & (1 << i))
- nouveau_event_trigger(priv->base.events, i);
- }
-
- nv_wr32(priv, 0xe054, intr0);
- if (nv_device(priv)->chipset > 0x92)
- nv_wr32(priv, 0xe074, intr1);
-}
-
-void
-nv50_gpio_intr_enable(struct nouveau_event *event, int line)
-{
- const u32 addr = line < 16 ? 0xe050 : 0xe070;
- const u32 mask = 0x00010001 << (line & 0xf);
- nv_wr32(event->priv, addr + 0x04, mask);
- nv_mask(event->priv, addr + 0x00, mask, mask);
-}
-
-void
-nv50_gpio_intr_disable(struct nouveau_event *event, int line)
-{
- const u32 addr = line < 16 ? 0xe050 : 0xe070;
- const u32 mask = 0x00010001 << (line & 0xf);
- nv_wr32(event->priv, addr + 0x04, mask);
- nv_mask(event->priv, addr + 0x00, mask, 0x00000000);
-}
-
-static int
-nv50_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv50_gpio_priv *priv;
- int ret;
-
- ret = nouveau_gpio_create(parent, engine, oclass,
- nv_device(parent)->chipset > 0x92 ? 32 : 16,
- &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- priv->base.reset = nv50_gpio_reset;
- priv->base.drive = nv50_gpio_drive;
- priv->base.sense = nv50_gpio_sense;
- priv->base.events->priv = priv;
- priv->base.events->enable = nv50_gpio_intr_enable;
- priv->base.events->disable = nv50_gpio_intr_disable;
- nv_subdev(priv)->intr = nv50_gpio_intr;
- return 0;
-}
-
-void
-nv50_gpio_dtor(struct nouveau_object *object)
-{
- struct nv50_gpio_priv *priv = (void *)object;
- nouveau_gpio_destroy(&priv->base);
-}
-
-int
-nv50_gpio_init(struct nouveau_object *object)
+static void
+nv50_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
{
- struct nv50_gpio_priv *priv = (void *)object;
- int ret;
-
- ret = nouveau_gpio_init(&priv->base);
- if (ret)
- return ret;
-
- /* disable, and ack any pending gpio interrupts */
- nv_wr32(priv, 0xe050, 0x00000000);
- nv_wr32(priv, 0xe054, 0xffffffff);
- if (nv_device(priv)->chipset > 0x92) {
- nv_wr32(priv, 0xe070, 0x00000000);
- nv_wr32(priv, 0xe074, 0xffffffff);
- }
-
- return 0;
+ u32 intr = nv_rd32(gpio, 0x00e054);
+ u32 stat = nv_rd32(gpio, 0x00e050) & intr;
+ *lo = (stat & 0xffff0000) >> 16;
+ *hi = (stat & 0x0000ffff);
+ nv_wr32(gpio, 0x00e054, intr);
}
-int
-nv50_gpio_fini(struct nouveau_object *object, bool suspend)
+static void
+nv50_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
{
- struct nv50_gpio_priv *priv = (void *)object;
- nv_wr32(priv, 0xe050, 0x00000000);
- if (nv_device(priv)->chipset > 0x92)
- nv_wr32(priv, 0xe070, 0x00000000);
- return nouveau_gpio_fini(&priv->base, suspend);
+ u32 inte = nv_rd32(gpio, 0x00e050);
+ if (type & NVKM_GPIO_LO)
+ inte = (inte & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte = (inte & ~mask) | data;
+ nv_wr32(gpio, 0x00e050, inte);
}
-struct nouveau_oclass
-nv50_gpio_oclass = {
- .handle = NV_SUBDEV(GPIO, 0x50),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nv50_gpio_ctor,
- .dtor = nv50_gpio_dtor,
- .init = nv50_gpio_init,
- .fini = nv50_gpio_fini,
+struct nouveau_oclass *
+nv50_gpio_oclass = &(struct nouveau_gpio_impl) {
+ .base.handle = NV_SUBDEV(GPIO, 0x50),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_gpio_ctor,
+ .dtor = _nouveau_gpio_dtor,
+ .init = _nouveau_gpio_init,
+ .fini = _nouveau_gpio_fini,
},
-};
+ .lines = 16,
+ .intr_stat = nv50_gpio_intr_stat,
+ .intr_mask = nv50_gpio_intr_mask,
+ .drive = nv50_gpio_drive,
+ .sense = nv50_gpio_sense,
+ .reset = nv50_gpio_reset,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c
new file mode 100644
index 000000000000..252083d376f5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nv92.c
@@ -0,0 +1,74 @@
+/*
+ * 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"
+
+void
+nv92_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
+{
+ u32 intr0 = nv_rd32(gpio, 0x00e054);
+ u32 intr1 = nv_rd32(gpio, 0x00e074);
+ u32 stat0 = nv_rd32(gpio, 0x00e050) & intr0;
+ u32 stat1 = nv_rd32(gpio, 0x00e070) & intr1;
+ *lo = (stat1 & 0xffff0000) | (stat0 >> 16);
+ *hi = (stat1 << 16) | (stat0 & 0x0000ffff);
+ nv_wr32(gpio, 0x00e054, intr0);
+ nv_wr32(gpio, 0x00e074, intr1);
+}
+
+void
+nv92_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);
+ if (type & NVKM_GPIO_LO)
+ inte0 = (inte0 & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff);
+ mask >>= 16;
+ data >>= 16;
+ if (type & NVKM_GPIO_LO)
+ inte1 = (inte1 & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte1 = (inte1 & ~mask) | data;
+ nv_wr32(gpio, 0x00e050, inte0);
+ nv_wr32(gpio, 0x00e070, inte1);
+}
+
+struct nouveau_oclass *
+nv92_gpio_oclass = &(struct nouveau_gpio_impl) {
+ .base.handle = NV_SUBDEV(GPIO, 0x92),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_gpio_ctor,
+ .dtor = _nouveau_gpio_dtor,
+ .init = _nouveau_gpio_init,
+ .fini = _nouveau_gpio_fini,
+ },
+ .lines = 32,
+ .intr_stat = nv92_gpio_intr_stat,
+ .intr_mask = nv92_gpio_intr_mask,
+ .drive = nv50_gpio_drive,
+ .sense = nv50_gpio_sense,
+ .reset = nv50_gpio_reset,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c
index 010431e3acec..a4682b0956ad 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nvd0.c
@@ -24,15 +24,10 @@
#include "priv.h"
-struct nvd0_gpio_priv {
- struct nouveau_gpio base;
-};
-
void
nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match)
{
struct nouveau_bios *bios = nouveau_bios(gpio);
- struct nvd0_gpio_priv *priv = (void *)gpio;
u8 ver, len;
u16 entry;
int ent = -1;
@@ -51,9 +46,9 @@ nvd0_gpio_reset(struct nouveau_gpio *gpio, u8 match)
gpio->set(gpio, 0, func, line, defs);
- nv_mask(priv, 0x00d610 + (line * 4), 0xff, unk0);
+ nv_mask(gpio, 0x00d610 + (line * 4), 0xff, unk0);
if (unk1--)
- nv_mask(priv, 0x00d740 + (unk1 * 4), 0xff, line);
+ nv_mask(gpio, 0x00d740 + (unk1 * 4), 0xff, line);
}
}
@@ -72,36 +67,19 @@ nvd0_gpio_sense(struct nouveau_gpio *gpio, int line)
return !!(nv_rd32(gpio, 0x00d610 + (line * 4)) & 0x00004000);
}
-static int
-nvd0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nvd0_gpio_priv *priv;
- int ret;
-
- ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- priv->base.reset = nvd0_gpio_reset;
- priv->base.drive = nvd0_gpio_drive;
- priv->base.sense = nvd0_gpio_sense;
- priv->base.events->priv = priv;
- priv->base.events->enable = nv50_gpio_intr_enable;
- priv->base.events->disable = nv50_gpio_intr_disable;
- nv_subdev(priv)->intr = nv50_gpio_intr;
- return 0;
-}
-
-struct nouveau_oclass
-nvd0_gpio_oclass = {
- .handle = NV_SUBDEV(GPIO, 0xd0),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nvd0_gpio_ctor,
- .dtor = nv50_gpio_dtor,
- .init = nv50_gpio_init,
- .fini = nv50_gpio_fini,
+struct nouveau_oclass *
+nvd0_gpio_oclass = &(struct nouveau_gpio_impl) {
+ .base.handle = NV_SUBDEV(GPIO, 0xd0),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_gpio_ctor,
+ .dtor = _nouveau_gpio_dtor,
+ .init = _nouveau_gpio_init,
+ .fini = _nouveau_gpio_fini,
},
-};
+ .lines = 32,
+ .intr_stat = nv92_gpio_intr_stat,
+ .intr_mask = nv92_gpio_intr_mask,
+ .drive = nvd0_gpio_drive,
+ .sense = nvd0_gpio_sense,
+ .reset = nvd0_gpio_reset,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c
index 16b8c5bf5efa..e1145b48c76c 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/nve0.c
@@ -24,108 +24,51 @@
#include "priv.h"
-struct nve0_gpio_priv {
- struct nouveau_gpio base;
-};
-
-void
-nve0_gpio_intr(struct nouveau_subdev *subdev)
+static void
+nve0_gpio_intr_stat(struct nouveau_gpio *gpio, u32 *hi, u32 *lo)
{
- struct nve0_gpio_priv *priv = (void *)subdev;
- u32 intr0 = nv_rd32(priv, 0xdc00) & nv_rd32(priv, 0xdc08);
- u32 intr1 = nv_rd32(priv, 0xdc80) & nv_rd32(priv, 0xdc88);
- u32 hi = (intr0 & 0x0000ffff) | (intr1 << 16);
- u32 lo = (intr0 >> 16) | (intr1 & 0xffff0000);
- int i;
-
- for (i = 0; (hi | lo) && i < 32; i++) {
- if ((hi | lo) & (1 << i))
- nouveau_event_trigger(priv->base.events, i);
- }
-
- nv_wr32(priv, 0xdc00, intr0);
- nv_wr32(priv, 0xdc88, intr1);
+ u32 intr0 = nv_rd32(gpio, 0x00dc00);
+ u32 intr1 = nv_rd32(gpio, 0x00dc80);
+ u32 stat0 = nv_rd32(gpio, 0x00dc08) & intr0;
+ u32 stat1 = nv_rd32(gpio, 0x00dc88) & intr1;
+ *lo = (stat1 & 0xffff0000) | (stat0 >> 16);
+ *hi = (stat1 << 16) | (stat0 & 0x0000ffff);
+ nv_wr32(gpio, 0x00dc00, intr0);
+ nv_wr32(gpio, 0x00dc80, intr1);
}
void
-nve0_gpio_intr_enable(struct nouveau_event *event, int line)
+nve0_gpio_intr_mask(struct nouveau_gpio *gpio, u32 type, u32 mask, u32 data)
{
- const u32 addr = line < 16 ? 0xdc00 : 0xdc80;
- const u32 mask = 0x00010001 << (line & 0xf);
- nv_wr32(event->priv, addr + 0x08, mask);
- nv_mask(event->priv, addr + 0x00, mask, mask);
-}
-
-void
-nve0_gpio_intr_disable(struct nouveau_event *event, int line)
-{
- const u32 addr = line < 16 ? 0xdc00 : 0xdc80;
- const u32 mask = 0x00010001 << (line & 0xf);
- nv_wr32(event->priv, addr + 0x08, mask);
- nv_mask(event->priv, addr + 0x00, mask, 0x00000000);
-}
-
-int
-nve0_gpio_fini(struct nouveau_object *object, bool suspend)
-{
- struct nve0_gpio_priv *priv = (void *)object;
- nv_wr32(priv, 0xdc08, 0x00000000);
- nv_wr32(priv, 0xdc88, 0x00000000);
- return nouveau_gpio_fini(&priv->base, suspend);
-}
-
-int
-nve0_gpio_init(struct nouveau_object *object)
-{
- struct nve0_gpio_priv *priv = (void *)object;
- int ret;
-
- ret = nouveau_gpio_init(&priv->base);
- if (ret)
- return ret;
-
- nv_wr32(priv, 0xdc00, 0xffffffff);
- nv_wr32(priv, 0xdc80, 0xffffffff);
- return 0;
-}
-
-void
-nve0_gpio_dtor(struct nouveau_object *object)
-{
- struct nve0_gpio_priv *priv = (void *)object;
- nouveau_gpio_destroy(&priv->base);
-}
-
-static int
-nve0_gpio_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nve0_gpio_priv *priv;
- int ret;
-
- ret = nouveau_gpio_create(parent, engine, oclass, 32, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- priv->base.reset = nvd0_gpio_reset;
- priv->base.drive = nvd0_gpio_drive;
- priv->base.sense = nvd0_gpio_sense;
- priv->base.events->priv = priv;
- priv->base.events->enable = nve0_gpio_intr_enable;
- priv->base.events->disable = nve0_gpio_intr_disable;
- nv_subdev(priv)->intr = nve0_gpio_intr;
- return 0;
+ u32 inte0 = nv_rd32(gpio, 0x00dc08);
+ u32 inte1 = nv_rd32(gpio, 0x00dc88);
+ if (type & NVKM_GPIO_LO)
+ inte0 = (inte0 & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff);
+ mask >>= 16;
+ data >>= 16;
+ if (type & NVKM_GPIO_LO)
+ inte1 = (inte1 & ~(mask << 16)) | (data << 16);
+ if (type & NVKM_GPIO_HI)
+ inte1 = (inte1 & ~mask) | data;
+ nv_wr32(gpio, 0x00dc08, inte0);
+ nv_wr32(gpio, 0x00dc88, inte1);
}
-struct nouveau_oclass
-nve0_gpio_oclass = {
- .handle = NV_SUBDEV(GPIO, 0xe0),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nve0_gpio_ctor,
- .dtor = nv50_gpio_dtor,
- .init = nve0_gpio_init,
- .fini = nve0_gpio_fini,
+struct nouveau_oclass *
+nve0_gpio_oclass = &(struct nouveau_gpio_impl) {
+ .base.handle = NV_SUBDEV(GPIO, 0xe0),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_gpio_ctor,
+ .dtor = _nouveau_gpio_dtor,
+ .init = _nouveau_gpio_init,
+ .fini = _nouveau_gpio_fini,
},
-};
+ .lines = 32,
+ .intr_stat = nve0_gpio_intr_stat,
+ .intr_mask = nve0_gpio_intr_mask,
+ .drive = nvd0_gpio_drive,
+ .sense = nvd0_gpio_sense,
+ .reset = nvd0_gpio_reset,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h
index 2ee1c895c782..e1724dfc86ae 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/gpio/priv.h
@@ -3,15 +3,65 @@
#include <subdev/gpio.h>
-void nv50_gpio_dtor(struct nouveau_object *);
-int nv50_gpio_init(struct nouveau_object *);
-int nv50_gpio_fini(struct nouveau_object *, bool);
-void nv50_gpio_intr(struct nouveau_subdev *);
-void nv50_gpio_intr_enable(struct nouveau_event *, int line);
-void nv50_gpio_intr_disable(struct nouveau_event *, int line);
+#define nouveau_gpio_create(p,e,o,d) \
+ nouveau_gpio_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_gpio_destroy(p) ({ \
+ struct nouveau_gpio *gpio = (p); \
+ _nouveau_gpio_dtor(nv_object(gpio)); \
+})
+#define nouveau_gpio_init(p) ({ \
+ struct nouveau_gpio *gpio = (p); \
+ _nouveau_gpio_init(nv_object(gpio)); \
+})
+#define nouveau_gpio_fini(p,s) ({ \
+ struct nouveau_gpio *gpio = (p); \
+ _nouveau_gpio_fini(nv_object(gpio), (s)); \
+})
+
+int nouveau_gpio_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, int, void **);
+int _nouveau_gpio_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+void _nouveau_gpio_dtor(struct nouveau_object *);
+int _nouveau_gpio_init(struct nouveau_object *);
+int _nouveau_gpio_fini(struct nouveau_object *, bool);
+
+struct nouveau_gpio_impl {
+ struct nouveau_oclass base;
+ int lines;
+
+ /* read and ack pending interrupts, returning only data
+ * for lines that have not been masked off, while still
+ * performing the ack for anything that was pending.
+ */
+ void (*intr_stat)(struct nouveau_gpio *, u32 *, u32 *);
+
+ /* mask on/off interrupts for hi/lo transitions on a
+ * given set of gpio lines
+ */
+ void (*intr_mask)(struct nouveau_gpio *, u32, u32, u32);
+
+ /* configure gpio direction and output value */
+ int (*drive)(struct nouveau_gpio *, int line, int dir, int out);
+
+ /* sense current state of given gpio line */
+ int (*sense)(struct nouveau_gpio *, int line);
+
+ /*XXX*/
+ void (*reset)(struct nouveau_gpio *, u8);
+};
+
+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 nvd0_gpio_reset(struct nouveau_gpio *, u8);
int nvd0_gpio_drive(struct nouveau_gpio *, int, int, int);
int nvd0_gpio_sense(struct nouveau_gpio *, int);
+
#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c
index 4b195ac4da66..2c2731a6cf91 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/anx9805.c
@@ -22,7 +22,7 @@
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
-#include <subdev/i2c.h>
+#include "port.h"
struct anx9805_i2c_port {
struct nouveau_i2c_port base;
@@ -37,6 +37,8 @@ anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh)
struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent;
u8 tmp, i;
+ DBG("ANX9805 train %d 0x%02x %d\n", link_nr, link_bw, enh);
+
nv_wri2cr(mast, chan->addr, 0xa0, link_bw);
nv_wri2cr(mast, chan->addr, 0xa1, link_nr | (enh ? 0x80 : 0x00));
nv_wri2cr(mast, chan->addr, 0xa2, 0x01);
@@ -60,21 +62,29 @@ anx9805_train(struct nouveau_i2c_port *port, int link_nr, int link_bw, bool enh)
}
static int
-anx9805_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size)
+anx9805_aux(struct nouveau_i2c_port *port, bool retry,
+ u8 type, u32 addr, u8 *data, u8 size)
{
struct anx9805_i2c_port *chan = (void *)port;
struct nouveau_i2c_port *mast = (void *)nv_object(chan)->parent;
int i, ret = -ETIMEDOUT;
+ u8 buf[16] = {};
u8 tmp;
+ DBG("%02x %05x %d\n", type, addr, size);
+
tmp = nv_rdi2cr(mast, chan->ctrl, 0x07) & ~0x04;
nv_wri2cr(mast, chan->ctrl, 0x07, tmp | 0x04);
nv_wri2cr(mast, chan->ctrl, 0x07, tmp);
nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01);
nv_wri2cr(mast, chan->addr, 0xe4, 0x80);
- for (i = 0; !(type & 1) && i < size; i++)
- nv_wri2cr(mast, chan->addr, 0xf0 + i, data[i]);
+ if (!(type & 1)) {
+ memcpy(buf, data, size);
+ DBG("%16ph", buf);
+ for (i = 0; i < size; i++)
+ nv_wri2cr(mast, chan->addr, 0xf0 + i, buf[i]);
+ }
nv_wri2cr(mast, chan->addr, 0xe5, ((size - 1) << 4) | type);
nv_wri2cr(mast, chan->addr, 0xe6, (addr & 0x000ff) >> 0);
nv_wri2cr(mast, chan->addr, 0xe7, (addr & 0x0ff00) >> 8);
@@ -93,8 +103,13 @@ anx9805_aux(struct nouveau_i2c_port *port, u8 type, u32 addr, u8 *data, u8 size)
goto done;
}
- for (i = 0; (type & 1) && i < size; i++)
- data[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i);
+ if (type & 1) {
+ for (i = 0; i < size; i++)
+ buf[i] = nv_rdi2cr(mast, chan->addr, 0xf0 + i);
+ DBG("%16ph", buf);
+ memcpy(data, buf, size);
+ }
+
ret = 0;
done:
nv_wri2cr(mast, chan->ctrl, 0xf7, 0x01);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c
index 5de074ad170b..02eb42be2e9e 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c
@@ -22,15 +22,19 @@
* Authors: Ben Skeggs
*/
-#include <subdev/i2c.h>
+#include "priv.h"
int
nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
{
+ struct nouveau_i2c *i2c = nouveau_i2c(port);
if (port->func->aux) {
- if (port->func->acquire)
- port->func->acquire(port);
- return port->func->aux(port, 9, addr, data, size);
+ int ret = i2c->acquire(port, 0);
+ if (ret == 0) {
+ ret = port->func->aux(port, true, 9, addr, data, size);
+ i2c->release(port);
+ }
+ return ret;
}
return -ENODEV;
}
@@ -38,10 +42,14 @@ nv_rdaux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
int
nv_wraux(struct nouveau_i2c_port *port, u32 addr, u8 *data, u8 size)
{
+ struct nouveau_i2c *i2c = nouveau_i2c(port);
if (port->func->aux) {
- if (port->func->acquire)
- port->func->acquire(port);
- return port->func->aux(port, 8, addr, data, size);
+ int ret = i2c->acquire(port, 0);
+ if (ret == 0) {
+ ret = port->func->aux(port, true, 8, addr, data, size);
+ i2c->release(port);
+ }
+ return ret;
}
return -ENODEV;
}
@@ -50,13 +58,16 @@ static int
aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct nouveau_i2c_port *port = adap->algo_data;
+ struct nouveau_i2c *i2c = nouveau_i2c(port);
struct i2c_msg *msg = msgs;
int ret, mcnt = num;
if (!port->func->aux)
return -ENODEV;
- if ( port->func->acquire)
- port->func->acquire(port);
+
+ ret = i2c->acquire(port, 0);
+ if (ret)
+ return ret;
while (mcnt--) {
u8 remaining = msg->len;
@@ -74,9 +85,11 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (mcnt || remaining > 16)
cmd |= 4; /* MOT */
- ret = port->func->aux(port, cmd, msg->addr, ptr, cnt);
- if (ret < 0)
+ ret = port->func->aux(port, true, cmd, msg->addr, ptr, cnt);
+ if (ret < 0) {
+ i2c->release(port);
return ret;
+ }
ptr += cnt;
remaining -= cnt;
@@ -85,6 +98,7 @@ aux_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
msg++;
}
+ i2c->release(port);
return num;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
index 378e05b88e6f..09ba2cc851cf 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c
@@ -23,13 +23,16 @@
*/
#include <core/option.h>
+#include <core/event.h>
#include <subdev/bios.h>
#include <subdev/bios/dcb.h>
#include <subdev/bios/i2c.h>
-#include <subdev/i2c.h>
#include <subdev/vga.h>
+#include "priv.h"
+#include "pad.h"
+
/******************************************************************************
* interface to linux i2c bit-banging algorithm
*****************************************************************************/
@@ -45,9 +48,15 @@ nouveau_i2c_pre_xfer(struct i2c_adapter *adap)
{
struct i2c_algo_bit_data *bit = adap->algo_data;
struct nouveau_i2c_port *port = bit->data;
- if (port->func->acquire)
- port->func->acquire(port);
- return 0;
+ return nouveau_i2c(port)->acquire(port, bit->timeout);
+}
+
+static void
+nouveau_i2c_post_xfer(struct i2c_adapter *adap)
+{
+ struct i2c_algo_bit_data *bit = adap->algo_data;
+ struct nouveau_i2c_port *port = bit->data;
+ return nouveau_i2c(port)->release(port);
}
static void
@@ -82,6 +91,15 @@ nouveau_i2c_getsda(void *data)
* base i2c "port" class implementation
*****************************************************************************/
+int
+_nouveau_i2c_port_fini(struct nouveau_object *object, bool suspend)
+{
+ struct nouveau_i2c_port *port = (void *)object;
+ struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+ nv_ofuncs(pad)->fini(nv_object(pad), suspend);
+ return nouveau_object_fini(&port->base, suspend);
+}
+
void
_nouveau_i2c_port_dtor(struct nouveau_object *object)
{
@@ -98,7 +116,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
const struct nouveau_i2c_func *func,
int size, void **pobject)
{
- struct nouveau_device *device = nv_device(parent);
+ struct nouveau_device *device = nv_device(engine);
struct nouveau_i2c *i2c = (void *)engine;
struct nouveau_i2c_port *port;
int ret;
@@ -113,8 +131,9 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
port->adapter.owner = THIS_MODULE;
port->adapter.dev.parent = nv_device_base(device);
port->index = index;
+ port->aux = -1;
port->func = func;
- i2c_set_adapdata(&port->adapter, i2c);
+ mutex_init(&port->mutex);
if ( algo == &nouveau_i2c_bit_algo &&
!nouveau_boolopt(device->cfgopt, "NvI2C", CSTMSEL)) {
@@ -128,6 +147,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
bit->timeout = usecs_to_jiffies(2200);
bit->data = port;
bit->pre_xfer = nouveau_i2c_pre_xfer;
+ bit->post_xfer = nouveau_i2c_post_xfer;
bit->setsda = nouveau_i2c_setsda;
bit->setscl = nouveau_i2c_setscl;
bit->getsda = nouveau_i2c_getsda;
@@ -141,7 +161,6 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,
ret = i2c_add_adapter(&port->adapter);
}
- /* drop port's i2c subdev refcount, i2c handles this itself */
if (ret == 0)
list_add_tail(&port->head, &i2c->ports);
return ret;
@@ -193,6 +212,75 @@ nouveau_i2c_find_type(struct nouveau_i2c *i2c, u16 type)
return NULL;
}
+static void
+nouveau_i2c_release_pad(struct nouveau_i2c_port *port)
+{
+ struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+ struct nouveau_i2c *i2c = nouveau_i2c(port);
+
+ if (atomic_dec_and_test(&nv_object(pad)->usecount)) {
+ nv_ofuncs(pad)->fini(nv_object(pad), false);
+ wake_up_all(&i2c->wait);
+ }
+}
+
+static int
+nouveau_i2c_try_acquire_pad(struct nouveau_i2c_port *port)
+{
+ struct nvkm_i2c_pad *pad = nvkm_i2c_pad(port);
+
+ if (atomic_add_return(1, &nv_object(pad)->usecount) != 1) {
+ struct nouveau_object *owner = (void *)pad->port;
+ do {
+ if (owner == (void *)port)
+ return 0;
+ owner = owner->parent;
+ } while(owner);
+ nouveau_i2c_release_pad(port);
+ return -EBUSY;
+ }
+
+ pad->next = port;
+ nv_ofuncs(pad)->init(nv_object(pad));
+ return 0;
+}
+
+static int
+nouveau_i2c_acquire_pad(struct nouveau_i2c_port *port, unsigned long timeout)
+{
+ struct nouveau_i2c *i2c = nouveau_i2c(port);
+
+ if (timeout) {
+ if (wait_event_timeout(i2c->wait,
+ nouveau_i2c_try_acquire_pad(port) == 0,
+ timeout) == 0)
+ return -EBUSY;
+ } else {
+ wait_event(i2c->wait, nouveau_i2c_try_acquire_pad(port) == 0);
+ }
+
+ return 0;
+}
+
+static void
+nouveau_i2c_release(struct nouveau_i2c_port *port)
+__releases(pad->mutex)
+{
+ nouveau_i2c(port)->release_pad(port);
+ mutex_unlock(&port->mutex);
+}
+
+static int
+nouveau_i2c_acquire(struct nouveau_i2c_port *port, unsigned long timeout)
+__acquires(pad->mutex)
+{
+ int ret;
+ mutex_lock(&port->mutex);
+ if ((ret = nouveau_i2c(port)->acquire_pad(port, timeout)))
+ mutex_unlock(&port->mutex);
+ return ret;
+}
+
static int
nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
struct nouveau_i2c_board_info *info,
@@ -237,11 +325,59 @@ nouveau_i2c_identify(struct nouveau_i2c *i2c, int index, const char *what,
return -ENODEV;
}
+static void
+nouveau_i2c_intr_disable(struct nouveau_event *event, int type, int index)
+{
+ struct nouveau_i2c *i2c = nouveau_i2c(event->priv);
+ 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, 0);
+}
+
+static void
+nouveau_i2c_intr_enable(struct nouveau_event *event, int type, int index)
+{
+ struct nouveau_i2c *i2c = nouveau_i2c(event->priv);
+ 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 void
+nouveau_i2c_intr(struct nouveau_subdev *subdev)
+{
+ struct nouveau_i2c_impl *impl = (void *)nv_oclass(subdev);
+ struct nouveau_i2c *i2c = nouveau_i2c(subdev);
+ struct nouveau_i2c_port *port;
+ u32 hi, lo, rq, tx, e;
+
+ if (impl->aux_stat) {
+ impl->aux_stat(i2c, &hi, &lo, &rq, &tx);
+ if (hi || lo || rq || tx) {
+ list_for_each_entry(port, &i2c->ports, head) {
+ if (e = 0, port->aux < 0)
+ continue;
+
+ if (hi & (1 << port->aux)) e |= NVKM_I2C_PLUG;
+ 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);
+ }
+ }
+ }
+}
+
int
_nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
{
+ struct nouveau_i2c_impl *impl = (void *)nv_oclass(object);
struct nouveau_i2c *i2c = (void *)object;
struct nouveau_i2c_port *port;
+ u32 mask;
int ret;
list_for_each_entry(port, &i2c->ports, head) {
@@ -250,6 +386,11 @@ _nouveau_i2c_fini(struct nouveau_object *object, bool suspend)
goto fail;
}
+ if ((mask = (1 << impl->aux) - 1), impl->aux_stat) {
+ impl->aux_mask(i2c, NVKM_I2C_ANY, mask, 0);
+ impl->aux_stat(i2c, &mask, &mask, &mask, &mask);
+ }
+
return nouveau_subdev_fini(&i2c->base, suspend);
fail:
list_for_each_entry_continue_reverse(port, &i2c->ports, head) {
@@ -290,6 +431,8 @@ _nouveau_i2c_dtor(struct nouveau_object *object)
struct nouveau_i2c *i2c = (void *)object;
struct nouveau_i2c_port *port, *temp;
+ nouveau_event_destroy(&i2c->ntfy);
+
list_for_each_entry_safe(port, temp, &i2c->ports, head) {
nouveau_object_ref(NULL, (struct nouveau_object **)&port);
}
@@ -306,14 +449,14 @@ int
nouveau_i2c_create_(struct nouveau_object *parent,
struct nouveau_object *engine,
struct nouveau_oclass *oclass,
- struct nouveau_oclass *sclass,
int length, void **pobject)
{
+ const struct nouveau_i2c_impl *impl = (void *)oclass;
struct nouveau_bios *bios = nouveau_bios(parent);
struct nouveau_i2c *i2c;
struct nouveau_object *object;
struct dcb_i2c_entry info;
- int ret, i, j, index = -1;
+ int ret, i, j, index = -1, pad;
struct dcb_output outp;
u8 ver, hdr;
u32 data;
@@ -324,24 +467,48 @@ nouveau_i2c_create_(struct nouveau_object *parent,
if (ret)
return ret;
+ nv_subdev(i2c)->intr = nouveau_i2c_intr;
i2c->find = nouveau_i2c_find;
i2c->find_type = nouveau_i2c_find_type;
+ i2c->acquire_pad = nouveau_i2c_acquire_pad;
+ i2c->release_pad = nouveau_i2c_release_pad;
+ i2c->acquire = nouveau_i2c_acquire;
+ i2c->release = nouveau_i2c_release;
i2c->identify = nouveau_i2c_identify;
+ init_waitqueue_head(&i2c->wait);
INIT_LIST_HEAD(&i2c->ports);
while (!dcb_i2c_parse(bios, ++index, &info)) {
if (info.type == DCB_I2C_UNUSED)
continue;
- oclass = sclass;
+ if (info.share != DCB_I2C_UNUSED) {
+ if (info.type == DCB_I2C_NVIO_AUX)
+ pad = info.drive;
+ else
+ pad = info.share;
+ oclass = impl->pad_s;
+ } else {
+ pad = 0x100 + info.drive;
+ oclass = impl->pad_x;
+ }
+
+ ret = nouveau_object_ctor(NULL, *pobject, oclass,
+ NULL, pad, &parent);
+ if (ret < 0)
+ continue;
+
+ oclass = impl->sclass;
do {
ret = -EINVAL;
if (oclass->handle == info.type) {
- ret = nouveau_object_ctor(*pobject, *pobject,
+ ret = nouveau_object_ctor(parent, *pobject,
oclass, &info,
index, &object);
}
} while (ret && (++oclass)->handle);
+
+ nouveau_object_ref(NULL, &parent);
}
/* in addition to the busses specified in the i2c table, there
@@ -380,5 +547,28 @@ nouveau_i2c_create_(struct nouveau_object *parent,
}
}
+ ret = nouveau_event_create(4, index, &i2c->ntfy);
+ if (ret)
+ return ret;
+
+ i2c->ntfy->priv = i2c;
+ i2c->ntfy->enable = nouveau_i2c_intr_enable;
+ i2c->ntfy->disable = nouveau_i2c_intr_disable;
+ return 0;
+}
+
+int
+_nouveau_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct nouveau_i2c *i2c;
+ int ret;
+
+ ret = nouveau_i2c_create(parent, engine, oclass, &i2c);
+ *pobject = nv_object(i2c);
+ if (ret)
+ return ret;
+
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c
index a6e72d3b06b5..813ffc96e864 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/bit.c
@@ -22,7 +22,7 @@
* Authors: Ben Skeggs
*/
-#include "subdev/i2c.h"
+#include "priv.h"
#ifdef CONFIG_NOUVEAU_I2C_INTERNAL
#define T_TIMEOUT 2200000
@@ -187,8 +187,9 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
struct i2c_msg *msg = msgs;
int ret = 0, mcnt = num;
- if (port->func->acquire)
- port->func->acquire(port);
+ ret = nouveau_i2c(port)->acquire(port, nsecs_to_jiffies(T_TIMEOUT));
+ if (ret)
+ return ret;
while (!ret && mcnt--) {
u8 remaining = msg->len;
@@ -210,6 +211,7 @@ i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
}
i2c_stop(port);
+ nouveau_i2c(port)->release(port);
return (ret < 0) ? ret : num;
}
#else
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c
index 860d5d2365da..b1725bdea967 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv04.c
@@ -22,9 +22,10 @@
* Authors: Ben Skeggs
*/
-#include <subdev/i2c.h>
#include <subdev/vga.h>
+#include "priv.h"
+
struct nv04_i2c_priv {
struct nouveau_i2c base;
};
@@ -115,29 +116,15 @@ nv04_i2c_sclass[] = {
{}
};
-static int
-nv04_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv04_i2c_priv *priv;
- int ret;
-
- ret = nouveau_i2c_create(parent, engine, oclass, nv04_i2c_sclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- return 0;
-}
-
-struct nouveau_oclass
-nv04_i2c_oclass = {
- .handle = NV_SUBDEV(I2C, 0x04),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nv04_i2c_ctor,
+struct nouveau_oclass *
+nv04_i2c_oclass = &(struct nouveau_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0x04),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_i2c_ctor,
.dtor = _nouveau_i2c_dtor,
.init = _nouveau_i2c_init,
.fini = _nouveau_i2c_fini,
},
-};
+ .sclass = nv04_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c
index 0c2655a03bb4..f16c87ce5ba1 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv4e.c
@@ -22,9 +22,10 @@
* Authors: Ben Skeggs
*/
-#include <subdev/i2c.h>
#include <subdev/vga.h>
+#include "priv.h"
+
struct nv4e_i2c_priv {
struct nouveau_i2c base;
};
@@ -107,29 +108,15 @@ nv4e_i2c_sclass[] = {
{}
};
-static int
-nv4e_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv4e_i2c_priv *priv;
- int ret;
-
- ret = nouveau_i2c_create(parent, engine, oclass, nv4e_i2c_sclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- return 0;
-}
-
-struct nouveau_oclass
-nv4e_i2c_oclass = {
- .handle = NV_SUBDEV(I2C, 0x4e),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nv4e_i2c_ctor,
+struct nouveau_oclass *
+nv4e_i2c_oclass = &(struct nouveau_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0x4e),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_i2c_ctor,
.dtor = _nouveau_i2c_dtor,
.init = _nouveau_i2c_init,
.fini = _nouveau_i2c_fini,
},
-};
+ .sclass = nv4e_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c
index a8d67a287704..7b8756d4df08 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.c
@@ -121,29 +121,15 @@ nv50_i2c_sclass[] = {
{}
};
-static int
-nv50_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv50_i2c_priv *priv;
- int ret;
-
- ret = nouveau_i2c_create(parent, engine, oclass, nv50_i2c_sclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- return 0;
-}
-
-struct nouveau_oclass
-nv50_i2c_oclass = {
- .handle = NV_SUBDEV(I2C, 0x50),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nv50_i2c_ctor,
+struct nouveau_oclass *
+nv50_i2c_oclass = &(struct nouveau_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0x50),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_i2c_ctor,
.dtor = _nouveau_i2c_dtor,
.init = _nouveau_i2c_init,
.fini = _nouveau_i2c_fini,
},
-};
+ .sclass = nv50_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h
index 4e5ba48ebf5a..5d2a77421c74 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv50.h
@@ -1,7 +1,7 @@
#ifndef __NV50_I2C_H__
#define __NV50_I2C_H__
-#include <subdev/i2c.h>
+#include "priv.h"
struct nv50_i2c_priv {
struct nouveau_i2c base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c
index df6d3e4b68be..f59c3a255462 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nv94.c
@@ -24,6 +24,36 @@
#include "nv50.h"
+void
+nv94_aux_stat(struct nouveau_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx)
+{
+ u32 intr = nv_rd32(i2c, 0x00e06c);
+ u32 stat = nv_rd32(i2c, 0x00e068) & intr, i;
+ for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) {
+ if ((stat & (1 << (i * 4)))) *hi |= 1 << i;
+ if ((stat & (2 << (i * 4)))) *lo |= 1 << i;
+ if ((stat & (4 << (i * 4)))) *rq |= 1 << i;
+ if ((stat & (8 << (i * 4)))) *tx |= 1 << i;
+ }
+ nv_wr32(i2c, 0x00e06c, intr);
+}
+
+void
+nv94_aux_mask(struct nouveau_i2c *i2c, u32 type, u32 mask, u32 data)
+{
+ u32 temp = nv_rd32(i2c, 0x00e068), i;
+ for (i = 0; i < 8; i++) {
+ if (mask & (1 << i)) {
+ if (!(data & (1 << i))) {
+ temp &= ~(type << (i * 4));
+ continue;
+ }
+ temp |= type << (i * 4);
+ }
+ }
+ nv_wr32(i2c, 0x00e068, temp);
+}
+
#define AUX_DBG(fmt, args...) nv_debug(aux, "AUXCH(%d): " fmt, ch, ##args)
#define AUX_ERR(fmt, args...) nv_error(aux, "AUXCH(%d): " fmt, ch, ##args)
@@ -69,7 +99,8 @@ auxch_init(struct nouveau_i2c *aux, int ch)
}
int
-nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size)
+nv94_aux(struct nouveau_i2c_port *base, bool retry,
+ u8 type, u32 addr, u8 *data, u8 size)
{
struct nouveau_i2c *aux = nouveau_i2c(base);
struct nv50_i2c_port *port = (void *)base;
@@ -105,9 +136,8 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size)
ctrl |= size - 1;
nv_wr32(aux, 0x00e4e0 + (ch * 0x50), addr);
- /* retry transaction a number of times on failure... */
- ret = -EREMOTEIO;
- for (retries = 0; retries < 32; retries++) {
+ /* (maybe) retry transaction a number of times on failure... */
+ for (retries = 0; !ret && retries < 32; retries++) {
/* reset, and delay a while if this is a retry */
nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x80000000 | ctrl);
nv_wr32(aux, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl);
@@ -123,16 +153,21 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size)
udelay(1);
if (!timeout--) {
AUX_ERR("tx req timeout 0x%08x\n", ctrl);
+ ret = -EIO;
goto out;
}
} while (ctrl & 0x00010000);
+ ret = 1;
/* read status, and check if transaction completed ok */
stat = nv_mask(aux, 0x00e4e8 + (ch * 0x50), 0, 0);
- if (!(stat & 0x000f0f00)) {
- ret = 0;
- break;
- }
+ if ((stat & 0x000f0000) == 0x00080000 ||
+ (stat & 0x000f0000) == 0x00020000)
+ ret = retry ? 0 : 1;
+ if ((stat & 0x00000100))
+ ret = -ETIMEDOUT;
+ if ((stat & 0x00000e00))
+ ret = -EIO;
AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat);
}
@@ -147,29 +182,11 @@ nv94_aux(struct nouveau_i2c_port *base, u8 type, u32 addr, u8 *data, u8 size)
out:
auxch_fini(aux, ch);
- return ret;
-}
-
-void
-nv94_i2c_acquire(struct nouveau_i2c_port *base)
-{
- struct nv50_i2c_priv *priv = (void *)nv_object(base)->engine;
- struct nv50_i2c_port *port = (void *)base;
- if (port->ctrl) {
- nv_mask(priv, port->ctrl + 0x0c, 0x00000001, 0x00000000);
- nv_mask(priv, port->ctrl + 0x00, 0x0000f003, port->data);
- }
-}
-
-void
-nv94_i2c_release(struct nouveau_i2c_port *base)
-{
+ return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
}
static const struct nouveau_i2c_func
nv94_i2c_func = {
- .acquire = nv94_i2c_acquire,
- .release = nv94_i2c_release,
.drive_scl = nv50_i2c_drive_scl,
.drive_sda = nv50_i2c_drive_sda,
.sense_scl = nv50_i2c_sense_scl,
@@ -206,8 +223,6 @@ nv94_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
static const struct nouveau_i2c_func
nv94_aux_func = {
- .acquire = nv94_i2c_acquire,
- .release = nv94_i2c_release,
.aux = nv94_aux,
};
@@ -227,6 +242,7 @@ nv94_aux_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
if (ret)
return ret;
+ port->base.aux = info->drive;
port->addr = info->drive;
if (info->share != DCB_I2C_UNUSED) {
port->ctrl = 0x00e500 + (info->drive * 0x50);
@@ -257,29 +273,19 @@ nv94_i2c_sclass[] = {
{}
};
-static int
-nv94_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv50_i2c_priv *priv;
- int ret;
-
- ret = nouveau_i2c_create(parent, engine, oclass, nv94_i2c_sclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- return 0;
-}
-
-struct nouveau_oclass
-nv94_i2c_oclass = {
- .handle = NV_SUBDEV(I2C, 0x94),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nv94_i2c_ctor,
+struct nouveau_oclass *
+nv94_i2c_oclass = &(struct nouveau_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0x94),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_i2c_ctor,
.dtor = _nouveau_i2c_dtor,
.init = _nouveau_i2c_init,
.fini = _nouveau_i2c_fini,
},
-};
+ .sclass = nv94_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+ .pad_s = &nv94_i2c_pad_oclass,
+ .aux = 4,
+ .aux_stat = nv94_aux_stat,
+ .aux_mask = nv94_aux_mask,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c
index 29967d30f97c..364ddb1c5f03 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nvd0.c
@@ -42,8 +42,6 @@ nvd0_i2c_sense_sda(struct nouveau_i2c_port *base)
static const struct nouveau_i2c_func
nvd0_i2c_func = {
- .acquire = nv94_i2c_acquire,
- .release = nv94_i2c_release,
.drive_scl = nv50_i2c_drive_scl,
.drive_sda = nv50_i2c_drive_sda,
.sense_scl = nvd0_i2c_sense_scl,
@@ -75,7 +73,7 @@ nvd0_i2c_port_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
return 0;
}
-static struct nouveau_oclass
+struct nouveau_oclass
nvd0_i2c_sclass[] = {
{ .handle = NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT),
.ofuncs = &(struct nouveau_ofuncs) {
@@ -96,29 +94,19 @@ nvd0_i2c_sclass[] = {
{}
};
-static int
-nvd0_i2c_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, void *data, u32 size,
- struct nouveau_object **pobject)
-{
- struct nv50_i2c_priv *priv;
- int ret;
-
- ret = nouveau_i2c_create(parent, engine, oclass, nvd0_i2c_sclass, &priv);
- *pobject = nv_object(priv);
- if (ret)
- return ret;
-
- return 0;
-}
-
-struct nouveau_oclass
-nvd0_i2c_oclass = {
- .handle = NV_SUBDEV(I2C, 0xd0),
- .ofuncs = &(struct nouveau_ofuncs) {
- .ctor = nvd0_i2c_ctor,
+struct nouveau_oclass *
+nvd0_i2c_oclass = &(struct nouveau_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0xd0),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_i2c_ctor,
.dtor = _nouveau_i2c_dtor,
.init = _nouveau_i2c_init,
.fini = _nouveau_i2c_fini,
},
-};
+ .sclass = nvd0_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+ .pad_s = &nv94_i2c_pad_oclass,
+ .aux = 4,
+ .aux_stat = nv94_aux_stat,
+ .aux_mask = nv94_aux_mask,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c
new file mode 100644
index 000000000000..cae77e1ad8dc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/nve0.c
@@ -0,0 +1,72 @@
+/*
+ * 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 "nv50.h"
+
+static void
+nve0_aux_stat(struct nouveau_i2c *i2c, u32 *hi, u32 *lo, u32 *rq, u32 *tx)
+{
+ u32 intr = nv_rd32(i2c, 0x00dc60);
+ u32 stat = nv_rd32(i2c, 0x00dc68) & intr, i;
+ for (i = 0, *hi = *lo = *rq = *tx = 0; i < 8; i++) {
+ if ((stat & (1 << (i * 4)))) *hi |= 1 << i;
+ if ((stat & (2 << (i * 4)))) *lo |= 1 << i;
+ if ((stat & (4 << (i * 4)))) *rq |= 1 << i;
+ if ((stat & (8 << (i * 4)))) *tx |= 1 << i;
+ }
+ nv_wr32(i2c, 0x00dc60, intr);
+}
+
+static void
+nve0_aux_mask(struct nouveau_i2c *i2c, u32 type, u32 mask, u32 data)
+{
+ u32 temp = nv_rd32(i2c, 0x00dc68), i;
+ for (i = 0; i < 8; i++) {
+ if (mask & (1 << i)) {
+ if (!(data & (1 << i))) {
+ temp &= ~(type << (i * 4));
+ continue;
+ }
+ temp |= type << (i * 4);
+ }
+ }
+ nv_wr32(i2c, 0x00dc68, temp);
+}
+
+struct nouveau_oclass *
+nve0_i2c_oclass = &(struct nouveau_i2c_impl) {
+ .base.handle = NV_SUBDEV(I2C, 0xe0),
+ .base.ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nouveau_i2c_ctor,
+ .dtor = _nouveau_i2c_dtor,
+ .init = _nouveau_i2c_init,
+ .fini = _nouveau_i2c_fini,
+ },
+ .sclass = nvd0_i2c_sclass,
+ .pad_x = &nv04_i2c_pad_oclass,
+ .pad_s = &nv94_i2c_pad_oclass,
+ .aux = 4,
+ .aux_stat = nve0_aux_stat,
+ .aux_mask = nve0_aux_mask,
+}.base;
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c
new file mode 100644
index 000000000000..e9e412477c12
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.c
@@ -0,0 +1,84 @@
+/*
+ * 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 "pad.h"
+
+int
+_nvkm_i2c_pad_fini(struct nouveau_object *object, bool suspend)
+{
+ struct nvkm_i2c_pad *pad = (void *)object;
+ DBG("-> NULL\n");
+ pad->port = NULL;
+ return nouveau_object_fini(&pad->base, suspend);
+}
+
+int
+_nvkm_i2c_pad_init(struct nouveau_object *object)
+{
+ struct nvkm_i2c_pad *pad = (void *)object;
+ DBG("-> PORT:%02x\n", pad->next->index);
+ pad->port = pad->next;
+ return nouveau_object_init(&pad->base);
+}
+
+int
+nvkm_i2c_pad_create_(struct nouveau_object *parent,
+ struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, int index,
+ int size, void **pobject)
+{
+ struct nouveau_i2c *i2c = (void *)engine;
+ struct nouveau_i2c_port *port;
+ struct nvkm_i2c_pad *pad;
+ int ret;
+
+ list_for_each_entry(port, &i2c->ports, head) {
+ pad = nvkm_i2c_pad(port);
+ if (pad->index == index) {
+ atomic_inc(&nv_object(pad)->refcount);
+ *pobject = pad;
+ return 1;
+ }
+ }
+
+ ret = nouveau_object_create_(parent, engine, oclass, 0, size, pobject);
+ pad = *pobject;
+ if (ret)
+ return ret;
+
+ pad->index = index;
+ return 0;
+}
+
+int
+_nvkm_i2c_pad_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 index,
+ struct nouveau_object **pobject)
+{
+ struct nvkm_i2c_pad *pad;
+ int ret;
+ ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad);
+ *pobject = nv_object(pad);
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h
new file mode 100644
index 000000000000..452ac10c3004
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/pad.h
@@ -0,0 +1,58 @@
+#ifndef __NVKM_I2C_PAD_H__
+#define __NVKM_I2C_PAD_H__
+
+#include "priv.h"
+
+struct nvkm_i2c_pad {
+ struct nouveau_object base;
+ int index;
+ struct nouveau_i2c_port *port;
+ struct nouveau_i2c_port *next;
+};
+
+static inline struct nvkm_i2c_pad *
+nvkm_i2c_pad(struct nouveau_i2c_port *port)
+{
+ struct nouveau_object *pad = nv_object(port);
+ while (pad->parent)
+ pad = pad->parent;
+ return (void *)pad;
+}
+
+#define nvkm_i2c_pad_create(p,e,o,i,d) \
+ nvkm_i2c_pad_create_((p), (e), (o), (i), sizeof(**d), (void **)d)
+#define nvkm_i2c_pad_destroy(p) ({ \
+ struct nvkm_i2c_pad *_p = (p); \
+ _nvkm_i2c_pad_dtor(nv_object(_p)); \
+})
+#define nvkm_i2c_pad_init(p) ({ \
+ struct nvkm_i2c_pad *_p = (p); \
+ _nvkm_i2c_pad_init(nv_object(_p)); \
+})
+#define nvkm_i2c_pad_fini(p,s) ({ \
+ struct nvkm_i2c_pad *_p = (p); \
+ _nvkm_i2c_pad_fini(nv_object(_p), (s)); \
+})
+
+int nvkm_i2c_pad_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, int index, int, void **);
+
+int _nvkm_i2c_pad_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+#define _nvkm_i2c_pad_dtor nouveau_object_destroy
+int _nvkm_i2c_pad_init(struct nouveau_object *);
+int _nvkm_i2c_pad_fini(struct nouveau_object *, bool);
+
+#ifndef MSG
+#define MSG(l,f,a...) do { \
+ struct nvkm_i2c_pad *_pad = (void *)pad; \
+ nv_##l(nv_object(_pad)->engine, "PAD:%c:%02x: "f, \
+ _pad->index >= 0x100 ? 'X' : 'S', \
+ _pad->index >= 0x100 ? _pad->index - 0x100 : _pad->index, ##a); \
+} while(0)
+#define DBG(f,a...) MSG(debug, f, ##a)
+#define ERR(f,a...) MSG(error, f, ##a)
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c
new file mode 100644
index 000000000000..2c4b61296dd1
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv04.c
@@ -0,0 +1,35 @@
+/*
+ * 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 "pad.h"
+
+struct nouveau_oclass
+nv04_i2c_pad_oclass = {
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = _nvkm_i2c_pad_ctor,
+ .dtor = _nvkm_i2c_pad_dtor,
+ .init = _nvkm_i2c_pad_init,
+ .fini = _nvkm_i2c_pad_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c
new file mode 100644
index 000000000000..0dc6753014f0
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/padnv94.c
@@ -0,0 +1,86 @@
+/*
+ * 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 "pad.h"
+
+struct nv94_i2c_pad {
+ struct nvkm_i2c_pad base;
+ int addr;
+};
+
+static int
+nv94_i2c_pad_fini(struct nouveau_object *object, bool suspend)
+{
+ struct nouveau_i2c *i2c = (void *)object->engine;
+ struct nv94_i2c_pad *pad = (void *)object;
+ nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000001);
+ return nvkm_i2c_pad_fini(&pad->base, suspend);
+}
+
+static int
+nv94_i2c_pad_init(struct nouveau_object *object)
+{
+ struct nouveau_i2c *i2c = (void *)object->engine;
+ struct nv94_i2c_pad *pad = (void *)object;
+
+ switch (nv_oclass(pad->base.next)->handle) {
+ case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_AUX):
+ nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x00000002);
+ break;
+ case NV_I2C_TYPE_DCBI2C(DCB_I2C_NVIO_BIT):
+ default:
+ nv_mask(i2c, 0x00e500 + pad->addr, 0x0000c003, 0x0000c001);
+ break;
+ }
+
+ nv_mask(i2c, 0x00e50c + pad->addr, 0x00000001, 0x00000000);
+ return nvkm_i2c_pad_init(&pad->base);
+}
+
+static int
+nv94_i2c_pad_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 index,
+ struct nouveau_object **pobject)
+{
+ struct nv94_i2c_pad *pad;
+ int ret;
+
+ ret = nvkm_i2c_pad_create(parent, engine, oclass, index, &pad);
+ *pobject = nv_object(pad);
+ if (ret)
+ return ret;
+
+ pad->addr = index * 0x50;;
+ return 0;
+}
+
+struct nouveau_oclass
+nv94_i2c_pad_oclass = {
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = nv94_i2c_pad_ctor,
+ .dtor = _nvkm_i2c_pad_dtor,
+ .init = nv94_i2c_pad_init,
+ .fini = nv94_i2c_pad_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/port.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/port.h
new file mode 100644
index 000000000000..a8ff6e077af5
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/port.h
@@ -0,0 +1,15 @@
+#ifndef __NVKM_I2C_PORT_H__
+#define __NVKM_I2C_PORT_H__
+
+#include "priv.h"
+
+#ifndef MSG
+#define MSG(l,f,a...) do { \
+ struct nouveau_i2c_port *_port = (void *)port; \
+ nv_##l(nv_object(_port)->engine, "PORT:%02x: "f, _port->index, ##a); \
+} while(0)
+#define DBG(f,a...) MSG(debug, f, ##a)
+#define ERR(f,a...) MSG(error, f, ##a)
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h b/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h
new file mode 100644
index 000000000000..780090b6425a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/priv.h
@@ -0,0 +1,85 @@
+#ifndef __NVKM_I2C_H__
+#define __NVKM_I2C_H__
+
+#include <subdev/i2c.h>
+
+extern struct nouveau_oclass nv04_i2c_pad_oclass;
+extern struct nouveau_oclass nv94_i2c_pad_oclass;
+
+#define nouveau_i2c_port_create(p,e,o,i,a,f,d) \
+ nouveau_i2c_port_create_((p), (e), (o), (i), (a), (f), \
+ sizeof(**d), (void **)d)
+#define nouveau_i2c_port_destroy(p) ({ \
+ struct nouveau_i2c_port *port = (p); \
+ _nouveau_i2c_port_dtor(nv_object(i2c)); \
+})
+#define nouveau_i2c_port_init(p) \
+ nouveau_object_init(&(p)->base)
+#define nouveau_i2c_port_fini(p,s) \
+ nouveau_object_fini(&(p)->base, (s))
+
+int nouveau_i2c_port_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, u8,
+ const struct i2c_algorithm *,
+ const struct nouveau_i2c_func *,
+ int, void **);
+void _nouveau_i2c_port_dtor(struct nouveau_object *);
+#define _nouveau_i2c_port_init nouveau_object_init
+int _nouveau_i2c_port_fini(struct nouveau_object *, bool);
+
+#define nouveau_i2c_create(p,e,o,d) \
+ nouveau_i2c_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_i2c_destroy(p) ({ \
+ struct nouveau_i2c *i2c = (p); \
+ _nouveau_i2c_dtor(nv_object(i2c)); \
+})
+#define nouveau_i2c_init(p) ({ \
+ struct nouveau_i2c *i2c = (p); \
+ _nouveau_i2c_init(nv_object(i2c)); \
+})
+#define nouveau_i2c_fini(p,s) ({ \
+ struct nouveau_i2c *i2c = (p); \
+ _nouveau_i2c_fini(nv_object(i2c), (s)); \
+})
+
+int nouveau_i2c_create_(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, int, void **);
+int _nouveau_i2c_ctor(struct nouveau_object *, struct nouveau_object *,
+ struct nouveau_oclass *, void *, u32,
+ struct nouveau_object **);
+void _nouveau_i2c_dtor(struct nouveau_object *);
+int _nouveau_i2c_init(struct nouveau_object *);
+int _nouveau_i2c_fini(struct nouveau_object *, bool);
+
+extern struct nouveau_oclass nouveau_anx9805_sclass[];
+extern struct nouveau_oclass nvd0_i2c_sclass[];
+
+extern const struct i2c_algorithm nouveau_i2c_bit_algo;
+extern const struct i2c_algorithm nouveau_i2c_aux_algo;
+
+struct nouveau_i2c_impl {
+ struct nouveau_oclass base;
+
+ /* supported i2c port classes */
+ struct nouveau_oclass *sclass;
+ struct nouveau_oclass *pad_x;
+ struct nouveau_oclass *pad_s;
+
+ /* number of native dp aux channels present */
+ int aux;
+
+ /* read and ack pending interrupts, returning only data
+ * for ports that have not been masked off, while still
+ * performing the ack for anything that was pending.
+ */
+ void (*aux_stat)(struct nouveau_i2c *, u32 *, u32 *, u32 *, u32 *);
+
+ /* mask on/off interrupt types for a given set of auxch
+ */
+ void (*aux_mask)(struct nouveau_i2c *, u32, u32, u32);
+};
+
+void nv94_aux_stat(struct nouveau_i2c *, u32 *, u32 *, u32 *, u32 *);
+void nv94_aux_mask(struct nouveau_i2c *, u32, u32, u32);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ibus/gk20a.c b/drivers/gpu/drm/nouveau/core/subdev/ibus/gk20a.c
new file mode 100644
index 000000000000..245f0ebaa6af
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/ibus/gk20a.c
@@ -0,0 +1,103 @@
+/*
+ * 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/ibus.h>
+#include <subdev/timer.h>
+
+struct gk20a_ibus_priv {
+ struct nouveau_ibus base;
+};
+
+static void
+gk20a_ibus_init_priv_ring(struct gk20a_ibus_priv *priv)
+{
+ nv_mask(priv, 0x137250, 0x3f, 0);
+
+ nv_mask(priv, 0x000200, 0x20, 0);
+ usleep_range(20, 30);
+ nv_mask(priv, 0x000200, 0x20, 0x20);
+
+ nv_wr32(priv, 0x12004c, 0x4);
+ nv_wr32(priv, 0x122204, 0x2);
+ nv_rd32(priv, 0x122204);
+}
+
+static void
+gk20a_ibus_intr(struct nouveau_subdev *subdev)
+{
+ struct gk20a_ibus_priv *priv = (void *)subdev;
+ u32 status0 = nv_rd32(priv, 0x120058);
+
+ if (status0 & 0x7) {
+ nv_debug(priv, "resetting priv ring\n");
+ gk20a_ibus_init_priv_ring(priv);
+ }
+
+ /* Acknowledge interrupt */
+ nv_mask(priv, 0x12004c, 0x2, 0x2);
+
+ if (!nv_wait(subdev, 0x12004c, 0x3f, 0x00))
+ nv_warn(priv, "timeout waiting for ringmaster ack\n");
+}
+
+static int
+gk20a_ibus_init(struct nouveau_object *object)
+{
+ struct gk20a_ibus_priv *priv = (void *)object;
+ int ret;
+
+ ret = _nouveau_ibus_init(object);
+ if (ret)
+ return ret;
+
+ gk20a_ibus_init_priv_ring(priv);
+
+ return 0;
+}
+
+static int
+gk20a_ibus_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+ struct nouveau_oclass *oclass, void *data, u32 size,
+ struct nouveau_object **pobject)
+{
+ struct gk20a_ibus_priv *priv;
+ int ret;
+
+ ret = nouveau_ibus_create(parent, engine, oclass, &priv);
+ *pobject = nv_object(priv);
+ if (ret)
+ return ret;
+
+ nv_subdev(priv)->intr = gk20a_ibus_intr;
+ return 0;
+}
+
+struct nouveau_oclass
+gk20a_ibus_oclass = {
+ .handle = NV_SUBDEV(IBUS, 0xea),
+ .ofuncs = &(struct nouveau_ofuncs) {
+ .ctor = gk20a_ibus_ctor,
+ .dtor = _nouveau_ibus_dtor,
+ .init = gk20a_ibus_init,
+ .fini = _nouveau_ibus_fini,
+ },
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c
index e8822a934c48..9ca93e2718f7 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c
@@ -26,6 +26,7 @@
const struct nouveau_mc_intr
nv50_mc_intr[] = {
+ { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP before FIFO, so pageflip-timestamping works! */
{ 0x00000001, NVDEV_ENGINE_MPEG },
{ 0x00000100, NVDEV_ENGINE_FIFO },
{ 0x00001000, NVDEV_ENGINE_GR },
@@ -33,8 +34,8 @@ nv50_mc_intr[] = {
{ 0x00008000, NVDEV_ENGINE_BSP }, /* NV84- */
{ 0x00020000, NVDEV_ENGINE_VP }, /* NV84- */
{ 0x00100000, NVDEV_SUBDEV_TIMER },
- { 0x00200000, NVDEV_SUBDEV_GPIO },
- { 0x04000000, NVDEV_ENGINE_DISP },
+ { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */
+ { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */
{ 0x10000000, NVDEV_SUBDEV_BUS },
{ 0x80000000, NVDEV_ENGINE_SW },
{ 0x0002d101, NVDEV_SUBDEV_FB },
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c
index f8a6f18e2d34..3c76d9038f38 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c
@@ -26,6 +26,7 @@
static const struct nouveau_mc_intr
nv98_mc_intr[] = {
+ { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP first, so pageflip timestamps work */
{ 0x00000001, NVDEV_ENGINE_PPP },
{ 0x00000100, NVDEV_ENGINE_FIFO },
{ 0x00001000, NVDEV_ENGINE_GR },
@@ -35,9 +36,9 @@ nv98_mc_intr[] = {
{ 0x00040000, NVDEV_SUBDEV_PWR }, /* NVA3:NVC0 */
{ 0x00080000, NVDEV_SUBDEV_THERM }, /* NVA3:NVC0 */
{ 0x00100000, NVDEV_SUBDEV_TIMER },
- { 0x00200000, NVDEV_SUBDEV_GPIO },
+ { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */
+ { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */
{ 0x00400000, NVDEV_ENGINE_COPY0 }, /* NVA3- */
- { 0x04000000, NVDEV_ENGINE_DISP },
{ 0x10000000, NVDEV_SUBDEV_BUS },
{ 0x80000000, NVDEV_ENGINE_SW },
{ 0x0042d101, NVDEV_SUBDEV_FB },
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
index 34472d317097..f9c6a678b47d 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
@@ -26,6 +26,7 @@
const struct nouveau_mc_intr
nvc0_mc_intr[] = {
+ { 0x04000000, NVDEV_ENGINE_DISP }, /* DISP first, so pageflip timestamps work. */
{ 0x00000001, NVDEV_ENGINE_PPP },
{ 0x00000020, NVDEV_ENGINE_COPY0 },
{ 0x00000040, NVDEV_ENGINE_COPY1 },
@@ -37,10 +38,10 @@ nvc0_mc_intr[] = {
{ 0x00040000, NVDEV_SUBDEV_THERM },
{ 0x00020000, NVDEV_ENGINE_VP },
{ 0x00100000, NVDEV_SUBDEV_TIMER },
- { 0x00200000, NVDEV_SUBDEV_GPIO },
+ { 0x00200000, NVDEV_SUBDEV_GPIO }, /* PMGR->GPIO */
+ { 0x00200000, NVDEV_SUBDEV_I2C }, /* PMGR->I2C/AUX */
{ 0x01000000, NVDEV_SUBDEV_PWR },
{ 0x02000000, NVDEV_SUBDEV_LTCG },
- { 0x04000000, NVDEV_ENGINE_DISP },
{ 0x08000000, NVDEV_SUBDEV_FB },
{ 0x10000000, NVDEV_SUBDEV_BUS },
{ 0x40000000, NVDEV_SUBDEV_IBUS },
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c
index 64f8b4702bf7..fcaabe8456e3 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mxm/nv50.c
@@ -150,7 +150,7 @@ mxm_dcb_sanitise_entry(struct nouveau_bios *bios, void *data, int idx, u16 pdcb)
* common example is DP->eDP.
*/
conn = bios->data;
- conn += dcb_conn(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len);
+ conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len);
type = conn[0];
switch (ctx.desc.conn_type) {
case 0x01: /* LVDS */
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c
index 7610fc5f8fa2..ca9ad9fd47be 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c
@@ -60,9 +60,9 @@ static struct nouveau_i2c_board_info
nv_board_infos[] = {
{ { I2C_BOARD_INFO("w83l785ts", 0x2d) }, 0 },
{ { I2C_BOARD_INFO("w83781d", 0x2d) }, 0 },
- { { I2C_BOARD_INFO("adt7473", 0x2e) }, 20 },
- { { I2C_BOARD_INFO("adt7473", 0x2d) }, 20 },
- { { I2C_BOARD_INFO("adt7473", 0x2c) }, 20 },
+ { { I2C_BOARD_INFO("adt7473", 0x2e) }, 40 },
+ { { I2C_BOARD_INFO("adt7473", 0x2d) }, 40 },
+ { { I2C_BOARD_INFO("adt7473", 0x2c) }, 40 },
{ { I2C_BOARD_INFO("f75375", 0x2e) }, 0 },
{ { I2C_BOARD_INFO("lm99", 0x4c) }, 0 },
{ { I2C_BOARD_INFO("lm90", 0x4c) }, 0 },
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
index 3b2c4580098b..0478b2e3fb1d 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
@@ -36,7 +36,7 @@ nva3_therm_fan_sense(struct nouveau_therm *therm)
u32 tach = nv_rd32(therm, 0x00e728) & 0x0000ffff;
u32 ctrl = nv_rd32(therm, 0x00e720);
if (ctrl & 0x00000001)
- return tach * 60;
+ return tach * 60 / 2;
return -ENODEV;
}
diff --git a/drivers/gpu/drm/nouveau/dispnv04/dac.c b/drivers/gpu/drm/nouveau/dispnv04/dac.c
index 434b920f6bd4..a96dda48718e 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/dac.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/dac.c
@@ -414,7 +414,7 @@ static void nv04_dac_commit(struct drm_encoder *encoder)
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
- drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+ nouveau_encoder_connector_get(nv_encoder)->base.name,
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
}
diff --git a/drivers/gpu/drm/nouveau/dispnv04/dfp.c b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
index a2d669b4acf2..e57babb206d3 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/dfp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/dfp.c
@@ -477,7 +477,7 @@ static void nv04_dfp_commit(struct drm_encoder *encoder)
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
- drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+ nouveau_encoder_connector_get(nv_encoder)->base.name,
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
}
diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c
index 2f1ed61f7c8c..4342fdaee707 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c
@@ -115,7 +115,7 @@ nv04_display_create(struct drm_device *dev)
&dev->mode_config.connector_list, head) {
if (!connector->encoder_ids[0]) {
NV_WARN(drm, "%s has no encoders, removing\n",
- drm_get_connector_name(connector));
+ connector->name);
connector->funcs->destroy(connector);
}
}
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
index 244822df8ffc..8667620b703a 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv04.c
@@ -171,7 +171,8 @@ static void nv04_tv_commit(struct drm_encoder *encoder)
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_DEBUG(drm, "Output %s is running on CRTC %d using output %c\n",
- drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
+ nouveau_encoder_connector_get(nv_encoder)->base.name,
+ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
}
static void nv04_tv_destroy(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
index acef48f4a4ea..195bd8e86c6a 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
@@ -612,8 +612,7 @@ static void nv17_tv_commit(struct drm_encoder *encoder)
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_INFO(drm, "Output %s is running on CRTC %d using output %c\n",
- drm_get_connector_name(
- &nouveau_encoder_connector_get(nv_encoder)->base),
+ nouveau_encoder_connector_get(nv_encoder)->base.name,
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index d07ce028af51..1fa222e8f007 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -44,6 +44,7 @@
#include <subdev/i2c.h>
#include <subdev/gpio.h>
+#include <engine/disp.h>
MODULE_PARM_DESC(tv_disable, "Disable TV-out detection");
static int nouveau_tv_disable = 0;
@@ -75,7 +76,8 @@ find_encoder(struct drm_connector *connector, int type)
continue;
nv_encoder = nouveau_encoder(obj_to_encoder(obj));
- if (type == DCB_OUTPUT_ANY || nv_encoder->dcb->type == type)
+ if (type == DCB_OUTPUT_ANY ||
+ (nv_encoder->dcb && nv_encoder->dcb->type == type))
return nv_encoder;
}
@@ -100,22 +102,24 @@ static void
nouveau_connector_destroy(struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
- nouveau_event_ref(NULL, &nv_connector->hpd_func);
+ nouveau_event_ref(NULL, &nv_connector->hpd);
kfree(nv_connector->edid);
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
+ if (nv_connector->aux.transfer)
+ drm_dp_aux_unregister(&nv_connector->aux);
kfree(connector);
}
-static struct nouveau_i2c_port *
-nouveau_connector_ddc_detect(struct drm_connector *connector,
- struct nouveau_encoder **pnv_encoder)
+static struct nouveau_encoder *
+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_i2c_port *port = NULL;
+ struct nouveau_encoder *nv_encoder;
+ struct drm_mode_object *obj;
int i, panel = -ENODEV;
/* eDP panels need powering on by us (if the VBIOS doesn't default it
@@ -130,13 +134,9 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
}
}
- for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
- struct nouveau_encoder *nv_encoder;
- struct drm_mode_object *obj;
- int id;
-
- id = connector->encoder_ids[i];
- if (!id)
+ for (i = 0; nv_encoder = NULL, i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+ int id = connector->encoder_ids[i];
+ if (id == 0)
break;
obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
@@ -144,22 +144,24 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
continue;
nv_encoder = nouveau_encoder(obj_to_encoder(obj));
- port = nv_encoder->i2c;
- if (port && nv_probe_i2c(port, 0x50)) {
- *pnv_encoder = nv_encoder;
- break;
+ if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
+ int ret = nouveau_dp_detect(nv_encoder);
+ if (ret == 0)
+ break;
+ } else
+ if (nv_encoder->i2c) {
+ if (nv_probe_i2c(nv_encoder->i2c, 0x50))
+ break;
}
-
- port = NULL;
}
/* eDP panel not detected, restore panel power GPIO to previous
* state to avoid confusing the SOR for other output types.
*/
- if (!port && panel == 0)
+ if (!nv_encoder && panel == 0)
gpio->set(gpio, 0, DCB_GPIO_PANEL_POWER, 0xff, panel);
- return port;
+ return nv_encoder;
}
static struct nouveau_encoder *
@@ -258,25 +260,17 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
if (ret < 0 && ret != -EACCES)
return conn_status;
- i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
- if (i2c) {
+ nv_encoder = nouveau_connector_ddc_detect(connector);
+ if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) {
nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
drm_mode_connector_update_edid_property(connector,
nv_connector->edid);
if (!nv_connector->edid) {
NV_ERROR(drm, "DDC responded, but no EDID for %s\n",
- drm_get_connector_name(connector));
+ connector->name);
goto detect_analog;
}
- if (nv_encoder->dcb->type == DCB_OUTPUT_DP &&
- !nouveau_dp_detect(to_drm_encoder(nv_encoder))) {
- NV_ERROR(drm, "Detected %s, but failed init\n",
- drm_get_connector_name(connector));
- conn_status = connector_status_disconnected;
- goto out;
- }
-
/* Override encoder type for DVI-I based on whether EDID
* says the display is digital or analog, both use the
* same i2c channel so the value returned from ddc_detect
@@ -437,7 +431,7 @@ nouveau_connector_force(struct drm_connector *connector)
nv_encoder = find_encoder(connector, type);
if (!nv_encoder) {
NV_ERROR(drm, "can't find encoder to force %s on!\n",
- drm_get_connector_name(connector));
+ connector->name);
connector->status = connector_status_disconnected;
return;
}
@@ -912,33 +906,103 @@ nouveau_connector_funcs_lvds = {
};
static void
+nouveau_connector_dp_dpms(struct drm_connector *connector, int mode)
+{
+ struct nouveau_encoder *nv_encoder = NULL;
+
+ if (connector->encoder)
+ nv_encoder = nouveau_encoder(connector->encoder);
+ if (nv_encoder && nv_encoder->dcb &&
+ nv_encoder->dcb->type == DCB_OUTPUT_DP) {
+ if (mode == DRM_MODE_DPMS_ON) {
+ u8 data = DP_SET_POWER_D0;
+ nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1);
+ usleep_range(1000, 2000);
+ } else {
+ u8 data = DP_SET_POWER_D3;
+ nv_wraux(nv_encoder->i2c, DP_SET_POWER, &data, 1);
+ }
+ }
+
+ drm_helper_connector_dpms(connector, mode);
+}
+
+static const struct drm_connector_funcs
+nouveau_connector_funcs_dp = {
+ .dpms = nouveau_connector_dp_dpms,
+ .save = NULL,
+ .restore = NULL,
+ .detect = nouveau_connector_detect,
+ .destroy = nouveau_connector_destroy,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = nouveau_connector_set_property,
+ .force = nouveau_connector_force
+};
+
+static void
nouveau_connector_hotplug_work(struct work_struct *work)
{
struct nouveau_connector *nv_connector =
- container_of(work, struct nouveau_connector, hpd_work);
+ container_of(work, typeof(*nv_connector), work);
struct drm_connector *connector = &nv_connector->base;
- struct drm_device *dev = connector->dev;
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
- bool plugged = gpio->get(gpio, 0, nv_connector->hpd.func, 0xff);
+ struct nouveau_drm *drm = nouveau_drm(connector->dev);
+ const char *name = connector->name;
- NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un",
- drm_get_connector_name(connector));
+ if (nv_connector->status & NVKM_HPD_IRQ) {
+ } else {
+ bool plugged = (nv_connector->status != NVKM_HPD_UNPLUG);
- if (plugged)
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
- else
- drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name);
- drm_helper_hpd_irq_event(dev);
+ if (plugged)
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
+ else
+ drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+ drm_helper_hpd_irq_event(connector->dev);
+ }
+
+ nouveau_event_get(nv_connector->hpd);
}
static int
-nouveau_connector_hotplug(void *data, int index)
+nouveau_connector_hotplug(void *data, u32 type, int index)
{
struct nouveau_connector *nv_connector = data;
- schedule_work(&nv_connector->hpd_work);
- return NVKM_EVENT_KEEP;
+ nv_connector->status = type;
+ schedule_work(&nv_connector->work);
+ return NVKM_EVENT_DROP;
+}
+
+static ssize_t
+nouveau_connector_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
+{
+ struct nouveau_connector *nv_connector =
+ container_of(aux, typeof(*nv_connector), aux);
+ struct nouveau_encoder *nv_encoder;
+ struct nouveau_i2c_port *port;
+ int ret;
+
+ nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP);
+ if (!nv_encoder || !(port = nv_encoder->i2c))
+ return -ENODEV;
+ if (WARN_ON(msg->size > 16))
+ return -E2BIG;
+ if (msg->size == 0)
+ return msg->size;
+
+ ret = nouveau_i2c(port)->acquire(port, 0);
+ if (ret)
+ return ret;
+
+ ret = port->func->aux(port, false, msg->request, msg->address,
+ msg->buffer, msg->size);
+ nouveau_i2c(port)->release(port);
+ if (ret >= 0) {
+ msg->reply = ret;
+ return msg->size;
+ }
+
+ return ret;
}
static int
@@ -974,9 +1038,9 @@ nouveau_connector_create(struct drm_device *dev, int index)
{
const struct drm_connector_funcs *funcs = &nouveau_connector_funcs;
struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
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;
@@ -992,33 +1056,15 @@ nouveau_connector_create(struct drm_device *dev, int index)
return ERR_PTR(-ENOMEM);
connector = &nv_connector->base;
- INIT_WORK(&nv_connector->hpd_work, nouveau_connector_hotplug_work);
nv_connector->index = index;
/* attempt to parse vbios connector type and hotplug gpio */
nv_connector->dcb = olddcb_conn(dev, index);
if (nv_connector->dcb) {
- static const u8 hpd[16] = {
- 0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60,
- };
-
u32 entry = ROM16(nv_connector->dcb[0]);
if (olddcb_conntab(dev)[3] >= 4)
entry |= (u32)ROM16(nv_connector->dcb[2]) << 16;
- ret = gpio->find(gpio, 0, hpd[ffs((entry & 0x07033000) >> 12)],
- DCB_GPIO_UNUSED, &nv_connector->hpd);
- if (ret)
- nv_connector->hpd.func = DCB_GPIO_UNUSED;
-
- if (nv_connector->hpd.func != DCB_GPIO_UNUSED) {
- nouveau_event_new(gpio->events, nv_connector->hpd.line,
- nouveau_connector_hotplug,
- nv_connector,
- &nv_connector->hpd_func);
- }
-
nv_connector->type = nv_connector->dcb[0];
if (drm_conntype_from_dcb(nv_connector->type) ==
DRM_MODE_CONNECTOR_Unknown) {
@@ -1040,7 +1086,6 @@ nouveau_connector_create(struct drm_device *dev, int index)
}
} else {
nv_connector->type = DCB_CONNECTOR_NONE;
- nv_connector->hpd.func = DCB_GPIO_UNUSED;
}
/* no vbios data, or an unknown dcb connector type - attempt to
@@ -1080,8 +1125,8 @@ nouveau_connector_create(struct drm_device *dev, int index)
}
}
- type = drm_conntype_from_dcb(nv_connector->type);
- if (type == DRM_MODE_CONNECTOR_LVDS) {
+ switch ((type = drm_conntype_from_dcb(nv_connector->type))) {
+ case DRM_MODE_CONNECTOR_LVDS:
ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy);
if (ret) {
NV_ERROR(drm, "Error parsing LVDS table, disabling\n");
@@ -1090,8 +1135,23 @@ nouveau_connector_create(struct drm_device *dev, int index)
}
funcs = &nouveau_connector_funcs_lvds;
- } else {
+ break;
+ case DRM_MODE_CONNECTOR_DisplayPort:
+ case DRM_MODE_CONNECTOR_eDP:
+ nv_connector->aux.dev = dev->dev;
+ nv_connector->aux.transfer = nouveau_connector_aux_xfer;
+ ret = drm_dp_aux_register(&nv_connector->aux);
+ if (ret) {
+ NV_ERROR(drm, "failed to register aux channel\n");
+ kfree(nv_connector);
+ return ERR_PTR(ret);
+ }
+
+ funcs = &nouveau_connector_funcs_dp;
+ break;
+ default:
funcs = &nouveau_connector_funcs;
+ break;
}
/* defaults, will get overridden in detect() */
@@ -1166,10 +1226,16 @@ nouveau_connector_create(struct drm_device *dev, int index)
break;
}
- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
- if (nv_connector->hpd.func != DCB_GPIO_UNUSED)
+ ret = nouveau_event_new(pdisp->hpd, NVKM_HPD, index,
+ nouveau_connector_hotplug,
+ nv_connector, &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);
return connector;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
index 264a778f473b..8861b6c579ad 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -28,12 +28,12 @@
#define __NOUVEAU_CONNECTOR_H__
#include <drm/drm_edid.h>
+#include <drm/drm_dp_helper.h>
#include "nouveau_crtc.h"
#include <core/event.h>
#include <subdev/bios.h>
-#include <subdev/bios/gpio.h>
struct nouveau_i2c_port;
@@ -67,9 +67,11 @@ struct nouveau_connector {
u8 index;
u8 *dcb;
- struct dcb_gpio_func hpd;
- struct work_struct hpd_work;
- struct nouveau_eventh *hpd_func;
+ struct nouveau_eventh *hpd;
+ u32 status;
+ struct work_struct work;
+
+ struct drm_dp_aux aux;
int dithering_mode;
int dithering_depth;
diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h
index d1e5890784d7..a0534489d23f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_crtc.h
+++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h
@@ -74,7 +74,7 @@ struct nouveau_crtc {
static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc)
{
- return container_of(crtc, struct nouveau_crtc, base);
+ return crtc ? container_of(crtc, struct nouveau_crtc, base) : NULL;
}
static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc)
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index da764a4ed958..26b5647188ef 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -42,7 +42,7 @@
#include <core/class.h>
static int
-nouveau_display_vblank_handler(void *data, int head)
+nouveau_display_vblank_handler(void *data, u32 type, int head)
{
struct nouveau_drm *drm = data;
drm_handle_vblank(drm->dev, head);
@@ -178,7 +178,7 @@ nouveau_display_vblank_init(struct drm_device *dev)
return -ENOMEM;
for (i = 0; i < dev->mode_config.num_crtc; i++) {
- ret = nouveau_event_new(pdisp->vblank, i,
+ ret = nouveau_event_new(pdisp->vblank, 1, i,
nouveau_display_vblank_handler,
drm, &disp->vblank[i]);
if (ret) {
@@ -393,7 +393,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_func) nouveau_event_get(conn->hpd_func);
+ if (conn->hpd) nouveau_event_get(conn->hpd);
}
return ret;
@@ -408,7 +408,7 @@ nouveau_display_fini(struct drm_device *dev)
/* disable hotplug interrupts */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct nouveau_connector *conn = nouveau_connector(connector);
- if (conn->hpd_func) nouveau_event_put(conn->hpd_func);
+ if (conn->hpd) nouveau_event_put(conn->hpd);
}
drm_kms_helper_poll_disable(dev);
@@ -798,6 +798,7 @@ nouveau_finish_page_flip(struct nouveau_channel *chan,
struct drm_device *dev = drm->dev;
struct nouveau_page_flip_state *s;
unsigned long flags;
+ int crtcid = -1;
spin_lock_irqsave(&dev->event_lock, flags);
@@ -808,8 +809,13 @@ nouveau_finish_page_flip(struct nouveau_channel *chan,
}
s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head);
- if (s->event)
- drm_send_vblank_event(dev, s->crtc, s->event);
+ if (s->event) {
+ /* Vblank timestamps/counts are only correct on >= NV-50 */
+ if (nv_device(drm->device)->card_type >= NV_50)
+ crtcid = s->crtc;
+
+ drm_send_vblank_event(dev, crtcid, s->event);
+ }
list_del(&s->head);
if (ps)
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 36fd22500569..5675ffc175ae 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -55,11 +55,10 @@ nouveau_dp_probe_oui(struct drm_device *dev, struct nouveau_i2c_port *auxch,
}
-bool
-nouveau_dp_detect(struct drm_encoder *encoder)
+int
+nouveau_dp_detect(struct nouveau_encoder *nv_encoder)
{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
+ struct drm_device *dev = nv_encoder->base.base.dev;
struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_i2c_port *auxch;
u8 *dpcd = nv_encoder->dp.dpcd;
@@ -67,11 +66,11 @@ nouveau_dp_detect(struct drm_encoder *encoder)
auxch = nv_encoder->i2c;
if (!auxch)
- return false;
+ return -ENODEV;
ret = nv_rdaux(auxch, DP_DPCD_REV, dpcd, 8);
if (ret)
- return false;
+ return ret;
nv_encoder->dp.link_bw = 27000 * dpcd[1];
nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK;
@@ -91,6 +90,5 @@ nouveau_dp_detect(struct drm_encoder *encoder)
nv_encoder->dp.link_nr, nv_encoder->dp.link_bw);
nouveau_dp_probe_oui(dev, auxch, dpcd);
-
- return true;
+ return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
index 24660c0f713d..5f0e37fc2849 100644
--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
@@ -46,6 +46,7 @@ struct nouveau_encoder {
/* different to drm_encoder.crtc, this reflects what's
* actually programmed on the hw, not the proposed crtc */
struct drm_crtc *crtc;
+ u32 ctrl;
struct drm_display_mode mode;
int last_dpms;
@@ -84,9 +85,7 @@ get_slave_funcs(struct drm_encoder *enc)
}
/* nouveau_dp.c */
-bool nouveau_dp_detect(struct drm_encoder *);
-void nouveau_dp_dpms(struct drm_encoder *, int mode, u32 datarate,
- struct nouveau_object *);
+int nouveau_dp_detect(struct nouveau_encoder *);
struct nouveau_connector *
nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index 90074d620e31..ab5ea3b0d666 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -166,7 +166,7 @@ nouveau_fence_done(struct nouveau_fence *fence)
}
static int
-nouveau_fence_wait_uevent_handler(void *data, int index)
+nouveau_fence_wait_uevent_handler(void *data, u32 type, int index)
{
struct nouveau_fence_priv *priv = data;
wake_up_all(&priv->waiting);
@@ -183,7 +183,7 @@ nouveau_fence_wait_uevent(struct nouveau_fence *fence, bool intr)
struct nouveau_eventh *handler;
int ret = 0;
- ret = nouveau_event_new(pfifo->uevent, 0,
+ ret = nouveau_event_new(pfifo->uevent, 1, 0,
nouveau_fence_wait_uevent_handler,
priv, &handler);
if (ret)
diff --git a/drivers/gpu/drm/nouveau/nouveau_ioc32.c b/drivers/gpu/drm/nouveau/nouveau_ioc32.c
index c1a7e5a73a26..462679a8fec5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_ioc32.c
+++ b/drivers/gpu/drm/nouveau/nouveau_ioc32.c
@@ -57,7 +57,7 @@ long nouveau_compat_ioctl(struct file *filp, unsigned int cmd,
return drm_compat_ioctl(filp, cmd, arg);
#if 0
- if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls))
+ if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(mga_compat_ioctls))
fn = nouveau_compat_ioctls[nr - DRM_COMMAND_BASE];
#endif
if (fn != NULL)
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c
index fb84da3cb50d..4f4c3fec6916 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -64,12 +64,13 @@ static bool
nouveau_switcheroo_can_switch(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
- bool can_switch;
- spin_lock(&dev->count_lock);
- can_switch = (dev->open_count == 0);
- spin_unlock(&dev->count_lock);
- return can_switch;
+ /*
+ * FIXME: open_count is protected by drm_global_mutex but that would lead to
+ * locking inversion with the driver load path. And the access here is
+ * completely racy anyway. So don't bother with locking for now.
+ */
+ return dev->open_count == 0;
}
static const struct vga_switcheroo_client_ops
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 58af547b0b93..afdf607df3e6 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -1,4 +1,4 @@
- /*
+/*
* Copyright 2011 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
@@ -26,6 +26,7 @@
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
#include "nouveau_drm.h"
#include "nouveau_dma.h"
@@ -957,7 +958,7 @@ nv50_crtc_prepare(struct drm_crtc *crtc)
nv50_display_flip_stop(crtc);
- push = evo_wait(mast, 2);
+ push = evo_wait(mast, 6);
if (push) {
if (nv50_vers(mast) < NV84_DISP_MAST_CLASS) {
evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
@@ -1207,6 +1208,7 @@ static void
nv50_crtc_disable(struct drm_crtc *crtc)
{
struct nv50_head *head = nv50_head(crtc);
+ evo_sync(crtc->dev);
if (head->image)
nouveau_bo_unpin(head->image);
nouveau_bo_ref(NULL, &head->image);
@@ -1700,10 +1702,9 @@ nv50_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode)
}
static void
-nv50_hdmi_disconnect(struct drm_encoder *encoder)
+nv50_hdmi_disconnect(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
struct nv50_disp *disp = nv50_disp(encoder->dev);
const u32 moff = (nv_crtc->index << 3) | nv_encoder->or;
@@ -1722,7 +1723,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
struct drm_device *dev = encoder->dev;
struct nv50_disp *disp = nv50_disp(dev);
struct drm_encoder *partner;
- int or = nv_encoder->or;
+ u32 mthd;
nv_encoder->last_dpms = mode;
@@ -1740,7 +1741,17 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
}
}
- nv_call(disp->core, NV50_DISP_SOR_PWR + or, (mode == DRM_MODE_DPMS_ON));
+ 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;
+ } else {
+ mthd |= NV50_DISP_SOR_PWR;
+ }
+
+ nv_call(disp->core, mthd, (mode == DRM_MODE_DPMS_ON));
}
static bool
@@ -1764,33 +1775,36 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder,
}
static void
-nv50_sor_disconnect(struct drm_encoder *encoder)
+nv50_sor_ctrl(struct nouveau_encoder *nv_encoder, u32 mask, u32 data)
{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct nv50_mast *mast = nv50_mast(encoder->dev);
- const int or = nv_encoder->or;
- u32 *push;
-
- if (nv_encoder->crtc) {
- nv50_crtc_prepare(nv_encoder->crtc);
-
- push = evo_wait(mast, 4);
- if (push) {
- if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) {
- evo_mthd(push, 0x0600 + (or * 0x40), 1);
- evo_data(push, 0x00000000);
- } else {
- evo_mthd(push, 0x0200 + (or * 0x20), 1);
- evo_data(push, 0x00000000);
- }
- evo_kick(push, mast);
+ 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) {
+ evo_mthd(push, 0x0600 + (nv_encoder->or * 0x40), 1);
+ evo_data(push, (nv_encoder->ctrl = temp));
+ } else {
+ evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1);
+ evo_data(push, (nv_encoder->ctrl = temp));
}
-
- nv50_hdmi_disconnect(encoder);
+ evo_kick(push, mast);
}
+}
+
+static void
+nv50_sor_disconnect(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
nv_encoder->crtc = NULL;
+
+ if (nv_crtc) {
+ nv50_crtc_prepare(&nv_crtc->base);
+ nv50_sor_ctrl(nv_encoder, 1 << nv_crtc->index, 0);
+ nv50_hdmi_disconnect(&nv_encoder->base.base, nv_crtc);
+ }
}
static void
@@ -1810,12 +1824,14 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct nouveau_connector *nv_connector;
struct nvbios *bios = &drm->vbios;
- u32 *push, lvds = 0;
+ u32 lvds = 0, mask, ctrl;
u8 owner = 1 << nv_crtc->index;
u8 proto = 0xf;
u8 depth = 0x0;
nv_connector = nouveau_encoder_connector_get(nv_encoder);
+ nv_encoder->crtc = encoder->crtc;
+
switch (nv_encoder->dcb->type) {
case DCB_OUTPUT_TMDS:
if (nv_encoder->dcb->sorconf.link & 1) {
@@ -1827,7 +1843,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
proto = 0x2;
}
- nv50_hdmi_mode_set(encoder, mode);
+ nv50_hdmi_mode_set(&nv_encoder->base.base, mode);
break;
case DCB_OUTPUT_LVDS:
proto = 0x0;
@@ -1883,19 +1899,11 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
break;
}
- nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
+ nv50_sor_dpms(&nv_encoder->base.base, DRM_MODE_DPMS_ON);
- push = evo_wait(nv50_mast(dev), 8);
- if (push) {
- if (nv50_vers(mast) < NVD0_DISP_CLASS) {
- u32 ctrl = (depth << 16) | (proto << 8) | owner;
- if (mode->flags & DRM_MODE_FLAG_NHSYNC)
- ctrl |= 0x00001000;
- if (mode->flags & DRM_MODE_FLAG_NVSYNC)
- ctrl |= 0x00002000;
- evo_mthd(push, 0x0600 + (nv_encoder->or * 0x040), 1);
- evo_data(push, ctrl);
- } else {
+ if (nv50_vers(mast) >= NVD0_DISP_CLASS) {
+ u32 *push = evo_wait(mast, 3);
+ if (push) {
u32 magic = 0x31ec6000 | (nv_crtc->index << 25);
u32 syncs = 0x00000001;
@@ -1910,14 +1918,21 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2);
evo_data(push, syncs | (depth << 6));
evo_data(push, magic);
- evo_mthd(push, 0x0200 + (nv_encoder->or * 0x020), 1);
- evo_data(push, owner | (proto << 8));
+ evo_kick(push, mast);
}
- evo_kick(push, mast);
+ ctrl = proto << 8;
+ mask = 0x00000f00;
+ } else {
+ ctrl = (depth << 16) | (proto << 8);
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ ctrl |= 0x00001000;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ ctrl |= 0x00002000;
+ mask = 0x000f3f00;
}
- nv_encoder->crtc = encoder->crtc;
+ nv50_sor_ctrl(nv_encoder, mask | owner, ctrl | owner);
}
static void
@@ -2295,7 +2310,7 @@ nv50_display_create(struct drm_device *dev)
continue;
NV_WARN(drm, "%s has no encoders, removing\n",
- drm_get_connector_name(connector));
+ connector->name);
connector->funcs->destroy(connector);
}
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index e3c47a8005ff..2d28dc337cfb 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -319,13 +319,13 @@ static void page_flip_worker(struct work_struct *work)
struct drm_display_mode *mode = &crtc->mode;
struct drm_gem_object *bo;
- mutex_lock(&crtc->mutex);
+ drm_modeset_lock(&crtc->mutex, NULL);
omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
0, 0, mode->hdisplay, mode->vdisplay,
crtc->x << 16, crtc->y << 16,
mode->hdisplay << 16, mode->vdisplay << 16,
vblank_cb, crtc);
- mutex_unlock(&crtc->mutex);
+ drm_modeset_unlock(&crtc->mutex);
bo = omap_framebuffer_bo(crtc->primary->fb, 0);
drm_gem_object_unreference_unlocked(bo);
@@ -465,7 +465,7 @@ static void apply_worker(struct work_struct *work)
* the callbacks and list modification all serialized
* with respect to modesetting ioctls from userspace.
*/
- mutex_lock(&crtc->mutex);
+ drm_modeset_lock(&crtc->mutex, NULL);
dispc_runtime_get();
/*
@@ -510,7 +510,7 @@ static void apply_worker(struct work_struct *work)
out:
dispc_runtime_put();
- mutex_unlock(&crtc->mutex);
+ drm_modeset_unlock(&crtc->mutex);
}
int omap_crtc_apply(struct drm_crtc *crtc,
@@ -518,7 +518,7 @@ int omap_crtc_apply(struct drm_crtc *crtc,
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- WARN_ON(!mutex_is_locked(&crtc->mutex));
+ WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
/* no need to queue it again if it is already queued: */
if (apply->queued)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index c8270e4b26f3..002b9721e85a 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -588,9 +588,7 @@ static void dev_lastclose(struct drm_device *dev)
}
}
- drm_modeset_lock_all(dev);
- ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
- drm_modeset_unlock_all(dev);
+ ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
if (ret)
DBG("failed to restore crtc mode");
}
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c
index 8b019602ffe6..2a5cacdc344b 100644
--- a/drivers/gpu/drm/omapdrm/omap_fb.c
+++ b/drivers/gpu/drm/omapdrm/omap_fb.c
@@ -346,6 +346,7 @@ void omap_framebuffer_flush(struct drm_framebuffer *fb,
VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
+ /* FIXME: This is racy - no protection against modeset config changes. */
while ((connector = omap_framebuffer_get_next_connector(fb, connector))) {
/* only consider connectors that are part of a chain */
if (connector->encoder && connector->encoder->crtc) {
diff --git a/drivers/gpu/drm/panel/panel-ld9040.c b/drivers/gpu/drm/panel/panel-ld9040.c
index 1f1f8371a199..db1601fdbe29 100644
--- a/drivers/gpu/drm/panel/panel-ld9040.c
+++ b/drivers/gpu/drm/panel/panel-ld9040.c
@@ -27,6 +27,7 @@
#define MCS_ELVSS_ON 0xb1
#define MCS_USER_SETTING 0xf0
#define MCS_DISPCTL 0xf2
+#define MCS_POWER_CTRL 0xf4
#define MCS_GTCON 0xf7
#define MCS_PANEL_CONDITION 0xf8
#define MCS_GAMMA_SET1 0xf9
@@ -182,6 +183,8 @@ static void ld9040_init(struct ld9040 *ctx)
ld9040_dcs_write_seq_static(ctx, MCS_DISPCTL,
0x02, 0x08, 0x08, 0x10, 0x10);
ld9040_dcs_write_seq_static(ctx, MCS_MANPWR, 0x04);
+ ld9040_dcs_write_seq_static(ctx, MCS_POWER_CTRL,
+ 0x0a, 0x87, 0x25, 0x6a, 0x44, 0x02, 0x88);
ld9040_dcs_write_seq_static(ctx, MCS_ELVSS_ON, 0x0d, 0x00, 0x16);
ld9040_dcs_write_seq_static(ctx, MCS_GTCON, 0x09, 0x00, 0x00);
ld9040_brightness_set(ctx);
diff --git a/drivers/gpu/drm/panel/panel-s6e8aa0.c b/drivers/gpu/drm/panel/panel-s6e8aa0.c
index 35941d2412b8..06e57a26db7a 100644
--- a/drivers/gpu/drm/panel/panel-s6e8aa0.c
+++ b/drivers/gpu/drm/panel/panel-s6e8aa0.c
@@ -847,6 +847,7 @@ static void s6e8aa0_read_mtp_id(struct s6e8aa0 *ctx)
if (i >= ARRAY_SIZE(s6e8aa0_variants)) {
dev_err(ctx->dev, "unsupported display version %d\n", id[1]);
ctx->error = -EINVAL;
+ return;
}
ctx->variant = &s6e8aa0_variants[i];
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 309f29e9234a..a25136132c31 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -262,6 +262,13 @@ static int panel_simple_remove(struct device *dev)
return 0;
}
+static void panel_simple_shutdown(struct device *dev)
+{
+ struct panel_simple *panel = dev_get_drvdata(dev);
+
+ panel_simple_disable(&panel->base);
+}
+
static const struct drm_display_mode auo_b101aw03_mode = {
.clock = 51450,
.hdisplay = 1024,
@@ -284,6 +291,28 @@ static const struct panel_desc auo_b101aw03 = {
},
};
+static const struct drm_display_mode auo_b133xtn01_mode = {
+ .clock = 69500,
+ .hdisplay = 1366,
+ .hsync_start = 1366 + 48,
+ .hsync_end = 1366 + 48 + 32,
+ .htotal = 1366 + 48 + 32 + 20,
+ .vdisplay = 768,
+ .vsync_start = 768 + 3,
+ .vsync_end = 768 + 3 + 6,
+ .vtotal = 768 + 3 + 6 + 13,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc auo_b133xtn01 = {
+ .modes = &auo_b133xtn01_mode,
+ .num_modes = 1,
+ .size = {
+ .width = 293,
+ .height = 165,
+ },
+};
+
static const struct drm_display_mode chunghwa_claa101wa01a_mode = {
.clock = 72070,
.hdisplay = 1366,
@@ -328,6 +357,52 @@ static const struct panel_desc chunghwa_claa101wb01 = {
},
};
+static const struct drm_display_mode edt_et057090dhu_mode = {
+ .clock = 25175,
+ .hdisplay = 640,
+ .hsync_start = 640 + 16,
+ .hsync_end = 640 + 16 + 30,
+ .htotal = 640 + 16 + 30 + 114,
+ .vdisplay = 480,
+ .vsync_start = 480 + 10,
+ .vsync_end = 480 + 10 + 3,
+ .vtotal = 480 + 10 + 3 + 32,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc edt_et057090dhu = {
+ .modes = &edt_et057090dhu_mode,
+ .num_modes = 1,
+ .size = {
+ .width = 115,
+ .height = 86,
+ },
+};
+
+static const struct drm_display_mode edt_etm0700g0dh6_mode = {
+ .clock = 33260,
+ .hdisplay = 800,
+ .hsync_start = 800 + 40,
+ .hsync_end = 800 + 40 + 128,
+ .htotal = 800 + 40 + 128 + 88,
+ .vdisplay = 480,
+ .vsync_start = 480 + 10,
+ .vsync_end = 480 + 10 + 2,
+ .vtotal = 480 + 10 + 2 + 33,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+};
+
+static const struct panel_desc edt_etm0700g0dh6 = {
+ .modes = &edt_etm0700g0dh6_mode,
+ .num_modes = 1,
+ .size = {
+ .width = 152,
+ .height = 91,
+ },
+};
+
static const struct drm_display_mode lg_lp129qe_mode = {
.clock = 285250,
.hdisplay = 2560,
@@ -377,12 +452,24 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "auo,b101aw03",
.data = &auo_b101aw03,
}, {
+ .compatible = "auo,b133xtn01",
+ .data = &auo_b133xtn01,
+ }, {
.compatible = "chunghwa,claa101wa01a",
.data = &chunghwa_claa101wa01a
}, {
.compatible = "chunghwa,claa101wb01",
.data = &chunghwa_claa101wb01
}, {
+ .compatible = "edt,et057090dhu",
+ .data = &edt_et057090dhu,
+ }, {
+ .compatible = "edt,et070080dh6",
+ .data = &edt_etm0700g0dh6,
+ }, {
+ .compatible = "edt,etm0700g0dh6",
+ .data = &edt_etm0700g0dh6,
+ }, {
.compatible = "lg,lp129qe",
.data = &lg_lp129qe,
}, {
@@ -412,6 +499,11 @@ static int panel_simple_platform_remove(struct platform_device *pdev)
return panel_simple_remove(&pdev->dev);
}
+static void panel_simple_platform_shutdown(struct platform_device *pdev)
+{
+ panel_simple_shutdown(&pdev->dev);
+}
+
static struct platform_driver panel_simple_platform_driver = {
.driver = {
.name = "panel-simple",
@@ -420,6 +512,7 @@ static struct platform_driver panel_simple_platform_driver = {
},
.probe = panel_simple_platform_probe,
.remove = panel_simple_platform_remove,
+ .shutdown = panel_simple_platform_shutdown,
};
struct panel_desc_dsi {
@@ -561,6 +654,11 @@ static int panel_simple_dsi_remove(struct mipi_dsi_device *dsi)
return panel_simple_remove(&dsi->dev);
}
+static void panel_simple_dsi_shutdown(struct mipi_dsi_device *dsi)
+{
+ panel_simple_shutdown(&dsi->dev);
+}
+
static struct mipi_dsi_driver panel_simple_dsi_driver = {
.driver = {
.name = "panel-simple-dsi",
@@ -569,6 +667,7 @@ static struct mipi_dsi_driver panel_simple_dsi_driver = {
},
.probe = panel_simple_dsi_probe,
.remove = panel_simple_dsi_remove,
+ .shutdown = panel_simple_dsi_shutdown,
};
static int __init panel_simple_init(void)
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index 41bdd174657e..5d7ea2461852 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -574,6 +574,10 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
bo->surf.height, bo->surf.stride, bo->surf.format);
qxl_io_create_primary(qdev, base_offset, bo);
bo->is_primary = true;
+ }
+
+ if (bo->is_primary) {
+ DRM_DEBUG_KMS("setting surface_id to 0 for primary surface %d on crtc %d\n", bo->surface_id, qcrtc->index);
surf_id = 0;
} else {
surf_id = bo->surface_id;
@@ -841,7 +845,7 @@ static const struct drm_connector_funcs qxl_connector_funcs = {
.save = qxl_conn_save,
.restore = qxl_conn_restore,
.detect = qxl_conn_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
+ .fill_modes = drm_helper_probe_single_connector_modes_nomerge,
.set_property = qxl_conn_set_property,
.destroy = qxl_conn_destroy,
};
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index fee8748bdca5..6e936634d65c 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -214,7 +214,6 @@ static struct pci_driver qxl_pci_driver = {
static struct drm_driver qxl_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET |
DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
- .dev_priv_size = 0,
.load = qxl_driver_load,
.unload = qxl_driver_unload,
diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c
index 0bb86e6d41b4..b110883f8253 100644
--- a/drivers/gpu/drm/qxl/qxl_ioctl.c
+++ b/drivers/gpu/drm/qxl/qxl_ioctl.c
@@ -451,4 +451,4 @@ const struct drm_ioctl_desc qxl_ioctls[] = {
DRM_AUTH|DRM_UNLOCKED),
};
-int qxl_max_ioctls = DRM_ARRAY_SIZE(qxl_ioctls);
+int qxl_max_ioctls = ARRAY_SIZE(qxl_ioctls);
diff --git a/drivers/gpu/drm/qxl/qxl_irq.c b/drivers/gpu/drm/qxl/qxl_irq.c
index 28f84b4fce32..34d6a85e9023 100644
--- a/drivers/gpu/drm/qxl/qxl_irq.c
+++ b/drivers/gpu/drm/qxl/qxl_irq.c
@@ -87,7 +87,7 @@ int qxl_irq_init(struct qxl_device *qdev)
atomic_set(&qdev->irq_received_cursor, 0);
atomic_set(&qdev->irq_received_io_cmd, 0);
qdev->irq_received_error = 0;
- ret = drm_irq_install(qdev->ddev);
+ ret = drm_irq_install(qdev->ddev, qdev->ddev->pdev->irq);
qdev->ram_header->int_mask = QXL_INTERRUPT_MASK;
if (unlikely(ret != 0)) {
DRM_ERROR("Failed installing irq: %d\n", ret);
diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c
index d52c27527b9a..71a1baeac14e 100644
--- a/drivers/gpu/drm/qxl/qxl_ttm.c
+++ b/drivers/gpu/drm/qxl/qxl_ttm.c
@@ -109,13 +109,11 @@ static const struct vm_operations_struct *ttm_vm_ops;
static int qxl_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct ttm_buffer_object *bo;
- struct qxl_device *qdev;
int r;
bo = (struct ttm_buffer_object *)vma->vm_private_data;
if (bo == NULL)
return VM_FAULT_NOPAGE;
- qdev = qxl_get_qdev(bo->bdev);
r = ttm_vm_ops->fault(vma, vmf);
return r;
}
@@ -162,10 +160,6 @@ static int qxl_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
static int qxl_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
struct ttm_mem_type_manager *man)
{
- struct qxl_device *qdev;
-
- qdev = qxl_get_qdev(bdev);
-
switch (type) {
case TTM_PL_SYSTEM:
/* System memory */
diff --git a/drivers/gpu/drm/r128/r128_ioc32.c b/drivers/gpu/drm/r128/r128_ioc32.c
index b0d0fd3e4376..663f38c63ba6 100644
--- a/drivers/gpu/drm/r128/r128_ioc32.c
+++ b/drivers/gpu/drm/r128/r128_ioc32.c
@@ -203,7 +203,7 @@ long r128_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (nr < DRM_COMMAND_BASE)
return drm_compat_ioctl(filp, cmd, arg);
- if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(r128_compat_ioctls))
+ if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(r128_compat_ioctls))
fn = r128_compat_ioctls[nr - DRM_COMMAND_BASE];
if (fn != NULL)
diff --git a/drivers/gpu/drm/r128/r128_state.c b/drivers/gpu/drm/r128/r128_state.c
index e806dacd452f..575e986f82a7 100644
--- a/drivers/gpu/drm/r128/r128_state.c
+++ b/drivers/gpu/drm/r128/r128_state.c
@@ -1594,7 +1594,7 @@ static int r128_getparam(struct drm_device *dev, void *data, struct drm_file *fi
switch (param->param) {
case R128_PARAM_IRQ_NR:
- value = drm_dev_to_irq(dev);
+ value = dev->pdev->irq;
break;
default:
return -EINVAL;
@@ -1641,4 +1641,4 @@ const struct drm_ioctl_desc r128_ioctls[] = {
DRM_IOCTL_DEF_DRV(R128_GETPARAM, r128_getparam, DRM_AUTH),
};
-int r128_max_ioctl = DRM_ARRAY_SIZE(r128_ioctls);
+int r128_max_ioctl = ARRAY_SIZE(r128_ioctls);
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
index 09433534dc47..dbcbfe80aac0 100644
--- a/drivers/gpu/drm/radeon/Makefile
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -72,7 +72,7 @@ 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 \
+ radeon_pm.o atombios_dp.o r600_audio.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 \
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index e911898348f8..26c12a3fe430 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -557,6 +557,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
u32 adjusted_clock = mode->clock;
int encoder_mode = atombios_get_encoder_mode(encoder);
u32 dp_clock = mode->clock;
+ u32 clock = mode->clock;
int bpc = radeon_crtc->bpc;
bool is_duallink = radeon_dig_monitor_is_duallink(encoder, mode->clock);
@@ -632,6 +633,24 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
radeon_crtc->pll_flags |= RADEON_PLL_USE_REF_DIV;
}
+ /* adjust pll for deep color modes */
+ if (encoder_mode == ATOM_ENCODER_MODE_HDMI) {
+ switch (bpc) {
+ case 8:
+ default:
+ break;
+ case 10:
+ clock = (clock * 5) / 4;
+ break;
+ case 12:
+ clock = (clock * 3) / 2;
+ break;
+ case 16:
+ clock = clock * 2;
+ break;
+ }
+ }
+
/* DCE3+ has an AdjustDisplayPll that will adjust the pixel clock
* accordingly based on the encoder/transmitter to work around
* special hw requirements.
@@ -653,7 +672,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
switch (crev) {
case 1:
case 2:
- args.v1.usPixelClock = cpu_to_le16(mode->clock / 10);
+ args.v1.usPixelClock = cpu_to_le16(clock / 10);
args.v1.ucTransmitterID = radeon_encoder->encoder_id;
args.v1.ucEncodeMode = encoder_mode;
if (radeon_crtc->ss_enabled && radeon_crtc->ss.percentage)
@@ -665,7 +684,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
adjusted_clock = le16_to_cpu(args.v1.usPixelClock) * 10;
break;
case 3:
- args.v3.sInput.usPixelClock = cpu_to_le16(mode->clock / 10);
+ args.v3.sInput.usPixelClock = cpu_to_le16(clock / 10);
args.v3.sInput.ucTransmitterID = radeon_encoder->encoder_id;
args.v3.sInput.ucEncodeMode = encoder_mode;
args.v3.sInput.ucDispPllConfig = 0;
@@ -679,10 +698,6 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
args.v3.sInput.usPixelClock = cpu_to_le16(dp_clock / 10);
} else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
- if (encoder_mode == ATOM_ENCODER_MODE_HDMI)
- /* deep color support */
- args.v3.sInput.usPixelClock =
- cpu_to_le16((mode->clock * bpc / 8) / 10);
if (dig->coherent_mode)
args.v3.sInput.ucDispPllConfig |=
DISPPLL_CONFIG_COHERENT_MODE;
@@ -862,14 +877,21 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */
if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC;
- switch (bpc) {
- case 8:
- default:
- args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP;
- break;
- case 10:
- args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP;
- break;
+ if (encoder_mode == ATOM_ENCODER_MODE_HDMI) {
+ switch (bpc) {
+ case 8:
+ default:
+ args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP;
+ break;
+ case 10:
+ /* yes this is correct, the atom define is wrong */
+ args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_32BPP;
+ break;
+ case 12:
+ /* yes this is correct, the atom define is wrong */
+ args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP;
+ break;
+ }
}
args.v5.ucTransmitterID = encoder_id;
args.v5.ucEncoderMode = encoder_mode;
@@ -884,20 +906,22 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */
if (ss_enabled && (ss->type & ATOM_EXTERNAL_SS_MASK))
args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC;
- switch (bpc) {
- case 8:
- default:
- args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP;
- break;
- case 10:
- args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP;
- break;
- case 12:
- args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP;
- break;
- case 16:
- args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP;
- break;
+ if (encoder_mode == ATOM_ENCODER_MODE_HDMI) {
+ switch (bpc) {
+ case 8:
+ default:
+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP;
+ break;
+ case 10:
+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP_V6;
+ break;
+ case 12:
+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP_V6;
+ break;
+ case 16:
+ args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP;
+ break;
+ }
}
args.v6.ucTransmitterID = encoder_id;
args.v6.ucEncoderMode = encoder_mode;
@@ -938,6 +962,9 @@ static bool atombios_crtc_prepare_pll(struct drm_crtc *crtc, struct drm_display_
struct radeon_connector_atom_dig *dig_connector =
radeon_connector->con_priv;
int dp_clock;
+
+ /* Assign mode clock for hdmi deep color max clock limit check */
+ radeon_connector->pixelclock_for_modeset = mode->clock;
radeon_crtc->bpc = radeon_get_monitor_bpc(connector);
switch (encoder_mode) {
@@ -1019,10 +1046,17 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
struct radeon_encoder *radeon_encoder =
to_radeon_encoder(radeon_crtc->encoder);
u32 pll_clock = mode->clock;
+ u32 clock = mode->clock;
u32 ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0;
struct radeon_pll *pll;
int encoder_mode = atombios_get_encoder_mode(radeon_crtc->encoder);
+ /* pass the actual clock to atombios_crtc_program_pll for DCE5,6 for HDMI */
+ if (ASIC_IS_DCE5(rdev) && !ASIC_IS_DCE8(rdev) &&
+ (encoder_mode == ATOM_ENCODER_MODE_HDMI) &&
+ (radeon_crtc->bpc > 8))
+ clock = radeon_crtc->adjusted_clock;
+
switch (radeon_crtc->pll_id) {
case ATOM_PPLL1:
pll = &rdev->clock.p1pll;
@@ -1057,7 +1091,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
radeon_crtc->crtc_id, &radeon_crtc->ss);
atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
- encoder_mode, radeon_encoder->encoder_id, mode->clock,
+ encoder_mode, radeon_encoder->encoder_id, clock,
ref_div, fb_div, frac_fb_div, post_div,
radeon_crtc->bpc, radeon_crtc->ss_enabled, &radeon_crtc->ss);
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 54e4f52549af..c5b1f2da3954 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -95,9 +95,12 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
unsigned char *base;
int recv_bytes;
+ int r = 0;
memset(&args, 0, sizeof(args));
+ mutex_lock(&chan->mutex);
+
base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1);
radeon_atom_copy_swap(base, send, send_bytes, true);
@@ -117,19 +120,22 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
/* timeout */
if (args.v1.ucReplyStatus == 1) {
DRM_DEBUG_KMS("dp_aux_ch timeout\n");
- return -ETIMEDOUT;
+ r = -ETIMEDOUT;
+ goto done;
}
/* flags not zero */
if (args.v1.ucReplyStatus == 2) {
DRM_DEBUG_KMS("dp_aux_ch flags not zero\n");
- return -EBUSY;
+ r = -EBUSY;
+ goto done;
}
/* error */
if (args.v1.ucReplyStatus == 3) {
DRM_DEBUG_KMS("dp_aux_ch error\n");
- return -EIO;
+ r = -EIO;
+ goto done;
}
recv_bytes = args.v1.ucDataOutLen;
@@ -139,7 +145,11 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
if (recv && recv_size)
radeon_atom_copy_swap(recv, base + 16, recv_bytes, false);
- return recv_bytes;
+ r = recv_bytes;
+done:
+ mutex_unlock(&chan->mutex);
+
+ return r;
}
#define BARE_ADDRESS_SIZE 3
@@ -212,11 +222,12 @@ void radeon_dp_aux_init(struct radeon_connector *radeon_connector)
radeon_connector->ddc_bus->rec.hpd = radeon_connector->hpd.hpd;
radeon_connector->ddc_bus->aux.dev = radeon_connector->base.kdev;
radeon_connector->ddc_bus->aux.transfer = radeon_dp_aux_transfer;
- ret = drm_dp_aux_register_i2c_bus(&radeon_connector->ddc_bus->aux);
+
+ ret = drm_dp_aux_register(&radeon_connector->ddc_bus->aux);
if (!ret)
radeon_connector->ddc_bus->has_aux = true;
- WARN(ret, "drm_dp_aux_register_i2c_bus() failed with error %d\n", ret);
+ WARN(ret, "drm_dp_aux_register() failed with error %d\n", ret);
}
/***** general DP utility functions *****/
@@ -281,6 +292,19 @@ static int dp_get_max_dp_pix_clock(int link_rate,
/***** radeon specific DP functions *****/
+static int radeon_dp_get_max_link_rate(struct drm_connector *connector,
+ u8 dpcd[DP_DPCD_SIZE])
+{
+ int max_link_rate;
+
+ if (radeon_connector_is_dp12_capable(connector))
+ max_link_rate = min(drm_dp_max_link_rate(dpcd), 540000);
+ else
+ max_link_rate = min(drm_dp_max_link_rate(dpcd), 270000);
+
+ return max_link_rate;
+}
+
/* First get the min lane# when low rate is used according to pixel clock
* (prefer low rate), second check max lane# supported by DP panel,
* if the max lane# < low rate lane# then use max lane# instead.
@@ -290,7 +314,7 @@ static int radeon_dp_get_dp_lane_number(struct drm_connector *connector,
int pix_clock)
{
int bpp = convert_bpc_to_bpp(radeon_get_monitor_bpc(connector));
- int max_link_rate = drm_dp_max_link_rate(dpcd);
+ int max_link_rate = radeon_dp_get_max_link_rate(connector, dpcd);
int max_lane_num = drm_dp_max_lane_count(dpcd);
int lane_num;
int max_dp_pix_clock;
@@ -328,7 +352,7 @@ static int radeon_dp_get_dp_link_clock(struct drm_connector *connector,
return 540000;
}
- return drm_dp_max_link_rate(dpcd);
+ return radeon_dp_get_max_link_rate(connector, dpcd);
}
static u8 radeon_dp_encoder_service(struct radeon_device *rdev,
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index e6eb5097597f..2b2908440644 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -1884,8 +1884,11 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
args.v2.ucEncodeMode = ATOM_ENCODER_MODE_CRT;
else
args.v2.ucEncodeMode = atombios_get_encoder_mode(encoder);
- } else
+ } else if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+ args.v2.ucEncodeMode = ATOM_ENCODER_MODE_LVDS;
+ } else {
args.v2.ucEncodeMode = atombios_get_encoder_mode(encoder);
+ }
switch (radeon_encoder->encoder_id) {
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
diff --git a/drivers/gpu/drm/radeon/atombios_i2c.c b/drivers/gpu/drm/radeon/atombios_i2c.c
index b5162c3b6111..9c570fb15b8c 100644
--- a/drivers/gpu/drm/radeon/atombios_i2c.c
+++ b/drivers/gpu/drm/radeon/atombios_i2c.c
@@ -43,15 +43,19 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction);
unsigned char *base;
u16 out = cpu_to_le16(0);
+ int r = 0;
memset(&args, 0, sizeof(args));
+ mutex_lock(&chan->mutex);
+
base = (unsigned char *)rdev->mode_info.atom_context->scratch;
if (flags & HW_I2C_WRITE) {
if (num > ATOM_MAX_HW_I2C_WRITE) {
DRM_ERROR("hw i2c: tried to write too many bytes (%d vs 3)\n", num);
- return -EINVAL;
+ r = -EINVAL;
+ goto done;
}
if (buf == NULL)
args.ucRegIndex = 0;
@@ -65,7 +69,8 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
} else {
if (num > ATOM_MAX_HW_I2C_READ) {
DRM_ERROR("hw i2c: tried to read too many bytes (%d vs 255)\n", num);
- return -EINVAL;
+ r = -EINVAL;
+ goto done;
}
args.ucRegIndex = 0;
args.lpI2CDataOut = 0;
@@ -82,13 +87,17 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
/* error */
if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) {
DRM_DEBUG_KMS("hw_i2c error\n");
- return -EIO;
+ r = -EIO;
+ goto done;
}
if (!(flags & HW_I2C_WRITE))
radeon_atom_copy_swap(buf, base, num, false);
- return 0;
+done:
+ mutex_unlock(&chan->mutex);
+
+ return r;
}
int radeon_atom_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
index d2fd98968085..dcd4518a9b08 100644
--- a/drivers/gpu/drm/radeon/cik.c
+++ b/drivers/gpu/drm/radeon/cik.c
@@ -80,6 +80,7 @@ extern int sumo_rlc_init(struct radeon_device *rdev);
extern void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
extern void si_rlc_reset(struct radeon_device *rdev);
extern void si_init_uvd_internal_cg(struct radeon_device *rdev);
+static u32 cik_get_cu_active_bitmap(struct radeon_device *rdev, u32 se, u32 sh);
extern int cik_sdma_resume(struct radeon_device *rdev);
extern void cik_sdma_enable(struct radeon_device *rdev, bool enable);
extern void cik_sdma_fini(struct radeon_device *rdev);
@@ -3257,7 +3258,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;
+ int i, j, k;
switch (rdev->family) {
case CHIP_BONAIRE:
@@ -3446,6 +3447,15 @@ static void cik_gpu_init(struct radeon_device *rdev)
rdev->config.cik.max_sh_per_se,
rdev->config.cik.max_backends_per_se);
+ 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));
+ }
+ }
+ }
+
/* set HW defaults for 3D engine */
WREG32(CP_MEQ_THRESHOLDS, MEQ1_START(0x30) | MEQ2_START(0x60));
@@ -3698,7 +3708,7 @@ bool cik_semaphore_ring_emit(struct radeon_device *rdev,
unsigned sel = emit_wait ? PACKET3_SEM_SEL_WAIT : PACKET3_SEM_SEL_SIGNAL;
radeon_ring_write(ring, PACKET3(PACKET3_MEM_SEMAPHORE, 1));
- radeon_ring_write(ring, addr & 0xffffffff);
+ radeon_ring_write(ring, lower_32_bits(addr));
radeon_ring_write(ring, (upper_32_bits(addr) & 0xffff) | sel);
return true;
@@ -3818,7 +3828,7 @@ void cik_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
radeon_ring_write(ring, WRITE_DATA_DST_SEL(1));
radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc);
- radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xffffffff);
+ radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr));
radeon_ring_write(ring, next_rptr);
}
@@ -5396,6 +5406,7 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev)
WREG32(MC_VM_MX_L1_TLB_CNTL,
(0xA << 7) |
ENABLE_L1_TLB |
+ ENABLE_L1_FRAGMENT_PROCESSING |
SYSTEM_ACCESS_MODE_NOT_IN_SYS |
ENABLE_ADVANCED_DRIVER_MODEL |
SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU);
@@ -5408,7 +5419,8 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev)
CONTEXT1_IDENTITY_ACCESS_MODE(1));
WREG32(VM_L2_CNTL2, INVALIDATE_ALL_L1_TLBS | INVALIDATE_L2_CACHE);
WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY |
- L2_CACHE_BIGK_FRAGMENT_SIZE(6));
+ BANK_SELECT(4) |
+ L2_CACHE_BIGK_FRAGMENT_SIZE(4));
/* setup context0 */
WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12);
WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12);
@@ -5444,6 +5456,7 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev)
(u32)(rdev->dummy_page.addr >> 12));
WREG32(VM_CONTEXT1_CNTL2, 4);
WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) |
+ PAGE_TABLE_BLOCK_SIZE(radeon_vm_block_size - 9) |
RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
RANGE_PROTECTION_FAULT_ENABLE_DEFAULT |
DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
@@ -7450,7 +7463,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[0]))
- radeon_crtc_handle_flip(rdev, 0);
+ radeon_crtc_handle_vblank(rdev, 0);
rdev->irq.stat_regs.cik.disp_int &= ~LB_D1_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D1 vblank\n");
}
@@ -7476,7 +7489,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[1]))
- radeon_crtc_handle_flip(rdev, 1);
+ radeon_crtc_handle_vblank(rdev, 1);
rdev->irq.stat_regs.cik.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D2 vblank\n");
}
@@ -7502,7 +7515,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[2]))
- radeon_crtc_handle_flip(rdev, 2);
+ radeon_crtc_handle_vblank(rdev, 2);
rdev->irq.stat_regs.cik.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D3 vblank\n");
}
@@ -7528,7 +7541,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[3]))
- radeon_crtc_handle_flip(rdev, 3);
+ radeon_crtc_handle_vblank(rdev, 3);
rdev->irq.stat_regs.cik.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D4 vblank\n");
}
@@ -7554,7 +7567,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[4]))
- radeon_crtc_handle_flip(rdev, 4);
+ radeon_crtc_handle_vblank(rdev, 4);
rdev->irq.stat_regs.cik.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D5 vblank\n");
}
@@ -7580,7 +7593,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[5]))
- radeon_crtc_handle_flip(rdev, 5);
+ radeon_crtc_handle_vblank(rdev, 5);
rdev->irq.stat_regs.cik.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D6 vblank\n");
}
diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c
index 72e464c79a88..8e9d0f1d858e 100644
--- a/drivers/gpu/drm/radeon/cik_sdma.c
+++ b/drivers/gpu/drm/radeon/cik_sdma.c
@@ -141,7 +141,7 @@ void cik_sdma_ring_ib_execute(struct radeon_device *rdev,
next_rptr += 4;
radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0));
radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc);
- radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xffffffff);
+ radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr));
radeon_ring_write(ring, 1); /* number of DWs to follow */
radeon_ring_write(ring, next_rptr);
}
@@ -151,7 +151,7 @@ void cik_sdma_ring_ib_execute(struct radeon_device *rdev,
radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0));
radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_INDIRECT_BUFFER, 0, extra_bits));
radeon_ring_write(ring, ib->gpu_addr & 0xffffffe0); /* base must be 32 byte aligned */
- radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xffffffff);
+ radeon_ring_write(ring, upper_32_bits(ib->gpu_addr));
radeon_ring_write(ring, ib->length_dw);
}
@@ -203,8 +203,8 @@ void cik_sdma_fence_ring_emit(struct radeon_device *rdev,
/* write the fence */
radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_FENCE, 0, 0));
- radeon_ring_write(ring, addr & 0xffffffff);
- radeon_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
+ radeon_ring_write(ring, lower_32_bits(addr));
+ radeon_ring_write(ring, upper_32_bits(addr));
radeon_ring_write(ring, fence->seq);
/* generate an interrupt */
radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_TRAP, 0, 0));
@@ -233,7 +233,7 @@ bool cik_sdma_semaphore_ring_emit(struct radeon_device *rdev,
radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SEMAPHORE, 0, extra_bits));
radeon_ring_write(ring, addr & 0xfffffff8);
- radeon_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
+ radeon_ring_write(ring, upper_32_bits(addr));
return true;
}
@@ -551,10 +551,10 @@ int cik_copy_dma(struct radeon_device *rdev,
radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_COPY, SDMA_COPY_SUB_OPCODE_LINEAR, 0));
radeon_ring_write(ring, cur_size_in_bytes);
radeon_ring_write(ring, 0); /* src/dst endian swap */
- radeon_ring_write(ring, src_offset & 0xffffffff);
- radeon_ring_write(ring, upper_32_bits(src_offset) & 0xffffffff);
- radeon_ring_write(ring, dst_offset & 0xffffffff);
- radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xffffffff);
+ radeon_ring_write(ring, lower_32_bits(src_offset));
+ radeon_ring_write(ring, upper_32_bits(src_offset));
+ radeon_ring_write(ring, lower_32_bits(dst_offset));
+ radeon_ring_write(ring, upper_32_bits(dst_offset));
src_offset += cur_size_in_bytes;
dst_offset += cur_size_in_bytes;
}
@@ -605,7 +605,7 @@ int cik_sdma_ring_test(struct radeon_device *rdev,
}
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) & 0xffffffff);
+ radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr));
radeon_ring_write(ring, 1); /* number of DWs to follow */
radeon_ring_write(ring, 0xDEADBEEF);
radeon_ring_unlock_commit(rdev, ring);
@@ -660,7 +660,7 @@ 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) & 0xffffffff;
+ ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr);
ib.ptr[3] = 1;
ib.ptr[4] = 0xDEADBEEF;
ib.length_dw = 5;
@@ -742,7 +742,26 @@ void cik_sdma_vm_set_page(struct radeon_device *rdev,
trace_radeon_vm_set_page(pe, addr, count, incr, flags);
- if (flags & R600_PTE_SYSTEM) {
+ 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)
diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h
index dd7926394a8f..ae88660f34ea 100644
--- a/drivers/gpu/drm/radeon/cikd.h
+++ b/drivers/gpu/drm/radeon/cikd.h
@@ -482,6 +482,7 @@
#define READ_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 16)
#define WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 18)
#define WRITE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 19)
+#define PAGE_TABLE_BLOCK_SIZE(x) (((x) & 0xF) << 24)
#define VM_CONTEXT1_CNTL 0x1414
#define VM_CONTEXT0_CNTL2 0x1430
#define VM_CONTEXT1_CNTL2 0x1434
diff --git a/drivers/gpu/drm/radeon/clearstate_cayman.h b/drivers/gpu/drm/radeon/clearstate_cayman.h
index aa908c55a513..e48a14037b76 100644
--- a/drivers/gpu/drm/radeon/clearstate_cayman.h
+++ b/drivers/gpu/drm/radeon/clearstate_cayman.h
@@ -1050,7 +1050,7 @@ static const struct cs_extent_def SECT_CONTEXT_defs[] =
{SECT_CONTEXT_def_5, 0x0000a29e, 5 },
{SECT_CONTEXT_def_6, 0x0000a2a5, 56 },
{SECT_CONTEXT_def_7, 0x0000a2de, 290 },
- { 0, 0, 0 }
+ { NULL, 0, 0 }
};
static const u32 SECT_CLEAR_def_1[] =
{
@@ -1061,7 +1061,7 @@ static const u32 SECT_CLEAR_def_1[] =
static const struct cs_extent_def SECT_CLEAR_defs[] =
{
{SECT_CLEAR_def_1, 0x0000ffc0, 3 },
- { 0, 0, 0 }
+ { NULL, 0, 0 }
};
static const u32 SECT_CTRLCONST_def_1[] =
{
@@ -1071,11 +1071,11 @@ static const u32 SECT_CTRLCONST_def_1[] =
static const struct cs_extent_def SECT_CTRLCONST_defs[] =
{
{SECT_CTRLCONST_def_1, 0x0000f3fc, 2 },
- { 0, 0, 0 }
+ { NULL, 0, 0 }
};
static const struct cs_section_def cayman_cs_data[] = {
{ SECT_CONTEXT_defs, SECT_CONTEXT },
{ SECT_CLEAR_defs, SECT_CLEAR },
{ SECT_CTRLCONST_defs, SECT_CTRLCONST },
- { 0, SECT_NONE }
+ { NULL, SECT_NONE }
};
diff --git a/drivers/gpu/drm/radeon/clearstate_ci.h b/drivers/gpu/drm/radeon/clearstate_ci.h
index c3982f9475fb..f55d06664e31 100644
--- a/drivers/gpu/drm/radeon/clearstate_ci.h
+++ b/drivers/gpu/drm/radeon/clearstate_ci.h
@@ -936,9 +936,9 @@ static const struct cs_extent_def ci_SECT_CONTEXT_defs[] =
{ci_SECT_CONTEXT_def_5, 0x0000a2a0, 2 },
{ci_SECT_CONTEXT_def_6, 0x0000a2a3, 1 },
{ci_SECT_CONTEXT_def_7, 0x0000a2a5, 233 },
- { 0, 0, 0 }
+ { NULL, 0, 0 }
};
static const struct cs_section_def ci_cs_data[] = {
{ ci_SECT_CONTEXT_defs, SECT_CONTEXT },
- { 0, SECT_NONE }
+ { NULL, SECT_NONE }
};
diff --git a/drivers/gpu/drm/radeon/clearstate_si.h b/drivers/gpu/drm/radeon/clearstate_si.h
index b994cb2a35a0..66e39cdb5cb0 100644
--- a/drivers/gpu/drm/radeon/clearstate_si.h
+++ b/drivers/gpu/drm/radeon/clearstate_si.h
@@ -933,9 +933,9 @@ static const struct cs_extent_def si_SECT_CONTEXT_defs[] =
{si_SECT_CONTEXT_def_5, 0x0000a2a1, 1 },
{si_SECT_CONTEXT_def_6, 0x0000a2a3, 1 },
{si_SECT_CONTEXT_def_7, 0x0000a2a5, 233 },
- { 0, 0, 0 }
+ { NULL, 0, 0 }
};
static const struct cs_section_def si_cs_data[] = {
{ si_SECT_CONTEXT_defs, SECT_CONTEXT },
- { 0, SECT_NONE }
+ { NULL, SECT_NONE }
};
diff --git a/drivers/gpu/drm/radeon/dce3_1_afmt.c b/drivers/gpu/drm/radeon/dce3_1_afmt.c
new file mode 100644
index 000000000000..51800e340a57
--- /dev/null
+++ b/drivers/gpu/drm/radeon/dce3_1_afmt.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ * Copyright 2014 Rafał Miłecki
+ *
+ * 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/hdmi.h>
+#include <drm/drmP.h>
+#include "radeon.h"
+#include "radeon_asic.h"
+#include "r600d.h"
+
+static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder)
+{
+ struct radeon_device *rdev = encoder->dev->dev_private;
+ struct drm_connector *connector;
+ struct radeon_connector *radeon_connector = NULL;
+ u32 tmp;
+ u8 *sadb;
+ int sad_count;
+
+ list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+ if (connector->encoder == encoder) {
+ radeon_connector = to_radeon_connector(connector);
+ break;
+ }
+ }
+
+ if (!radeon_connector) {
+ DRM_ERROR("Couldn't find encoder's connector\n");
+ 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;
+ }
+
+ /* program the speaker allocation */
+ tmp = RREG32(AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER);
+ tmp &= ~(DP_CONNECTION | SPEAKER_ALLOCATION_MASK);
+ /* set HDMI mode */
+ tmp |= HDMI_CONNECTION;
+ if (sad_count)
+ tmp |= SPEAKER_ALLOCATION(sadb[0]);
+ else
+ tmp |= SPEAKER_ALLOCATION(5); /* stereo */
+ WREG32(AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER, tmp);
+
+ kfree(sadb);
+}
+
+static void dce3_2_afmt_write_sad_regs(struct drm_encoder *encoder)
+{
+ struct radeon_device *rdev = encoder->dev->dev_private;
+ struct drm_connector *connector;
+ struct radeon_connector *radeon_connector = NULL;
+ struct cea_sad *sads;
+ int i, sad_count;
+
+ static const u16 eld_reg_to_type[][2] = {
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR0, HDMI_AUDIO_CODING_TYPE_PCM },
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR1, HDMI_AUDIO_CODING_TYPE_AC3 },
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR2, HDMI_AUDIO_CODING_TYPE_MPEG1 },
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR3, HDMI_AUDIO_CODING_TYPE_MP3 },
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR4, HDMI_AUDIO_CODING_TYPE_MPEG2 },
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR5, HDMI_AUDIO_CODING_TYPE_AAC_LC },
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR6, HDMI_AUDIO_CODING_TYPE_DTS },
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR7, HDMI_AUDIO_CODING_TYPE_ATRAC },
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR9, HDMI_AUDIO_CODING_TYPE_EAC3 },
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR10, HDMI_AUDIO_CODING_TYPE_DTS_HD },
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR11, HDMI_AUDIO_CODING_TYPE_MLP },
+ { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR13, HDMI_AUDIO_CODING_TYPE_WMA_PRO },
+ };
+
+ list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
+ if (connector->encoder == encoder) {
+ radeon_connector = to_radeon_connector(connector);
+ break;
+ }
+ }
+
+ if (!radeon_connector) {
+ DRM_ERROR("Couldn't find encoder's connector\n");
+ return;
+ }
+
+ sad_count = drm_edid_to_sad(radeon_connector->edid, &sads);
+ if (sad_count < 0) {
+ DRM_ERROR("Couldn't read SADs: %d\n", sad_count);
+ return;
+ }
+ BUG_ON(!sads);
+
+ for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) {
+ u32 value = 0;
+ u8 stereo_freqs = 0;
+ int max_channels = -1;
+ int j;
+
+ for (j = 0; j < sad_count; j++) {
+ struct cea_sad *sad = &sads[j];
+
+ if (sad->format == eld_reg_to_type[i][1]) {
+ if (sad->channels > max_channels) {
+ value = MAX_CHANNELS(sad->channels) |
+ DESCRIPTOR_BYTE_2(sad->byte2) |
+ SUPPORTED_FREQUENCIES(sad->freq);
+ max_channels = sad->channels;
+ }
+
+ if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
+ stereo_freqs |= sad->freq;
+ else
+ break;
+ }
+ }
+
+ value |= SUPPORTED_FREQUENCIES_STEREO(stereo_freqs);
+
+ WREG32(eld_reg_to_type[i][0], value);
+ }
+
+ kfree(sads);
+}
+
+/*
+ * update the info frames with the data from the current display mode
+ */
+void dce3_1_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode)
+{
+ 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;
+ u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
+ struct hdmi_avi_infoframe frame;
+ uint32_t offset;
+ ssize_t err;
+
+ if (!dig || !dig->afmt)
+ return;
+
+ /* Silent, r600_hdmi_enable will raise WARN for us */
+ if (!dig->afmt->enabled)
+ return;
+ offset = dig->afmt->offset;
+
+ /* 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_set_dto(encoder, mode->clock);
+
+ WREG32(HDMI0_VBI_PACKET_CONTROL + offset,
+ HDMI0_NULL_SEND); /* send null packets when required */
+
+ WREG32(HDMI0_AUDIO_CRC_CONTROL + offset, 0x1000);
+
+ if (ASIC_IS_DCE32(rdev)) {
+ WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset,
+ HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */
+ HDMI0_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */
+ WREG32(AFMT_AUDIO_PACKET_CONTROL + offset,
+ AFMT_AUDIO_SAMPLE_SEND | /* send audio packets */
+ AFMT_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */
+ } else {
+ WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset,
+ HDMI0_AUDIO_SAMPLE_SEND | /* send audio packets */
+ HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */
+ HDMI0_AUDIO_PACKETS_PER_LINE(3) | /* should be suffient for all audio modes and small enough for all hblanks */
+ HDMI0_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */
+ }
+
+ if (ASIC_IS_DCE32(rdev)) {
+ dce3_2_afmt_write_speaker_allocation(encoder);
+ dce3_2_afmt_write_sad_regs(encoder);
+ }
+
+ WREG32(HDMI0_ACR_PACKET_CONTROL + offset,
+ HDMI0_ACR_SOURCE | /* select SW CTS value - XXX verify that hw CTS works on all families */
+ HDMI0_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
+
+ WREG32(HDMI0_VBI_PACKET_CONTROL + offset,
+ HDMI0_NULL_SEND | /* send null packets when required */
+ HDMI0_GC_SEND | /* send general control packets */
+ HDMI0_GC_CONT); /* send general control packets every frame */
+
+ /* TODO: HDMI0_AUDIO_INFO_UPDATE */
+ WREG32(HDMI0_INFOFRAME_CONTROL0 + offset,
+ HDMI0_AVI_INFO_SEND | /* enable AVI info frames */
+ HDMI0_AVI_INFO_CONT | /* send AVI info frames every frame/field */
+ HDMI0_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */
+ HDMI0_AUDIO_INFO_CONT); /* send audio info frames every frame/field */
+
+ WREG32(HDMI0_INFOFRAME_CONTROL1 + offset,
+ HDMI0_AVI_INFO_LINE(2) | /* anything other than 0 */
+ HDMI0_AUDIO_INFO_LINE(2)); /* anything other than 0 */
+
+ WREG32(HDMI0_GC + offset, 0); /* unset HDMI0_GC_AVMUTE */
+
+ err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
+ if (err < 0) {
+ DRM_ERROR("failed to setup AVI infoframe: %zd\n", err);
+ return;
+ }
+
+ err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (err < 0) {
+ DRM_ERROR("failed to pack AVI infoframe: %zd\n", err);
+ return;
+ }
+
+ r600_hdmi_update_avi_infoframe(encoder, buffer, sizeof(buffer));
+ r600_hdmi_update_ACR(encoder, mode->clock);
+
+ /* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */
+ WREG32(HDMI0_RAMP_CONTROL0 + offset, 0x00FFFFFF);
+ WREG32(HDMI0_RAMP_CONTROL1 + offset, 0x007FFFFF);
+ WREG32(HDMI0_RAMP_CONTROL2 + offset, 0x00000001);
+ WREG32(HDMI0_RAMP_CONTROL3 + offset, 0x00000001);
+
+ r600_hdmi_audio_workaround(encoder);
+
+ /* enable audio after to setting up hw */
+ r600_audio_enable(rdev, dig->afmt->pin, true);
+}
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 0f7a51a3694f..e2f605224e8c 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -1301,36 +1301,6 @@ void dce4_wait_for_vblank(struct radeon_device *rdev, int crtc)
}
/**
- * radeon_irq_kms_pflip_irq_get - pre-pageflip callback.
- *
- * @rdev: radeon_device pointer
- * @crtc: crtc to prepare for pageflip on
- *
- * Pre-pageflip callback (evergreen+).
- * Enables the pageflip irq (vblank irq).
- */
-void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc)
-{
- /* enable the pflip int */
- radeon_irq_kms_pflip_irq_get(rdev, crtc);
-}
-
-/**
- * evergreen_post_page_flip - pos-pageflip callback.
- *
- * @rdev: radeon_device pointer
- * @crtc: crtc to cleanup pageflip on
- *
- * Post-pageflip callback (evergreen+).
- * Disables the pageflip irq (vblank irq).
- */
-void evergreen_post_page_flip(struct radeon_device *rdev, int crtc)
-{
- /* disable the pflip int */
- radeon_irq_kms_pflip_irq_put(rdev, crtc);
-}
-
-/**
* evergreen_page_flip - pageflip callback.
*
* @rdev: radeon_device pointer
@@ -1343,7 +1313,7 @@ void evergreen_post_page_flip(struct radeon_device *rdev, int crtc)
* double buffered update to take place.
* Returns the current update pending status.
*/
-u32 evergreen_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
+void evergreen_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
{
struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
u32 tmp = RREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset);
@@ -1375,9 +1345,23 @@ u32 evergreen_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
/* Unlock the lock, so double-buffering can take place inside vblank */
tmp &= ~EVERGREEN_GRPH_UPDATE_LOCK;
WREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset, tmp);
+}
+
+/**
+ * evergreen_page_flip_pending - check if page flip is still pending
+ *
+ * @rdev: radeon_device pointer
+ * @crtc_id: crtc to check
+ *
+ * Returns the current update pending status.
+ */
+bool evergreen_page_flip_pending(struct radeon_device *rdev, int crtc_id)
+{
+ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
/* Return current update_pending status: */
- return RREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset) & EVERGREEN_GRPH_SURFACE_UPDATE_PENDING;
+ return !!(RREG32(EVERGREEN_GRPH_UPDATE + radeon_crtc->crtc_offset) &
+ EVERGREEN_GRPH_SURFACE_UPDATE_PENDING);
}
/* get temperature in millidegrees */
@@ -3353,6 +3337,18 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
disabled_rb_mask &= ~(1 << i);
}
+ for (i = 0; i < rdev->config.evergreen.num_ses; i++) {
+ u32 simd_disable_bitmap;
+
+ WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i));
+ WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i));
+ simd_disable_bitmap = (RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffff0000) >> 16;
+ simd_disable_bitmap |= 0xffffffff << rdev->config.evergreen.max_simds;
+ tmp <<= 16;
+ tmp |= simd_disable_bitmap;
+ }
+ rdev->config.evergreen.active_simds = hweight32(~tmp);
+
WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES);
WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES);
@@ -4810,7 +4806,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[0]))
- radeon_crtc_handle_flip(rdev, 0);
+ radeon_crtc_handle_vblank(rdev, 0);
rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D1 vblank\n");
}
@@ -4836,7 +4832,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[1]))
- radeon_crtc_handle_flip(rdev, 1);
+ radeon_crtc_handle_vblank(rdev, 1);
rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D2 vblank\n");
}
@@ -4862,7 +4858,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[2]))
- radeon_crtc_handle_flip(rdev, 2);
+ radeon_crtc_handle_vblank(rdev, 2);
rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D3 vblank\n");
}
@@ -4888,7 +4884,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[3]))
- radeon_crtc_handle_flip(rdev, 3);
+ radeon_crtc_handle_vblank(rdev, 3);
rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D4 vblank\n");
}
@@ -4914,7 +4910,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[4]))
- radeon_crtc_handle_flip(rdev, 4);
+ radeon_crtc_handle_vblank(rdev, 4);
rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D5 vblank\n");
}
@@ -4940,7 +4936,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[5]))
- radeon_crtc_handle_flip(rdev, 5);
+ radeon_crtc_handle_vblank(rdev, 5);
rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D6 vblank\n");
}
diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c
index 05b0c95813fd..1ec0e6e83f9f 100644
--- a/drivers/gpu/drm/radeon/evergreen_hdmi.c
+++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c
@@ -293,10 +293,13 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
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;
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
struct hdmi_avi_infoframe frame;
uint32_t offset;
ssize_t err;
+ uint32_t val;
+ int bpc = 8;
if (!dig || !dig->afmt)
return;
@@ -306,6 +309,12 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
return;
offset = dig->afmt->offset;
+ /* hdmi deep color mode general control packets setup, if bpc > 8 */
+ if (encoder->crtc) {
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+ bpc = radeon_crtc->bpc;
+ }
+
/* disable audio prior to setting up hw */
if (ASIC_IS_DCE6(rdev)) {
dig->afmt->pin = dce6_audio_get_pin(rdev);
@@ -322,6 +331,35 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
WREG32(AFMT_AUDIO_CRC_CONTROL + offset, 0x1000);
+ val = RREG32(HDMI_CONTROL + offset);
+ val &= ~HDMI_DEEP_COLOR_ENABLE;
+ val &= ~HDMI_DEEP_COLOR_DEPTH_MASK;
+
+ switch (bpc) {
+ case 0:
+ case 6:
+ case 8:
+ case 16:
+ default:
+ DRM_DEBUG("%s: Disabling hdmi deep color for %d bpc.\n",
+ connector->name, bpc);
+ break;
+ case 10:
+ val |= HDMI_DEEP_COLOR_ENABLE;
+ val |= HDMI_DEEP_COLOR_DEPTH(HDMI_30BIT_DEEP_COLOR);
+ DRM_DEBUG("%s: Enabling hdmi deep color 30 for 10 bpc.\n",
+ connector->name);
+ break;
+ case 12:
+ val |= HDMI_DEEP_COLOR_ENABLE;
+ val |= HDMI_DEEP_COLOR_DEPTH(HDMI_36BIT_DEEP_COLOR);
+ DRM_DEBUG("%s: Enabling hdmi deep color 36 for 12 bpc.\n",
+ connector->name);
+ break;
+ }
+
+ WREG32(HDMI_CONTROL + offset, val);
+
WREG32(HDMI_VBI_PACKET_CONTROL + offset,
HDMI_NULL_SEND | /* send null packets when required */
HDMI_GC_SEND | /* send general control packets */
@@ -348,9 +386,13 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
/* fglrx clears sth in AFMT_AUDIO_PACKET_CONTROL2 here */
- WREG32(HDMI_ACR_PACKET_CONTROL + offset,
- HDMI_ACR_SOURCE | /* select SW CTS value */
- HDMI_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
+ if (bpc > 8)
+ WREG32(HDMI_ACR_PACKET_CONTROL + offset,
+ HDMI_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
+ else
+ WREG32(HDMI_ACR_PACKET_CONTROL + offset,
+ HDMI_ACR_SOURCE | /* select SW CTS value */
+ HDMI_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
evergreen_hdmi_update_ACR(encoder, mode->clock);
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index f9c7963b3ee6..b066d6711b8d 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -517,10 +517,11 @@
# define HDMI_ERROR_ACK (1 << 8)
# define HDMI_ERROR_MASK (1 << 9)
# define HDMI_DEEP_COLOR_ENABLE (1 << 24)
-# define HDMI_DEEP_COLOR_DEPTH (((x) & 3) << 28)
+# define HDMI_DEEP_COLOR_DEPTH(x) (((x) & 3) << 28)
# define HDMI_24BIT_DEEP_COLOR 0
# define HDMI_30BIT_DEEP_COLOR 1
# define HDMI_36BIT_DEEP_COLOR 2
+# define HDMI_DEEP_COLOR_DEPTH_MASK (3 << 28)
#define HDMI_STATUS 0x7034
# define HDMI_ACTIVE_AVMUTE (1 << 0)
# define HDMI_AUDIO_PACKET_ERROR (1 << 16)
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index d246e043421a..5a33ca681867 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -1057,6 +1057,18 @@ static void cayman_gpu_init(struct radeon_device *rdev)
disabled_rb_mask &= ~(1 << i);
}
+ for (i = 0; i < rdev->config.cayman.max_shader_engines; i++) {
+ u32 simd_disable_bitmap;
+
+ WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i));
+ WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i));
+ simd_disable_bitmap = (RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffff0000) >> 16;
+ simd_disable_bitmap |= 0xffffffff << rdev->config.cayman.max_simds_per_se;
+ tmp <<= 16;
+ tmp |= simd_disable_bitmap;
+ }
+ rdev->config.cayman.active_simds = hweight32(~tmp);
+
WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES);
WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES);
@@ -1228,12 +1240,14 @@ static int cayman_pcie_gart_enable(struct radeon_device *rdev)
SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU);
/* Setup L2 cache */
WREG32(VM_L2_CNTL, ENABLE_L2_CACHE |
+ ENABLE_L2_FRAGMENT_PROCESSING |
ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE |
ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE |
EFFECTIVE_L2_QUEUE_SIZE(7) |
CONTEXT1_IDENTITY_ACCESS_MODE(1));
WREG32(VM_L2_CNTL2, INVALIDATE_ALL_L1_TLBS | INVALIDATE_L2_CACHE);
WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY |
+ BANK_SELECT(6) |
L2_CACHE_BIGK_FRAGMENT_SIZE(6));
/* setup context0 */
WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12);
@@ -1266,6 +1280,7 @@ static int cayman_pcie_gart_enable(struct radeon_device *rdev)
(u32)(rdev->dummy_page.addr >> 12));
WREG32(VM_CONTEXT1_CNTL2, 4);
WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) |
+ PAGE_TABLE_BLOCK_SIZE(radeon_vm_block_size - 9) |
RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
RANGE_PROTECTION_FAULT_ENABLE_DEFAULT |
DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
@@ -1343,7 +1358,7 @@ void cayman_fence_ring_emit(struct radeon_device *rdev,
/* EVENT_WRITE_EOP - flush caches, send int */
radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT_TS) | EVENT_INDEX(5));
- radeon_ring_write(ring, addr & 0xffffffff);
+ radeon_ring_write(ring, lower_32_bits(addr));
radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2));
radeon_ring_write(ring, fence->seq);
radeon_ring_write(ring, 0);
diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h
index d996033c243e..2e12e4d69253 100644
--- a/drivers/gpu/drm/radeon/nid.h
+++ b/drivers/gpu/drm/radeon/nid.h
@@ -128,6 +128,7 @@
#define READ_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 16)
#define WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 18)
#define WRITE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 19)
+#define PAGE_TABLE_BLOCK_SIZE(x) (((x) & 0xF) << 24)
#define VM_CONTEXT1_CNTL 0x1414
#define VM_CONTEXT0_CNTL2 0x1430
#define VM_CONTEXT1_CNTL2 0x1434
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index b6c32640df20..1544efcf1c3a 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -142,36 +142,6 @@ void r100_wait_for_vblank(struct radeon_device *rdev, int crtc)
}
/**
- * r100_pre_page_flip - pre-pageflip callback.
- *
- * @rdev: radeon_device pointer
- * @crtc: crtc to prepare for pageflip on
- *
- * Pre-pageflip callback (r1xx-r4xx).
- * Enables the pageflip irq (vblank irq).
- */
-void r100_pre_page_flip(struct radeon_device *rdev, int crtc)
-{
- /* enable the pflip int */
- radeon_irq_kms_pflip_irq_get(rdev, crtc);
-}
-
-/**
- * r100_post_page_flip - pos-pageflip callback.
- *
- * @rdev: radeon_device pointer
- * @crtc: crtc to cleanup pageflip on
- *
- * Post-pageflip callback (r1xx-r4xx).
- * Disables the pageflip irq (vblank irq).
- */
-void r100_post_page_flip(struct radeon_device *rdev, int crtc)
-{
- /* disable the pflip int */
- radeon_irq_kms_pflip_irq_put(rdev, crtc);
-}
-
-/**
* r100_page_flip - pageflip callback.
*
* @rdev: radeon_device pointer
@@ -182,9 +152,8 @@ void r100_post_page_flip(struct radeon_device *rdev, int crtc)
* During vblank we take the crtc lock and wait for the update_pending
* bit to go high, when it does, we release the lock, and allow the
* double buffered update to take place.
- * Returns the current update pending status.
*/
-u32 r100_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
+void r100_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
{
struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
u32 tmp = ((u32)crtc_base) | RADEON_CRTC_OFFSET__OFFSET_LOCK;
@@ -206,8 +175,24 @@ u32 r100_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
tmp &= ~RADEON_CRTC_OFFSET__OFFSET_LOCK;
WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, tmp);
+}
+
+/**
+ * r100_page_flip_pending - check if page flip is still pending
+ *
+ * @rdev: radeon_device pointer
+ * @crtc_id: crtc to check
+ *
+ * Check if the last pagefilp is still pending (r1xx-r4xx).
+ * Returns the current update pending status.
+ */
+bool r100_page_flip_pending(struct radeon_device *rdev, int crtc_id)
+{
+ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
+
/* Return current update_pending status: */
- return RREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset) & RADEON_CRTC_OFFSET__GUI_TRIG_OFFSET;
+ return !!(RREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset) &
+ RADEON_CRTC_OFFSET__GUI_TRIG_OFFSET);
}
/**
@@ -697,15 +682,11 @@ void r100_pci_gart_disable(struct radeon_device *rdev)
WREG32(RADEON_AIC_HI_ADDR, 0);
}
-int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
+void r100_pci_gart_set_page(struct radeon_device *rdev, unsigned i,
+ uint64_t addr)
{
u32 *gtt = rdev->gart.ptr;
-
- if (i < 0 || i > rdev->gart.num_gpu_pages) {
- return -EINVAL;
- }
gtt[i] = cpu_to_le32(lower_32_bits(addr));
- return 0;
}
void r100_pci_gart_fini(struct radeon_device *rdev)
@@ -794,7 +775,7 @@ int r100_irq_process(struct radeon_device *rdev)
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[0]))
- radeon_crtc_handle_flip(rdev, 0);
+ radeon_crtc_handle_vblank(rdev, 0);
}
if (status & RADEON_CRTC2_VBLANK_STAT) {
if (rdev->irq.crtc_vblank_int[1]) {
@@ -803,7 +784,7 @@ int r100_irq_process(struct radeon_device *rdev)
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[1]))
- radeon_crtc_handle_flip(rdev, 1);
+ radeon_crtc_handle_vblank(rdev, 1);
}
if (status & RADEON_FP_DETECT_STAT) {
queue_hotplug = true;
diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
index 206caf9700b7..3c21d77a483d 100644
--- a/drivers/gpu/drm/radeon/r300.c
+++ b/drivers/gpu/drm/radeon/r300.c
@@ -72,13 +72,11 @@ void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev)
#define R300_PTE_WRITEABLE (1 << 2)
#define R300_PTE_READABLE (1 << 3)
-int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
+void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i,
+ uint64_t addr)
{
void __iomem *ptr = rdev->gart.ptr;
- if (i < 0 || i > rdev->gart.num_gpu_pages) {
- return -EINVAL;
- }
addr = (lower_32_bits(addr) >> 8) |
((upper_32_bits(addr) & 0xff) << 24) |
R300_PTE_WRITEABLE | R300_PTE_READABLE;
@@ -86,7 +84,6 @@ int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
* on powerpc without HW swappers, it'll get swapped on way
* into VRAM - so no need for cpu_to_le32 on VRAM tables */
writel(addr, ((void __iomem *)ptr) + (i * 4));
- return 0;
}
int rv370_pcie_gart_init(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index bbc189fd3ddc..c66952d4b00c 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -1958,6 +1958,9 @@ static void r600_gpu_init(struct radeon_device *rdev)
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 = (tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT;
@@ -2724,7 +2727,7 @@ void r600_fence_ring_emit(struct radeon_device *rdev,
/* EVENT_WRITE_EOP - flush caches, send int */
radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_EVENT_TS) | EVENT_INDEX(5));
- radeon_ring_write(ring, addr & 0xffffffff);
+ radeon_ring_write(ring, lower_32_bits(addr));
radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2));
radeon_ring_write(ring, fence->seq);
radeon_ring_write(ring, 0);
@@ -2763,7 +2766,7 @@ bool r600_semaphore_ring_emit(struct radeon_device *rdev,
sel |= PACKET3_SEM_WAIT_ON_SIGNAL;
radeon_ring_write(ring, PACKET3(PACKET3_MEM_SEMAPHORE, 1));
- radeon_ring_write(ring, addr & 0xffffffff);
+ radeon_ring_write(ring, lower_32_bits(addr));
radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | sel);
return true;
@@ -2824,9 +2827,9 @@ int r600_copy_cpdma(struct radeon_device *rdev,
if (size_in_bytes == 0)
tmp |= PACKET3_CP_DMA_CP_SYNC;
radeon_ring_write(ring, PACKET3(PACKET3_CP_DMA, 4));
- radeon_ring_write(ring, src_offset & 0xffffffff);
+ radeon_ring_write(ring, lower_32_bits(src_offset));
radeon_ring_write(ring, tmp);
- radeon_ring_write(ring, dst_offset & 0xffffffff);
+ radeon_ring_write(ring, lower_32_bits(dst_offset));
radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xff);
radeon_ring_write(ring, cur_size_in_bytes);
src_offset += cur_size_in_bytes;
@@ -3876,7 +3879,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[0]))
- radeon_crtc_handle_flip(rdev, 0);
+ radeon_crtc_handle_vblank(rdev, 0);
rdev->irq.stat_regs.r600.disp_int &= ~LB_D1_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D1 vblank\n");
}
@@ -3902,7 +3905,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[1]))
- radeon_crtc_handle_flip(rdev, 1);
+ radeon_crtc_handle_vblank(rdev, 1);
rdev->irq.stat_regs.r600.disp_int &= ~LB_D2_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D2 vblank\n");
}
diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c
index 85a2bb28aed2..26ef8ced6f89 100644
--- a/drivers/gpu/drm/radeon/r600_hdmi.c
+++ b/drivers/gpu/drm/radeon/r600_hdmi.c
@@ -133,7 +133,7 @@ struct radeon_hdmi_acr r600_hdmi_acr(uint32_t clock)
/*
* update the N and CTS parameters for a given pixel clock rate
*/
-static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock)
+void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock)
{
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
@@ -142,21 +142,33 @@ static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock)
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
uint32_t offset = dig->afmt->offset;
- WREG32(HDMI0_ACR_32_0 + offset, HDMI0_ACR_CTS_32(acr.cts_32khz));
- WREG32(HDMI0_ACR_32_1 + offset, acr.n_32khz);
-
- WREG32(HDMI0_ACR_44_0 + offset, HDMI0_ACR_CTS_44(acr.cts_44_1khz));
- WREG32(HDMI0_ACR_44_1 + offset, acr.n_44_1khz);
-
- WREG32(HDMI0_ACR_48_0 + offset, HDMI0_ACR_CTS_48(acr.cts_48khz));
- WREG32(HDMI0_ACR_48_1 + offset, acr.n_48khz);
+ WREG32_P(HDMI0_ACR_32_0 + offset,
+ HDMI0_ACR_CTS_32(acr.cts_32khz),
+ ~HDMI0_ACR_CTS_32_MASK);
+ WREG32_P(HDMI0_ACR_32_1 + offset,
+ HDMI0_ACR_N_32(acr.n_32khz),
+ ~HDMI0_ACR_N_32_MASK);
+
+ WREG32_P(HDMI0_ACR_44_0 + offset,
+ HDMI0_ACR_CTS_44(acr.cts_44_1khz),
+ ~HDMI0_ACR_CTS_44_MASK);
+ WREG32_P(HDMI0_ACR_44_1 + offset,
+ HDMI0_ACR_N_44(acr.n_44_1khz),
+ ~HDMI0_ACR_N_44_MASK);
+
+ WREG32_P(HDMI0_ACR_48_0 + offset,
+ HDMI0_ACR_CTS_48(acr.cts_48khz),
+ ~HDMI0_ACR_CTS_48_MASK);
+ WREG32_P(HDMI0_ACR_48_1 + offset,
+ HDMI0_ACR_N_48(acr.n_48khz),
+ ~HDMI0_ACR_N_48_MASK);
}
/*
* build a HDMI Video Info Frame
*/
-static void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder,
- void *buffer, size_t size)
+void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder, void *buffer,
+ size_t size)
{
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
@@ -231,7 +243,7 @@ int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder)
/*
* write the audio workaround status to the hardware
*/
-static void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
+void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
@@ -250,7 +262,7 @@ static void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
value, ~HDMI0_AUDIO_TEST_EN);
}
-static void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock)
+void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock)
{
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
@@ -320,121 +332,6 @@ static void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock)
}
}
-static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder)
-{
- struct radeon_device *rdev = encoder->dev->dev_private;
- struct drm_connector *connector;
- struct radeon_connector *radeon_connector = NULL;
- u32 tmp;
- u8 *sadb;
- int sad_count;
-
- list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder) {
- radeon_connector = to_radeon_connector(connector);
- break;
- }
- }
-
- if (!radeon_connector) {
- DRM_ERROR("Couldn't find encoder's connector\n");
- 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;
- }
-
- /* program the speaker allocation */
- tmp = RREG32(AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER);
- tmp &= ~(DP_CONNECTION | SPEAKER_ALLOCATION_MASK);
- /* set HDMI mode */
- tmp |= HDMI_CONNECTION;
- if (sad_count)
- tmp |= SPEAKER_ALLOCATION(sadb[0]);
- else
- tmp |= SPEAKER_ALLOCATION(5); /* stereo */
- WREG32(AZ_F0_CODEC_PIN0_CONTROL_CHANNEL_SPEAKER, tmp);
-
- kfree(sadb);
-}
-
-static void dce3_2_afmt_write_sad_regs(struct drm_encoder *encoder)
-{
- struct radeon_device *rdev = encoder->dev->dev_private;
- struct drm_connector *connector;
- struct radeon_connector *radeon_connector = NULL;
- struct cea_sad *sads;
- int i, sad_count;
-
- static const u16 eld_reg_to_type[][2] = {
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR0, HDMI_AUDIO_CODING_TYPE_PCM },
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR1, HDMI_AUDIO_CODING_TYPE_AC3 },
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR2, HDMI_AUDIO_CODING_TYPE_MPEG1 },
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR3, HDMI_AUDIO_CODING_TYPE_MP3 },
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR4, HDMI_AUDIO_CODING_TYPE_MPEG2 },
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR5, HDMI_AUDIO_CODING_TYPE_AAC_LC },
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR6, HDMI_AUDIO_CODING_TYPE_DTS },
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR7, HDMI_AUDIO_CODING_TYPE_ATRAC },
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR9, HDMI_AUDIO_CODING_TYPE_EAC3 },
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR10, HDMI_AUDIO_CODING_TYPE_DTS_HD },
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR11, HDMI_AUDIO_CODING_TYPE_MLP },
- { AZ_F0_CODEC_PIN0_CONTROL_AUDIO_DESCRIPTOR13, HDMI_AUDIO_CODING_TYPE_WMA_PRO },
- };
-
- list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) {
- if (connector->encoder == encoder) {
- radeon_connector = to_radeon_connector(connector);
- break;
- }
- }
-
- if (!radeon_connector) {
- DRM_ERROR("Couldn't find encoder's connector\n");
- return;
- }
-
- sad_count = drm_edid_to_sad(radeon_connector->edid, &sads);
- if (sad_count < 0) {
- DRM_ERROR("Couldn't read SADs: %d\n", sad_count);
- return;
- }
- BUG_ON(!sads);
-
- for (i = 0; i < ARRAY_SIZE(eld_reg_to_type); i++) {
- u32 value = 0;
- u8 stereo_freqs = 0;
- int max_channels = -1;
- int j;
-
- for (j = 0; j < sad_count; j++) {
- struct cea_sad *sad = &sads[j];
-
- if (sad->format == eld_reg_to_type[i][1]) {
- if (sad->channels > max_channels) {
- value = MAX_CHANNELS(sad->channels) |
- DESCRIPTOR_BYTE_2(sad->byte2) |
- SUPPORTED_FREQUENCIES(sad->freq);
- max_channels = sad->channels;
- }
-
- if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
- stereo_freqs |= sad->freq;
- else
- break;
- }
- }
-
- value |= SUPPORTED_FREQUENCIES_STEREO(stereo_freqs);
-
- WREG32(eld_reg_to_type[i][0], value);
- }
-
- kfree(sads);
-}
-
/*
* update the info frames with the data from the current display mode
*/
@@ -447,6 +344,7 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
struct hdmi_avi_infoframe frame;
uint32_t offset;
+ uint32_t acr_ctl;
ssize_t err;
if (!dig || !dig->afmt)
@@ -463,52 +361,44 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
r600_audio_set_dto(encoder, mode->clock);
- WREG32(HDMI0_VBI_PACKET_CONTROL + offset,
- HDMI0_NULL_SEND); /* send null packets when required */
-
- WREG32(HDMI0_AUDIO_CRC_CONTROL + offset, 0x1000);
-
- if (ASIC_IS_DCE32(rdev)) {
- WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset,
- HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */
- HDMI0_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */
- WREG32(AFMT_AUDIO_PACKET_CONTROL + offset,
- AFMT_AUDIO_SAMPLE_SEND | /* send audio packets */
- AFMT_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */
- } else {
- WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset,
- HDMI0_AUDIO_SAMPLE_SEND | /* send audio packets */
- HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */
- HDMI0_AUDIO_PACKETS_PER_LINE(3) | /* should be suffient for all audio modes and small enough for all hblanks */
- HDMI0_60958_CS_UPDATE); /* allow 60958 channel status fields to be updated */
- }
-
- if (ASIC_IS_DCE32(rdev)) {
- dce3_2_afmt_write_speaker_allocation(encoder);
- dce3_2_afmt_write_sad_regs(encoder);
- }
-
- WREG32(HDMI0_ACR_PACKET_CONTROL + offset,
- HDMI0_ACR_SOURCE | /* select SW CTS value - XXX verify that hw CTS works on all families */
- HDMI0_ACR_AUTO_SEND); /* allow hw to sent ACR packets when required */
-
- WREG32(HDMI0_VBI_PACKET_CONTROL + offset,
- HDMI0_NULL_SEND | /* send null packets when required */
- HDMI0_GC_SEND | /* send general control packets */
- HDMI0_GC_CONT); /* send general control packets every frame */
-
- /* TODO: HDMI0_AUDIO_INFO_UPDATE */
- WREG32(HDMI0_INFOFRAME_CONTROL0 + offset,
- HDMI0_AVI_INFO_SEND | /* enable AVI info frames */
- HDMI0_AVI_INFO_CONT | /* send AVI info frames every frame/field */
- HDMI0_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */
- HDMI0_AUDIO_INFO_CONT); /* send audio info frames every frame/field */
-
- WREG32(HDMI0_INFOFRAME_CONTROL1 + offset,
- HDMI0_AVI_INFO_LINE(2) | /* anything other than 0 */
- HDMI0_AUDIO_INFO_LINE(2)); /* anything other than 0 */
-
- WREG32(HDMI0_GC + offset, 0); /* unset HDMI0_GC_AVMUTE */
+ WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset,
+ HDMI0_AUDIO_SAMPLE_SEND | /* send audio packets */
+ HDMI0_AUDIO_DELAY_EN(1) | /* default audio delay */
+ HDMI0_AUDIO_PACKETS_PER_LINE(3) | /* should be suffient for all audio modes and small enough for all hblanks */
+ HDMI0_60958_CS_UPDATE, /* allow 60958 channel status fields to be updated */
+ ~(HDMI0_AUDIO_SAMPLE_SEND |
+ HDMI0_AUDIO_DELAY_EN_MASK |
+ HDMI0_AUDIO_PACKETS_PER_LINE_MASK |
+ HDMI0_60958_CS_UPDATE));
+
+ /* DCE 3.0 uses register that's normally for CRC_CONTROL */
+ acr_ctl = ASIC_IS_DCE3(rdev) ? DCE3_HDMI0_ACR_PACKET_CONTROL :
+ HDMI0_ACR_PACKET_CONTROL;
+ WREG32_P(acr_ctl + offset,
+ HDMI0_ACR_SOURCE | /* select SW CTS value - XXX verify that hw CTS works on all families */
+ HDMI0_ACR_AUTO_SEND, /* allow hw to sent ACR packets when required */
+ ~(HDMI0_ACR_SOURCE |
+ HDMI0_ACR_AUTO_SEND));
+
+ WREG32_OR(HDMI0_VBI_PACKET_CONTROL + offset,
+ HDMI0_NULL_SEND | /* send null packets when required */
+ HDMI0_GC_SEND | /* send general control packets */
+ HDMI0_GC_CONT); /* send general control packets every frame */
+
+ WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset,
+ HDMI0_AVI_INFO_SEND | /* enable AVI info frames */
+ HDMI0_AVI_INFO_CONT | /* send AVI info frames every frame/field */
+ HDMI0_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */
+ HDMI0_AUDIO_INFO_UPDATE); /* required for audio info values to be updated */
+
+ WREG32_P(HDMI0_INFOFRAME_CONTROL1 + offset,
+ HDMI0_AVI_INFO_LINE(2) | /* anything other than 0 */
+ HDMI0_AUDIO_INFO_LINE(2), /* anything other than 0 */
+ ~(HDMI0_AVI_INFO_LINE_MASK |
+ HDMI0_AUDIO_INFO_LINE_MASK));
+
+ WREG32_AND(HDMI0_GC + offset,
+ ~HDMI0_GC_AVMUTE); /* unset HDMI0_GC_AVMUTE */
err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
if (err < 0) {
@@ -523,22 +413,45 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
}
r600_hdmi_update_avi_infoframe(encoder, buffer, sizeof(buffer));
+
+ /* fglrx duplicates INFOFRAME_CONTROL0 & INFOFRAME_CONTROL1 ops here */
+
+ WREG32_AND(HDMI0_GENERIC_PACKET_CONTROL + offset,
+ ~(HDMI0_GENERIC0_SEND |
+ HDMI0_GENERIC0_CONT |
+ HDMI0_GENERIC0_UPDATE |
+ HDMI0_GENERIC1_SEND |
+ HDMI0_GENERIC1_CONT |
+ HDMI0_GENERIC0_LINE_MASK |
+ HDMI0_GENERIC1_LINE_MASK));
+
r600_hdmi_update_ACR(encoder, mode->clock);
+ WREG32_P(HDMI0_60958_0 + offset,
+ HDMI0_60958_CS_CHANNEL_NUMBER_L(1),
+ ~(HDMI0_60958_CS_CHANNEL_NUMBER_L_MASK |
+ HDMI0_60958_CS_CLOCK_ACCURACY_MASK));
+
+ WREG32_P(HDMI0_60958_1 + offset,
+ HDMI0_60958_CS_CHANNEL_NUMBER_R(2),
+ ~HDMI0_60958_CS_CHANNEL_NUMBER_R_MASK);
+
/* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */
WREG32(HDMI0_RAMP_CONTROL0 + offset, 0x00FFFFFF);
WREG32(HDMI0_RAMP_CONTROL1 + offset, 0x007FFFFF);
WREG32(HDMI0_RAMP_CONTROL2 + offset, 0x00000001);
WREG32(HDMI0_RAMP_CONTROL3 + offset, 0x00000001);
- r600_hdmi_audio_workaround(encoder);
-
/* enable audio after to setting up hw */
r600_audio_enable(rdev, dig->afmt->pin, true);
}
-/*
- * update settings with current parameters from audio engine
+/**
+ * r600_hdmi_update_audio_settings - Update audio infoframe
+ *
+ * @encoder: drm encoder
+ *
+ * Gets info about current audio stream and updates audio infoframe.
*/
void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
{
@@ -550,7 +463,7 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
struct hdmi_audio_infoframe frame;
uint32_t offset;
- uint32_t iec;
+ uint32_t value;
ssize_t err;
if (!dig->afmt || !dig->afmt->enabled)
@@ -563,60 +476,6 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
DRM_DEBUG("0x%02X IEC60958 status bits and 0x%02X category code\n",
(int)audio.status_bits, (int)audio.category_code);
- iec = 0;
- if (audio.status_bits & AUDIO_STATUS_PROFESSIONAL)
- iec |= 1 << 0;
- if (audio.status_bits & AUDIO_STATUS_NONAUDIO)
- iec |= 1 << 1;
- if (audio.status_bits & AUDIO_STATUS_COPYRIGHT)
- iec |= 1 << 2;
- if (audio.status_bits & AUDIO_STATUS_EMPHASIS)
- iec |= 1 << 3;
-
- iec |= HDMI0_60958_CS_CATEGORY_CODE(audio.category_code);
-
- switch (audio.rate) {
- case 32000:
- iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x3);
- break;
- case 44100:
- iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x0);
- break;
- case 48000:
- iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x2);
- break;
- case 88200:
- iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0x8);
- break;
- case 96000:
- iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xa);
- break;
- case 176400:
- iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xc);
- break;
- case 192000:
- iec |= HDMI0_60958_CS_SAMPLING_FREQUENCY(0xe);
- break;
- }
-
- WREG32(HDMI0_60958_0 + offset, iec);
-
- iec = 0;
- switch (audio.bits_per_sample) {
- case 16:
- iec |= HDMI0_60958_CS_WORD_LENGTH(0x2);
- break;
- case 20:
- iec |= HDMI0_60958_CS_WORD_LENGTH(0x3);
- break;
- case 24:
- iec |= HDMI0_60958_CS_WORD_LENGTH(0xb);
- break;
- }
- if (audio.status_bits & AUDIO_STATUS_V)
- iec |= 0x5 << 16;
- WREG32_P(HDMI0_60958_1 + offset, iec, ~0x5000f);
-
err = hdmi_audio_infoframe_init(&frame);
if (err < 0) {
DRM_ERROR("failed to setup audio infoframe\n");
@@ -631,8 +490,22 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
return;
}
+ value = RREG32(HDMI0_AUDIO_PACKET_CONTROL + offset);
+ if (value & HDMI0_AUDIO_TEST_EN)
+ WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset,
+ value & ~HDMI0_AUDIO_TEST_EN);
+
+ WREG32_OR(HDMI0_CONTROL + offset,
+ HDMI0_ERROR_ACK);
+
+ WREG32_AND(HDMI0_INFOFRAME_CONTROL0 + offset,
+ ~HDMI0_AUDIO_INFO_SOURCE);
+
r600_hdmi_update_audio_infoframe(encoder, buffer, sizeof(buffer));
- r600_hdmi_audio_workaround(encoder);
+
+ WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset,
+ HDMI0_AUDIO_INFO_CONT |
+ HDMI0_AUDIO_INFO_UPDATE);
}
/*
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index 37455f65107f..f94e7a9afe75 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -1029,15 +1029,18 @@
#define HDMI0_AUDIO_PACKET_CONTROL 0x7408
# define HDMI0_AUDIO_SAMPLE_SEND (1 << 0)
# define HDMI0_AUDIO_DELAY_EN(x) (((x) & 3) << 4)
+# define HDMI0_AUDIO_DELAY_EN_MASK (3 << 4)
# define HDMI0_AUDIO_SEND_MAX_PACKETS (1 << 8)
# define HDMI0_AUDIO_TEST_EN (1 << 12)
# define HDMI0_AUDIO_PACKETS_PER_LINE(x) (((x) & 0x1f) << 16)
+# define HDMI0_AUDIO_PACKETS_PER_LINE_MASK (0x1f << 16)
# define HDMI0_AUDIO_CHANNEL_SWAP (1 << 24)
# define HDMI0_60958_CS_UPDATE (1 << 26)
# define HDMI0_AZ_FORMAT_WTRIG_MASK (1 << 28)
# define HDMI0_AZ_FORMAT_WTRIG_ACK (1 << 29)
#define HDMI0_AUDIO_CRC_CONTROL 0x740c
# define HDMI0_AUDIO_CRC_EN (1 << 0)
+#define DCE3_HDMI0_ACR_PACKET_CONTROL 0x740c
#define HDMI0_VBI_PACKET_CONTROL 0x7410
# define HDMI0_NULL_SEND (1 << 0)
# define HDMI0_GC_SEND (1 << 4)
@@ -1054,7 +1057,9 @@
# define HDMI0_MPEG_INFO_UPDATE (1 << 10)
#define HDMI0_INFOFRAME_CONTROL1 0x7418
# define HDMI0_AVI_INFO_LINE(x) (((x) & 0x3f) << 0)
+# define HDMI0_AVI_INFO_LINE_MASK (0x3f << 0)
# define HDMI0_AUDIO_INFO_LINE(x) (((x) & 0x3f) << 8)
+# define HDMI0_AUDIO_INFO_LINE_MASK (0x3f << 8)
# define HDMI0_MPEG_INFO_LINE(x) (((x) & 0x3f) << 16)
#define HDMI0_GENERIC_PACKET_CONTROL 0x741c
# define HDMI0_GENERIC0_SEND (1 << 0)
@@ -1063,7 +1068,9 @@
# define HDMI0_GENERIC1_SEND (1 << 4)
# define HDMI0_GENERIC1_CONT (1 << 5)
# define HDMI0_GENERIC0_LINE(x) (((x) & 0x3f) << 16)
+# define HDMI0_GENERIC0_LINE_MASK (0x3f << 16)
# define HDMI0_GENERIC1_LINE(x) (((x) & 0x3f) << 24)
+# define HDMI0_GENERIC1_LINE_MASK (0x3f << 24)
#define HDMI0_GC 0x7428
# define HDMI0_GC_AVMUTE (1 << 0)
#define HDMI0_AVI_INFO0 0x7454
@@ -1119,16 +1126,22 @@
#define HDMI0_GENERIC1_6 0x74a8
#define HDMI0_ACR_32_0 0x74ac
# define HDMI0_ACR_CTS_32(x) (((x) & 0xfffff) << 12)
+# define HDMI0_ACR_CTS_32_MASK (0xfffff << 12)
#define HDMI0_ACR_32_1 0x74b0
# define HDMI0_ACR_N_32(x) (((x) & 0xfffff) << 0)
+# define HDMI0_ACR_N_32_MASK (0xfffff << 0)
#define HDMI0_ACR_44_0 0x74b4
# define HDMI0_ACR_CTS_44(x) (((x) & 0xfffff) << 12)
+# define HDMI0_ACR_CTS_44_MASK (0xfffff << 12)
#define HDMI0_ACR_44_1 0x74b8
# define HDMI0_ACR_N_44(x) (((x) & 0xfffff) << 0)
+# define HDMI0_ACR_N_44_MASK (0xfffff << 0)
#define HDMI0_ACR_48_0 0x74bc
# define HDMI0_ACR_CTS_48(x) (((x) & 0xfffff) << 12)
+# define HDMI0_ACR_CTS_48_MASK (0xfffff << 12)
#define HDMI0_ACR_48_1 0x74c0
# define HDMI0_ACR_N_48(x) (((x) & 0xfffff) << 0)
+# define HDMI0_ACR_N_48_MASK (0xfffff << 0)
#define HDMI0_ACR_STATUS_0 0x74c4
#define HDMI0_ACR_STATUS_1 0x74c8
#define HDMI0_AUDIO_INFO0 0x74cc
@@ -1148,14 +1161,17 @@
# define HDMI0_60958_CS_CATEGORY_CODE(x) (((x) & 0xff) << 8)
# define HDMI0_60958_CS_SOURCE_NUMBER(x) (((x) & 0xf) << 16)
# define HDMI0_60958_CS_CHANNEL_NUMBER_L(x) (((x) & 0xf) << 20)
+# define HDMI0_60958_CS_CHANNEL_NUMBER_L_MASK (0xf << 20)
# define HDMI0_60958_CS_SAMPLING_FREQUENCY(x) (((x) & 0xf) << 24)
# define HDMI0_60958_CS_CLOCK_ACCURACY(x) (((x) & 3) << 28)
+# define HDMI0_60958_CS_CLOCK_ACCURACY_MASK (3 << 28)
#define HDMI0_60958_1 0x74d8
# define HDMI0_60958_CS_WORD_LENGTH(x) (((x) & 0xf) << 0)
# define HDMI0_60958_CS_ORIGINAL_SAMPLING_FREQUENCY(x) (((x) & 0xf) << 4)
# define HDMI0_60958_CS_VALID_L(x) (((x) & 1) << 16)
# define HDMI0_60958_CS_VALID_R(x) (((x) & 1) << 18)
# define HDMI0_60958_CS_CHANNEL_NUMBER_R(x) (((x) & 0xf) << 20)
+# define HDMI0_60958_CS_CHANNEL_NUMBER_R_MASK (0xf << 20)
#define HDMI0_ACR_PACKET_CONTROL 0x74dc
# define HDMI0_ACR_SEND (1 << 0)
# define HDMI0_ACR_CONT (1 << 1)
@@ -1166,6 +1182,7 @@
# define HDMI0_ACR_48 3
# define HDMI0_ACR_SOURCE (1 << 8) /* 0 - hw; 1 - cts value */
# define HDMI0_ACR_AUTO_SEND (1 << 12)
+#define DCE3_HDMI0_AUDIO_CRC_CONTROL 0x74dc
#define HDMI0_RAMP_CONTROL0 0x74e0
# define HDMI0_RAMP_MAX_COUNT(x) (((x) & 0xffffff) << 0)
#define HDMI0_RAMP_CONTROL1 0x74e4
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 8149e7cf4303..4b0bbf88d5c0 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -100,6 +100,8 @@ extern int radeon_dpm;
extern int radeon_aspm;
extern int radeon_runtime_pm;
extern int radeon_hard_reset;
+extern int radeon_vm_size;
+extern int radeon_vm_block_size;
/*
* Copy from radeon_drv.h so we don't have to include both and have conflicting
@@ -676,14 +678,16 @@ void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell);
* IRQS.
*/
-struct radeon_unpin_work {
- struct work_struct work;
- struct radeon_device *rdev;
- int crtc_id;
- struct radeon_fence *fence;
+struct radeon_flip_work {
+ struct work_struct flip_work;
+ struct work_struct unpin_work;
+ struct radeon_device *rdev;
+ int crtc_id;
+ struct drm_framebuffer *fb;
struct drm_pending_vblank_event *event;
- struct radeon_bo *old_rbo;
- u64 new_crtc_base;
+ struct radeon_bo *old_rbo;
+ struct radeon_bo *new_rbo;
+ struct radeon_fence *fence;
};
struct r500_irq_stat_regs {
@@ -835,13 +839,8 @@ struct radeon_mec {
/* maximum number of VMIDs */
#define RADEON_NUM_VM 16
-/* defines number of bits in page table versus page directory,
- * a page is 4KB so we have 12 bits offset, 9 bits in the page
- * table and the remaining 19 bits are in the page directory */
-#define RADEON_VM_BLOCK_SIZE 9
-
/* number of entries in page table */
-#define RADEON_VM_PTE_COUNT (1 << RADEON_VM_BLOCK_SIZE)
+#define RADEON_VM_PTE_COUNT (1 << radeon_vm_block_size)
/* PTBs (Page Table Blocks) need to be aligned to 32K */
#define RADEON_VM_PTB_ALIGN_SIZE 32768
@@ -854,6 +853,15 @@ struct radeon_mec {
#define R600_PTE_READABLE (1 << 5)
#define R600_PTE_WRITEABLE (1 << 6)
+/* PTE (Page Table Entry) fragment field for different page sizes */
+#define R600_PTE_FRAG_4KB (0 << 7)
+#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)
+
struct radeon_vm_pt {
struct radeon_bo *bo;
uint64_t addr;
@@ -986,8 +994,8 @@ struct radeon_cs_reloc {
struct radeon_bo *robj;
struct ttm_validate_buffer tv;
uint64_t gpu_offset;
- unsigned domain;
- unsigned alt_domain;
+ unsigned prefered_domains;
+ unsigned allowed_domains;
uint32_t tiling_flags;
uint32_t handle;
};
@@ -1771,7 +1779,8 @@ struct radeon_asic {
/* gart */
struct {
void (*tlb_flush)(struct radeon_device *rdev);
- int (*set_page)(struct radeon_device *rdev, int i, uint64_t addr);
+ void (*set_page)(struct radeon_device *rdev, unsigned i,
+ uint64_t addr);
} gart;
struct {
int (*init)(struct radeon_device *rdev);
@@ -1883,9 +1892,8 @@ struct radeon_asic {
} dpm;
/* pageflipping */
struct {
- void (*pre_page_flip)(struct radeon_device *rdev, int crtc);
- u32 (*page_flip)(struct radeon_device *rdev, int crtc, u64 crtc_base);
- void (*post_page_flip)(struct radeon_device *rdev, int crtc);
+ void (*page_flip)(struct radeon_device *rdev, int crtc, u64 crtc_base);
+ bool (*page_flip_pending)(struct radeon_device *rdev, int crtc);
} pflip;
};
@@ -1924,6 +1932,7 @@ struct r600_asic {
unsigned tiling_group_size;
unsigned tile_config;
unsigned backend_map;
+ unsigned active_simds;
};
struct rv770_asic {
@@ -1949,6 +1958,7 @@ struct rv770_asic {
unsigned tiling_group_size;
unsigned tile_config;
unsigned backend_map;
+ unsigned active_simds;
};
struct evergreen_asic {
@@ -1975,6 +1985,7 @@ struct evergreen_asic {
unsigned tiling_group_size;
unsigned tile_config;
unsigned backend_map;
+ unsigned active_simds;
};
struct cayman_asic {
@@ -2013,6 +2024,7 @@ struct cayman_asic {
unsigned multi_gpu_tile_size;
unsigned tile_config;
+ unsigned active_simds;
};
struct si_asic {
@@ -2043,6 +2055,7 @@ struct si_asic {
unsigned tile_config;
uint32_t tile_mode_array[32];
+ uint32_t active_cus;
};
struct cik_asic {
@@ -2074,6 +2087,7 @@ struct cik_asic {
unsigned tile_config;
uint32_t tile_mode_array[32];
uint32_t macrotile_mode_array[16];
+ uint32_t active_cus;
};
union radeon_asic_config {
@@ -2745,9 +2759,8 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
#define radeon_pm_finish(rdev) (rdev)->asic->pm.finish((rdev))
#define radeon_pm_init_profile(rdev) (rdev)->asic->pm.init_profile((rdev))
#define radeon_pm_get_dynpm_state(rdev) (rdev)->asic->pm.get_dynpm_state((rdev))
-#define radeon_pre_page_flip(rdev, crtc) (rdev)->asic->pflip.pre_page_flip((rdev), (crtc))
#define radeon_page_flip(rdev, crtc, base) (rdev)->asic->pflip.page_flip((rdev), (crtc), (base))
-#define radeon_post_page_flip(rdev, crtc) (rdev)->asic->pflip.post_page_flip((rdev), (crtc))
+#define radeon_page_flip_pending(rdev, crtc) (rdev)->asic->pflip.page_flip_pending((rdev), (crtc))
#define radeon_wait_for_vblank(rdev, crtc) (rdev)->asic->display.wait_for_vblank((rdev), (crtc))
#define radeon_mc_wait_for_idle(rdev) (rdev)->asic->mc_wait_for_idle((rdev))
#define radeon_get_xclk(rdev) (rdev)->asic->get_xclk((rdev))
diff --git a/drivers/gpu/drm/radeon/radeon_agp.c b/drivers/gpu/drm/radeon/radeon_agp.c
index 42433344cb1b..a9297b2c3524 100644
--- a/drivers/gpu/drm/radeon/radeon_agp.c
+++ b/drivers/gpu/drm/radeon/radeon_agp.c
@@ -117,9 +117,6 @@ static struct radeon_agpmode_quirk radeon_agpmode_quirk_list[] = {
/* ATI Host Bridge / RV280 [M9+] Needs AGPMode 1 (phoronix forum) */
{ PCI_VENDOR_ID_ATI, 0xcbb2, PCI_VENDOR_ID_ATI, 0x5c61,
PCI_VENDOR_ID_SONY, 0x8175, 1},
- /* HP Host Bridge / R300 [FireGL X1] Needs AGPMode 2 (fdo #7770) */
- { PCI_VENDOR_ID_HP, 0x122e, PCI_VENDOR_ID_ATI, 0x4e47,
- PCI_VENDOR_ID_ATI, 0x0152, 2},
{ 0, 0, 0, 0, 0, 0, 0 },
};
#endif
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index e5f0177bea1e..34b9aa9e3c06 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -248,9 +248,8 @@ static struct radeon_asic r100_asic = {
.set_clock_gating = &radeon_legacy_set_clock_gating,
},
.pflip = {
- .pre_page_flip = &r100_pre_page_flip,
.page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .page_flip_pending = &r100_page_flip_pending,
},
};
@@ -315,9 +314,8 @@ static struct radeon_asic r200_asic = {
.set_clock_gating = &radeon_legacy_set_clock_gating,
},
.pflip = {
- .pre_page_flip = &r100_pre_page_flip,
.page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .page_flip_pending = &r100_page_flip_pending,
},
};
@@ -396,9 +394,8 @@ static struct radeon_asic r300_asic = {
.set_clock_gating = &radeon_legacy_set_clock_gating,
},
.pflip = {
- .pre_page_flip = &r100_pre_page_flip,
.page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .page_flip_pending = &r100_page_flip_pending,
},
};
@@ -463,9 +460,8 @@ static struct radeon_asic r300_asic_pcie = {
.set_clock_gating = &radeon_legacy_set_clock_gating,
},
.pflip = {
- .pre_page_flip = &r100_pre_page_flip,
.page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .page_flip_pending = &r100_page_flip_pending,
},
};
@@ -530,9 +526,8 @@ static struct radeon_asic r420_asic = {
.set_clock_gating = &radeon_atom_set_clock_gating,
},
.pflip = {
- .pre_page_flip = &r100_pre_page_flip,
.page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .page_flip_pending = &r100_page_flip_pending,
},
};
@@ -597,9 +592,8 @@ static struct radeon_asic rs400_asic = {
.set_clock_gating = &radeon_legacy_set_clock_gating,
},
.pflip = {
- .pre_page_flip = &r100_pre_page_flip,
.page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .page_flip_pending = &r100_page_flip_pending,
},
};
@@ -666,9 +660,8 @@ static struct radeon_asic rs600_asic = {
.set_clock_gating = &radeon_atom_set_clock_gating,
},
.pflip = {
- .pre_page_flip = &rs600_pre_page_flip,
.page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .page_flip_pending = &rs600_page_flip_pending,
},
};
@@ -735,9 +728,8 @@ static struct radeon_asic rs690_asic = {
.set_clock_gating = &radeon_atom_set_clock_gating,
},
.pflip = {
- .pre_page_flip = &rs600_pre_page_flip,
.page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .page_flip_pending = &rs600_page_flip_pending,
},
};
@@ -802,9 +794,8 @@ static struct radeon_asic rv515_asic = {
.set_clock_gating = &radeon_atom_set_clock_gating,
},
.pflip = {
- .pre_page_flip = &rs600_pre_page_flip,
.page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .page_flip_pending = &rs600_page_flip_pending,
},
};
@@ -869,9 +860,8 @@ static struct radeon_asic r520_asic = {
.set_clock_gating = &radeon_atom_set_clock_gating,
},
.pflip = {
- .pre_page_flip = &rs600_pre_page_flip,
.page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .page_flip_pending = &rs600_page_flip_pending,
},
};
@@ -968,9 +958,8 @@ static struct radeon_asic r600_asic = {
.get_temperature = &rv6xx_get_temp,
},
.pflip = {
- .pre_page_flip = &rs600_pre_page_flip,
.page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .page_flip_pending = &rs600_page_flip_pending,
},
};
@@ -1059,9 +1048,8 @@ static struct radeon_asic rv6xx_asic = {
.force_performance_level = &rv6xx_dpm_force_performance_level,
},
.pflip = {
- .pre_page_flip = &rs600_pre_page_flip,
.page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .page_flip_pending = &rs600_page_flip_pending,
},
};
@@ -1150,9 +1138,8 @@ static struct radeon_asic rs780_asic = {
.force_performance_level = &rs780_dpm_force_performance_level,
},
.pflip = {
- .pre_page_flip = &rs600_pre_page_flip,
.page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .page_flip_pending = &rs600_page_flip_pending,
},
};
@@ -1201,7 +1188,7 @@ static struct radeon_asic rv770_asic = {
.set_backlight_level = &atombios_set_backlight_level,
.get_backlight_level = &atombios_get_backlight_level,
.hdmi_enable = &r600_hdmi_enable,
- .hdmi_setmode = &r600_hdmi_setmode,
+ .hdmi_setmode = &dce3_1_hdmi_setmode,
},
.copy = {
.blit = &r600_copy_cpdma,
@@ -1256,9 +1243,8 @@ static struct radeon_asic rv770_asic = {
.vblank_too_short = &rv770_dpm_vblank_too_short,
},
.pflip = {
- .pre_page_flip = &rs600_pre_page_flip,
.page_flip = &rv770_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .page_flip_pending = &rv770_page_flip_pending,
},
};
@@ -1375,9 +1361,8 @@ static struct radeon_asic evergreen_asic = {
.vblank_too_short = &cypress_dpm_vblank_too_short,
},
.pflip = {
- .pre_page_flip = &evergreen_pre_page_flip,
.page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
+ .page_flip_pending = &evergreen_page_flip_pending,
},
};
@@ -1467,9 +1452,8 @@ static struct radeon_asic sumo_asic = {
.force_performance_level = &sumo_dpm_force_performance_level,
},
.pflip = {
- .pre_page_flip = &evergreen_pre_page_flip,
.page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
+ .page_flip_pending = &evergreen_page_flip_pending,
},
};
@@ -1560,9 +1544,8 @@ static struct radeon_asic btc_asic = {
.vblank_too_short = &btc_dpm_vblank_too_short,
},
.pflip = {
- .pre_page_flip = &evergreen_pre_page_flip,
.page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
+ .page_flip_pending = &evergreen_page_flip_pending,
},
};
@@ -1704,9 +1687,8 @@ static struct radeon_asic cayman_asic = {
.vblank_too_short = &ni_dpm_vblank_too_short,
},
.pflip = {
- .pre_page_flip = &evergreen_pre_page_flip,
.page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
+ .page_flip_pending = &evergreen_page_flip_pending,
},
};
@@ -1805,9 +1787,8 @@ static struct radeon_asic trinity_asic = {
.enable_bapm = &trinity_dpm_enable_bapm,
},
.pflip = {
- .pre_page_flip = &evergreen_pre_page_flip,
.page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
+ .page_flip_pending = &evergreen_page_flip_pending,
},
};
@@ -1936,9 +1917,8 @@ static struct radeon_asic si_asic = {
.vblank_too_short = &ni_dpm_vblank_too_short,
},
.pflip = {
- .pre_page_flip = &evergreen_pre_page_flip,
.page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
+ .page_flip_pending = &evergreen_page_flip_pending,
},
};
@@ -2049,8 +2029,8 @@ static struct radeon_asic ci_asic = {
.blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
.dma = &cik_copy_dma,
.dma_ring_index = R600_RING_TYPE_DMA_INDEX,
- .copy = &cik_copy_cpdma,
- .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &cik_copy_dma,
+ .copy_ring_index = R600_RING_TYPE_DMA_INDEX,
},
.surface = {
.set_reg = r600_set_surface_reg,
@@ -2099,9 +2079,8 @@ static struct radeon_asic ci_asic = {
.powergate_uvd = &ci_dpm_powergate_uvd,
},
.pflip = {
- .pre_page_flip = &evergreen_pre_page_flip,
.page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
+ .page_flip_pending = &evergreen_page_flip_pending,
},
};
@@ -2204,9 +2183,8 @@ static struct radeon_asic kv_asic = {
.enable_bapm = &kv_dpm_enable_bapm,
},
.pflip = {
- .pre_page_flip = &evergreen_pre_page_flip,
.page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
+ .page_flip_pending = &evergreen_page_flip_pending,
},
};
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index 3d55a3a39e82..01e7c0ad8f01 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -67,7 +67,8 @@ bool r100_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp);
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);
-int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
+void r100_pci_gart_set_page(struct radeon_device *rdev, unsigned i,
+ uint64_t addr);
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);
@@ -135,9 +136,9 @@ extern void r100_pm_prepare(struct radeon_device *rdev);
extern void r100_pm_finish(struct radeon_device *rdev);
extern void r100_pm_init_profile(struct radeon_device *rdev);
extern void r100_pm_get_dynpm_state(struct radeon_device *rdev);
-extern void r100_pre_page_flip(struct radeon_device *rdev, int crtc);
-extern u32 r100_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
-extern void r100_post_page_flip(struct radeon_device *rdev, int crtc);
+extern void r100_page_flip(struct radeon_device *rdev, int crtc,
+ u64 crtc_base);
+extern bool r100_page_flip_pending(struct radeon_device *rdev, int crtc);
extern void r100_wait_for_vblank(struct radeon_device *rdev, int crtc);
extern int r100_mc_wait_for_idle(struct radeon_device *rdev);
@@ -171,7 +172,8 @@ extern void r300_fence_ring_emit(struct radeon_device *rdev,
struct radeon_fence *fence);
extern int r300_cs_parse(struct radeon_cs_parser *p);
extern void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev);
-extern int rv370_pcie_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
+extern void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i,
+ uint64_t addr);
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);
@@ -206,7 +208,8 @@ extern void rs400_fini(struct radeon_device *rdev);
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);
-int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
+void rs400_gart_set_page(struct radeon_device *rdev, unsigned i,
+ uint64_t addr);
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);
@@ -229,7 +232,8 @@ int rs600_irq_process(struct radeon_device *rdev);
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);
-int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
+void rs600_gart_set_page(struct radeon_device *rdev, unsigned i,
+ uint64_t addr);
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);
@@ -241,9 +245,9 @@ void rs600_hpd_set_polarity(struct radeon_device *rdev,
extern void rs600_pm_misc(struct radeon_device *rdev);
extern void rs600_pm_prepare(struct radeon_device *rdev);
extern void rs600_pm_finish(struct radeon_device *rdev);
-extern void rs600_pre_page_flip(struct radeon_device *rdev, int crtc);
-extern u32 rs600_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
-extern void rs600_post_page_flip(struct radeon_device *rdev, int crtc);
+extern void rs600_page_flip(struct radeon_device *rdev, int crtc,
+ u64 crtc_base);
+extern bool rs600_page_flip_pending(struct radeon_device *rdev, int crtc);
void rs600_set_safe_registers(struct radeon_device *rdev);
extern void avivo_wait_for_vblank(struct radeon_device *rdev, int crtc);
extern int rs600_mc_wait_for_idle(struct radeon_device *rdev);
@@ -387,6 +391,11 @@ void r600_rlc_stop(struct radeon_device *rdev);
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,
+ size_t size);
+void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock);
+void r600_hdmi_audio_workaround(struct drm_encoder *encoder);
int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder);
void r600_hdmi_update_audio_settings(struct drm_encoder *encoder);
void r600_hdmi_enable(struct drm_encoder *encoder, bool enable);
@@ -447,7 +456,8 @@ void rv770_fini(struct radeon_device *rdev);
int rv770_suspend(struct radeon_device *rdev);
int rv770_resume(struct radeon_device *rdev);
void rv770_pm_misc(struct radeon_device *rdev);
-u32 rv770_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
+void rv770_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
+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);
@@ -458,6 +468,8 @@ int rv770_copy_dma(struct radeon_device *rdev,
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);
+/* hdmi */
+void dce3_1_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode);
/* rv7xx pm */
int rv770_dpm_init(struct radeon_device *rdev);
int rv770_dpm_enable(struct radeon_device *rdev);
@@ -513,9 +525,9 @@ extern void sumo_pm_init_profile(struct radeon_device *rdev);
extern void btc_pm_init_profile(struct radeon_device *rdev);
int sumo_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
-extern void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc);
-extern u32 evergreen_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
-extern void evergreen_post_page_flip(struct radeon_device *rdev, int crtc);
+extern void evergreen_page_flip(struct radeon_device *rdev, int crtc,
+ u64 crtc_base);
+extern bool evergreen_page_flip_pending(struct radeon_device *rdev, int crtc);
extern void dce4_wait_for_vblank(struct radeon_device *rdev, int crtc);
void evergreen_disable_interrupt_state(struct radeon_device *rdev);
int evergreen_mc_wait_for_idle(struct radeon_device *rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c
index 9ab30976287d..6a03624fadaa 100644
--- a/drivers/gpu/drm/radeon/radeon_bios.c
+++ b/drivers/gpu/drm/radeon/radeon_bios.c
@@ -626,7 +626,7 @@ static bool radeon_acpi_vfct_bios(struct radeon_device *rdev)
vhdr->DeviceID != rdev->pdev->device) {
DRM_INFO("ACPI VFCT table is not for this card\n");
goto out_unmap;
- };
+ }
if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) {
DRM_ERROR("ACPI VFCT image truncated\n");
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index ea50e0ae7bf7..933c5c39654d 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -48,6 +48,7 @@ void radeon_connector_hotplug(struct drm_connector *connector)
radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
/* if the connector is already off, don't turn it back on */
+ /* FIXME: This access isn't protected by any locks. */
if (connector->dpms != DRM_MODE_DPMS_ON)
return;
@@ -100,6 +101,7 @@ int radeon_get_monitor_bpc(struct drm_connector *connector)
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct radeon_connector_atom_dig *dig_connector;
int bpc = 8;
+ int mode_clock, max_tmds_clock;
switch (connector->connector_type) {
case DRM_MODE_CONNECTOR_DVII:
@@ -145,6 +147,61 @@ int radeon_get_monitor_bpc(struct drm_connector *connector)
}
break;
}
+
+ if (drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ /* 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",
+ connector->name, bpc);
+ bpc = 8;
+ }
+
+ /*
+ * Pre DCE-8 hw can't handle > 12 bpc, and more than 12 bpc doesn't make
+ * much sense without support for > 12 bpc framebuffers. RGB 4:4:4 at
+ * 12 bpc is always supported on hdmi deep color sinks, as this is
+ * required by the HDMI-1.3 spec. Clamp to a safe 12 bpc maximum.
+ */
+ if (bpc > 12) {
+ DRM_DEBUG("%s: HDMI deep color %d bpc unsupported. Using 12 bpc.\n",
+ connector->name, bpc);
+ bpc = 12;
+ }
+
+ /* Any defined maximum tmds clock limit we must not exceed? */
+ if (connector->max_tmds_clock > 0) {
+ /* mode_clock is clock in kHz for mode to be modeset on this connector */
+ mode_clock = radeon_connector->pixelclock_for_modeset;
+
+ /* Maximum allowable input clock in kHz */
+ max_tmds_clock = connector->max_tmds_clock * 1000;
+
+ DRM_DEBUG("%s: hdmi mode dotclock %d kHz, max tmds input clock %d kHz.\n",
+ connector->name, mode_clock, max_tmds_clock);
+
+ /* Check if bpc is within clock limit. Try to degrade gracefully otherwise */
+ if ((bpc == 12) && (mode_clock * 3/2 > max_tmds_clock)) {
+ if ((connector->display_info.edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) &&
+ (mode_clock * 5/4 <= max_tmds_clock))
+ bpc = 10;
+ else
+ bpc = 8;
+
+ DRM_DEBUG("%s: HDMI deep color 12 bpc exceeds max tmds clock. Using %d bpc.\n",
+ connector->name, bpc);
+ }
+
+ if ((bpc == 10) && (mode_clock * 5/4 > max_tmds_clock)) {
+ bpc = 8;
+ DRM_DEBUG("%s: HDMI deep color 10 bpc exceeds max tmds clock. Using %d bpc.\n",
+ connector->name, bpc);
+ }
+ }
+ }
+
+ DRM_DEBUG("%s: Display bpc=%d, returned bpc=%d\n",
+ connector->name, connector->display_info.bpc, bpc);
+
return bpc;
}
@@ -260,13 +317,17 @@ radeon_connector_analog_encoder_conflict_solve(struct drm_connector *connector,
continue;
if (priority == true) {
- DRM_DEBUG_KMS("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict));
- DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(connector));
+ DRM_DEBUG_KMS("1: conflicting encoders switching off %s\n",
+ conflict->name);
+ DRM_DEBUG_KMS("in favor of %s\n",
+ connector->name);
conflict->status = connector_status_disconnected;
radeon_connector_update_scratch_regs(conflict, connector_status_disconnected);
} else {
- DRM_DEBUG_KMS("2: conflicting encoders switching off %s\n", drm_get_connector_name(connector));
- DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(conflict));
+ DRM_DEBUG_KMS("2: conflicting encoders switching off %s\n",
+ connector->name);
+ DRM_DEBUG_KMS("in favor of %s\n",
+ conflict->name);
current_status = connector_status_disconnected;
}
break;
@@ -787,7 +848,7 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
if (!radeon_connector->edid) {
DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
- drm_get_connector_name(connector));
+ connector->name);
ret = connector_status_connected;
} else {
radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL);
@@ -1010,12 +1071,13 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
if (!radeon_connector->edid) {
DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
- drm_get_connector_name(connector));
+ 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) {
ret = connector_status_disconnected;
- DRM_ERROR("%s: detected RS690 floating bus bug, stopping ddc detect\n", drm_get_connector_name(connector));
+ DRM_ERROR("%s: detected RS690 floating bus bug, stopping ddc detect\n",
+ connector->name);
radeon_connector->ddc_bus = NULL;
} else {
ret = connector_status_connected;
@@ -1387,7 +1449,7 @@ bool radeon_connector_is_dp12_capable(struct drm_connector *connector)
struct radeon_device *rdev = dev->dev_private;
if (ASIC_IS_DCE5(rdev) &&
- (rdev->clock.dp_extclk >= 53900) &&
+ (rdev->clock.default_dispclk >= 53900) &&
radeon_connector_encoder_is_hbr2(connector)) {
return true;
}
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index 41ecf8a60611..71a143461478 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -140,10 +140,10 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
if (p->ring == R600_RING_TYPE_UVD_INDEX &&
(i == 0 || drm_pci_device_is_agp(p->rdev->ddev))) {
/* TODO: is this still needed for NI+ ? */
- p->relocs[i].domain =
+ p->relocs[i].prefered_domains =
RADEON_GEM_DOMAIN_VRAM;
- p->relocs[i].alt_domain =
+ p->relocs[i].allowed_domains =
RADEON_GEM_DOMAIN_VRAM;
/* prioritize this over any other relocation */
@@ -158,10 +158,10 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
return -EINVAL;
}
- p->relocs[i].domain = domain;
+ p->relocs[i].prefered_domains = domain;
if (domain == RADEON_GEM_DOMAIN_VRAM)
domain |= RADEON_GEM_DOMAIN_GTT;
- p->relocs[i].alt_domain = domain;
+ p->relocs[i].allowed_domains = domain;
}
p->relocs[i].tv.bo = &p->relocs[i].robj->tbo;
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 2cd144c378d6..03686fab842d 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1052,6 +1052,43 @@ static void radeon_check_arguments(struct radeon_device *rdev)
radeon_agpmode = 0;
break;
}
+
+ if (!radeon_check_pot_argument(radeon_vm_size)) {
+ dev_warn(rdev->dev, "VM size (%d) must be a power of 2\n",
+ radeon_vm_size);
+ radeon_vm_size = 4096;
+ }
+
+ if (radeon_vm_size < 4) {
+ dev_warn(rdev->dev, "VM size (%d) to small, min is 4MB\n",
+ radeon_vm_size);
+ radeon_vm_size = 4096;
+ }
+
+ /*
+ * Max GPUVM size for Cayman, SI and CI are 40 bits.
+ */
+ if (radeon_vm_size > 1024*1024) {
+ dev_warn(rdev->dev, "VM size (%d) to large, max is 1TB\n",
+ radeon_vm_size);
+ radeon_vm_size = 4096;
+ }
+
+ /* 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) {
+ dev_warn(rdev->dev, "VM page table size (%d) to small\n",
+ radeon_vm_block_size);
+ radeon_vm_block_size = 9;
+ }
+
+ if (radeon_vm_block_size > 24 ||
+ radeon_vm_size < (1ull << radeon_vm_block_size)) {
+ dev_warn(rdev->dev, "VM page table size (%d) to large\n",
+ radeon_vm_block_size);
+ radeon_vm_block_size = 9;
+ }
}
/**
@@ -1126,12 +1163,13 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
static bool radeon_switcheroo_can_switch(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
- bool can_switch;
- spin_lock(&dev->count_lock);
- can_switch = (dev->open_count == 0);
- spin_unlock(&dev->count_lock);
- return can_switch;
+ /*
+ * FIXME: open_count is protected by drm_global_mutex but that would lead to
+ * locking inversion with the driver load path. And the access here is
+ * completely racy anyway. So don't bother with locking for now.
+ */
+ return dev->open_count == 0;
}
static const struct vga_switcheroo_client_ops radeon_switcheroo_ops = {
@@ -1196,17 +1234,16 @@ int radeon_device_init(struct radeon_device *rdev,
if (r)
return r;
+ radeon_check_arguments(rdev);
/* Adjust VM size here.
- * Currently set to 4GB ((1 << 20) 4k pages).
- * Max GPUVM size for cayman and SI is 40 bits.
+ * Max GPUVM size for cayman+ is 40 bits.
*/
- rdev->vm_manager.max_pfn = 1 << 20;
+ rdev->vm_manager.max_pfn = radeon_vm_size << 8;
/* Set asic functions */
r = radeon_asic_init(rdev);
if (r)
return r;
- radeon_check_arguments(rdev);
/* all of the newer IGP chips have an internal gart
* However some rs4xx report as AGP, so remove that here.
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 356b733caafe..5ed617056b9c 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -249,16 +249,21 @@ static void radeon_crtc_destroy(struct drm_crtc *crtc)
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
drm_crtc_cleanup(crtc);
+ destroy_workqueue(radeon_crtc->flip_queue);
kfree(radeon_crtc);
}
-/*
- * Handle unpin events outside the interrupt handler proper.
+/**
+ * radeon_unpin_work_func - unpin old buffer object
+ *
+ * @__work - kernel work item
+ *
+ * Unpin the old frame buffer object outside of the interrupt handler
*/
static void radeon_unpin_work_func(struct work_struct *__work)
{
- struct radeon_unpin_work *work =
- container_of(__work, struct radeon_unpin_work, work);
+ struct radeon_flip_work *work =
+ container_of(__work, struct radeon_flip_work, unpin_work);
int r;
/* unpin of the old buffer */
@@ -276,10 +281,10 @@ static void radeon_unpin_work_func(struct work_struct *__work)
kfree(work);
}
-void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
+void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id)
{
struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
- struct radeon_unpin_work *work;
+ struct radeon_flip_work *work;
unsigned long flags;
u32 update_pending;
int vpos, hpos;
@@ -289,24 +294,13 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
return;
spin_lock_irqsave(&rdev->ddev->event_lock, flags);
- work = radeon_crtc->unpin_work;
- if (work == NULL ||
- (work->fence && !radeon_fence_signaled(work->fence))) {
+ work = radeon_crtc->flip_work;
+ if (work == NULL) {
spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
return;
}
- /* New pageflip, or just completion of a previous one? */
- if (!radeon_crtc->deferred_flip_completion) {
- /* do the flip (mmio) */
- update_pending = radeon_page_flip(rdev, crtc_id, work->new_crtc_base);
- } else {
- /* This is just a completion of a flip queued in crtc
- * at last invocation. Make sure we go directly to
- * completion routine.
- */
- update_pending = 0;
- radeon_crtc->deferred_flip_completion = 0;
- }
+
+ update_pending = radeon_page_flip_pending(rdev, crtc_id);
/* Has the pageflip already completed in crtc, or is it certain
* to complete in this vblank?
@@ -324,19 +318,38 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
*/
update_pending = 0;
}
- if (update_pending) {
- /* crtc didn't flip in this target vblank interval,
- * but flip is pending in crtc. It will complete it
- * in next vblank interval, so complete the flip at
- * next vblank irq.
- */
- radeon_crtc->deferred_flip_completion = 1;
+ spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
+ if (!update_pending)
+ radeon_crtc_handle_flip(rdev, crtc_id);
+}
+
+/**
+ * radeon_crtc_handle_flip - page flip completed
+ *
+ * @rdev: radeon device pointer
+ * @crtc_id: crtc number this event is for
+ *
+ * Called when we are sure that a page flip for this crtc is completed.
+ */
+void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
+{
+ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
+ struct radeon_flip_work *work;
+ unsigned long flags;
+
+ /* this can happen at init */
+ if (radeon_crtc == NULL)
+ return;
+
+ spin_lock_irqsave(&rdev->ddev->event_lock, flags);
+ work = radeon_crtc->flip_work;
+ if (work == NULL) {
spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
return;
}
- /* Pageflip (will be) certainly completed in this vblank. Clean up. */
- radeon_crtc->unpin_work = NULL;
+ /* Pageflip completed. Clean up. */
+ radeon_crtc->flip_work = NULL;
/* wakeup userspace */
if (work->event)
@@ -344,86 +357,71 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
- drm_vblank_put(rdev->ddev, radeon_crtc->crtc_id);
radeon_fence_unref(&work->fence);
- radeon_post_page_flip(work->rdev, work->crtc_id);
- schedule_work(&work->work);
+ radeon_irq_kms_pflip_irq_get(rdev, work->crtc_id);
+ queue_work(radeon_crtc->flip_queue, &work->unpin_work);
}
-static int radeon_crtc_page_flip(struct drm_crtc *crtc,
- struct drm_framebuffer *fb,
- struct drm_pending_vblank_event *event,
- uint32_t page_flip_flags)
+/**
+ * radeon_flip_work_func - page flip framebuffer
+ *
+ * @work - kernel work item
+ *
+ * Wait for the buffer object to become idle and do the actual page flip
+ */
+static void radeon_flip_work_func(struct work_struct *__work)
{
- struct drm_device *dev = crtc->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
- struct radeon_framebuffer *old_radeon_fb;
- struct radeon_framebuffer *new_radeon_fb;
- struct drm_gem_object *obj;
- struct radeon_bo *rbo;
- struct radeon_unpin_work *work;
- unsigned long flags;
- u32 tiling_flags, pitch_pixels;
- u64 base;
- int r;
+ struct radeon_flip_work *work =
+ container_of(__work, struct radeon_flip_work, flip_work);
+ struct radeon_device *rdev = work->rdev;
+ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[work->crtc_id];
- work = kzalloc(sizeof *work, GFP_KERNEL);
- if (work == NULL)
- return -ENOMEM;
+ struct drm_crtc *crtc = &radeon_crtc->base;
+ struct drm_framebuffer *fb = work->fb;
- work->event = event;
- work->rdev = rdev;
- work->crtc_id = radeon_crtc->crtc_id;
- old_radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
- new_radeon_fb = to_radeon_framebuffer(fb);
- /* schedule unpin of the old buffer */
- obj = old_radeon_fb->obj;
- /* take a reference to the old object */
- drm_gem_object_reference(obj);
- rbo = gem_to_radeon_bo(obj);
- work->old_rbo = rbo;
- obj = new_radeon_fb->obj;
- rbo = gem_to_radeon_bo(obj);
+ uint32_t tiling_flags, pitch_pixels;
+ uint64_t base;
- spin_lock(&rbo->tbo.bdev->fence_lock);
- if (rbo->tbo.sync_obj)
- work->fence = radeon_fence_ref(rbo->tbo.sync_obj);
- spin_unlock(&rbo->tbo.bdev->fence_lock);
+ unsigned long flags;
+ int r;
- INIT_WORK(&work->work, radeon_unpin_work_func);
+ down_read(&rdev->exclusive_lock);
+ while (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);
+ }
- /* We borrow the event spin lock for protecting unpin_work */
- spin_lock_irqsave(&dev->event_lock, flags);
- if (radeon_crtc->unpin_work) {
- DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
- r = -EBUSY;
- goto unlock_free;
+ if (r) {
+ DRM_ERROR("failed to wait on page flip fence (%d)!\n",
+ r);
+ goto cleanup;
+ } else
+ radeon_fence_unref(&work->fence);
}
- radeon_crtc->unpin_work = work;
- radeon_crtc->deferred_flip_completion = 0;
- spin_unlock_irqrestore(&dev->event_lock, flags);
/* pin the new buffer */
DRM_DEBUG_DRIVER("flip-ioctl() cur_fbo = %p, cur_bbo = %p\n",
- work->old_rbo, rbo);
+ work->old_rbo, work->new_rbo);
- r = radeon_bo_reserve(rbo, false);
+ r = radeon_bo_reserve(work->new_rbo, false);
if (unlikely(r != 0)) {
DRM_ERROR("failed to reserve new rbo buffer before flip\n");
- goto pflip_cleanup;
+ goto cleanup;
}
/* Only 27 bit offset for legacy CRTC */
- r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM,
+ r = radeon_bo_pin_restricted(work->new_rbo, RADEON_GEM_DOMAIN_VRAM,
ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, &base);
if (unlikely(r != 0)) {
- radeon_bo_unreserve(rbo);
+ radeon_bo_unreserve(work->new_rbo);
r = -EINVAL;
DRM_ERROR("failed to pin new rbo buffer before flip\n");
- goto pflip_cleanup;
+ goto cleanup;
}
- radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
- radeon_bo_unreserve(rbo);
+ radeon_bo_get_tiling_flags(work->new_rbo, &tiling_flags, NULL);
+ radeon_bo_unreserve(work->new_rbo);
if (!ASIC_IS_AVIVO(rdev)) {
/* crtc offset is from display base addr not FB location */
@@ -461,44 +459,91 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
base &= ~7;
}
- spin_lock_irqsave(&dev->event_lock, flags);
- work->new_crtc_base = base;
- spin_unlock_irqrestore(&dev->event_lock, flags);
+ /* We borrow the event spin lock for protecting flip_work */
+ spin_lock_irqsave(&crtc->dev->event_lock, flags);
- /* update crtc fb */
- crtc->primary->fb = fb;
+ /* set the proper interrupt */
+ radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id);
- r = drm_vblank_get(dev, radeon_crtc->crtc_id);
- if (r) {
- DRM_ERROR("failed to get vblank before flip\n");
- goto pflip_cleanup1;
- }
+ /* do the flip (mmio) */
+ radeon_page_flip(rdev, radeon_crtc->crtc_id, base);
- /* set the proper interrupt */
- radeon_pre_page_flip(rdev, radeon_crtc->crtc_id);
+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+ up_read(&rdev->exclusive_lock);
- return 0;
+ return;
-pflip_cleanup1:
- if (unlikely(radeon_bo_reserve(rbo, false) != 0)) {
- DRM_ERROR("failed to reserve new rbo in error path\n");
- goto pflip_cleanup;
- }
- if (unlikely(radeon_bo_unpin(rbo) != 0)) {
- DRM_ERROR("failed to unpin new rbo in error path\n");
- }
- radeon_bo_unreserve(rbo);
-
-pflip_cleanup:
- spin_lock_irqsave(&dev->event_lock, flags);
- radeon_crtc->unpin_work = NULL;
-unlock_free:
- spin_unlock_irqrestore(&dev->event_lock, flags);
- drm_gem_object_unreference_unlocked(old_radeon_fb->obj);
+cleanup:
+ drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base);
radeon_fence_unref(&work->fence);
kfree(work);
+ up_read(&rdev->exclusive_lock);
+}
- return r;
+static int radeon_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 radeon_device *rdev = dev->dev_private;
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct radeon_framebuffer *old_radeon_fb;
+ struct radeon_framebuffer *new_radeon_fb;
+ struct drm_gem_object *obj;
+ struct radeon_flip_work *work;
+ unsigned long flags;
+
+ work = kzalloc(sizeof *work, GFP_KERNEL);
+ if (work == NULL)
+ return -ENOMEM;
+
+ INIT_WORK(&work->flip_work, radeon_flip_work_func);
+ INIT_WORK(&work->unpin_work, radeon_unpin_work_func);
+
+ work->rdev = rdev;
+ work->crtc_id = radeon_crtc->crtc_id;
+ work->fb = fb;
+ work->event = event;
+
+ /* schedule unpin of the old buffer */
+ old_radeon_fb = to_radeon_framebuffer(crtc->primary->fb);
+ obj = old_radeon_fb->obj;
+
+ /* take a reference to the old object */
+ drm_gem_object_reference(obj);
+ work->old_rbo = gem_to_radeon_bo(obj);
+
+ new_radeon_fb = to_radeon_framebuffer(fb);
+ obj = new_radeon_fb->obj;
+ work->new_rbo = gem_to_radeon_bo(obj);
+
+ spin_lock(&work->new_rbo->tbo.bdev->fence_lock);
+ if (work->new_rbo->tbo.sync_obj)
+ work->fence = radeon_fence_ref(work->new_rbo->tbo.sync_obj);
+ spin_unlock(&work->new_rbo->tbo.bdev->fence_lock);
+
+ /* We borrow the event spin lock for protecting flip_work */
+ spin_lock_irqsave(&crtc->dev->event_lock, flags);
+
+ if (radeon_crtc->flip_work) {
+ DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+ drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base);
+ radeon_fence_unref(&work->fence);
+ kfree(work);
+ return -EBUSY;
+ }
+ radeon_crtc->flip_work = work;
+
+ /* update crtc fb */
+ crtc->primary->fb = fb;
+
+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+
+ queue_work(radeon_crtc->flip_queue, &work->flip_work);
+
+ return 0;
}
static int
@@ -568,6 +613,7 @@ static void radeon_crtc_init(struct drm_device *dev, int index)
drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256);
radeon_crtc->crtc_id = index;
+ radeon_crtc->flip_queue = create_singlethread_workqueue("radeon-crtc");
rdev->mode_info.crtcs[index] = radeon_crtc;
if (rdev->family >= CHIP_BONAIRE) {
@@ -661,7 +707,7 @@ static void radeon_print_display_setup(struct drm_device *dev)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
radeon_connector = to_radeon_connector(connector);
DRM_INFO("Connector %d:\n", i);
- DRM_INFO(" %s\n", drm_get_connector_name(connector));
+ DRM_INFO(" %s\n", connector->name);
if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
DRM_INFO(" %s\n", hpd_names[radeon_connector->hpd.hpd]);
if (radeon_connector->ddc_bus) {
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index c00a2f585185..6e3017413386 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -81,9 +81,10 @@
* 2.37.0 - allow GS ring setup on r6xx/r7xx
* 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
*/
#define KMS_DRIVER_MAJOR 2
-#define KMS_DRIVER_MINOR 38
+#define KMS_DRIVER_MINOR 39
#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);
@@ -172,6 +173,8 @@ int radeon_dpm = -1;
int radeon_aspm = -1;
int radeon_runtime_pm = -1;
int radeon_hard_reset = 0;
+int radeon_vm_size = 4096;
+int radeon_vm_block_size = 9;
MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
module_param_named(no_wb, radeon_no_wb, int, 0444);
@@ -239,6 +242,12 @@ module_param_named(runpm, radeon_runtime_pm, int, 0444);
MODULE_PARM_DESC(hard_reset, "PCI config reset (1 = force enable, 0 = disable (default))");
module_param_named(hard_reset, radeon_hard_reset, int, 0444);
+MODULE_PARM_DESC(vm_size, "VM address space size in megabytes (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_param_named(vm_block_size, radeon_vm_block_size, int, 0444);
+
static struct pci_device_id pciidlist[] = {
radeon_PCI_IDS
};
@@ -519,7 +528,6 @@ static struct drm_driver kms_driver = {
DRIVER_USE_AGP |
DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM |
DRIVER_PRIME | DRIVER_RENDER,
- .dev_priv_size = 0,
.load = radeon_driver_load_kms,
.open = radeon_driver_open_kms,
.preclose = radeon_driver_preclose_kms,
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
index a77b1c13ea43..913787085dfa 100644
--- a/drivers/gpu/drm/radeon/radeon_fence.c
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
@@ -819,15 +819,35 @@ static int radeon_debugfs_fence_info(struct seq_file *m, void *data)
return 0;
}
+/**
+ * radeon_debugfs_gpu_reset - manually trigger a gpu reset
+ *
+ * Manually trigger a gpu reset at the next fence wait.
+ */
+static int radeon_debugfs_gpu_reset(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;
+
+ down_read(&rdev->exclusive_lock);
+ seq_printf(m, "%d\n", rdev->needs_reset);
+ rdev->needs_reset = true;
+ up_read(&rdev->exclusive_lock);
+
+ return 0;
+}
+
static struct drm_info_list radeon_debugfs_fence_list[] = {
{"radeon_fence_info", &radeon_debugfs_fence_info, 0, NULL},
+ {"radeon_gpu_reset", &radeon_debugfs_gpu_reset, 0, NULL}
};
#endif
int radeon_debugfs_fence_init(struct radeon_device *rdev)
{
#if defined(CONFIG_DEBUG_FS)
- return radeon_debugfs_add_files(rdev, radeon_debugfs_fence_list, 1);
+ return radeon_debugfs_add_files(rdev, radeon_debugfs_fence_list, 2);
#else
return 0;
#endif
diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
index 7b944142a9fd..add622008407 100644
--- a/drivers/gpu/drm/radeon/radeon_i2c.c
+++ b/drivers/gpu/drm/radeon/radeon_i2c.c
@@ -94,6 +94,8 @@ static int pre_xfer(struct i2c_adapter *i2c_adap)
struct radeon_i2c_bus_rec *rec = &i2c->rec;
uint32_t temp;
+ mutex_lock(&i2c->mutex);
+
/* RV410 appears to have a bug where the hw i2c in reset
* holds the i2c port in a bad state - switch hw i2c away before
* doing DDC - do this for all r200s/r300s/r400s for safety sake
@@ -170,6 +172,8 @@ static void post_xfer(struct i2c_adapter *i2c_adap)
temp = RREG32(rec->mask_data_reg) & ~rec->mask_data_mask;
WREG32(rec->mask_data_reg, temp);
temp = RREG32(rec->mask_data_reg);
+
+ mutex_unlock(&i2c->mutex);
}
static int get_clock(void *i2c_priv)
@@ -813,6 +817,8 @@ static int radeon_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
struct radeon_i2c_bus_rec *rec = &i2c->rec;
int ret = 0;
+ mutex_lock(&i2c->mutex);
+
switch (rdev->family) {
case CHIP_R100:
case CHIP_RV100:
@@ -879,6 +885,8 @@ static int radeon_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
break;
}
+ mutex_unlock(&i2c->mutex);
+
return ret;
}
@@ -919,6 +927,7 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
i2c->adapter.dev.parent = &dev->pdev->dev;
i2c->dev = dev;
i2c_set_adapdata(&i2c->adapter, i2c);
+ mutex_init(&i2c->mutex);
if (rec->mm_i2c ||
(rec->hw_capable &&
radeon_hw_i2c &&
@@ -979,7 +988,7 @@ void radeon_i2c_destroy(struct radeon_i2c_chan *i2c)
return;
i2c_del_adapter(&i2c->adapter);
if (i2c->has_aux)
- drm_dp_aux_unregister_i2c_bus(&i2c->aux);
+ drm_dp_aux_unregister(&i2c->aux);
kfree(i2c);
}
diff --git a/drivers/gpu/drm/radeon/radeon_ioc32.c b/drivers/gpu/drm/radeon/radeon_ioc32.c
index bdb0f93e73bc..0b98ea134579 100644
--- a/drivers/gpu/drm/radeon/radeon_ioc32.c
+++ b/drivers/gpu/drm/radeon/radeon_ioc32.c
@@ -399,7 +399,7 @@ long radeon_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (nr < DRM_COMMAND_BASE)
return drm_compat_ioctl(filp, cmd, arg);
- if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(radeon_compat_ioctls))
+ if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(radeon_compat_ioctls))
fn = radeon_compat_ioctls[nr - DRM_COMMAND_BASE];
if (fn != NULL)
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 089c9ffb0aa9..16807afab362 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -287,7 +287,7 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
INIT_WORK(&rdev->reset_work, radeon_irq_reset_work_func);
rdev->irq.installed = true;
- r = drm_irq_install(rdev->ddev);
+ r = drm_irq_install(rdev->ddev, rdev->ddev->pdev->irq);
if (r) {
rdev->irq.installed = false;
flush_work(&rdev->hotplug_work);
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index eaaedba04675..35d931881b4b 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -513,6 +513,22 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file
value_size = sizeof(uint64_t);
value64 = atomic64_read(&rdev->gtt_usage);
break;
+ case RADEON_INFO_ACTIVE_CU_COUNT:
+ if (rdev->family >= CHIP_BONAIRE)
+ *value = rdev->config.cik.active_cus;
+ else if (rdev->family >= CHIP_TAHITI)
+ *value = rdev->config.si.active_cus;
+ else if (rdev->family >= CHIP_CAYMAN)
+ *value = rdev->config.cayman.active_simds;
+ else if (rdev->family >= CHIP_CEDAR)
+ *value = rdev->config.evergreen.active_simds;
+ else if (rdev->family >= CHIP_RV770)
+ *value = rdev->config.rv770.active_simds;
+ else if (rdev->family >= CHIP_R600)
+ *value = rdev->config.r600.active_simds;
+ else
+ *value = 1;
+ break;
default:
DRM_DEBUG_KMS("Invalid request %d\n", info->request);
return -EINVAL;
@@ -859,4 +875,4 @@ const struct drm_ioctl_desc radeon_ioctls_kms[] = {
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),
};
-int radeon_max_kms_ioctl = DRM_ARRAY_SIZE(radeon_ioctls_kms);
+int radeon_max_kms_ioctl = ARRAY_SIZE(radeon_ioctls_kms);
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 6ddf31a2d34e..ad0e4b8cc7e3 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -191,6 +191,7 @@ struct radeon_i2c_chan {
struct radeon_i2c_bus_rec rec;
struct drm_dp_aux aux;
bool has_aux;
+ struct mutex mutex;
};
/* mostly for macs, but really any system without connector tables */
@@ -324,8 +325,8 @@ struct radeon_crtc {
struct drm_display_mode native_mode;
int pll_id;
/* page flipping */
- struct radeon_unpin_work *unpin_work;
- int deferred_flip_completion;
+ struct workqueue_struct *flip_queue;
+ struct radeon_flip_work *flip_work;
/* pll sharing */
struct radeon_atom_ss ss;
bool ss_enabled;
@@ -505,6 +506,7 @@ struct radeon_connector {
struct radeon_i2c_chan *router_bus;
enum radeon_connector_audio audio;
enum radeon_connector_dither dither;
+ int pixelclock_for_modeset;
};
struct radeon_framebuffer {
@@ -906,6 +908,7 @@ bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
void radeon_fb_output_poll_changed(struct radeon_device *rdev);
+void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id);
void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id);
int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled);
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 4faa4d6f9bb4..6c717b257d6d 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -446,7 +446,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev,
list_for_each_entry(lobj, head, tv.head) {
bo = lobj->robj;
if (!bo->pin_count) {
- u32 domain = lobj->domain;
+ u32 domain = lobj->prefered_domains;
u32 current_domain =
radeon_mem_type_to_domain(bo->tbo.mem.mem_type);
@@ -458,7 +458,7 @@ int radeon_bo_list_validate(struct radeon_device *rdev,
* into account. We don't want to disallow buffer moves
* completely.
*/
- if ((lobj->alt_domain & current_domain) != 0 &&
+ if ((lobj->allowed_domains & current_domain) != 0 &&
(domain & current_domain) == 0 && /* will be moved */
bytes_moved > bytes_moved_threshold) {
/* don't move it */
@@ -476,8 +476,9 @@ int radeon_bo_list_validate(struct radeon_device *rdev,
initial_bytes_moved;
if (unlikely(r)) {
- if (r != -ERESTARTSYS && domain != lobj->alt_domain) {
- domain = lobj->alt_domain;
+ if (r != -ERESTARTSYS &&
+ domain != lobj->allowed_domains) {
+ domain = lobj->allowed_domains;
goto retry;
}
ttm_eu_backoff_reservation(ticket, head);
@@ -730,7 +731,7 @@ int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, 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))
return r;
spin_lock(&bo->tbo.bdev->fence_lock);
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index 9e7b25a0629d..5a873f31a171 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -65,7 +65,7 @@ static inline int radeon_bo_reserve(struct radeon_bo *bo, bool no_intr)
{
int r;
- r = ttm_bo_reserve(&bo->tbo, !no_intr, false, false, 0);
+ r = ttm_bo_reserve(&bo->tbo, !no_intr, false, false, NULL);
if (unlikely(r != 0)) {
if (r != -ERESTARTSYS)
dev_err(bo->rdev->dev, "%p reserve failed\n", bo);
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 2bdae61c0ac0..12c663e86ca1 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -984,6 +984,8 @@ void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
if (enable) {
mutex_lock(&rdev->pm.mutex);
rdev->pm.dpm.uvd_active = true;
+ /* disable this for now */
+#if 0
if ((rdev->pm.dpm.sd == 1) && (rdev->pm.dpm.hd == 0))
dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_SD;
else if ((rdev->pm.dpm.sd == 2) && (rdev->pm.dpm.hd == 0))
@@ -993,6 +995,7 @@ void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)
else if ((rdev->pm.dpm.sd == 0) && (rdev->pm.dpm.hd == 2))
dpm_state = POWER_STATE_TYPE_INTERNAL_UVD_HD2;
else
+#endif
dpm_state = POWER_STATE_TYPE_INTERNAL_UVD;
rdev->pm.dpm.state = dpm_state;
mutex_unlock(&rdev->pm.mutex);
diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c
index 956ab7f14e16..23bb64fd775f 100644
--- a/drivers/gpu/drm/radeon/radeon_state.c
+++ b/drivers/gpu/drm/radeon/radeon_state.c
@@ -3054,7 +3054,7 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil
if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
value = 0;
else
- value = drm_dev_to_irq(dev);
+ value = dev->pdev->irq;
break;
case RADEON_PARAM_GART_BASE:
value = dev_priv->gart_vm_start;
@@ -3258,4 +3258,4 @@ struct drm_ioctl_desc radeon_ioctls[] = {
DRM_IOCTL_DEF_DRV(RADEON_CS, r600_cs_legacy_ioctl, DRM_AUTH)
};
-int radeon_max_ioctl = DRM_ARRAY_SIZE(radeon_ioctls);
+int radeon_max_ioctl = ARRAY_SIZE(radeon_ioctls);
diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c
index 1b65ae2433cd..a4ad270e8261 100644
--- a/drivers/gpu/drm/radeon/radeon_uvd.c
+++ b/drivers/gpu/drm/radeon/radeon_uvd.c
@@ -812,7 +812,8 @@ void radeon_uvd_note_usage(struct radeon_device *rdev)
(rdev->pm.dpm.hd != hd)) {
rdev->pm.dpm.sd = sd;
rdev->pm.dpm.hd = hd;
- streams_changed = true;
+ /* disable this for now */
+ /*streams_changed = true;*/
}
}
diff --git a/drivers/gpu/drm/radeon/radeon_vce.c b/drivers/gpu/drm/radeon/radeon_vce.c
index 3971d968af6c..aa21c31a846c 100644
--- a/drivers/gpu/drm/radeon/radeon_vce.c
+++ b/drivers/gpu/drm/radeon/radeon_vce.c
@@ -66,6 +66,7 @@ int radeon_vce_init(struct radeon_device *rdev)
case CHIP_BONAIRE:
case CHIP_KAVERI:
case CHIP_KABINI:
+ case CHIP_HAWAII:
case CHIP_MULLINS:
fw_name = FIRMWARE_BONAIRE;
break;
diff --git a/drivers/gpu/drm/radeon/radeon_vm.c b/drivers/gpu/drm/radeon/radeon_vm.c
index c11b71d249e3..899d9126cad6 100644
--- a/drivers/gpu/drm/radeon/radeon_vm.c
+++ b/drivers/gpu/drm/radeon/radeon_vm.c
@@ -59,7 +59,7 @@
*/
static unsigned radeon_vm_num_pdes(struct radeon_device *rdev)
{
- return rdev->vm_manager.max_pfn >> RADEON_VM_BLOCK_SIZE;
+ return rdev->vm_manager.max_pfn >> radeon_vm_block_size;
}
/**
@@ -140,8 +140,8 @@ struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev,
/* add the vm page table to the list */
list[0].gobj = NULL;
list[0].robj = vm->page_directory;
- list[0].domain = RADEON_GEM_DOMAIN_VRAM;
- list[0].alt_domain = RADEON_GEM_DOMAIN_VRAM;
+ 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].tiling_flags = 0;
list[0].handle = 0;
@@ -153,8 +153,8 @@ struct radeon_cs_reloc *radeon_vm_get_bos(struct radeon_device *rdev,
list[idx].gobj = NULL;
list[idx].robj = vm->page_tables[i].bo;
- list[idx].domain = RADEON_GEM_DOMAIN_VRAM;
- list[idx].alt_domain = RADEON_GEM_DOMAIN_VRAM;
+ 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].tiling_flags = 0;
list[idx].handle = 0;
@@ -474,8 +474,10 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
bo_va->valid = false;
list_move(&bo_va->vm_list, head);
- soffset = (soffset / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE;
- eoffset = (eoffset / RADEON_GPU_PAGE_SIZE) >> RADEON_VM_BLOCK_SIZE;
+ soffset = (soffset / RADEON_GPU_PAGE_SIZE) >> radeon_vm_block_size;
+ eoffset = (eoffset / RADEON_GPU_PAGE_SIZE) >> radeon_vm_block_size;
+
+ BUG_ON(eoffset >= radeon_vm_num_pdes(rdev));
if (eoffset > vm->max_pde_used)
vm->max_pde_used = eoffset;
@@ -583,10 +585,9 @@ static uint32_t radeon_vm_page_flags(uint32_t flags)
int radeon_vm_update_page_directory(struct radeon_device *rdev,
struct radeon_vm *vm)
{
- static const uint32_t incr = RADEON_VM_PTE_COUNT * 8;
-
struct radeon_bo *pd = vm->page_directory;
uint64_t pd_addr = radeon_bo_gpu_offset(pd);
+ uint32_t incr = RADEON_VM_PTE_COUNT * 8;
uint64_t last_pde = ~0, last_pt = ~0;
unsigned count = 0, pt_idx, ndw;
struct radeon_ib ib;
@@ -660,6 +661,84 @@ int radeon_vm_update_page_directory(struct radeon_device *rdev,
}
/**
+ * radeon_vm_frag_ptes - add fragment information to PTEs
+ *
+ * @rdev: radeon_device pointer
+ * @ib: IB for the update
+ * @pe_start: first PTE to handle
+ * @pe_end: last PTE to handle
+ * @addr: addr those PTEs should point to
+ * @flags: hw mapping flags
+ *
+ * Global and local mutex must be locked!
+ */
+static void radeon_vm_frag_ptes(struct radeon_device *rdev,
+ struct radeon_ib *ib,
+ uint64_t pe_start, uint64_t pe_end,
+ uint64_t addr, uint32_t flags)
+{
+ /**
+ * The MC L1 TLB supports variable sized pages, based on a fragment
+ * field in the PTE. When this field is set to a non-zero value, page
+ * granularity is increased from 4KB to (1 << (12 + frag)). The PTE
+ * flags are considered valid for all PTEs within the fragment range
+ * and corresponding mappings are assumed to be physically contiguous.
+ *
+ * The L1 TLB can store a single PTE for the whole fragment,
+ * significantly increasing the space available for translation
+ * caching. This leads to large improvements in throughput when the
+ * TLB is under pressure.
+ *
+ * The L2 TLB distributes small and large fragments into two
+ * asymmetric partitions. The large fragment cache is significantly
+ * larger. Thus, we try to use large fragments wherever possible.
+ * Userspace can support this by aligning virtual base address and
+ * allocation size to the fragment size.
+ */
+
+ /* NI is optimized for 256KB fragments, SI and newer for 64KB */
+ uint64_t frag_flags = rdev->family == CHIP_CAYMAN ?
+ R600_PTE_FRAG_256KB : R600_PTE_FRAG_64KB;
+ uint64_t frag_align = rdev->family == CHIP_CAYMAN ? 0x200 : 0x80;
+
+ uint64_t frag_start = ALIGN(pe_start, frag_align);
+ uint64_t frag_end = pe_end & ~(frag_align - 1);
+
+ unsigned count;
+
+ /* system pages are non continuously */
+ if ((flags & R600_PTE_SYSTEM) || !(flags & R600_PTE_VALID) ||
+ (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);
+ 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);
+ 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);
+
+ /* 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_update_ptes - make sure that page tables are valid
*
* @rdev: radeon_device pointer
@@ -679,8 +758,7 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev,
uint64_t start, uint64_t end,
uint64_t dst, uint32_t flags)
{
- static const uint64_t mask = RADEON_VM_PTE_COUNT - 1;
-
+ uint64_t mask = RADEON_VM_PTE_COUNT - 1;
uint64_t last_pte = ~0, last_dst = ~0;
unsigned count = 0;
uint64_t addr;
@@ -690,7 +768,7 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev,
/* walk over the address space and update the page tables */
for (addr = start; addr < end; ) {
- uint64_t pt_idx = addr >> RADEON_VM_BLOCK_SIZE;
+ uint64_t pt_idx = addr >> radeon_vm_block_size;
struct radeon_bo *pt = vm->page_tables[pt_idx].bo;
unsigned nptes;
uint64_t pte;
@@ -708,10 +786,9 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev,
if ((last_pte + 8 * count) != pte) {
if (count) {
- radeon_asic_vm_set_page(rdev, ib, last_pte,
- last_dst, count,
- RADEON_GPU_PAGE_SIZE,
- flags);
+ radeon_vm_frag_ptes(rdev, ib, last_pte,
+ last_pte + 8 * count,
+ last_dst, flags);
}
count = nptes;
@@ -726,9 +803,9 @@ static void radeon_vm_update_ptes(struct radeon_device *rdev,
}
if (count) {
- radeon_asic_vm_set_page(rdev, ib, last_pte,
- last_dst, count,
- RADEON_GPU_PAGE_SIZE, flags);
+ radeon_vm_frag_ptes(rdev, ib, last_pte,
+ last_pte + 8 * count,
+ last_dst, flags);
}
}
@@ -796,13 +873,13 @@ int radeon_vm_bo_update(struct radeon_device *rdev,
/* padding, etc. */
ndw = 64;
- if (RADEON_VM_BLOCK_SIZE > 11)
+ 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;
+ ndw += (nptes >> radeon_vm_block_size) * 4;
/* reserve space for pte addresses */
ndw += nptes * 2;
@@ -892,6 +969,8 @@ void radeon_vm_bo_invalidate(struct radeon_device *rdev,
*/
int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm)
{
+ const unsigned align = min(RADEON_VM_PTB_ALIGN_SIZE,
+ RADEON_VM_PTE_COUNT * 8);
unsigned pd_size, pd_entries, pts_size;
int r;
@@ -913,7 +992,7 @@ int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm)
return -ENOMEM;
}
- r = radeon_bo_create(rdev, pd_size, RADEON_VM_PTB_ALIGN_SIZE, false,
+ r = radeon_bo_create(rdev, pd_size, align, false,
RADEON_GEM_DOMAIN_VRAM, NULL,
&vm->page_directory);
if (r)
diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c
index 130d5cc50d43..a0f96decece3 100644
--- a/drivers/gpu/drm/radeon/rs400.c
+++ b/drivers/gpu/drm/radeon/rs400.c
@@ -212,21 +212,16 @@ void rs400_gart_fini(struct radeon_device *rdev)
#define RS400_PTE_WRITEABLE (1 << 2)
#define RS400_PTE_READABLE (1 << 3)
-int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
+void rs400_gart_set_page(struct radeon_device *rdev, unsigned i, uint64_t addr)
{
uint32_t entry;
u32 *gtt = rdev->gart.ptr;
- if (i < 0 || i > rdev->gart.num_gpu_pages) {
- return -EINVAL;
- }
-
entry = (lower_32_bits(addr) & PAGE_MASK) |
((upper_32_bits(addr) & 0xff) << 4) |
RS400_PTE_WRITEABLE | RS400_PTE_READABLE;
entry = cpu_to_le32(entry);
gtt[i] = entry;
- return 0;
}
int rs400_mc_wait_for_idle(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 72d3616de08e..d1a35cb1c91d 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -109,19 +109,7 @@ void avivo_wait_for_vblank(struct radeon_device *rdev, int crtc)
}
}
-void rs600_pre_page_flip(struct radeon_device *rdev, int crtc)
-{
- /* enable the pflip int */
- radeon_irq_kms_pflip_irq_get(rdev, crtc);
-}
-
-void rs600_post_page_flip(struct radeon_device *rdev, int crtc)
-{
- /* disable the pflip int */
- radeon_irq_kms_pflip_irq_put(rdev, crtc);
-}
-
-u32 rs600_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
+void rs600_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
{
struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
u32 tmp = RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset);
@@ -148,9 +136,15 @@ u32 rs600_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
/* Unlock the lock, so double-buffering can take place inside vblank */
tmp &= ~AVIVO_D1GRPH_UPDATE_LOCK;
WREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset, tmp);
+}
+
+bool rs600_page_flip_pending(struct radeon_device *rdev, int crtc_id)
+{
+ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
/* Return current update_pending status: */
- return RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) & AVIVO_D1GRPH_SURFACE_UPDATE_PENDING;
+ return !!(RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) &
+ AVIVO_D1GRPH_SURFACE_UPDATE_PENDING);
}
void avivo_program_fmt(struct drm_encoder *encoder)
@@ -632,24 +626,16 @@ static void rs600_gart_fini(struct radeon_device *rdev)
radeon_gart_table_vram_free(rdev);
}
-#define R600_PTE_VALID (1 << 0)
-#define R600_PTE_SYSTEM (1 << 1)
-#define R600_PTE_SNOOPED (1 << 2)
-#define R600_PTE_READABLE (1 << 5)
-#define R600_PTE_WRITEABLE (1 << 6)
-
-int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
+void rs600_gart_set_page(struct radeon_device *rdev, unsigned i, uint64_t addr)
{
void __iomem *ptr = (void *)rdev->gart.ptr;
- if (i < 0 || i > rdev->gart.num_gpu_pages) {
- return -EINVAL;
- }
addr = addr & 0xFFFFFFFFFFFFF000ULL;
- addr |= R600_PTE_VALID | R600_PTE_SYSTEM | R600_PTE_SNOOPED;
- addr |= R600_PTE_READABLE | R600_PTE_WRITEABLE;
+ if (addr == rdev->dummy_page.addr)
+ addr |= R600_PTE_SYSTEM | R600_PTE_SNOOPED;
+ else
+ addr |= R600_PTE_GART;
writeq(addr, ptr + (i * 8));
- return 0;
}
int rs600_irq_set(struct radeon_device *rdev)
@@ -787,7 +773,7 @@ int rs600_irq_process(struct radeon_device *rdev)
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[0]))
- radeon_crtc_handle_flip(rdev, 0);
+ radeon_crtc_handle_vblank(rdev, 0);
}
if (G_007EDC_LB_D2_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) {
if (rdev->irq.crtc_vblank_int[1]) {
@@ -796,7 +782,7 @@ int rs600_irq_process(struct radeon_device *rdev)
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[1]))
- radeon_crtc_handle_flip(rdev, 1);
+ radeon_crtc_handle_vblank(rdev, 1);
}
if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) {
queue_hotplug = true;
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index fef310773aad..da8703d8d455 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -801,7 +801,7 @@ u32 rv770_get_xclk(struct radeon_device *rdev)
return reference_clock;
}
-u32 rv770_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
+void rv770_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
{
struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
u32 tmp = RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset);
@@ -835,9 +835,15 @@ u32 rv770_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base)
/* Unlock the lock, so double-buffering can take place inside vblank */
tmp &= ~AVIVO_D1GRPH_UPDATE_LOCK;
WREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset, tmp);
+}
+
+bool rv770_page_flip_pending(struct radeon_device *rdev, int crtc_id)
+{
+ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
/* Return current update_pending status: */
- return RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) & AVIVO_D1GRPH_SURFACE_UPDATE_PENDING;
+ return !!(RREG32(AVIVO_D1GRPH_UPDATE + radeon_crtc->crtc_offset) &
+ AVIVO_D1GRPH_SURFACE_UPDATE_PENDING);
}
/* get temperature in millidegrees */
@@ -1321,6 +1327,9 @@ static void rv770_gpu_init(struct radeon_device *rdev)
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;
switch (rdev->config.rv770.max_tile_pipes) {
case 1:
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index 22a63c98ba14..730cee2c34cf 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -71,6 +71,7 @@ MODULE_FIRMWARE("radeon/HAINAN_mc2.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);
extern void sumo_rlc_fini(struct radeon_device *rdev);
@@ -2900,7 +2901,7 @@ static void si_gpu_init(struct radeon_device *rdev)
u32 sx_debug_1;
u32 hdp_host_path_cntl;
u32 tmp;
- int i, j;
+ int i, j, k;
switch (rdev->family) {
case CHIP_TAHITI:
@@ -3098,6 +3099,14 @@ static void si_gpu_init(struct radeon_device *rdev)
rdev->config.si.max_sh_per_se,
rdev->config.si.max_cu_per_sh);
+ 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));
+ }
+ }
+ }
/* set HW defaults for 3D engine */
WREG32(CP_QUEUE_THRESHOLDS, (ROQ_IB1_START(0x16) |
@@ -3186,7 +3195,7 @@ void si_fence_ring_emit(struct radeon_device *rdev,
/* EVENT_WRITE_EOP - flush caches, send int */
radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) | EVENT_INDEX(5));
- radeon_ring_write(ring, addr & 0xffffffff);
+ radeon_ring_write(ring, lower_32_bits(addr));
radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2));
radeon_ring_write(ring, fence->seq);
radeon_ring_write(ring, 0);
@@ -3219,7 +3228,7 @@ void si_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
radeon_ring_write(ring, (1 << 8));
radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc);
- radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xffffffff);
+ radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr));
radeon_ring_write(ring, next_rptr);
}
@@ -4044,18 +4053,21 @@ static int si_pcie_gart_enable(struct radeon_device *rdev)
WREG32(MC_VM_MX_L1_TLB_CNTL,
(0xA << 7) |
ENABLE_L1_TLB |
+ ENABLE_L1_FRAGMENT_PROCESSING |
SYSTEM_ACCESS_MODE_NOT_IN_SYS |
ENABLE_ADVANCED_DRIVER_MODEL |
SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU);
/* Setup L2 cache */
WREG32(VM_L2_CNTL, ENABLE_L2_CACHE |
+ ENABLE_L2_FRAGMENT_PROCESSING |
ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE |
ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE |
EFFECTIVE_L2_QUEUE_SIZE(7) |
CONTEXT1_IDENTITY_ACCESS_MODE(1));
WREG32(VM_L2_CNTL2, INVALIDATE_ALL_L1_TLBS | INVALIDATE_L2_CACHE);
WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY |
- L2_CACHE_BIGK_FRAGMENT_SIZE(0));
+ BANK_SELECT(4) |
+ L2_CACHE_BIGK_FRAGMENT_SIZE(4));
/* setup context0 */
WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12);
WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12);
@@ -4092,6 +4104,7 @@ static int si_pcie_gart_enable(struct radeon_device *rdev)
(u32)(rdev->dummy_page.addr >> 12));
WREG32(VM_CONTEXT1_CNTL2, 4);
WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) |
+ PAGE_TABLE_BLOCK_SIZE(radeon_vm_block_size - 9) |
RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
RANGE_PROTECTION_FAULT_ENABLE_DEFAULT |
DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT |
@@ -6151,7 +6164,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[0]))
- radeon_crtc_handle_flip(rdev, 0);
+ radeon_crtc_handle_vblank(rdev, 0);
rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D1 vblank\n");
}
@@ -6177,7 +6190,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[1]))
- radeon_crtc_handle_flip(rdev, 1);
+ radeon_crtc_handle_vblank(rdev, 1);
rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D2 vblank\n");
}
@@ -6203,7 +6216,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[2]))
- radeon_crtc_handle_flip(rdev, 2);
+ radeon_crtc_handle_vblank(rdev, 2);
rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D3 vblank\n");
}
@@ -6229,7 +6242,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[3]))
- radeon_crtc_handle_flip(rdev, 3);
+ radeon_crtc_handle_vblank(rdev, 3);
rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D4 vblank\n");
}
@@ -6255,7 +6268,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[4]))
- radeon_crtc_handle_flip(rdev, 4);
+ radeon_crtc_handle_vblank(rdev, 4);
rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D5 vblank\n");
}
@@ -6281,7 +6294,7 @@ restart_ih:
wake_up(&rdev->irq.vblank_queue);
}
if (atomic_read(&rdev->irq.pflip[5]))
- radeon_crtc_handle_flip(rdev, 5);
+ radeon_crtc_handle_vblank(rdev, 5);
rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D6 vblank\n");
}
diff --git a/drivers/gpu/drm/radeon/si_dma.c b/drivers/gpu/drm/radeon/si_dma.c
index de0ca070122f..e24c94b6d14d 100644
--- a/drivers/gpu/drm/radeon/si_dma.c
+++ b/drivers/gpu/drm/radeon/si_dma.c
@@ -79,7 +79,25 @@ void si_dma_vm_set_page(struct radeon_device *rdev,
trace_radeon_vm_set_page(pe, addr, count, incr, flags);
- if (flags & R600_PTE_SYSTEM) {
+ 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)
@@ -202,8 +220,8 @@ int si_copy_dma(struct radeon_device *rdev,
cur_size_in_bytes = 0xFFFFF;
size_in_bytes -= cur_size_in_bytes;
radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_COPY, 1, 0, 0, cur_size_in_bytes));
- radeon_ring_write(ring, dst_offset & 0xffffffff);
- radeon_ring_write(ring, src_offset & 0xffffffff);
+ radeon_ring_write(ring, lower_32_bits(dst_offset));
+ radeon_ring_write(ring, lower_32_bits(src_offset));
radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xff);
radeon_ring_write(ring, upper_32_bits(src_offset) & 0xff);
src_offset += cur_size_in_bytes;
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index 9a3567bedaae..58918868f894 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -1948,6 +1948,10 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev)
si_pi->cac_weights = cac_weights_cape_verde_pro;
si_pi->dte_data = dte_data_cape_verde;
break;
+ case 0x682C:
+ si_pi->cac_weights = cac_weights_cape_verde_pro;
+ si_pi->dte_data = dte_data_sun_xt;
+ break;
case 0x6825:
case 0x6827:
si_pi->cac_weights = cac_weights_heathrow;
@@ -1971,10 +1975,9 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev)
si_pi->dte_data = dte_data_venus_xt;
break;
case 0x6823:
- si_pi->cac_weights = cac_weights_chelsea_pro;
- si_pi->dte_data = dte_data_venus_pro;
- break;
case 0x682B:
+ case 0x6822:
+ case 0x682A:
si_pi->cac_weights = cac_weights_chelsea_pro;
si_pi->dte_data = dte_data_venus_pro;
break;
@@ -1988,6 +1991,7 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev)
case 0x6601:
case 0x6621:
case 0x6603:
+ case 0x6605:
si_pi->cac_weights = cac_weights_mars_pro;
si_pi->lcac_config = lcac_mars_pro;
si_pi->cac_override = cac_override_oland;
@@ -1998,6 +2002,7 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev)
case 0x6600:
case 0x6606:
case 0x6620:
+ case 0x6604:
si_pi->cac_weights = cac_weights_mars_xt;
si_pi->lcac_config = lcac_mars_pro;
si_pi->cac_override = cac_override_oland;
@@ -2006,6 +2011,8 @@ static void si_initialize_powertune_defaults(struct radeon_device *rdev)
update_dte_from_pl2 = true;
break;
case 0x6611:
+ case 0x6613:
+ case 0x6608:
si_pi->cac_weights = cac_weights_oland_pro;
si_pi->lcac_config = lcac_mars_pro;
si_pi->cac_override = cac_override_oland;
diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h
index 7321283602ce..fd414d34d885 100644
--- a/drivers/gpu/drm/radeon/sid.h
+++ b/drivers/gpu/drm/radeon/sid.h
@@ -362,6 +362,7 @@
#define READ_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 16)
#define WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 18)
#define WRITE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 19)
+#define PAGE_TABLE_BLOCK_SIZE(x) (((x) & 0xF) << 24)
#define VM_CONTEXT1_CNTL 0x1414
#define VM_CONTEXT0_CNTL2 0x1430
#define VM_CONTEXT1_CNTL2 0x1434
diff --git a/drivers/gpu/drm/radeon/uvd_v2_2.c b/drivers/gpu/drm/radeon/uvd_v2_2.c
index d1771004cb52..8bfdadd56598 100644
--- a/drivers/gpu/drm/radeon/uvd_v2_2.c
+++ b/drivers/gpu/drm/radeon/uvd_v2_2.c
@@ -45,7 +45,7 @@ void uvd_v2_2_fence_emit(struct radeon_device *rdev,
radeon_ring_write(ring, PACKET0(UVD_CONTEXT_ID, 0));
radeon_ring_write(ring, fence->seq);
radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA0, 0));
- radeon_ring_write(ring, addr & 0xffffffff);
+ radeon_ring_write(ring, lower_32_bits(addr));
radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_DATA1, 0));
radeon_ring_write(ring, upper_32_bits(addr) & 0xff);
radeon_ring_write(ring, PACKET0(UVD_GPCOM_VCPU_CMD, 0));
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index d8e835ac2c5e..2e3d7b5b0ad7 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -1,6 +1,7 @@
config DRM_RCAR_DU
tristate "DRM Support for R-Car Display Unit"
depends on DRM && ARM
+ depends on ARCH_SHMOBILE || COMPILE_TEST
select DRM_KMS_HELPER
select DRM_KMS_CMA_HELPER
select DRM_GEM_CMA_HELPER
@@ -12,6 +13,7 @@ config DRM_RCAR_DU
config DRM_RCAR_LVDS
bool "R-Car DU LVDS Encoder Support"
depends on DRM_RCAR_DU
+ depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST
help
Enable support the R-Car Display Unit embedded LVDS encoders
(currently only on R8A7790).
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
index 4f3ba93cd91d..289048d1c7b2 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
@@ -57,15 +57,8 @@ static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
return 1;
}
-static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- return MODE_OK;
-}
-
static const struct drm_connector_helper_funcs connector_helper_funcs = {
.get_modes = rcar_du_lvds_connector_get_modes,
- .mode_valid = rcar_du_lvds_connector_mode_valid,
.best_encoder = rcar_du_connector_best_encoder,
};
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
index 41d563adfeaa..ccfe64c7188f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
@@ -25,15 +25,8 @@ static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
return 0;
}
-static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- return MODE_OK;
-}
-
static const struct drm_connector_helper_funcs connector_helper_funcs = {
.get_modes = rcar_du_vga_connector_get_modes,
- .mode_valid = rcar_du_vga_connector_mode_valid,
.best_encoder = rcar_du_connector_best_encoder,
};
diff --git a/drivers/gpu/drm/savage/savage_bci.c b/drivers/gpu/drm/savage/savage_bci.c
index d2b2df9e26f3..c97cdc9ab239 100644
--- a/drivers/gpu/drm/savage/savage_bci.c
+++ b/drivers/gpu/drm/savage/savage_bci.c
@@ -1079,4 +1079,4 @@ const struct drm_ioctl_desc savage_ioctls[] = {
DRM_IOCTL_DEF_DRV(SAVAGE_BCI_EVENT_WAIT, savage_bci_event_wait, DRM_AUTH),
};
-int savage_max_ioctl = DRM_ARRAY_SIZE(savage_ioctls);
+int savage_max_ioctl = ARRAY_SIZE(savage_ioctls);
diff --git a/drivers/gpu/drm/shmobile/Kconfig b/drivers/gpu/drm/shmobile/Kconfig
index 2ee44ca9d67f..a50fe0eeaa0d 100644
--- a/drivers/gpu/drm/shmobile/Kconfig
+++ b/drivers/gpu/drm/shmobile/Kconfig
@@ -1,6 +1,7 @@
config DRM_SHMOBILE
tristate "DRM Support for SH Mobile"
- depends on DRM && (ARM || SUPERH)
+ depends on DRM && ARM
+ depends on ARCH_SHMOBILE || COMPILE_TEST
select BACKLIGHT_CLASS_DEVICE
select DRM_KMS_HELPER
select DRM_KMS_FB_HELPER
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
index e9e5e6d368cc..faf176b2daf9 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
@@ -674,12 +674,6 @@ static int shmob_drm_connector_get_modes(struct drm_connector *connector)
return 1;
}
-static int shmob_drm_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- return MODE_OK;
-}
-
static struct drm_encoder *
shmob_drm_connector_best_encoder(struct drm_connector *connector)
{
@@ -690,7 +684,6 @@ shmob_drm_connector_best_encoder(struct drm_connector *connector)
static const struct drm_connector_helper_funcs connector_helper_funcs = {
.get_modes = shmob_drm_connector_get_modes,
- .mode_valid = shmob_drm_connector_mode_valid,
.best_encoder = shmob_drm_connector_best_encoder,
};
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
index c839c9c89efb..82c84c7fd4f6 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
@@ -185,7 +185,7 @@ static int shmob_drm_load(struct drm_device *dev, unsigned long flags)
goto done;
}
- ret = drm_irq_install(dev);
+ ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
if (ret < 0) {
dev_err(&pdev->dev, "failed to install IRQ handler\n");
goto done;
diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c
index 0573be0d2933..77f288e4a0a6 100644
--- a/drivers/gpu/drm/sis/sis_mm.c
+++ b/drivers/gpu/drm/sis/sis_mm.c
@@ -359,4 +359,4 @@ const struct drm_ioctl_desc sis_ioctls[] = {
DRM_IOCTL_DEF_DRV(SIS_FB_INIT, sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY),
};
-int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls);
+int sis_max_ioctl = ARRAY_SIZE(sis_ioctls);
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
index d43f21bb4596..2c66a8db9da4 100644
--- a/drivers/gpu/drm/tegra/Makefile
+++ b/drivers/gpu/drm/tegra/Makefile
@@ -1,7 +1,6 @@
ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
tegra-drm-y := \
- bus.o \
drm.o \
gem.o \
fb.o \
diff --git a/drivers/gpu/drm/tegra/bus.c b/drivers/gpu/drm/tegra/bus.c
deleted file mode 100644
index 71cef5c13dc8..000000000000
--- a/drivers/gpu/drm/tegra/bus.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2013 NVIDIA Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include "drm.h"
-
-static int drm_host1x_set_busid(struct drm_device *dev,
- struct drm_master *master)
-{
- const char *device = dev_name(dev->dev);
- const char *driver = dev->driver->name;
- const char *bus = dev->dev->bus->name;
- int length;
-
- master->unique_len = strlen(bus) + 1 + strlen(device);
- master->unique_size = master->unique_len;
-
- master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL);
- if (!master->unique)
- return -ENOMEM;
-
- snprintf(master->unique, master->unique_len + 1, "%s:%s", bus, device);
-
- length = strlen(driver) + 1 + master->unique_len;
-
- dev->devname = kmalloc(length + 1, GFP_KERNEL);
- if (!dev->devname)
- return -ENOMEM;
-
- snprintf(dev->devname, length + 1, "%s@%s", driver, master->unique);
-
- return 0;
-}
-
-static struct drm_bus drm_host1x_bus = {
- .bus_type = DRIVER_BUS_HOST1X,
- .set_busid = drm_host1x_set_busid,
-};
-
-int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device)
-{
- struct drm_device *drm;
- int ret;
-
- driver->bus = &drm_host1x_bus;
-
- drm = drm_dev_alloc(driver, &device->dev);
- if (!drm)
- return -ENOMEM;
-
- ret = drm_dev_register(drm, 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, drm->primary->index);
-
- return 0;
-
-err_free:
- drm_dev_unref(drm);
- return ret;
-}
-
-void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device)
-{
- struct tegra_drm *tegra = dev_get_drvdata(&device->dev);
-
- drm_put_dev(tegra->drm);
-}
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index edb871d7d395..ef40381f3909 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -17,6 +17,7 @@
struct tegra_dc_soc_info {
bool supports_interlacing;
+ bool supports_cursor;
};
struct tegra_plane {
@@ -29,6 +30,254 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
return container_of(plane, struct tegra_plane, base);
}
+static unsigned int tegra_dc_format(uint32_t format, uint32_t *swap)
+{
+ /* assume no swapping of fetched data */
+ if (swap)
+ *swap = BYTE_SWAP_NOSWAP;
+
+ switch (format) {
+ case DRM_FORMAT_XBGR8888:
+ return WIN_COLOR_DEPTH_R8G8B8A8;
+
+ case DRM_FORMAT_XRGB8888:
+ return WIN_COLOR_DEPTH_B8G8R8A8;
+
+ case DRM_FORMAT_RGB565:
+ return WIN_COLOR_DEPTH_B5G6R5;
+
+ case DRM_FORMAT_UYVY:
+ return WIN_COLOR_DEPTH_YCbCr422;
+
+ case DRM_FORMAT_YUYV:
+ if (swap)
+ *swap = BYTE_SWAP_SWAP2;
+
+ return WIN_COLOR_DEPTH_YCbCr422;
+
+ case DRM_FORMAT_YUV420:
+ return WIN_COLOR_DEPTH_YCbCr420P;
+
+ case DRM_FORMAT_YUV422:
+ return WIN_COLOR_DEPTH_YCbCr422P;
+
+ default:
+ break;
+ }
+
+ WARN(1, "unsupported pixel format %u, using default\n", format);
+ return WIN_COLOR_DEPTH_B8G8R8A8;
+}
+
+static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar)
+{
+ switch (format) {
+ case WIN_COLOR_DEPTH_YCbCr422:
+ case WIN_COLOR_DEPTH_YUV422:
+ if (planar)
+ *planar = false;
+
+ return true;
+
+ case WIN_COLOR_DEPTH_YCbCr420P:
+ case WIN_COLOR_DEPTH_YUV420P:
+ case WIN_COLOR_DEPTH_YCbCr422P:
+ case WIN_COLOR_DEPTH_YUV422P:
+ case WIN_COLOR_DEPTH_YCbCr422R:
+ case WIN_COLOR_DEPTH_YUV422R:
+ case WIN_COLOR_DEPTH_YCbCr422RA:
+ case WIN_COLOR_DEPTH_YUV422RA:
+ if (planar)
+ *planar = true;
+
+ return true;
+ }
+
+ return false;
+}
+
+static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
+ unsigned int bpp)
+{
+ fixed20_12 outf = dfixed_init(out);
+ fixed20_12 inf = dfixed_init(in);
+ u32 dda_inc;
+ int max;
+
+ if (v)
+ max = 15;
+ else {
+ switch (bpp) {
+ case 2:
+ max = 8;
+ break;
+
+ default:
+ WARN_ON_ONCE(1);
+ /* fallthrough */
+ case 4:
+ max = 4;
+ break;
+ }
+ }
+
+ outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
+ inf.full -= dfixed_const(1);
+
+ dda_inc = dfixed_div(inf, outf);
+ dda_inc = min_t(u32, dda_inc, dfixed_const(max));
+
+ return dda_inc;
+}
+
+static inline u32 compute_initial_dda(unsigned int in)
+{
+ fixed20_12 inf = dfixed_init(in);
+ return dfixed_frac(inf);
+}
+
+static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
+ const struct tegra_dc_window *window)
+{
+ unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
+ unsigned long value;
+ bool yuv, planar;
+
+ /*
+ * For YUV planar modes, the number of bytes per pixel takes into
+ * account only the luma component and therefore is 1.
+ */
+ yuv = tegra_dc_format_is_yuv(window->format, &planar);
+ if (!yuv)
+ bpp = window->bits_per_pixel / 8;
+ else
+ bpp = planar ? 1 : 2;
+
+ value = WINDOW_A_SELECT << index;
+ tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
+
+ tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH);
+ tegra_dc_writel(dc, window->swap, DC_WIN_BYTE_SWAP);
+
+ value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
+ tegra_dc_writel(dc, value, DC_WIN_POSITION);
+
+ value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
+ tegra_dc_writel(dc, value, DC_WIN_SIZE);
+
+ h_offset = window->src.x * bpp;
+ v_offset = window->src.y;
+ h_size = window->src.w * bpp;
+ v_size = window->src.h;
+
+ value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
+ tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE);
+
+ /*
+ * For DDA computations the number of bytes per pixel for YUV planar
+ * modes needs to take into account all Y, U and V components.
+ */
+ if (yuv && planar)
+ bpp = 2;
+
+ h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
+ v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
+
+ value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
+ tegra_dc_writel(dc, value, DC_WIN_DDA_INC);
+
+ h_dda = compute_initial_dda(window->src.x);
+ v_dda = compute_initial_dda(window->src.y);
+
+ tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA);
+ tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA);
+
+ tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
+ tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
+
+ tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR);
+
+ if (yuv && planar) {
+ tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U);
+ tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V);
+ value = window->stride[1] << 16 | window->stride[0];
+ tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE);
+ } else {
+ tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE);
+ }
+
+ if (window->bottom_up)
+ v_offset += window->src.h - 1;
+
+ 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;
+ } else {
+ value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
+ DC_WIN_BUFFER_ADDR_MODE_LINEAR;
+ }
+
+ tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
+
+ value = WIN_ENABLE;
+
+ if (yuv) {
+ /* setup default colorspace conversion coefficients */
+ tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF);
+ tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB);
+ tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR);
+ tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR);
+ tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG);
+ tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG);
+ tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB);
+ tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB);
+
+ value |= CSC_ENABLE;
+ } else if (window->bits_per_pixel < 24) {
+ value |= COLOR_EXPAND;
+ }
+
+ if (window->bottom_up)
+ value |= V_DIRECTION;
+
+ tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
+
+ /*
+ * Disable blending and assume Window A is the bottom-most window,
+ * Window C is the top-most window and Window B is in the middle.
+ */
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY);
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN);
+
+ switch (index) {
+ case 0:
+ tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X);
+ tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
+ tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
+ break;
+
+ case 1:
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
+ tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
+ tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
+ break;
+
+ case 2:
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y);
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY);
+ break;
+ }
+
+ tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL);
+
+ return 0;
+}
+
static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x,
int crtc_y, unsigned int crtc_w,
@@ -49,7 +298,7 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
window.dst.y = crtc_y;
window.dst.w = crtc_w;
window.dst.h = crtc_h;
- window.format = tegra_dc_format(fb->pixel_format);
+ 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);
@@ -117,6 +366,7 @@ static const uint32_t plane_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB565,
DRM_FORMAT_UYVY,
+ DRM_FORMAT_YUYV,
DRM_FORMAT_YUV420,
DRM_FORMAT_YUV422,
};
@@ -150,9 +400,9 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
struct drm_framebuffer *fb)
{
- unsigned int format = tegra_dc_format(fb->pixel_format);
struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
unsigned int h_offset = 0, v_offset = 0;
+ unsigned int format, swap;
unsigned long value;
tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
@@ -162,7 +412,10 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR);
tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE);
+
+ format = tegra_dc_format(fb->pixel_format, &swap);
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 |
@@ -177,13 +430,13 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
/* make sure bottom-up buffers are properly displayed */
if (tegra_fb_is_bottom_up(fb)) {
value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
- value |= INVERT_V;
+ value |= V_DIRECTION;
tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
v_offset += fb->height - 1;
} else {
value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
- value &= ~INVERT_V;
+ value &= ~V_DIRECTION;
tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
}
@@ -225,6 +478,109 @@ void tegra_dc_disable_vblank(struct tegra_dc *dc)
spin_unlock_irqrestore(&dc->lock, flags);
}
+static int tegra_dc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file,
+ uint32_t handle, uint32_t width,
+ uint32_t height, int32_t hot_x, int32_t hot_y)
+{
+ unsigned long value = CURSOR_CLIP_DISPLAY;
+ struct tegra_dc *dc = to_tegra_dc(crtc);
+ struct drm_gem_object *gem;
+ struct tegra_bo *bo = NULL;
+
+ if (!dc->soc->supports_cursor)
+ return -ENXIO;
+
+ if (width != height)
+ return -EINVAL;
+
+ switch (width) {
+ case 32:
+ value |= CURSOR_SIZE_32x32;
+ break;
+
+ case 64:
+ value |= CURSOR_SIZE_64x64;
+ break;
+
+ case 128:
+ value |= CURSOR_SIZE_128x128;
+
+ case 256:
+ value |= CURSOR_SIZE_256x256;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (handle) {
+ gem = drm_gem_object_lookup(crtc->dev, file, handle);
+ if (!gem)
+ return -ENOENT;
+
+ bo = to_tegra_bo(gem);
+ }
+
+ if (bo) {
+ unsigned long addr = (bo->paddr & 0xfffffc00) >> 10;
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ unsigned long high = (bo->paddr & 0xfffffffc) >> 32;
+#endif
+
+ tegra_dc_writel(dc, value | addr, DC_DISP_CURSOR_START_ADDR);
+
+#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+ tegra_dc_writel(dc, high, DC_DISP_CURSOR_START_ADDR_HI);
+#endif
+
+ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+ value |= CURSOR_ENABLE;
+ tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+
+ value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
+ value &= ~CURSOR_DST_BLEND_MASK;
+ value &= ~CURSOR_SRC_BLEND_MASK;
+ value |= CURSOR_MODE_NORMAL;
+ value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
+ value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
+ value |= CURSOR_ALPHA;
+ tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
+ } else {
+ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+ value &= ~CURSOR_ENABLE;
+ tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+ }
+
+ tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+ tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+ return 0;
+}
+
+static int tegra_dc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+ struct tegra_dc *dc = to_tegra_dc(crtc);
+ unsigned long value;
+
+ if (!dc->soc->supports_cursor)
+ return -ENXIO;
+
+ value = ((y & 0x3fff) << 16) | (x & 0x3fff);
+ tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
+
+ tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+ /* XXX: only required on generations earlier than Tegra124? */
+ tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+ return 0;
+}
+
static void tegra_dc_finish_page_flip(struct tegra_dc *dc)
{
struct drm_device *drm = dc->base.dev;
@@ -301,6 +657,8 @@ static void tegra_dc_destroy(struct drm_crtc *crtc)
}
static const struct drm_crtc_funcs tegra_crtc_funcs = {
+ .cursor_set2 = tegra_dc_cursor_set2,
+ .cursor_move = tegra_dc_cursor_move,
.page_flip = tegra_dc_page_flip,
.set_config = drm_crtc_helper_set_config,
.destroy = tegra_dc_destroy,
@@ -334,52 +692,11 @@ static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
return true;
}
-static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
- unsigned int bpp)
-{
- fixed20_12 outf = dfixed_init(out);
- fixed20_12 inf = dfixed_init(in);
- u32 dda_inc;
- int max;
-
- if (v)
- max = 15;
- else {
- switch (bpp) {
- case 2:
- max = 8;
- break;
-
- default:
- WARN_ON_ONCE(1);
- /* fallthrough */
- case 4:
- max = 4;
- break;
- }
- }
-
- outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
- inf.full -= dfixed_const(1);
-
- dda_inc = dfixed_div(inf, outf);
- dda_inc = min_t(u32, dda_inc, dfixed_const(max));
-
- return dda_inc;
-}
-
-static inline u32 compute_initial_dda(unsigned int in)
-{
- fixed20_12 inf = dfixed_init(in);
- return dfixed_frac(inf);
-}
-
static int tegra_dc_set_timings(struct tegra_dc *dc,
struct drm_display_mode *mode)
{
- /* TODO: For HDMI compliance, h & v ref_to_sync should be set to 1 */
- unsigned int h_ref_to_sync = 0;
- unsigned int v_ref_to_sync = 0;
+ unsigned int h_ref_to_sync = 1;
+ unsigned int v_ref_to_sync = 1;
unsigned long value;
tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
@@ -406,13 +723,14 @@ static int tegra_dc_set_timings(struct tegra_dc *dc,
}
static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
- struct drm_display_mode *mode,
- unsigned long *div)
+ struct drm_display_mode *mode)
{
- unsigned long pclk = mode->clock * 1000, rate;
+ unsigned long pclk = mode->clock * 1000;
struct tegra_dc *dc = to_tegra_dc(crtc);
struct tegra_output *output = NULL;
struct drm_encoder *encoder;
+ unsigned int div;
+ u32 value;
long err;
list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, head)
@@ -425,221 +743,23 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
return -ENODEV;
/*
- * This assumes that the display controller will divide its parent
- * clock by 2 to generate the pixel clock.
+ * This assumes that the parent clock is pll_d_out0 or pll_d2_out
+ * respectively, each of which divides the base pll_d by 2.
*/
- err = tegra_output_setup_clock(output, dc->clk, pclk * 2);
+ err = tegra_output_setup_clock(output, dc->clk, pclk, &div);
if (err < 0) {
dev_err(dc->dev, "failed to setup clock: %ld\n", err);
return err;
}
- rate = clk_get_rate(dc->clk);
- *div = (rate * 2 / pclk) - 2;
-
- DRM_DEBUG_KMS("rate: %lu, div: %lu\n", rate, *div);
-
- return 0;
-}
-
-static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar)
-{
- switch (format) {
- case WIN_COLOR_DEPTH_YCbCr422:
- case WIN_COLOR_DEPTH_YUV422:
- if (planar)
- *planar = false;
-
- return true;
-
- case WIN_COLOR_DEPTH_YCbCr420P:
- case WIN_COLOR_DEPTH_YUV420P:
- case WIN_COLOR_DEPTH_YCbCr422P:
- case WIN_COLOR_DEPTH_YUV422P:
- case WIN_COLOR_DEPTH_YCbCr422R:
- case WIN_COLOR_DEPTH_YUV422R:
- case WIN_COLOR_DEPTH_YCbCr422RA:
- case WIN_COLOR_DEPTH_YUV422RA:
- if (planar)
- *planar = true;
-
- return true;
- }
-
- return false;
-}
-
-int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
- const struct tegra_dc_window *window)
-{
- unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
- unsigned long value;
- bool yuv, planar;
-
- /*
- * For YUV planar modes, the number of bytes per pixel takes into
- * account only the luma component and therefore is 1.
- */
- yuv = tegra_dc_format_is_yuv(window->format, &planar);
- if (!yuv)
- bpp = window->bits_per_pixel / 8;
- else
- bpp = planar ? 1 : 2;
-
- value = WINDOW_A_SELECT << index;
- tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
-
- tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH);
- tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
-
- value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
- tegra_dc_writel(dc, value, DC_WIN_POSITION);
-
- value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
- tegra_dc_writel(dc, value, DC_WIN_SIZE);
-
- h_offset = window->src.x * bpp;
- v_offset = window->src.y;
- h_size = window->src.w * bpp;
- v_size = window->src.h;
-
- value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
- tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE);
-
- /*
- * For DDA computations the number of bytes per pixel for YUV planar
- * modes needs to take into account all Y, U and V components.
- */
- if (yuv && planar)
- bpp = 2;
-
- h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
- v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
-
- value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
- tegra_dc_writel(dc, value, DC_WIN_DDA_INC);
-
- h_dda = compute_initial_dda(window->src.x);
- v_dda = compute_initial_dda(window->src.y);
-
- tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA);
- tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA);
-
- tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
- tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
-
- tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR);
-
- if (yuv && planar) {
- tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U);
- tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V);
- value = window->stride[1] << 16 | window->stride[0];
- tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE);
- } else {
- tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE);
- }
-
- if (window->bottom_up)
- v_offset += window->src.h - 1;
+ DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), div);
- 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;
- } else {
- value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
- DC_WIN_BUFFER_ADDR_MODE_LINEAR;
- }
-
- tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
-
- value = WIN_ENABLE;
-
- if (yuv) {
- /* setup default colorspace conversion coefficients */
- tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF);
- tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB);
- tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR);
- tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR);
- tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG);
- tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG);
- tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB);
- tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB);
-
- value |= CSC_ENABLE;
- } else if (window->bits_per_pixel < 24) {
- value |= COLOR_EXPAND;
- }
-
- if (window->bottom_up)
- value |= INVERT_V;
-
- tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
-
- /*
- * Disable blending and assume Window A is the bottom-most window,
- * Window C is the top-most window and Window B is in the middle.
- */
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY);
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN);
-
- switch (index) {
- case 0:
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
- break;
-
- case 1:
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
- break;
-
- case 2:
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y);
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY);
- break;
- }
-
- tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL);
- tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL);
+ value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1;
+ tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
return 0;
}
-unsigned int tegra_dc_format(uint32_t format)
-{
- switch (format) {
- case DRM_FORMAT_XBGR8888:
- return WIN_COLOR_DEPTH_R8G8B8A8;
-
- case DRM_FORMAT_XRGB8888:
- return WIN_COLOR_DEPTH_B8G8R8A8;
-
- case DRM_FORMAT_RGB565:
- return WIN_COLOR_DEPTH_B5G6R5;
-
- case DRM_FORMAT_UYVY:
- return WIN_COLOR_DEPTH_YCbCr422;
-
- case DRM_FORMAT_YUV420:
- return WIN_COLOR_DEPTH_YCbCr420P;
-
- case DRM_FORMAT_YUV422:
- return WIN_COLOR_DEPTH_YCbCr422P;
-
- default:
- break;
- }
-
- WARN(1, "unsupported pixel format %u, using default\n", format);
- return WIN_COLOR_DEPTH_B8G8R8A8;
-}
-
static int tegra_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted,
@@ -648,12 +768,12 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0);
struct tegra_dc *dc = to_tegra_dc(crtc);
struct tegra_dc_window window;
- unsigned long div, value;
+ u32 value;
int err;
drm_vblank_pre_modeset(crtc->dev, dc->pipe);
- err = tegra_crtc_setup_clk(crtc, mode, &div);
+ err = tegra_crtc_setup_clk(crtc, mode);
if (err) {
dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
return err;
@@ -669,9 +789,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
}
- value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1;
- tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
-
/* setup window parameters */
memset(&window, 0, sizeof(window));
window.src.x = 0;
@@ -682,7 +799,8 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
window.dst.y = 0;
window.dst.w = mode->hdisplay;
window.dst.h = mode->vdisplay;
- window.format = tegra_dc_format(crtc->primary->fb->pixel_format);
+ window.format = tegra_dc_format(crtc->primary->fb->pixel_format,
+ &window.swap);
window.bits_per_pixel = crtc->primary->fb->bits_per_pixel;
window.stride[0] = crtc->primary->fb->pitches[0];
window.base[0] = bo->paddr;
@@ -728,10 +846,6 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
- value = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
- PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
- tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
/* initialize timer */
value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
@@ -991,6 +1105,8 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data)
DUMP_REG(DC_DISP_SD_BL_CONTROL);
DUMP_REG(DC_DISP_SD_HW_K_VALUES);
DUMP_REG(DC_DISP_SD_MAN_K_VALUES);
+ DUMP_REG(DC_DISP_CURSOR_START_ADDR_HI);
+ DUMP_REG(DC_DISP_BLEND_CURSOR_CONTROL);
DUMP_REG(DC_WIN_WIN_OPTIONS);
DUMP_REG(DC_WIN_BYTE_SWAP);
DUMP_REG(DC_WIN_BUFFER_CONTROL);
@@ -1096,26 +1212,26 @@ static int tegra_dc_debugfs_exit(struct tegra_dc *dc)
static int tegra_dc_init(struct host1x_client *client)
{
- struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+ struct drm_device *drm = dev_get_drvdata(client->parent);
struct tegra_dc *dc = host1x_client_to_dc(client);
int err;
- drm_crtc_init(tegra->drm, &dc->base, &tegra_crtc_funcs);
+ 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);
- err = tegra_dc_rgb_init(tegra->drm, dc);
+ err = tegra_dc_rgb_init(drm, dc);
if (err < 0 && err != -ENODEV) {
dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
return err;
}
- err = tegra_dc_add_planes(tegra->drm, dc);
+ err = tegra_dc_add_planes(drm, dc);
if (err < 0)
return err;
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
- err = tegra_dc_debugfs_init(dc, tegra->drm->primary);
+ err = tegra_dc_debugfs_init(dc, drm->primary);
if (err < 0)
dev_err(dc->dev, "debugfs setup failed: %d\n", err);
}
@@ -1160,14 +1276,17 @@ 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,
};
static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
.supports_interlacing = false,
+ .supports_cursor = false,
};
static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
.supports_interlacing = true,
+ .supports_cursor = true,
};
static const struct of_device_id tegra_dc_of_match[] = {
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index c94101494826..78c5feff95d2 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -67,10 +67,12 @@
#define WIN_A_ACT_REQ (1 << 1)
#define WIN_B_ACT_REQ (1 << 2)
#define WIN_C_ACT_REQ (1 << 3)
+#define CURSOR_ACT_REQ (1 << 7)
#define GENERAL_UPDATE (1 << 8)
#define WIN_A_UPDATE (1 << 9)
#define WIN_B_UPDATE (1 << 10)
#define WIN_C_UPDATE (1 << 11)
+#define CURSOR_UPDATE (1 << 15)
#define NC_HOST_TRIG (1 << 24)
#define DC_CMD_DISPLAY_WINDOW_HEADER 0x042
@@ -116,9 +118,10 @@
#define DC_DISP_DISP_SIGNAL_OPTIONS1 0x401
#define DC_DISP_DISP_WIN_OPTIONS 0x402
-#define HDMI_ENABLE (1 << 30)
-#define DSI_ENABLE (1 << 29)
-#define SOR_ENABLE (1 << 25)
+#define HDMI_ENABLE (1 << 30)
+#define DSI_ENABLE (1 << 29)
+#define SOR_ENABLE (1 << 25)
+#define CURSOR_ENABLE (1 << 16)
#define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403
#define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24)
@@ -266,6 +269,14 @@
#define DC_DISP_CURSOR_BACKGROUND 0x43d
#define DC_DISP_CURSOR_START_ADDR 0x43e
+#define CURSOR_CLIP_DISPLAY (0 << 28)
+#define CURSOR_CLIP_WIN_A (1 << 28)
+#define CURSOR_CLIP_WIN_B (2 << 28)
+#define CURSOR_CLIP_WIN_C (3 << 28)
+#define CURSOR_SIZE_32x32 (0 << 24)
+#define CURSOR_SIZE_64x64 (1 << 24)
+#define CURSOR_SIZE_128x128 (2 << 24)
+#define CURSOR_SIZE_256x256 (3 << 24)
#define DC_DISP_CURSOR_START_ADDR_NS 0x43f
#define DC_DISP_CURSOR_POSITION 0x440
@@ -302,6 +313,19 @@
#define INTERLACE_START (1 << 1)
#define INTERLACE_ENABLE (1 << 0)
+#define DC_DISP_CURSOR_START_ADDR_HI 0x4ec
+#define DC_DISP_BLEND_CURSOR_CONTROL 0x4f1
+#define CURSOR_MODE_LEGACY (0 << 24)
+#define CURSOR_MODE_NORMAL (1 << 24)
+#define CURSOR_DST_BLEND_ZERO (0 << 16)
+#define CURSOR_DST_BLEND_K1 (1 << 16)
+#define CURSOR_DST_BLEND_NEG_K1_TIMES_SRC (2 << 16)
+#define CURSOR_DST_BLEND_MASK (3 << 16)
+#define CURSOR_SRC_BLEND_K1 (0 << 8)
+#define CURSOR_SRC_BLEND_K1_TIMES_SRC (1 << 8)
+#define CURSOR_SRC_BLEND_MASK (3 << 8)
+#define CURSOR_ALPHA 0xff
+
#define DC_WIN_CSC_YOF 0x611
#define DC_WIN_CSC_KYRGB 0x612
#define DC_WIN_CSC_KUR 0x613
@@ -312,7 +336,8 @@
#define DC_WIN_CSC_KVB 0x618
#define DC_WIN_WIN_OPTIONS 0x700
-#define INVERT_V (1 << 2)
+#define H_DIRECTION (1 << 0)
+#define V_DIRECTION (1 << 2)
#define COLOR_EXPAND (1 << 6)
#define CSC_ENABLE (1 << 18)
#define WIN_ENABLE (1 << 30)
diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c
index 005c19bd92df..3f132e356e9c 100644
--- a/drivers/gpu/drm/tegra/dpaux.c
+++ b/drivers/gpu/drm/tegra/dpaux.c
@@ -15,6 +15,7 @@
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_panel.h>
@@ -41,6 +42,7 @@ struct tegra_dpaux {
struct regulator *vdd;
struct completion complete;
+ struct work_struct work;
struct list_head list;
};
@@ -49,6 +51,11 @@ static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux)
return container_of(aux, struct tegra_dpaux, aux);
}
+static inline struct tegra_dpaux *work_to_dpaux(struct work_struct *work)
+{
+ return container_of(work, struct tegra_dpaux, work);
+}
+
static inline unsigned long tegra_dpaux_readl(struct tegra_dpaux *dpaux,
unsigned long offset)
{
@@ -231,6 +238,14 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
return ret;
}
+static void tegra_dpaux_hotplug(struct work_struct *work)
+{
+ struct tegra_dpaux *dpaux = work_to_dpaux(work);
+
+ if (dpaux->output)
+ drm_helper_hpd_irq_event(dpaux->output->connector.dev);
+}
+
static irqreturn_t tegra_dpaux_irq(int irq, void *data)
{
struct tegra_dpaux *dpaux = data;
@@ -241,16 +256,8 @@ static irqreturn_t tegra_dpaux_irq(int irq, void *data)
value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX);
tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX);
- if (value & DPAUX_INTR_PLUG_EVENT) {
- if (dpaux->output) {
- drm_helper_hpd_irq_event(dpaux->output->connector.dev);
- }
- }
-
- if (value & DPAUX_INTR_UNPLUG_EVENT) {
- if (dpaux->output)
- drm_helper_hpd_irq_event(dpaux->output->connector.dev);
- }
+ if (value & (DPAUX_INTR_PLUG_EVENT | DPAUX_INTR_UNPLUG_EVENT))
+ schedule_work(&dpaux->work);
if (value & DPAUX_INTR_IRQ_EVENT) {
/* TODO: handle this */
@@ -273,6 +280,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
if (!dpaux)
return -ENOMEM;
+ INIT_WORK(&dpaux->work, tegra_dpaux_hotplug);
init_completion(&dpaux->complete);
INIT_LIST_HEAD(&dpaux->list);
dpaux->dev = &pdev->dev;
@@ -332,7 +340,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
dpaux->aux.transfer = tegra_dpaux_transfer;
dpaux->aux.dev = &pdev->dev;
- err = drm_dp_aux_register_i2c_bus(&dpaux->aux);
+ err = drm_dp_aux_register(&dpaux->aux);
if (err < 0)
return err;
@@ -355,12 +363,14 @@ static int tegra_dpaux_remove(struct platform_device *pdev)
{
struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
- drm_dp_aux_unregister_i2c_bus(&dpaux->aux);
+ drm_dp_aux_unregister(&dpaux->aux);
mutex_lock(&dpaux_lock);
list_del(&dpaux->list);
mutex_unlock(&dpaux_lock);
+ cancel_work_sync(&dpaux->work);
+
clk_disable_unprepare(dpaux->clk_parent);
reset_control_assert(dpaux->rst);
clk_disable_unprepare(dpaux->clk);
@@ -404,6 +414,7 @@ int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output)
unsigned long timeout;
int err;
+ output->connector.polled = DRM_CONNECTOR_POLL_HPD;
dpaux->output = output;
err = regulator_enable(dpaux->vdd);
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 6f5b6e2f552e..3396f9f6a9f7 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -33,7 +33,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
if (!tegra)
return -ENOMEM;
- dev_set_drvdata(drm->dev, tegra);
mutex_init(&tegra->clients_lock);
INIT_LIST_HEAD(&tegra->clients);
drm->dev_private = tegra;
@@ -640,14 +639,40 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra,
return 0;
}
-static int host1x_drm_probe(struct host1x_device *device)
+static int host1x_drm_probe(struct host1x_device *dev)
{
- return drm_host1x_init(&tegra_drm_driver, device);
+ struct drm_driver *driver = &tegra_drm_driver;
+ struct drm_device *drm;
+ int err;
+
+ drm = drm_dev_alloc(driver, &dev->dev);
+ if (!drm)
+ return -ENOMEM;
+
+ drm_dev_set_unique(drm, dev_name(&dev->dev));
+ dev_set_drvdata(&dev->dev, drm);
+
+ err = drm_dev_register(drm, 0);
+ if (err < 0)
+ goto unref;
+
+ DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
+ driver->major, driver->minor, driver->patchlevel,
+ driver->date, drm->primary->index);
+
+ return 0;
+
+unref:
+ drm_dev_unref(drm);
+ return err;
}
-static int host1x_drm_remove(struct host1x_device *device)
+static int host1x_drm_remove(struct host1x_device *dev)
{
- drm_host1x_exit(&tegra_drm_driver, device);
+ struct drm_device *drm = dev_get_drvdata(&dev->dev);
+
+ drm_dev_unregister(drm);
+ drm_dev_unref(drm);
return 0;
}
@@ -666,6 +691,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra114-gr3d", },
{ .compatible = "nvidia,tegra124-dc", },
{ .compatible = "nvidia,tegra124-sor", },
+ { .compatible = "nvidia,tegra124-hdmi", },
{ /* sentinel */ }
};
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 126332c3ecbb..6b8fe9d86ed4 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -80,13 +80,13 @@ host1x_to_drm_client(struct host1x_client *client)
return container_of(client, struct tegra_drm_client, base);
}
-extern int tegra_drm_register_client(struct tegra_drm *tegra,
- struct tegra_drm_client *client);
-extern int tegra_drm_unregister_client(struct tegra_drm *tegra,
- struct tegra_drm_client *client);
+int tegra_drm_register_client(struct tegra_drm *tegra,
+ struct tegra_drm_client *client);
+int tegra_drm_unregister_client(struct tegra_drm *tegra,
+ struct tegra_drm_client *client);
-extern int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
-extern int tegra_drm_exit(struct tegra_drm *tegra);
+int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
+int tegra_drm_exit(struct tegra_drm *tegra);
struct tegra_dc_soc_info;
struct tegra_output;
@@ -156,6 +156,7 @@ struct tegra_dc_window {
} dst;
unsigned int bits_per_pixel;
unsigned int format;
+ unsigned int swap;
unsigned int stride[2];
unsigned long base[3];
bool bottom_up;
@@ -163,19 +164,15 @@ struct tegra_dc_window {
};
/* from dc.c */
-extern unsigned int tegra_dc_format(uint32_t format);
-extern int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
- const struct tegra_dc_window *window);
-extern void tegra_dc_enable_vblank(struct tegra_dc *dc);
-extern void tegra_dc_disable_vblank(struct tegra_dc *dc);
-extern void tegra_dc_cancel_page_flip(struct drm_crtc *crtc,
- struct drm_file *file);
+void tegra_dc_enable_vblank(struct tegra_dc *dc);
+void tegra_dc_disable_vblank(struct tegra_dc *dc);
+void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
struct tegra_output_ops {
int (*enable)(struct tegra_output *output);
int (*disable)(struct tegra_output *output);
int (*setup_clock)(struct tegra_output *output, struct clk *clk,
- unsigned long pclk);
+ unsigned long pclk, unsigned int *div);
int (*check_mode)(struct tegra_output *output,
struct drm_display_mode *mode,
enum drm_mode_status *status);
@@ -233,10 +230,11 @@ static inline int tegra_output_disable(struct tegra_output *output)
}
static inline int tegra_output_setup_clock(struct tegra_output *output,
- struct clk *clk, unsigned long pclk)
+ struct clk *clk, unsigned long pclk,
+ unsigned int *div)
{
if (output && output->ops && output->ops->setup_clock)
- return output->ops->setup_clock(output, clk, pclk);
+ return output->ops->setup_clock(output, clk, pclk, div);
return output ? -ENOSYS : -EINVAL;
}
@@ -251,27 +249,21 @@ static inline int tegra_output_check_mode(struct tegra_output *output,
return output ? -ENOSYS : -EINVAL;
}
-/* from bus.c */
-int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device);
-void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device);
-
/* from rgb.c */
-extern int tegra_dc_rgb_probe(struct tegra_dc *dc);
-extern int tegra_dc_rgb_remove(struct tegra_dc *dc);
-extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
-extern int tegra_dc_rgb_exit(struct tegra_dc *dc);
+int tegra_dc_rgb_probe(struct tegra_dc *dc);
+int tegra_dc_rgb_remove(struct tegra_dc *dc);
+int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
+int tegra_dc_rgb_exit(struct tegra_dc *dc);
/* from output.c */
-extern int tegra_output_probe(struct tegra_output *output);
-extern int tegra_output_remove(struct tegra_output *output);
-extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
-extern int tegra_output_exit(struct tegra_output *output);
+int tegra_output_probe(struct tegra_output *output);
+int tegra_output_remove(struct tegra_output *output);
+int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
+int tegra_output_exit(struct tegra_output *output);
/* from dpaux.c */
-
struct tegra_dpaux;
struct drm_dp_link;
-struct drm_dp_aux;
struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np);
enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux);
@@ -288,10 +280,10 @@ 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);
-extern int tegra_drm_fb_init(struct drm_device *drm);
-extern void tegra_drm_fb_exit(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
-extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
+void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
#endif
extern struct platform_driver tegra_dc_driver;
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 0e599f0417c0..bd56f2affa78 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -14,6 +14,8 @@
#include <linux/platform_device.h>
#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
@@ -43,11 +45,15 @@ struct tegra_dsi {
struct drm_minor *minor;
struct dentry *debugfs;
+ unsigned long flags;
enum mipi_dsi_pixel_format format;
unsigned int lanes;
struct tegra_mipi_device *mipi;
struct mipi_dsi_host host;
+
+ struct regulator *vdd;
+ bool enabled;
};
static inline struct tegra_dsi *
@@ -244,8 +250,10 @@ static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi)
#define PKT_LP (1 << 30)
#define NUM_PKT_SEQ 12
-/* non-burst mode with sync-end */
-static const u32 pkt_seq_vnb_syne[NUM_PKT_SEQ] = {
+/*
+ * non-burst mode with sync pulses
+ */
+static const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = {
[ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
@@ -280,6 +288,36 @@ static const u32 pkt_seq_vnb_syne[NUM_PKT_SEQ] = {
PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4),
};
+/*
+ * non-burst mode with sync events
+ */
+static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = {
+ [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 1] = 0,
+ [ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 3] = 0,
+ [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 5] = 0,
+ [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
+ PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
+ [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
+ [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 9] = 0,
+ [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
+ PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
+ [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
+};
+
static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)
{
struct mipi_dphy_timing timing;
@@ -361,28 +399,70 @@ static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format,
return 0;
}
+static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format,
+ enum tegra_dsi_format *fmt)
+{
+ switch (format) {
+ case MIPI_DSI_FMT_RGB888:
+ *fmt = TEGRA_DSI_FORMAT_24P;
+ break;
+
+ case MIPI_DSI_FMT_RGB666:
+ *fmt = TEGRA_DSI_FORMAT_18NP;
+ break;
+
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ *fmt = TEGRA_DSI_FORMAT_18P;
+ break;
+
+ case MIPI_DSI_FMT_RGB565:
+ *fmt = TEGRA_DSI_FORMAT_16P;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int tegra_output_dsi_enable(struct tegra_output *output)
{
struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
struct drm_display_mode *mode = &dc->base.mode;
unsigned int hact, hsw, hbp, hfp, i, mul, div;
struct tegra_dsi *dsi = to_dsi(output);
- /* FIXME: don't hardcode this */
- const u32 *pkt_seq = pkt_seq_vnb_syne;
+ enum tegra_dsi_format format;
unsigned long value;
+ const u32 *pkt_seq;
int err;
+ if (dsi->enabled)
+ return 0;
+
+ if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
+ DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n");
+ pkt_seq = pkt_seq_video_non_burst_sync_pulses;
+ } else {
+ DRM_DEBUG_KMS("Non-burst video mode with sync events\n");
+ pkt_seq = pkt_seq_video_non_burst_sync_events;
+ }
+
err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
if (err < 0)
return err;
+ err = tegra_dsi_get_format(dsi->format, &format);
+ if (err < 0)
+ return err;
+
err = clk_enable(dsi->clk);
if (err < 0)
return err;
reset_control_deassert(dsi->rst);
- value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(dsi->format) |
+ value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) |
DSI_CONTROL_LANES(dsi->lanes - 1) |
DSI_CONTROL_SOURCE(dc->pipe);
tegra_dsi_writel(dsi, value, DSI_CONTROL);
@@ -454,6 +534,8 @@ static int tegra_output_dsi_enable(struct tegra_output *output)
value |= DSI_POWER_CONTROL_ENABLE;
tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
+ dsi->enabled = true;
+
return 0;
}
@@ -463,9 +545,12 @@ static int tegra_output_dsi_disable(struct tegra_output *output)
struct tegra_dsi *dsi = to_dsi(output);
unsigned long value;
+ if (!dsi->enabled)
+ return 0;
+
/* disable DSI controller */
value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
- value &= DSI_POWER_CONTROL_ENABLE;
+ value &= ~DSI_POWER_CONTROL_ENABLE;
tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
/*
@@ -492,30 +577,44 @@ static int tegra_output_dsi_disable(struct tegra_output *output)
clk_disable(dsi->clk);
+ dsi->enabled = false;
+
return 0;
}
static int tegra_output_dsi_setup_clock(struct tegra_output *output,
- struct clk *clk, unsigned long pclk)
+ struct clk *clk, unsigned long pclk,
+ unsigned int *divp)
{
struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
struct drm_display_mode *mode = &dc->base.mode;
unsigned int timeout, mul, div, vrefresh;
struct tegra_dsi *dsi = to_dsi(output);
unsigned long bclk, plld, value;
- struct clk *base;
int err;
err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
if (err < 0)
return err;
+ DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", mul, div, dsi->lanes);
vrefresh = drm_mode_vrefresh(mode);
+ DRM_DEBUG_KMS("vrefresh: %u\n", vrefresh);
- pclk = mode->htotal * mode->vtotal * vrefresh;
+ /* compute byte clock */
bclk = (pclk * mul) / (div * dsi->lanes);
- plld = DIV_ROUND_UP(bclk * 8, 1000000);
- pclk = (plld * 1000000) / 2;
+
+ /*
+ * Compute bit clock and round up to the next MHz.
+ */
+ plld = DIV_ROUND_UP(bclk * 8, 1000000) * 1000000;
+
+ /*
+ * We divide the frequency by two here, but we make up for that by
+ * setting the shift clock divider (further below) to half of the
+ * correct value.
+ */
+ plld /= 2;
err = clk_set_parent(clk, dsi->clk_parent);
if (err < 0) {
@@ -523,20 +622,26 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output,
return err;
}
- base = clk_get_parent(dsi->clk_parent);
-
- /*
- * This assumes that the parent clock is pll_d_out0 or pll_d2_out
- * respectively, each of which divides the base pll_d by 2.
- */
- err = clk_set_rate(base, pclk * 2);
+ err = clk_set_rate(dsi->clk_parent, plld);
if (err < 0) {
dev_err(dsi->dev, "failed to set base clock rate to %lu Hz\n",
- pclk * 2);
+ plld);
return err;
}
/*
+ * Derive pixel clock from bit clock using the shift clock divider.
+ * Note that this is only half of what we would expect, but we need
+ * that to make up for the fact that we divided the bit clock by a
+ * factor of two above.
+ *
+ * It's not clear exactly why this is necessary, but the display is
+ * not working properly otherwise. Perhaps the PLLs cannot generate
+ * frequencies sufficiently high.
+ */
+ *divp = ((8 * mul) / (div * dsi->lanes)) - 2;
+
+ /*
* XXX: Move the below somewhere else so that we don't need to have
* access to the vrefresh in this function?
*/
@@ -610,61 +715,32 @@ static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi)
static int tegra_dsi_init(struct host1x_client *client)
{
- struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+ struct drm_device *drm = dev_get_drvdata(client->parent);
struct tegra_dsi *dsi = host1x_client_to_dsi(client);
- unsigned long value, i;
int err;
dsi->output.type = TEGRA_OUTPUT_DSI;
dsi->output.dev = client->dev;
dsi->output.ops = &dsi_ops;
- err = tegra_output_init(tegra->drm, &dsi->output);
+ err = tegra_output_init(drm, &dsi->output);
if (err < 0) {
dev_err(client->dev, "output setup failed: %d\n", err);
return err;
}
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
- err = tegra_dsi_debugfs_init(dsi, tegra->drm->primary);
+ err = tegra_dsi_debugfs_init(dsi, drm->primary);
if (err < 0)
dev_err(dsi->dev, "debugfs setup failed: %d\n", err);
}
- /*
- * enable high-speed mode, checksum generation, ECC generation and
- * disable raw mode
- */
- value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL);
- value |= DSI_HOST_CONTROL_ECC | DSI_HOST_CONTROL_CS |
- DSI_HOST_CONTROL_HS;
- value &= ~DSI_HOST_CONTROL_RAW;
- tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
-
- tegra_dsi_writel(dsi, 0, DSI_SOL_DELAY);
- tegra_dsi_writel(dsi, 0, DSI_MAX_THRESHOLD);
-
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL);
-
- for (i = 0; i < 8; i++) {
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + i);
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_8 + i);
- }
-
- for (i = 0; i < 12; i++)
- tegra_dsi_writel(dsi, 0, DSI_PKT_SEQ_0_LO + i);
-
- tegra_dsi_writel(dsi, 0, DSI_DCS_CMDS);
-
err = tegra_dsi_pad_calibrate(dsi);
if (err < 0) {
dev_err(dsi->dev, "MIPI calibration failed: %d\n", err);
return err;
}
- tegra_dsi_writel(dsi, DSI_POWER_CONTROL_ENABLE, DSI_POWER_CONTROL);
- usleep_range(300, 1000);
-
return 0;
}
@@ -715,66 +791,13 @@ static int tegra_dsi_setup_clocks(struct tegra_dsi *dsi)
return 0;
}
-static void tegra_dsi_initialize(struct tegra_dsi *dsi)
-{
- unsigned int i;
-
- tegra_dsi_writel(dsi, 0, DSI_POWER_CONTROL);
-
- tegra_dsi_writel(dsi, 0, DSI_INT_ENABLE);
- tegra_dsi_writel(dsi, 0, DSI_INT_STATUS);
- tegra_dsi_writel(dsi, 0, DSI_INT_MASK);
-
- tegra_dsi_writel(dsi, 0, DSI_HOST_CONTROL);
- tegra_dsi_writel(dsi, 0, DSI_CONTROL);
-
- tegra_dsi_writel(dsi, 0, DSI_SOL_DELAY);
- tegra_dsi_writel(dsi, 0, DSI_MAX_THRESHOLD);
-
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL);
-
- for (i = 0; i < 8; i++) {
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + i);
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_8 + i);
- }
-
- for (i = 0; i < 12; i++)
- tegra_dsi_writel(dsi, 0, DSI_PKT_SEQ_0_LO + i);
-
- tegra_dsi_writel(dsi, 0, DSI_DCS_CMDS);
-
- for (i = 0; i < 4; i++)
- tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1 + i);
-
- tegra_dsi_writel(dsi, 0x00000000, DSI_PHY_TIMING_0);
- tegra_dsi_writel(dsi, 0x00000000, DSI_PHY_TIMING_1);
- tegra_dsi_writel(dsi, 0x000000ff, DSI_PHY_TIMING_2);
- tegra_dsi_writel(dsi, 0x00000000, DSI_BTA_TIMING);
-
- tegra_dsi_writel(dsi, 0, DSI_TIMEOUT_0);
- tegra_dsi_writel(dsi, 0, DSI_TIMEOUT_1);
- tegra_dsi_writel(dsi, 0, DSI_TO_TALLY);
-
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_CD);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CD_STATUS);
- tegra_dsi_writel(dsi, 0, DSI_VIDEO_MODE_CONTROL);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4);
-
- tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL);
- tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_START);
- tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_SIZE);
-}
-
static int tegra_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct tegra_dsi *dsi = host_to_tegra(host);
struct tegra_output *output = &dsi->output;
+ dsi->flags = device->mode_flags;
dsi->format = device->format;
dsi->lanes = device->lanes;
@@ -829,6 +852,7 @@ static int tegra_dsi_probe(struct platform_device *pdev)
* attaches to the DSI host, the parameters will be taken from
* the attached device.
*/
+ dsi->flags = MIPI_DSI_MODE_VIDEO;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->lanes = 4;
@@ -872,6 +896,18 @@ static int tegra_dsi_probe(struct platform_device *pdev)
return err;
}
+ dsi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi");
+ if (IS_ERR(dsi->vdd)) {
+ dev_err(&pdev->dev, "cannot get VDD supply\n");
+ return PTR_ERR(dsi->vdd);
+ }
+
+ err = regulator_enable(dsi->vdd);
+ if (err < 0) {
+ dev_err(&pdev->dev, "cannot enable VDD supply\n");
+ return err;
+ }
+
err = tegra_dsi_setup_clocks(dsi);
if (err < 0) {
dev_err(&pdev->dev, "cannot setup clocks\n");
@@ -883,8 +919,6 @@ static int tegra_dsi_probe(struct platform_device *pdev)
if (IS_ERR(dsi->regs))
return PTR_ERR(dsi->regs);
- tegra_dsi_initialize(dsi);
-
dsi->mipi = tegra_mipi_request(&pdev->dev);
if (IS_ERR(dsi->mipi))
return PTR_ERR(dsi->mipi);
@@ -929,9 +963,11 @@ static int tegra_dsi_remove(struct platform_device *pdev)
mipi_dsi_host_unregister(&dsi->host);
tegra_mipi_free(dsi->mipi);
+ regulator_disable(dsi->vdd);
clk_disable_unprepare(dsi->clk_parent);
clk_disable_unprepare(dsi->clk_lp);
clk_disable_unprepare(dsi->clk);
+ reset_control_assert(dsi->rst);
err = tegra_output_remove(&dsi->output);
if (err < 0) {
diff --git a/drivers/gpu/drm/tegra/dsi.h b/drivers/gpu/drm/tegra/dsi.h
index 1db5cc24ea91..5ce610d08d77 100644
--- a/drivers/gpu/drm/tegra/dsi.h
+++ b/drivers/gpu/drm/tegra/dsi.h
@@ -117,4 +117,14 @@
#define DSI_INIT_SEQ_DATA_14 0x5e
#define DSI_INIT_SEQ_DATA_15 0x5f
+/*
+ * pixel format as used in the DSI_CONTROL_FORMAT field
+ */
+enum tegra_dsi_format {
+ TEGRA_DSI_FORMAT_16P,
+ TEGRA_DSI_FORMAT_18NP,
+ TEGRA_DSI_FORMAT_18P,
+ TEGRA_DSI_FORMAT_24P,
+};
+
#endif
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index f7fca09d4921..9798a7080322 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -346,11 +346,8 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
{
- if (fbdev) {
- drm_modeset_lock_all(fbdev->base.dev);
- drm_fb_helper_restore_fbdev_mode(&fbdev->base);
- drm_modeset_unlock_all(fbdev->base.dev);
- }
+ if (fbdev)
+ drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base);
}
static void tegra_fb_output_poll_changed(struct drm_device *drm)
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c
index bcf9895cef9f..aa85b7b26f10 100644
--- a/drivers/gpu/drm/tegra/gem.c
+++ b/drivers/gpu/drm/tegra/gem.c
@@ -169,7 +169,8 @@ err:
return ERR_PTR(ret);
}
-struct tegra_bo *tegra_bo_import(struct drm_device *drm, struct dma_buf *buf)
+static struct tegra_bo *tegra_bo_import(struct drm_device *drm,
+ struct dma_buf *buf)
{
struct dma_buf_attachment *attach;
struct tegra_bo *bo;
diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c
index 2c7ca748edf5..7c53941f2a9e 100644
--- a/drivers/gpu/drm/tegra/gr2d.c
+++ b/drivers/gpu/drm/tegra/gr2d.c
@@ -28,7 +28,7 @@ static inline struct gr2d *to_gr2d(struct tegra_drm_client *client)
static int gr2d_init(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
- struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+ struct drm_device *dev = dev_get_drvdata(client->parent);
unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
struct gr2d *gr2d = to_gr2d(drm);
@@ -42,17 +42,17 @@ static int gr2d_init(struct host1x_client *client)
return -ENOMEM;
}
- return tegra_drm_register_client(tegra, drm);
+ return tegra_drm_register_client(dev->dev_private, drm);
}
static int gr2d_exit(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
- struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+ struct drm_device *dev = dev_get_drvdata(client->parent);
struct gr2d *gr2d = to_gr2d(drm);
int err;
- err = tegra_drm_unregister_client(tegra, drm);
+ err = tegra_drm_unregister_client(dev->dev_private, drm);
if (err < 0)
return err;
diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c
index 0cbb24b1ae04..30f5ba9bd6d0 100644
--- a/drivers/gpu/drm/tegra/gr3d.c
+++ b/drivers/gpu/drm/tegra/gr3d.c
@@ -37,7 +37,7 @@ static inline struct gr3d *to_gr3d(struct tegra_drm_client *client)
static int gr3d_init(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
- struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+ struct drm_device *dev = dev_get_drvdata(client->parent);
unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
struct gr3d *gr3d = to_gr3d(drm);
@@ -51,17 +51,17 @@ static int gr3d_init(struct host1x_client *client)
return -ENOMEM;
}
- return tegra_drm_register_client(tegra, drm);
+ return tegra_drm_register_client(dev->dev_private, drm);
}
static int gr3d_exit(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
- struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+ struct drm_device *dev = dev_get_drvdata(client->parent);
struct gr3d *gr3d = to_gr3d(drm);
int err;
- err = tegra_drm_unregister_client(tegra, drm);
+ err = tegra_drm_unregister_client(dev->dev_private, drm);
if (err < 0)
return err;
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 6928015d11a4..ba067bb767e3 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -42,8 +42,9 @@ struct tegra_hdmi {
struct device *dev;
bool enabled;
- struct regulator *vdd;
+ struct regulator *hdmi;
struct regulator *pll;
+ struct regulator *vdd;
void __iomem *regs;
unsigned int irq;
@@ -317,6 +318,85 @@ static const struct tmds_config tegra114_tmds_config[] = {
},
};
+static const struct tmds_config tegra124_tmds_config[] = {
+ { /* 480p/576p / 25.2MHz/27MHz modes */
+ .pclk = 27000000,
+ .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+ SOR_PLL_VCOCAP(0) | SOR_PLL_RESISTORSEL,
+ .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(0),
+ .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT1(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT2(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT3(PE_CURRENT_0_mA_T114),
+ .drive_current =
+ DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114),
+ .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+ }, { /* 720p / 74.25MHz modes */
+ .pclk = 74250000,
+ .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+ SOR_PLL_VCOCAP(1) | SOR_PLL_RESISTORSEL,
+ .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) |
+ SOR_PLL_TMDS_TERMADJ(0),
+ .pe_current = PE_CURRENT0(PE_CURRENT_15_mA_T114) |
+ PE_CURRENT1(PE_CURRENT_15_mA_T114) |
+ PE_CURRENT2(PE_CURRENT_15_mA_T114) |
+ PE_CURRENT3(PE_CURRENT_15_mA_T114),
+ .drive_current =
+ DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114),
+ .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+ }, { /* 1080p / 148.5MHz modes */
+ .pclk = 148500000,
+ .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+ SOR_PLL_VCOCAP(3) | SOR_PLL_RESISTORSEL,
+ .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) |
+ SOR_PLL_TMDS_TERMADJ(0),
+ .pe_current = PE_CURRENT0(PE_CURRENT_10_mA_T114) |
+ PE_CURRENT1(PE_CURRENT_10_mA_T114) |
+ PE_CURRENT2(PE_CURRENT_10_mA_T114) |
+ PE_CURRENT3(PE_CURRENT_10_mA_T114),
+ .drive_current =
+ DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_12_400_mA_T114) |
+ DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_12_400_mA_T114) |
+ DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_12_400_mA_T114) |
+ DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_12_400_mA_T114),
+ .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+ }, { /* 225/297MHz modes */
+ .pclk = UINT_MAX,
+ .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+ SOR_PLL_VCOCAP(0xf) | SOR_PLL_RESISTORSEL,
+ .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(7)
+ | SOR_PLL_TMDS_TERM_ENABLE,
+ .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT1(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT2(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT3(PE_CURRENT_0_mA_T114),
+ .drive_current =
+ DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_25_200_mA_T114) |
+ DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_25_200_mA_T114) |
+ DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_25_200_mA_T114) |
+ DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_19_200_mA_T114),
+ .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_3_000_mA) |
+ PEAK_CURRENT_LANE1(PEAK_CURRENT_3_000_mA) |
+ PEAK_CURRENT_LANE2(PEAK_CURRENT_3_000_mA) |
+ PEAK_CURRENT_LANE3(PEAK_CURRENT_0_800_mA),
+ },
+};
+
static const struct tegra_hdmi_audio_config *
tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pclk)
{
@@ -716,13 +796,9 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
return err;
}
- /*
- * This assumes that the display controller will divide its parent
- * clock by 2 to generate the pixel clock.
- */
- err = tegra_output_setup_clock(output, hdmi->clk, pclk * 2);
+ err = regulator_enable(hdmi->vdd);
if (err < 0) {
- dev_err(hdmi->dev, "failed to setup clock: %d\n", err);
+ dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err);
return err;
}
@@ -730,7 +806,7 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
if (err < 0)
return err;
- err = clk_enable(hdmi->clk);
+ err = clk_prepare_enable(hdmi->clk);
if (err < 0) {
dev_err(hdmi->dev, "failed to enable clock: %d\n", err);
return err;
@@ -740,6 +816,17 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
usleep_range(1000, 2000);
reset_control_deassert(hdmi->rst);
+ /* power up sequence */
+ value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0);
+ value &= ~SOR_PLL_PDBG;
+ tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_PLL0);
+
+ usleep_range(10, 20);
+
+ value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0);
+ value &= ~SOR_PLL_PWR;
+ tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_PLL0);
+
tegra_dc_writel(dc, VSYNC_H_POSITION(1),
DC_DISP_DISP_TIMING_OPTIONS);
tegra_dc_writel(dc, DITHER_CONTROL_DISABLE | BASE_COLOR_SIZE888,
@@ -838,9 +925,13 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_SEQ_INST(0));
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_SEQ_INST(8));
- value = 0x1c800;
+ value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_CSTM);
value &= ~SOR_CSTM_ROTCLK(~0);
value |= SOR_CSTM_ROTCLK(2);
+ value |= SOR_CSTM_PLLDIV;
+ value &= ~SOR_CSTM_LVDS_ENABLE;
+ value &= ~SOR_CSTM_MODE_MASK;
+ value |= SOR_CSTM_MODE_TMDS;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_CSTM);
/* start SOR */
@@ -930,10 +1021,18 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
* sure it's only executed when the output is attached to one.
*/
if (dc) {
+ /*
+ * XXX: We can't do this here because it causes HDMI to go
+ * into an erroneous state with the result that HDMI won't
+ * properly work once disabled. See also a similar symptom
+ * for the SOR output.
+ */
+ /*
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+ */
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
value &= ~DISP_CTRL_MODE_MASK;
@@ -947,8 +1046,9 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
}
+ clk_disable_unprepare(hdmi->clk);
reset_control_assert(hdmi->rst);
- clk_disable(hdmi->clk);
+ regulator_disable(hdmi->vdd);
regulator_disable(hdmi->pll);
hdmi->enabled = false;
@@ -957,10 +1057,10 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
}
static int tegra_output_hdmi_setup_clock(struct tegra_output *output,
- struct clk *clk, unsigned long pclk)
+ struct clk *clk, unsigned long pclk,
+ unsigned int *div)
{
struct tegra_hdmi *hdmi = to_hdmi(output);
- struct clk *base;
int err;
err = clk_set_parent(clk, hdmi->clk_parent);
@@ -969,17 +1069,12 @@ static int tegra_output_hdmi_setup_clock(struct tegra_output *output,
return err;
}
- base = clk_get_parent(hdmi->clk_parent);
-
- /*
- * This assumes that the parent clock is pll_d_out0 or pll_d2_out
- * respectively, each of which divides the base pll_d by 2.
- */
- err = clk_set_rate(base, pclk * 2);
+ err = clk_set_rate(hdmi->clk_parent, pclk);
if (err < 0)
- dev_err(output->dev,
- "failed to set base clock rate to %lu Hz\n",
- pclk * 2);
+ dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
+ pclk);
+
+ *div = 0;
return 0;
}
@@ -1017,7 +1112,7 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
struct tegra_hdmi *hdmi = node->info_ent->data;
int err;
- err = clk_enable(hdmi->clk);
+ err = clk_prepare_enable(hdmi->clk);
if (err)
return err;
@@ -1186,7 +1281,7 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
#undef DUMP_REG
- clk_disable(hdmi->clk);
+ clk_disable_unprepare(hdmi->clk);
return 0;
}
@@ -1252,33 +1347,33 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi)
static int tegra_hdmi_init(struct host1x_client *client)
{
- struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+ struct drm_device *drm = dev_get_drvdata(client->parent);
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
int err;
- err = regulator_enable(hdmi->vdd);
- if (err < 0) {
- dev_err(client->dev, "failed to enable VDD regulator: %d\n",
- err);
- return err;
- }
-
hdmi->output.type = TEGRA_OUTPUT_HDMI;
hdmi->output.dev = client->dev;
hdmi->output.ops = &hdmi_ops;
- err = tegra_output_init(tegra->drm, &hdmi->output);
+ err = tegra_output_init(drm, &hdmi->output);
if (err < 0) {
dev_err(client->dev, "output setup failed: %d\n", err);
return err;
}
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
- err = tegra_hdmi_debugfs_init(hdmi, tegra->drm->primary);
+ err = tegra_hdmi_debugfs_init(hdmi, drm->primary);
if (err < 0)
dev_err(client->dev, "debugfs setup failed: %d\n", err);
}
+ err = regulator_enable(hdmi->hdmi);
+ if (err < 0) {
+ dev_err(client->dev, "failed to enable HDMI regulator: %d\n",
+ err);
+ return err;
+ }
+
return 0;
}
@@ -1287,6 +1382,8 @@ static int tegra_hdmi_exit(struct host1x_client *client)
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
int err;
+ regulator_disable(hdmi->hdmi);
+
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_hdmi_debugfs_exit(hdmi);
if (err < 0)
@@ -1306,8 +1403,6 @@ static int tegra_hdmi_exit(struct host1x_client *client)
return err;
}
- regulator_disable(hdmi->vdd);
-
return 0;
}
@@ -1340,7 +1435,16 @@ static const struct tegra_hdmi_config tegra114_hdmi_config = {
.has_sor_io_peak_current = true,
};
+static const struct tegra_hdmi_config tegra124_hdmi_config = {
+ .tmds = tegra124_tmds_config,
+ .num_tmds = ARRAY_SIZE(tegra124_tmds_config),
+ .fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0,
+ .fuse_override_value = 1 << 31,
+ .has_sor_io_peak_current = true,
+};
+
static const struct of_device_id tegra_hdmi_of_match[] = {
+ { .compatible = "nvidia,tegra124-hdmi", .data = &tegra124_hdmi_config },
{ .compatible = "nvidia,tegra114-hdmi", .data = &tegra114_hdmi_config },
{ .compatible = "nvidia,tegra30-hdmi", .data = &tegra30_hdmi_config },
{ .compatible = "nvidia,tegra20-hdmi", .data = &tegra20_hdmi_config },
@@ -1381,28 +1485,20 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
return PTR_ERR(hdmi->rst);
}
- err = clk_prepare(hdmi->clk);
- if (err < 0)
- return err;
-
hdmi->clk_parent = devm_clk_get(&pdev->dev, "parent");
if (IS_ERR(hdmi->clk_parent))
return PTR_ERR(hdmi->clk_parent);
- err = clk_prepare(hdmi->clk_parent);
- if (err < 0)
- return err;
-
err = clk_set_parent(hdmi->clk, hdmi->clk_parent);
if (err < 0) {
dev_err(&pdev->dev, "failed to setup clocks: %d\n", err);
return err;
}
- hdmi->vdd = devm_regulator_get(&pdev->dev, "vdd");
- if (IS_ERR(hdmi->vdd)) {
- dev_err(&pdev->dev, "failed to get VDD regulator\n");
- return PTR_ERR(hdmi->vdd);
+ hdmi->hdmi = devm_regulator_get(&pdev->dev, "hdmi");
+ if (IS_ERR(hdmi->hdmi)) {
+ dev_err(&pdev->dev, "failed to get HDMI regulator\n");
+ return PTR_ERR(hdmi->hdmi);
}
hdmi->pll = devm_regulator_get(&pdev->dev, "pll");
@@ -1411,6 +1507,12 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
return PTR_ERR(hdmi->pll);
}
+ hdmi->vdd = devm_regulator_get(&pdev->dev, "vdd");
+ if (IS_ERR(hdmi->vdd)) {
+ dev_err(&pdev->dev, "failed to get VDD regulator\n");
+ return PTR_ERR(hdmi->vdd);
+ }
+
hdmi->output.dev = &pdev->dev;
err = tegra_output_probe(&hdmi->output);
@@ -1462,8 +1564,8 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
return err;
}
- clk_unprepare(hdmi->clk_parent);
- clk_unprepare(hdmi->clk);
+ clk_disable_unprepare(hdmi->clk_parent);
+ clk_disable_unprepare(hdmi->clk);
return 0;
}
diff --git a/drivers/gpu/drm/tegra/hdmi.h b/drivers/gpu/drm/tegra/hdmi.h
index 0aebc485f7fa..919a19df4e1b 100644
--- a/drivers/gpu/drm/tegra/hdmi.h
+++ b/drivers/gpu/drm/tegra/hdmi.h
@@ -190,6 +190,11 @@
#define HDMI_NV_PDISP_SOR_CSTM 0x5a
#define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24)
+#define SOR_CSTM_PLLDIV (1 << 21)
+#define SOR_CSTM_LVDS_ENABLE (1 << 16)
+#define SOR_CSTM_MODE_LVDS (0 << 12)
+#define SOR_CSTM_MODE_TMDS (1 << 12)
+#define SOR_CSTM_MODE_MASK (3 << 12)
#define HDMI_NV_PDISP_SOR_LVDS 0x5b
#define HDMI_NV_PDISP_SOR_CRCA 0x5c
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 0266fb40479e..d6af9be48f42 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -159,11 +159,38 @@ static int tegra_output_rgb_disable(struct tegra_output *output)
}
static int tegra_output_rgb_setup_clock(struct tegra_output *output,
- struct clk *clk, unsigned long pclk)
+ struct clk *clk, unsigned long pclk,
+ unsigned int *div)
{
struct tegra_rgb *rgb = to_rgb(output);
+ int err;
+
+ err = clk_set_parent(clk, rgb->clk_parent);
+ if (err < 0) {
+ dev_err(output->dev, "failed to set parent: %d\n", err);
+ return err;
+ }
- return clk_set_parent(clk, rgb->clk_parent);
+ /*
+ * We may not want to change the frequency of the parent clock, since
+ * it may be a parent for other peripherals. This is due to the fact
+ * that on Tegra20 there's only a single clock dedicated to display
+ * (pll_d_out0), whereas later generations have a second one that can
+ * be used to independently drive a second output (pll_d2_out0).
+ *
+ * As a way to support multiple outputs on Tegra20 as well, pll_p is
+ * typically used as the parent clock for the display controllers.
+ * But this comes at a cost: pll_p is the parent of several other
+ * peripherals, so its frequency shouldn't change out of the blue.
+ *
+ * The best we can do at this point is to use the shift clock divider
+ * and hope that the desired frequency can be matched (or at least
+ * matched sufficiently close that the panel will still work).
+ */
+
+ *div = ((clk_get_rate(clk) * 2) / pclk) - 2;
+
+ return 0;
}
static int tegra_output_rgb_check_mode(struct tegra_output *output,
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 49ef5729f435..27c979b50111 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -7,6 +7,7 @@
*/
#include <linux/clk.h>
+#include <linux/debugfs.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
@@ -33,7 +34,23 @@ struct tegra_sor {
struct tegra_dpaux *dpaux;
+ struct mutex lock;
bool enabled;
+
+ struct dentry *debugfs;
+};
+
+struct tegra_sor_config {
+ u32 bits_per_pixel;
+
+ u32 active_polarity;
+ u32 active_count;
+ u32 tu_size;
+ u32 active_frac;
+ u32 watermark;
+
+ u32 hblank_symbols;
+ u32 vblank_symbols;
};
static inline struct tegra_sor *
@@ -289,34 +306,232 @@ static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout)
return -ETIMEDOUT;
}
+struct tegra_sor_params {
+ /* number of link clocks per line */
+ unsigned int num_clocks;
+ /* ratio between input and output */
+ u64 ratio;
+ /* precision factor */
+ u64 precision;
+
+ unsigned int active_polarity;
+ unsigned int active_count;
+ unsigned int active_frac;
+ unsigned int tu_size;
+ unsigned int error;
+};
+
+static int tegra_sor_compute_params(struct tegra_sor *sor,
+ struct tegra_sor_params *params,
+ unsigned int tu_size)
+{
+ u64 active_sym, active_count, frac, approx;
+ u32 active_polarity, active_frac = 0;
+ const u64 f = params->precision;
+ s64 error;
+
+ active_sym = params->ratio * tu_size;
+ active_count = div_u64(active_sym, f) * f;
+ frac = active_sym - active_count;
+
+ /* fraction < 0.5 */
+ if (frac >= (f / 2)) {
+ active_polarity = 1;
+ frac = f - frac;
+ } else {
+ active_polarity = 0;
+ }
+
+ if (frac != 0) {
+ frac = div_u64(f * f, frac); /* 1/fraction */
+ if (frac <= (15 * f)) {
+ active_frac = div_u64(frac, f);
+
+ /* round up */
+ if (active_polarity)
+ active_frac++;
+ } else {
+ active_frac = active_polarity ? 1 : 15;
+ }
+ }
+
+ if (active_frac == 1)
+ active_polarity = 0;
+
+ if (active_polarity == 1) {
+ if (active_frac) {
+ approx = active_count + (active_frac * (f - 1)) * f;
+ approx = div_u64(approx, active_frac * f);
+ } else {
+ approx = active_count + f;
+ }
+ } else {
+ if (active_frac)
+ approx = active_count + div_u64(f, active_frac);
+ else
+ approx = active_count;
+ }
+
+ error = div_s64(active_sym - approx, tu_size);
+ error *= params->num_clocks;
+
+ if (error <= 0 && abs64(error) < params->error) {
+ params->active_count = div_u64(active_count, f);
+ params->active_polarity = active_polarity;
+ params->active_frac = active_frac;
+ params->error = abs64(error);
+ params->tu_size = tu_size;
+
+ if (error == 0)
+ return true;
+ }
+
+ return false;
+}
+
+static int tegra_sor_calc_config(struct tegra_sor *sor,
+ struct drm_display_mode *mode,
+ struct tegra_sor_config *config,
+ struct drm_dp_link *link)
+{
+ const u64 f = 100000, link_rate = link->rate * 1000;
+ const u64 pclk = mode->clock * 1000;
+ u64 input, output, watermark, num;
+ struct tegra_sor_params params;
+ u32 num_syms_per_line;
+ unsigned int i;
+
+ if (!link_rate || !link->num_lanes || !pclk || !config->bits_per_pixel)
+ return -EINVAL;
+
+ output = link_rate * 8 * link->num_lanes;
+ input = pclk * config->bits_per_pixel;
+
+ if (input >= output)
+ return -ERANGE;
+
+ memset(&params, 0, sizeof(params));
+ params.ratio = div64_u64(input * f, output);
+ params.num_clocks = div_u64(link_rate * mode->hdisplay, pclk);
+ params.precision = f;
+ params.error = 64 * f;
+ params.tu_size = 64;
+
+ for (i = params.tu_size; i >= 32; i--)
+ if (tegra_sor_compute_params(sor, &params, i))
+ break;
+
+ if (params.active_frac == 0) {
+ config->active_polarity = 0;
+ config->active_count = params.active_count;
+
+ if (!params.active_polarity)
+ config->active_count--;
+
+ config->tu_size = params.tu_size;
+ config->active_frac = 1;
+ } else {
+ config->active_polarity = params.active_polarity;
+ config->active_count = params.active_count;
+ config->active_frac = params.active_frac;
+ config->tu_size = params.tu_size;
+ }
+
+ dev_dbg(sor->dev,
+ "polarity: %d active count: %d tu size: %d active frac: %d\n",
+ config->active_polarity, config->active_count,
+ config->tu_size, config->active_frac);
+
+ watermark = params.ratio * config->tu_size * (f - params.ratio);
+ watermark = div_u64(watermark, f);
+
+ watermark = div_u64(watermark + params.error, f);
+ config->watermark = watermark + (config->bits_per_pixel / 8) + 2;
+ num_syms_per_line = (mode->hdisplay * config->bits_per_pixel) *
+ (link->num_lanes * 8);
+
+ if (config->watermark > 30) {
+ config->watermark = 30;
+ dev_err(sor->dev,
+ "unable to compute TU size, forcing watermark to %u\n",
+ config->watermark);
+ } else if (config->watermark > num_syms_per_line) {
+ config->watermark = num_syms_per_line;
+ dev_err(sor->dev, "watermark too high, forcing to %u\n",
+ config->watermark);
+ }
+
+ /* compute the number of symbols per horizontal blanking interval */
+ num = ((mode->htotal - mode->hdisplay) - 7) * link_rate;
+ config->hblank_symbols = div_u64(num, pclk);
+
+ if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
+ config->hblank_symbols -= 3;
+
+ config->hblank_symbols -= 12 / link->num_lanes;
+
+ /* compute the number of symbols per vertical blanking interval */
+ num = (mode->hdisplay - 25) * link_rate;
+ config->vblank_symbols = div_u64(num, pclk);
+ config->vblank_symbols -= 36 / link->num_lanes + 4;
+
+ dev_dbg(sor->dev, "blank symbols: H:%u V:%u\n", config->hblank_symbols,
+ config->vblank_symbols);
+
+ return 0;
+}
+
static int tegra_output_sor_enable(struct tegra_output *output)
{
struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
struct drm_display_mode *mode = &dc->base.mode;
unsigned int vbe, vse, hbe, hse, vbs, hbs, i;
struct tegra_sor *sor = to_sor(output);
+ struct tegra_sor_config config;
+ struct drm_dp_link link;
+ struct drm_dp_aux *aux;
unsigned long value;
- int err;
+ int err = 0;
+
+ mutex_lock(&sor->lock);
if (sor->enabled)
- return 0;
+ goto unlock;
err = clk_prepare_enable(sor->clk);
if (err < 0)
- return err;
+ goto unlock;
reset_control_deassert(sor->rst);
+ /* FIXME: properly convert to struct drm_dp_aux */
+ aux = (struct drm_dp_aux *)sor->dpaux;
+
if (sor->dpaux) {
err = tegra_dpaux_enable(sor->dpaux);
if (err < 0)
dev_err(sor->dev, "failed to enable DP: %d\n", err);
+
+ err = drm_dp_link_probe(aux, &link);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to probe eDP link: %d\n",
+ err);
+ return err;
+ }
}
err = clk_set_parent(sor->clk, sor->clk_safe);
if (err < 0)
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? */
+
+ err = tegra_sor_calc_config(sor, mode, &config, &link);
+ if (err < 0)
+ dev_err(sor->dev, "failed to compute link configuration: %d\n",
+ err);
+
value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
@@ -385,7 +600,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS);
if (err < 0) {
dev_err(sor->dev, "failed to power on I/O rail: %d\n", err);
- return err;
+ goto unlock;
}
usleep_range(5, 100);
@@ -419,15 +634,29 @@ static int tegra_output_sor_enable(struct tegra_output *output)
if (err < 0)
dev_err(sor->dev, "failed to set DP parent clock: %d\n", err);
- /* power dplanes (XXX parameterize based on link?) */
+ /* power DP lanes */
value = tegra_sor_readl(sor, SOR_DP_PADCTL_0);
- value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
- SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2;
+
+ if (link.num_lanes <= 2)
+ value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2);
+ else
+ value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2;
+
+ if (link.num_lanes <= 1)
+ value &= ~SOR_DP_PADCTL_PD_TXD_1;
+ else
+ value |= SOR_DP_PADCTL_PD_TXD_1;
+
+ if (link.num_lanes == 0)
+ value &= ~SOR_DP_PADCTL_PD_TXD_0;
+ else
+ value |= SOR_DP_PADCTL_PD_TXD_0;
+
tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0);
value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
- value |= SOR_DP_LINKCTL_LANE_COUNT(4);
+ value |= SOR_DP_LINKCTL_LANE_COUNT(link.num_lanes);
tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0);
/* start lane sequencer */
@@ -443,10 +672,10 @@ static int tegra_output_sor_enable(struct tegra_output *output)
usleep_range(250, 1000);
}
- /* set link bandwidth (2.7 GHz, XXX: parameterize based on link?) */
+ /* set link bandwidth */
value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
- value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70;
+ value |= drm_dp_link_rate_to_bw_code(link.rate) << 2;
tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
/* set linkctl */
@@ -454,7 +683,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
value |= SOR_DP_LINKCTL_ENABLE;
value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK;
- value |= SOR_DP_LINKCTL_TU_SIZE(59); /* XXX: don't hardcode? */
+ value |= SOR_DP_LINKCTL_TU_SIZE(config.tu_size);
value |= SOR_DP_LINKCTL_ENHANCED_FRAME;
tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0);
@@ -470,28 +699,31 @@ static int tegra_output_sor_enable(struct tegra_output *output)
value = tegra_sor_readl(sor, SOR_DP_CONFIG_0);
value &= ~SOR_DP_CONFIG_WATERMARK_MASK;
- value |= SOR_DP_CONFIG_WATERMARK(14); /* XXX: don't hardcode? */
+ value |= SOR_DP_CONFIG_WATERMARK(config.watermark);
value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK;
- value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(47); /* XXX: don't hardcode? */
+ value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config.active_count);
value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK;
- value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(9); /* XXX: don't hardcode? */
+ value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config.active_frac);
- value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; /* XXX: don't hardcode? */
+ if (config.active_polarity)
+ value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
+ else
+ value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE;
- value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; /* XXX: don't hardcode? */
+ value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE;
tegra_sor_writel(sor, value, SOR_DP_CONFIG_0);
value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS);
value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK;
- value |= 137; /* XXX: don't hardcode? */
+ value |= config.hblank_symbols & 0xffff;
tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS);
value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS);
value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK;
- value |= 2368; /* XXX: don't hardcode? */
+ value |= config.vblank_symbols & 0xffff;
tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS);
/* enable pad calibration logic */
@@ -500,30 +732,27 @@ static int tegra_output_sor_enable(struct tegra_output *output)
tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
if (sor->dpaux) {
- /* FIXME: properly convert to struct drm_dp_aux */
- struct drm_dp_aux *aux = (struct drm_dp_aux *)sor->dpaux;
- struct drm_dp_link link;
u8 rate, lanes;
err = drm_dp_link_probe(aux, &link);
if (err < 0) {
dev_err(sor->dev, "failed to probe eDP link: %d\n",
err);
- return err;
+ goto unlock;
}
err = drm_dp_link_power_up(aux, &link);
if (err < 0) {
dev_err(sor->dev, "failed to power up eDP link: %d\n",
err);
- return err;
+ goto unlock;
}
err = drm_dp_link_configure(aux, &link);
if (err < 0) {
dev_err(sor->dev, "failed to configure eDP link: %d\n",
err);
- return err;
+ goto unlock;
}
rate = drm_dp_link_rate_to_bw_code(link.rate);
@@ -558,7 +787,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
if (err < 0) {
dev_err(sor->dev, "DP fast link training failed: %d\n",
err);
- return err;
+ goto unlock;
}
dev_dbg(sor->dev, "fast link training succeeded\n");
@@ -567,7 +796,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
err = tegra_sor_power_up(sor, 250);
if (err < 0) {
dev_err(sor->dev, "failed to power up SOR: %d\n", err);
- return err;
+ goto unlock;
}
/* start display controller in continuous mode */
@@ -586,12 +815,26 @@ 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_PIXELDEPTH_BPP_24_444 |
- SOR_STATE_ASY_VSYNCPOL |
+ value = SOR_STATE_ASY_VSYNCPOL |
SOR_STATE_ASY_HSYNCPOL |
SOR_STATE_ASY_PROTOCOL_DP_A |
SOR_STATE_ASY_CRC_MODE_COMPLETE |
SOR_STATE_ASY_OWNER(dc->pipe + 1);
+
+ switch (config.bits_per_pixel) {
+ case 24:
+ value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444;
+ break;
+
+ case 18:
+ value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444;
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
tegra_sor_writel(sor, value, SOR_STATE_1);
/*
@@ -620,11 +863,8 @@ static int tegra_output_sor_enable(struct tegra_output *output)
value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0));
- /* XXX interlaced mode */
- tegra_sor_writel(sor, 0x00000001, SOR_HEAD_STATE_5(0));
-
/* CSTM (LVDS, link A/B, upper) */
- value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_B | SOR_CSTM_LINK_ACT_B |
+ value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
SOR_CSTM_UPPER;
tegra_sor_writel(sor, value, SOR_CSTM);
@@ -632,7 +872,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
err = tegra_sor_setup_pwm(sor, 250);
if (err < 0) {
dev_err(sor->dev, "failed to setup PWM: %d\n", err);
- return err;
+ goto unlock;
}
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
@@ -644,18 +884,20 @@ static int tegra_output_sor_enable(struct tegra_output *output)
err = tegra_sor_attach(sor);
if (err < 0) {
dev_err(sor->dev, "failed to attach SOR: %d\n", err);
- return err;
+ goto unlock;
}
err = tegra_sor_wakeup(sor);
if (err < 0) {
dev_err(sor->dev, "failed to enable DC: %d\n", err);
- return err;
+ goto unlock;
}
sor->enabled = true;
- return 0;
+unlock:
+ mutex_unlock(&sor->lock);
+ return err;
}
static int tegra_sor_detach(struct tegra_sor *sor)
@@ -740,7 +982,7 @@ static int tegra_sor_power_down(struct tegra_sor *sor)
tegra_sor_writel(sor, value, SOR_DP_PADCTL_0);
/* stop lane sequencer */
- value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
+ value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
@@ -783,15 +1025,17 @@ static int tegra_output_sor_disable(struct tegra_output *output)
struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
struct tegra_sor *sor = to_sor(output);
unsigned long value;
- int err;
+ int err = 0;
+
+ mutex_lock(&sor->lock);
if (!sor->enabled)
- return 0;
+ goto unlock;
err = tegra_sor_detach(sor);
if (err < 0) {
dev_err(sor->dev, "failed to detach SOR: %d\n", err);
- return err;
+ goto unlock;
}
tegra_sor_writel(sor, 0, SOR_STATE_1);
@@ -832,21 +1076,21 @@ static int tegra_output_sor_disable(struct tegra_output *output)
err = tegra_sor_power_down(sor);
if (err < 0) {
dev_err(sor->dev, "failed to power down SOR: %d\n", err);
- return err;
+ goto unlock;
}
if (sor->dpaux) {
err = tegra_dpaux_disable(sor->dpaux);
if (err < 0) {
dev_err(sor->dev, "failed to disable DP: %d\n", err);
- return err;
+ goto unlock;
}
}
err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS);
if (err < 0) {
dev_err(sor->dev, "failed to power off I/O rail: %d\n", err);
- return err;
+ goto unlock;
}
reset_control_assert(sor->rst);
@@ -854,18 +1098,18 @@ static int tegra_output_sor_disable(struct tegra_output *output)
sor->enabled = false;
- return 0;
+unlock:
+ mutex_unlock(&sor->lock);
+ return err;
}
static int tegra_output_sor_setup_clock(struct tegra_output *output,
- struct clk *clk, unsigned long pclk)
+ struct clk *clk, unsigned long pclk,
+ unsigned int *div)
{
struct tegra_sor *sor = to_sor(output);
int err;
- /* round to next MHz */
- pclk = DIV_ROUND_UP(pclk / 2, 1000000) * 1000000;
-
err = clk_set_parent(clk, sor->clk_parent);
if (err < 0) {
dev_err(sor->dev, "failed to set parent clock: %d\n", err);
@@ -874,11 +1118,12 @@ static int tegra_output_sor_setup_clock(struct tegra_output *output,
err = clk_set_rate(sor->clk_parent, pclk);
if (err < 0) {
- dev_err(sor->dev, "failed to set base clock rate to %lu Hz\n",
- pclk * 2);
+ dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk);
return err;
}
+ *div = 0;
+
return 0;
}
@@ -914,9 +1159,124 @@ static const struct tegra_output_ops sor_ops = {
.detect = tegra_output_sor_detect,
};
+static int tegra_sor_crc_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static int tegra_sor_crc_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
+{
+ u32 value;
+
+ timeout = jiffies + msecs_to_jiffies(timeout);
+
+ while (time_before(jiffies, timeout)) {
+ value = tegra_sor_readl(sor, SOR_CRC_A);
+ if (value & SOR_CRC_A_VALID)
+ return 0;
+
+ usleep_range(100, 200);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
+ size_t size, loff_t *ppos)
+{
+ struct tegra_sor *sor = file->private_data;
+ ssize_t num, err;
+ char buf[10];
+ u32 value;
+
+ mutex_lock(&sor->lock);
+
+ if (!sor->enabled) {
+ err = -EAGAIN;
+ goto unlock;
+ }
+
+ value = tegra_sor_readl(sor, SOR_STATE_1);
+ value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
+ tegra_sor_writel(sor, value, SOR_STATE_1);
+
+ value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
+ value |= SOR_CRC_CNTRL_ENABLE;
+ tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
+
+ value = tegra_sor_readl(sor, SOR_TEST);
+ value &= ~SOR_TEST_CRC_POST_SERIALIZE;
+ tegra_sor_writel(sor, value, SOR_TEST);
+
+ err = tegra_sor_crc_wait(sor, 100);
+ if (err < 0)
+ goto unlock;
+
+ tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
+ value = tegra_sor_readl(sor, SOR_CRC_B);
+
+ num = scnprintf(buf, sizeof(buf), "%08x\n", value);
+
+ err = simple_read_from_buffer(buffer, size, ppos, buf, num);
+
+unlock:
+ mutex_unlock(&sor->lock);
+ return err;
+}
+
+static const struct file_operations tegra_sor_crc_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_sor_crc_open,
+ .read = tegra_sor_crc_read,
+ .release = tegra_sor_crc_release,
+};
+
+static int tegra_sor_debugfs_init(struct tegra_sor *sor,
+ struct drm_minor *minor)
+{
+ struct dentry *entry;
+ int err = 0;
+
+ sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root);
+ if (!sor->debugfs)
+ return -ENOMEM;
+
+ entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
+ &tegra_sor_crc_fops);
+ if (!entry) {
+ dev_err(sor->dev,
+ "cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
+ minor->debugfs_root->d_name.name);
+ err = -ENOMEM;
+ goto remove;
+ }
+
+ return err;
+
+remove:
+ debugfs_remove(sor->debugfs);
+ sor->debugfs = NULL;
+ return err;
+}
+
+static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
+{
+ debugfs_remove_recursive(sor->debugfs);
+ sor->debugfs = NULL;
+
+ return 0;
+}
+
static int tegra_sor_init(struct host1x_client *client)
{
- struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+ struct drm_device *drm = dev_get_drvdata(client->parent);
struct tegra_sor *sor = host1x_client_to_sor(client);
int err;
@@ -928,12 +1288,18 @@ static int tegra_sor_init(struct host1x_client *client)
sor->output.dev = sor->dev;
sor->output.ops = &sor_ops;
- err = tegra_output_init(tegra->drm, &sor->output);
+ err = tegra_output_init(drm, &sor->output);
if (err < 0) {
dev_err(sor->dev, "output setup failed: %d\n", err);
return err;
}
+ if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+ err = tegra_sor_debugfs_init(sor, drm->primary);
+ if (err < 0)
+ dev_err(sor->dev, "debugfs setup failed: %d\n", err);
+ }
+
if (sor->dpaux) {
err = tegra_dpaux_attach(sor->dpaux, &sor->output);
if (err < 0) {
@@ -964,6 +1330,12 @@ static int tegra_sor_exit(struct host1x_client *client)
}
}
+ if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+ err = tegra_sor_debugfs_exit(sor);
+ if (err < 0)
+ dev_err(sor->dev, "debugfs cleanup failed: %d\n", err);
+ }
+
err = tegra_output_exit(&sor->output);
if (err < 0) {
dev_err(sor->dev, "output cleanup failed: %d\n", err);
@@ -1045,6 +1417,8 @@ static int tegra_sor_probe(struct platform_device *pdev)
sor->client.ops = &sor_client_ops;
sor->client.dev = &pdev->dev;
+ mutex_init(&sor->lock);
+
err = host1x_client_register(&sor->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
diff --git a/drivers/gpu/drm/tegra/sor.h b/drivers/gpu/drm/tegra/sor.h
index f4156d54cd05..a5f8853fedb5 100644
--- a/drivers/gpu/drm/tegra/sor.h
+++ b/drivers/gpu/drm/tegra/sor.h
@@ -47,6 +47,7 @@
#define SOR_HEAD_STATE_4(x) (0x0d + (x))
#define SOR_HEAD_STATE_5(x) (0x0f + (x))
#define SOR_CRC_CNTRL 0x11
+#define SOR_CRC_CNTRL_ENABLE (1 << 0)
#define SOR_DP_DEBUG_MVID 0x12
#define SOR_CLK_CNTRL 0x13
@@ -69,6 +70,7 @@
#define SOR_PWR_NORMAL_STATE_PU (1 << 0)
#define SOR_TEST 0x16
+#define SOR_TEST_CRC_POST_SERIALIZE (1 << 23)
#define SOR_TEST_ATTACHED (1 << 10)
#define SOR_TEST_HEAD_MODE_MASK (3 << 8)
#define SOR_TEST_HEAD_MODE_AWAKE (2 << 8)
@@ -115,6 +117,8 @@
#define SOR_LVDS 0x1c
#define SOR_CRC_A 0x1d
+#define SOR_CRC_A_VALID (1 << 0)
+#define SOR_CRC_A_RESET (1 << 0)
#define SOR_CRC_B 0x1e
#define SOR_BLANK 0x1f
#define SOR_SEQ_CTL 0x20
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
index 171a8203892c..b20b69488dc9 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
@@ -268,7 +268,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
}
pm_runtime_get_sync(dev->dev);
- ret = drm_irq_install(dev);
+ ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
pm_runtime_put_sync(dev->dev);
if (ret < 0) {
dev_err(dev->dev, "failed to install IRQ handler\n");
diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c
index afdf383f630a..7094b92d1ec7 100644
--- a/drivers/gpu/drm/udl/udl_main.c
+++ b/drivers/gpu/drm/udl/udl_main.c
@@ -294,6 +294,7 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags)
dev->dev_private = udl;
if (!udl_parse_vendor_descriptor(dev, dev->usbdev)) {
+ ret = -ENODEV;
DRM_ERROR("firmware not recognized. Assume incompatible device\n");
goto err;
}
diff --git a/drivers/gpu/drm/via/via_dma.c b/drivers/gpu/drm/via/via_dma.c
index a18479c6b6da..6fc0648dd37f 100644
--- a/drivers/gpu/drm/via/via_dma.c
+++ b/drivers/gpu/drm/via/via_dma.c
@@ -737,4 +737,4 @@ const struct drm_ioctl_desc via_ioctls[] = {
DRM_IOCTL_DEF_DRV(VIA_BLIT_SYNC, via_dma_blit_sync, DRM_AUTH)
};
-int via_max_ioctl = DRM_ARRAY_SIZE(via_ioctls);
+int via_max_ioctl = ARRAY_SIZE(via_ioctls);
diff --git a/drivers/gpu/drm/via/via_mm.c b/drivers/gpu/drm/via/via_mm.c
index 927889105483..d70b1e1544bf 100644
--- a/drivers/gpu/drm/via/via_mm.c
+++ b/drivers/gpu/drm/via/via_mm.c
@@ -79,7 +79,7 @@ int via_final_context(struct drm_device *dev, int context)
/* Linux specific until context tracking code gets ported to BSD */
/* Last context, perform cleanup */
- if (list_is_singular(&dev->ctxlist) && dev->dev_private) {
+ if (list_is_singular(&dev->ctxlist)) {
DRM_DEBUG("Last Context\n");
drm_irq_uninstall(dev);
via_cleanup_futex(dev_priv);
diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig
index b71bcd0bfbbf..67720f70fe29 100644
--- a/drivers/gpu/drm/vmwgfx/Kconfig
+++ b/drivers/gpu/drm/vmwgfx/Kconfig
@@ -1,11 +1,14 @@
config DRM_VMWGFX
tristate "DRM driver for VMware Virtual GPU"
- depends on DRM && PCI && FB
+ depends on DRM && PCI
select FB_DEFERRED_IO
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select DRM_TTM
+ # Only needed for the transitional use of drm_crtc_init - can be removed
+ # again once vmwgfx sets up the primary plane itself.
+ select DRM_KMS_HELPER
help
Choose this option if you would like to run 3D acceleration
in a VMware virtual machine.
@@ -14,7 +17,7 @@ config DRM_VMWGFX
The compiled module will be called "vmwgfx.ko".
config DRM_VMWGFX_FBCON
- depends on DRM_VMWGFX
+ depends on DRM_VMWGFX && FB
bool "Enable framebuffer console under vmwgfx by default"
help
Choose this option if you are shipping a new vmwgfx
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 4a223bbea3b3..246a62bab378 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -806,7 +806,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
}
if (dev_priv->capabilities & SVGA_CAP_IRQMASK) {
- ret = drm_irq_install(dev);
+ ret = drm_irq_install(dev, dev->pdev->irq);
if (ret != 0) {
DRM_ERROR("Failed installing irq: %d\n", ret);
goto out_no_irq;
@@ -1417,7 +1417,7 @@ static struct drm_driver driver = {
.enable_vblank = vmw_enable_vblank,
.disable_vblank = vmw_disable_vblank,
.ioctls = vmw_ioctls,
- .num_ioctls = DRM_ARRAY_SIZE(vmw_ioctls),
+ .num_ioctls = ARRAY_SIZE(vmw_ioctls),
.master_create = vmw_master_create,
.master_destroy = vmw_master_destroy,
.master_set = vmw_master_set,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index a2dde5ad8138..8f3edc4710f2 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -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.
*/
- mutex_unlock(&crtc->mutex);
+ drm_modeset_unlock(&crtc->mutex);
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);
- mutex_lock(&crtc->mutex);
+ drm_modeset_lock(&crtc->mutex, NULL);
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.
*/
- mutex_unlock(&crtc->mutex);
+ drm_modeset_unlock(&crtc->mutex);
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);
- mutex_lock(&crtc->mutex);
+ drm_modeset_lock(&crtc->mutex, NULL);
return 0;
}
@@ -2001,7 +2001,7 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
if (du->pref_mode)
list_move(&du->pref_mode->head, &connector->probed_modes);
- drm_mode_connector_list_update(connector);
+ drm_mode_connector_list_update(connector, true);
return 1;
}
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
index ccdd2e6da5e3..aaf54859adb0 100644
--- a/drivers/gpu/host1x/bus.c
+++ b/drivers/gpu/host1x/bus.c
@@ -216,8 +216,8 @@ int host1x_device_exit(struct host1x_device *device)
}
EXPORT_SYMBOL(host1x_device_exit);
-static int host1x_register_client(struct host1x *host1x,
- struct host1x_client *client)
+static int host1x_add_client(struct host1x *host1x,
+ struct host1x_client *client)
{
struct host1x_device *device;
struct host1x_subdev *subdev;
@@ -238,8 +238,8 @@ static int host1x_register_client(struct host1x *host1x,
return -ENODEV;
}
-static int host1x_unregister_client(struct host1x *host1x,
- struct host1x_client *client)
+static int host1x_del_client(struct host1x *host1x,
+ struct host1x_client *client)
{
struct host1x_device *device, *dt;
struct host1x_subdev *subdev;
@@ -503,7 +503,7 @@ int host1x_client_register(struct host1x_client *client)
mutex_lock(&devices_lock);
list_for_each_entry(host1x, &devices, list) {
- err = host1x_register_client(host1x, client);
+ err = host1x_add_client(host1x, client);
if (!err) {
mutex_unlock(&devices_lock);
return 0;
@@ -529,7 +529,7 @@ int host1x_client_unregister(struct host1x_client *client)
mutex_lock(&devices_lock);
list_for_each_entry(host1x, &devices, list) {
- err = host1x_unregister_client(host1x, client);
+ err = host1x_del_client(host1x, client);
if (!err) {
mutex_unlock(&devices_lock);
return 0;
diff --git a/drivers/gpu/ipu-v3/Kconfig b/drivers/gpu/ipu-v3/Kconfig
new file mode 100644
index 000000000000..2f228a2f2a48
--- /dev/null
+++ b/drivers/gpu/ipu-v3/Kconfig
@@ -0,0 +1,7 @@
+config IMX_IPUV3_CORE
+ tristate "IPUv3 core support"
+ depends on SOC_IMX5 || SOC_IMX6Q || SOC_IMX6SL || ARCH_MULTIPLATFORM
+ depends on RESET_CONTROLLER
+ 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
new file mode 100644
index 000000000000..1887972b4ac2
--- /dev/null
+++ b/drivers/gpu/ipu-v3/Makefile
@@ -0,0 +1,3 @@
+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
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
new file mode 100644
index 000000000000..04e7b2eafbdd
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -0,0 +1,1362 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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/module.h>
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/reset.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of_device.h>
+
+#include <drm/drm_fourcc.h>
+
+#include <video/imx-ipu-v3.h>
+#include "ipu-prv.h"
+
+static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
+{
+ return readl(ipu->cm_reg + offset);
+}
+
+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;
+
+ val = ipu_cm_read(ipu, IPU_SRM_PRI2);
+ val |= 0x8;
+ ipu_cm_write(ipu, val, IPU_SRM_PRI2);
+}
+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)
+{
+ 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(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);
+
+void ipu_cpmem_set_yuv_interleaved(struct ipu_ch_param __iomem *p,
+ u32 pixel_format)
+{
+ switch (pixel_format) {
+ 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;
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved);
+
+void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p,
+ u32 pixel_format, int stride, int u_offset, int v_offset)
+{
+ switch (pixel_format) {
+ 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;
+ }
+}
+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;
+ }
+}
+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)
+
+int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 drm_fourcc)
+{
+ 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;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
+
+/*
+ * 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_YVU420:
+ return DRM_FORMAT_YVU420;
+ }
+
+ return -EINVAL;
+}
+
+enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
+{
+ 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;
+ default:
+ return IPUV3_COLORSPACE_UNKNOWN;
+ }
+}
+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;
+
+ pr_debug("%s: resolution: %dx%d stride: %d\n",
+ __func__, pix->width, pix->height,
+ pix->bytesperline);
+
+ ipu_cpmem_set_resolution(cpmem, image->rect.width,
+ image->rect.height);
+ ipu_cpmem_set_stride(cpmem, pix->bytesperline);
+
+ ipu_cpmem_set_fmt(cpmem, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat));
+
+ 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);
+ 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);
+ 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);
+ 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);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ 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);
+
+struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
+{
+ struct ipuv3_channel *channel;
+
+ dev_dbg(ipu->dev, "%s %d\n", __func__, num);
+
+ if (num > 63)
+ return ERR_PTR(-ENODEV);
+
+ mutex_lock(&ipu->channel_lock);
+
+ channel = &ipu->channel[num];
+
+ if (channel->busy) {
+ channel = ERR_PTR(-EBUSY);
+ goto out;
+ }
+
+ channel->busy = true;
+ channel->num = num;
+
+out:
+ mutex_unlock(&ipu->channel_lock);
+
+ return channel;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_get);
+
+void ipu_idmac_put(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+
+ dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
+
+ mutex_lock(&ipu->channel_lock);
+
+ channel->busy = false;
+
+ mutex_unlock(&ipu->channel_lock);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_put);
+
+#define idma_mask(ch) (1 << (ch & 0x1f))
+
+void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
+ bool doublebuffer)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
+ if (doublebuffer)
+ reg |= idma_mask(channel->num);
+ else
+ reg &= ~idma_mask(channel->num);
+ ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
+
+int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
+{
+ unsigned long lock_flags;
+ u32 val;
+
+ spin_lock_irqsave(&ipu->lock, lock_flags);
+
+ val = ipu_cm_read(ipu, IPU_DISP_GEN);
+
+ if (mask & IPU_CONF_DI0_EN)
+ val |= IPU_DI0_COUNTER_RELEASE;
+ if (mask & IPU_CONF_DI1_EN)
+ val |= IPU_DI1_COUNTER_RELEASE;
+
+ ipu_cm_write(ipu, val, IPU_DISP_GEN);
+
+ val = ipu_cm_read(ipu, IPU_CONF);
+ val |= mask;
+ ipu_cm_write(ipu, val, IPU_CONF);
+
+ spin_unlock_irqrestore(&ipu->lock, lock_flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_module_enable);
+
+int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
+{
+ unsigned long lock_flags;
+ u32 val;
+
+ spin_lock_irqsave(&ipu->lock, lock_flags);
+
+ val = ipu_cm_read(ipu, IPU_CONF);
+ val &= ~mask;
+ ipu_cm_write(ipu, val, IPU_CONF);
+
+ val = ipu_cm_read(ipu, IPU_DISP_GEN);
+
+ if (mask & IPU_CONF_DI0_EN)
+ val &= ~IPU_DI0_COUNTER_RELEASE;
+ if (mask & IPU_CONF_DI1_EN)
+ val &= ~IPU_DI1_COUNTER_RELEASE;
+
+ ipu_cm_write(ipu, val, IPU_DISP_GEN);
+
+ spin_unlock_irqrestore(&ipu->lock, lock_flags);
+
+ return 0;
+}
+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;
+ unsigned int chno = channel->num;
+
+ return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer);
+
+void ipu_idmac_select_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);
+
+ /* Mark buffer as ready. */
+ if (buf_num == 0)
+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
+ else
+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
+
+int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
+ val |= idma_mask(channel->num);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
+
+bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
+{
+ return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
+
+int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(ms);
+ while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
+ idma_mask(channel->num)) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
+
+int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(ms);
+ ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32));
+ while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_wait_interrupt);
+
+int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ /* Disable DMA channel(s) */
+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
+
+ /* Set channel buffers NOT to be ready */
+ ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
+
+ if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
+ idma_mask(channel->num)) {
+ ipu_cm_write(ipu, idma_mask(channel->num),
+ IPU_CHA_BUF0_RDY(channel->num));
+ }
+
+ if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
+ idma_mask(channel->num)) {
+ ipu_cm_write(ipu, idma_mask(channel->num),
+ IPU_CHA_BUF1_RDY(channel->num));
+ }
+
+ ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
+
+ /* Reset the double buffer */
+ val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
+
+static int ipu_memory_reset(struct ipu_soc *ipu)
+{
+ unsigned long timeout;
+
+ ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+ while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
+ if (time_after(jiffies, timeout))
+ return -ETIME;
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+struct ipu_devtype {
+ const char *name;
+ unsigned long cm_ofs;
+ unsigned long cpmem_ofs;
+ unsigned long srm_ofs;
+ unsigned long tpm_ofs;
+ unsigned long disp0_ofs;
+ unsigned long disp1_ofs;
+ unsigned long dc_tmpl_ofs;
+ unsigned long vdi_ofs;
+ enum ipuv3_type type;
+};
+
+static struct ipu_devtype ipu_type_imx51 = {
+ .name = "IPUv3EX",
+ .cm_ofs = 0x1e000000,
+ .cpmem_ofs = 0x1f000000,
+ .srm_ofs = 0x1f040000,
+ .tpm_ofs = 0x1f060000,
+ .disp0_ofs = 0x1e040000,
+ .disp1_ofs = 0x1e048000,
+ .dc_tmpl_ofs = 0x1f080000,
+ .vdi_ofs = 0x1e068000,
+ .type = IPUV3EX,
+};
+
+static struct ipu_devtype ipu_type_imx53 = {
+ .name = "IPUv3M",
+ .cm_ofs = 0x06000000,
+ .cpmem_ofs = 0x07000000,
+ .srm_ofs = 0x07040000,
+ .tpm_ofs = 0x07060000,
+ .disp0_ofs = 0x06040000,
+ .disp1_ofs = 0x06048000,
+ .dc_tmpl_ofs = 0x07080000,
+ .vdi_ofs = 0x06068000,
+ .type = IPUV3M,
+};
+
+static struct ipu_devtype ipu_type_imx6q = {
+ .name = "IPUv3H",
+ .cm_ofs = 0x00200000,
+ .cpmem_ofs = 0x00300000,
+ .srm_ofs = 0x00340000,
+ .tpm_ofs = 0x00360000,
+ .disp0_ofs = 0x00240000,
+ .disp1_ofs = 0x00248000,
+ .dc_tmpl_ofs = 0x00380000,
+ .vdi_ofs = 0x00268000,
+ .type = IPUV3H,
+};
+
+static const struct of_device_id imx_ipu_dt_ids[] = {
+ { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
+ { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
+ { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
+
+static int ipu_submodules_init(struct ipu_soc *ipu,
+ struct platform_device *pdev, unsigned long ipu_base,
+ struct clk *ipu_clk)
+{
+ char *unit;
+ int ret;
+ struct device *dev = &pdev->dev;
+ const struct ipu_devtype *devtype = ipu->devtype;
+
+ ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
+ IPU_CONF_DI0_EN, ipu_clk);
+ if (ret) {
+ unit = "di0";
+ goto err_di_0;
+ }
+
+ ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
+ IPU_CONF_DI1_EN, ipu_clk);
+ if (ret) {
+ unit = "di1";
+ goto err_di_1;
+ }
+
+ ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
+ IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
+ if (ret) {
+ unit = "dc_template";
+ goto err_dc;
+ }
+
+ ret = ipu_dmfc_init(ipu, dev, ipu_base +
+ devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
+ if (ret) {
+ unit = "dmfc";
+ goto err_dmfc;
+ }
+
+ ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
+ if (ret) {
+ unit = "dp";
+ goto err_dp;
+ }
+
+ ret = ipu_smfc_init(ipu, dev, ipu_base +
+ devtype->cm_ofs + IPU_CM_SMFC_REG_OFS);
+ if (ret) {
+ unit = "smfc";
+ goto err_smfc;
+ }
+
+ return 0;
+
+err_smfc:
+ ipu_dp_exit(ipu);
+err_dp:
+ ipu_dmfc_exit(ipu);
+err_dmfc:
+ ipu_dc_exit(ipu);
+err_dc:
+ ipu_di_exit(ipu, 1);
+err_di_1:
+ ipu_di_exit(ipu, 0);
+err_di_0:
+ dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
+ return ret;
+}
+
+static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
+{
+ unsigned long status;
+ int i, bit, irq;
+
+ for (i = 0; i < num_regs; i++) {
+
+ status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
+ status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
+
+ for_each_set_bit(bit, &status, 32) {
+ irq = irq_linear_revmap(ipu->domain,
+ regs[i] * 32 + bit);
+ if (irq)
+ generic_handle_irq(irq);
+ }
+ }
+}
+
+static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
+ const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ chained_irq_enter(chip, desc);
+
+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
+
+ chained_irq_exit(chip, desc);
+}
+
+static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
+ const int int_reg[] = { 4, 5, 8, 9};
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ chained_irq_enter(chip, desc);
+
+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
+
+ chained_irq_exit(chip, desc);
+}
+
+int ipu_map_irq(struct ipu_soc *ipu, int irq)
+{
+ int virq;
+
+ virq = irq_linear_revmap(ipu->domain, irq);
+ if (!virq)
+ virq = irq_create_mapping(ipu->domain, irq);
+
+ return virq;
+}
+EXPORT_SYMBOL_GPL(ipu_map_irq);
+
+int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
+ enum ipu_channel_irq irq_type)
+{
+ return ipu_map_irq(ipu, irq_type + channel->num);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
+
+static void ipu_submodules_exit(struct ipu_soc *ipu)
+{
+ ipu_smfc_exit(ipu);
+ ipu_dp_exit(ipu);
+ ipu_dmfc_exit(ipu);
+ ipu_dc_exit(ipu);
+ ipu_di_exit(ipu, 1);
+ ipu_di_exit(ipu, 0);
+}
+
+static int platform_remove_devices_fn(struct device *dev, void *unused)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
+static void platform_device_unregister_children(struct platform_device *pdev)
+{
+ device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
+}
+
+struct ipu_platform_reg {
+ struct ipu_client_platformdata pdata;
+ const char *name;
+ int reg_offset;
+};
+
+static const struct ipu_platform_reg client_reg[] = {
+ {
+ .pdata = {
+ .di = 0,
+ .dc = 5,
+ .dp = IPU_DP_FLOW_SYNC_BG,
+ .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
+ .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC,
+ },
+ .name = "imx-ipuv3-crtc",
+ }, {
+ .pdata = {
+ .di = 1,
+ .dc = 1,
+ .dp = -EINVAL,
+ .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
+ .dma[1] = -EINVAL,
+ },
+ .name = "imx-ipuv3-crtc",
+ }, {
+ .pdata = {
+ .csi = 0,
+ .dma[0] = IPUV3_CHANNEL_CSI0,
+ .dma[1] = -EINVAL,
+ },
+ .reg_offset = IPU_CM_CSI0_REG_OFS,
+ .name = "imx-ipuv3-camera",
+ }, {
+ .pdata = {
+ .csi = 1,
+ .dma[0] = IPUV3_CHANNEL_CSI1,
+ .dma[1] = -EINVAL,
+ },
+ .reg_offset = IPU_CM_CSI1_REG_OFS,
+ .name = "imx-ipuv3-camera",
+ },
+};
+
+static DEFINE_MUTEX(ipu_client_id_mutex);
+static int ipu_client_id;
+
+static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
+{
+ struct device *dev = ipu->dev;
+ unsigned i;
+ int id, ret;
+
+ mutex_lock(&ipu_client_id_mutex);
+ id = ipu_client_id;
+ ipu_client_id += ARRAY_SIZE(client_reg);
+ mutex_unlock(&ipu_client_id_mutex);
+
+ for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
+ const struct ipu_platform_reg *reg = &client_reg[i];
+ struct platform_device *pdev;
+ struct resource res;
+
+ if (reg->reg_offset) {
+ memset(&res, 0, sizeof(res));
+ res.flags = IORESOURCE_MEM;
+ res.start = ipu_base + ipu->devtype->cm_ofs + reg->reg_offset;
+ res.end = res.start + PAGE_SIZE - 1;
+ pdev = platform_device_register_resndata(dev, reg->name,
+ id++, &res, 1, &reg->pdata, sizeof(reg->pdata));
+ } else {
+ pdev = platform_device_register_data(dev, reg->name,
+ id++, &reg->pdata, sizeof(reg->pdata));
+ }
+
+ if (IS_ERR(pdev))
+ goto err_register;
+ }
+
+ return 0;
+
+err_register:
+ platform_device_unregister_children(to_platform_device(dev));
+
+ return ret;
+}
+
+
+static int ipu_irq_init(struct ipu_soc *ipu)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+ unsigned long unused[IPU_NUM_IRQS / 32] = {
+ 0x400100d0, 0xffe000fd,
+ 0x400100d0, 0xffe000fd,
+ 0x400100d0, 0xffe000fd,
+ 0x4077ffff, 0xffe7e1fd,
+ 0x23fffffe, 0x8880fff0,
+ 0xf98fe7d0, 0xfff81fff,
+ 0x400100d0, 0xffe000fd,
+ 0x00000000,
+ };
+ int ret, i;
+
+ ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS,
+ &irq_generic_chip_ops, ipu);
+ if (!ipu->domain) {
+ dev_err(ipu->dev, "failed to add irq domain\n");
+ return -ENODEV;
+ }
+
+ ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU",
+ handle_level_irq, 0,
+ IRQF_VALID, 0);
+ if (ret < 0) {
+ dev_err(ipu->dev, "failed to alloc generic irq chips\n");
+ irq_domain_remove(ipu->domain);
+ return ret;
+ }
+
+ for (i = 0; i < IPU_NUM_IRQS; i += 32) {
+ gc = irq_get_domain_generic_chip(ipu->domain, i);
+ gc->reg_base = ipu->cm_reg;
+ gc->unused = unused[i / 32];
+ ct = gc->chip_types;
+ ct->chip.irq_ack = irq_gc_ack_set_bit;
+ ct->chip.irq_mask = irq_gc_mask_clr_bit;
+ ct->chip.irq_unmask = irq_gc_mask_set_bit;
+ ct->regs.ack = IPU_INT_STAT(i / 32);
+ ct->regs.mask = IPU_INT_CTRL(i / 32);
+ }
+
+ irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler);
+ irq_set_handler_data(ipu->irq_sync, ipu);
+ irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler);
+ irq_set_handler_data(ipu->irq_err, ipu);
+
+ return 0;
+}
+
+static void ipu_irq_exit(struct ipu_soc *ipu)
+{
+ int i, irq;
+
+ irq_set_chained_handler(ipu->irq_err, NULL);
+ irq_set_handler_data(ipu->irq_err, NULL);
+ irq_set_chained_handler(ipu->irq_sync, NULL);
+ irq_set_handler_data(ipu->irq_sync, NULL);
+
+ /* TODO: remove irq_domain_generic_chips */
+
+ for (i = 0; i < IPU_NUM_IRQS; i++) {
+ irq = irq_linear_revmap(ipu->domain, i);
+ if (irq)
+ irq_dispose_mapping(irq);
+ }
+
+ irq_domain_remove(ipu->domain);
+}
+
+static int ipu_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(imx_ipu_dt_ids, &pdev->dev);
+ struct ipu_soc *ipu;
+ struct resource *res;
+ unsigned long ipu_base;
+ int i, ret, irq_sync, irq_err;
+ const struct ipu_devtype *devtype;
+
+ devtype = of_id->data;
+
+ irq_sync = platform_get_irq(pdev, 0);
+ irq_err = platform_get_irq(pdev, 1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n",
+ irq_sync, irq_err);
+
+ if (!res || irq_sync < 0 || irq_err < 0)
+ return -ENODEV;
+
+ ipu_base = res->start;
+
+ ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
+ if (!ipu)
+ return -ENODEV;
+
+ for (i = 0; i < 64; i++)
+ ipu->channel[i].ipu = ipu;
+ ipu->devtype = devtype;
+ ipu->ipu_type = devtype->type;
+
+ spin_lock_init(&ipu->lock);
+ mutex_init(&ipu->channel_lock);
+
+ dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs);
+ dev_dbg(&pdev->dev, "idmac: 0x%08lx\n",
+ 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, "disp0: 0x%08lx\n",
+ ipu_base + devtype->disp0_ofs);
+ dev_dbg(&pdev->dev, "disp1: 0x%08lx\n",
+ ipu_base + devtype->disp1_ofs);
+ dev_dbg(&pdev->dev, "srm: 0x%08lx\n",
+ ipu_base + devtype->srm_ofs);
+ dev_dbg(&pdev->dev, "tpm: 0x%08lx\n",
+ ipu_base + devtype->tpm_ofs);
+ dev_dbg(&pdev->dev, "dc: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
+ dev_dbg(&pdev->dev, "ic: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
+ dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
+ dev_dbg(&pdev->dev, "vdi: 0x%08lx\n",
+ ipu_base + devtype->vdi_ofs);
+
+ ipu->cm_reg = devm_ioremap(&pdev->dev,
+ ipu_base + devtype->cm_ofs, PAGE_SIZE);
+ 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)
+ return -ENOMEM;
+
+ ipu->clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(ipu->clk)) {
+ ret = PTR_ERR(ipu->clk);
+ dev_err(&pdev->dev, "clk_get failed with %d", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, ipu);
+
+ ret = clk_prepare_enable(ipu->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ ipu->dev = &pdev->dev;
+ ipu->irq_sync = irq_sync;
+ ipu->irq_err = irq_err;
+
+ ret = ipu_irq_init(ipu);
+ if (ret)
+ goto out_failed_irq;
+
+ ret = device_reset(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to reset: %d\n", ret);
+ goto out_failed_reset;
+ }
+ ret = ipu_memory_reset(ipu);
+ if (ret)
+ goto out_failed_reset;
+
+ /* Set MCU_T to divide MCU access window into 2 */
+ ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
+ IPU_DISP_GEN);
+
+ ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
+ if (ret)
+ goto failed_submodules_init;
+
+ ret = ipu_add_client_devices(ipu, ipu_base);
+ if (ret) {
+ dev_err(&pdev->dev, "adding client devices failed with %d\n",
+ ret);
+ goto failed_add_clients;
+ }
+
+ dev_info(&pdev->dev, "%s probed\n", devtype->name);
+
+ return 0;
+
+failed_add_clients:
+ ipu_submodules_exit(ipu);
+failed_submodules_init:
+out_failed_reset:
+ ipu_irq_exit(ipu);
+out_failed_irq:
+ clk_disable_unprepare(ipu->clk);
+ return ret;
+}
+
+static int ipu_remove(struct platform_device *pdev)
+{
+ struct ipu_soc *ipu = platform_get_drvdata(pdev);
+
+ platform_device_unregister_children(pdev);
+ ipu_submodules_exit(ipu);
+ ipu_irq_exit(ipu);
+
+ clk_disable_unprepare(ipu->clk);
+
+ return 0;
+}
+
+static struct platform_driver imx_ipu_driver = {
+ .driver = {
+ .name = "imx-ipuv3",
+ .of_match_table = imx_ipu_dt_ids,
+ },
+ .probe = ipu_probe,
+ .remove = ipu_remove,
+};
+
+module_platform_driver(imx_ipu_driver);
+
+MODULE_ALIAS("platform:imx-ipuv3");
+MODULE_DESCRIPTION("i.MX IPU v3 driver");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c
new file mode 100644
index 000000000000..2326c752d89b
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-dc.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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/interrupt.h>
+#include <linux/io.h>
+
+#include <video/imx-ipu-v3.h>
+#include "ipu-prv.h"
+
+#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2)
+#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~0x1) * 2)
+
+#define DC_EVT_NF 0
+#define DC_EVT_NL 1
+#define DC_EVT_EOF 2
+#define DC_EVT_NFIELD 3
+#define DC_EVT_EOL 4
+#define DC_EVT_EOFIELD 5
+#define DC_EVT_NEW_ADDR 6
+#define DC_EVT_NEW_CHAN 7
+#define DC_EVT_NEW_DATA 8
+
+#define DC_EVT_NEW_ADDR_W_0 0
+#define DC_EVT_NEW_ADDR_W_1 1
+#define DC_EVT_NEW_CHAN_W_0 2
+#define DC_EVT_NEW_CHAN_W_1 3
+#define DC_EVT_NEW_DATA_W_0 4
+#define DC_EVT_NEW_DATA_W_1 5
+#define DC_EVT_NEW_ADDR_R_0 6
+#define DC_EVT_NEW_ADDR_R_1 7
+#define DC_EVT_NEW_CHAN_R_0 8
+#define DC_EVT_NEW_CHAN_R_1 9
+#define DC_EVT_NEW_DATA_R_0 10
+#define DC_EVT_NEW_DATA_R_1 11
+
+#define DC_WR_CH_CONF 0x0
+#define DC_WR_CH_ADDR 0x4
+#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2)
+
+#define DC_GEN 0xd4
+#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4)
+#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4)
+#define DC_STAT 0x1c8
+
+#define WROD(lf) (0x18 | ((lf) << 1))
+#define WRG 0x01
+#define WCLK 0xc9
+
+#define SYNC_WAVE 0
+#define NULL_WAVE (-1)
+
+#define DC_GEN_SYNC_1_6_SYNC (2 << 1)
+#define DC_GEN_SYNC_PRIORITY_1 (1 << 7)
+
+#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0)
+#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3)
+#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3)
+#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4)
+#define DC_WR_CH_CONF_FIELD_MODE (1 << 9)
+#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5)
+#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5)
+#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2)
+#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3)
+
+#define IPU_DC_NUM_CHANNELS 10
+
+struct ipu_dc_priv;
+
+enum ipu_dc_map {
+ IPU_DC_MAP_RGB24,
+ IPU_DC_MAP_RGB565,
+ IPU_DC_MAP_GBR24, /* TVEv2 */
+ IPU_DC_MAP_BGR666,
+ IPU_DC_MAP_LVDS666,
+ IPU_DC_MAP_BGR24,
+};
+
+struct ipu_dc {
+ /* The display interface number assigned to this dc channel */
+ unsigned int di;
+ void __iomem *base;
+ struct ipu_dc_priv *priv;
+ int chno;
+ bool in_use;
+};
+
+struct ipu_dc_priv {
+ void __iomem *dc_reg;
+ void __iomem *dc_tmpl_reg;
+ struct ipu_soc *ipu;
+ struct device *dev;
+ struct ipu_dc channels[IPU_DC_NUM_CHANNELS];
+ struct mutex mutex;
+ struct completion comp;
+ int dc_irq;
+ int dp_irq;
+};
+
+static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority)
+{
+ u32 reg;
+
+ reg = readl(dc->base + DC_RL_CH(event));
+ reg &= ~(0xffff << (16 * (event & 0x1)));
+ reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
+ writel(reg, dc->base + DC_RL_CH(event));
+}
+
+static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand,
+ int map, int wave, int glue, int sync, int stop)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ u32 reg1, reg2;
+
+ if (opcode == WCLK) {
+ reg1 = (operand << 20) & 0xfff00000;
+ reg2 = operand >> 12 | opcode << 1 | stop << 9;
+ } else if (opcode == WRG) {
+ reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000);
+ reg2 = operand >> 17 | opcode << 7 | stop << 9;
+ } else {
+ reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000);
+ reg2 = operand >> 12 | opcode << 4 | stop << 9;
+ }
+ writel(reg1, priv->dc_tmpl_reg + word * 8);
+ writel(reg2, priv->dc_tmpl_reg + word * 8 + 4);
+}
+
+static int ipu_pixfmt_to_map(u32 fmt)
+{
+ switch (fmt) {
+ case V4L2_PIX_FMT_RGB24:
+ return IPU_DC_MAP_RGB24;
+ case V4L2_PIX_FMT_RGB565:
+ return IPU_DC_MAP_RGB565;
+ case IPU_PIX_FMT_GBR24:
+ return IPU_DC_MAP_GBR24;
+ case V4L2_PIX_FMT_BGR666:
+ return IPU_DC_MAP_BGR666;
+ case v4l2_fourcc('L', 'V', 'D', '6'):
+ return IPU_DC_MAP_LVDS666;
+ case V4L2_PIX_FMT_BGR24:
+ return IPU_DC_MAP_BGR24;
+ default:
+ return -EINVAL;
+ }
+}
+
+int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
+ u32 pixel_fmt, u32 width)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ u32 reg = 0;
+ int map;
+
+ dc->di = ipu_di_get_num(di);
+
+ map = ipu_pixfmt_to_map(pixel_fmt);
+ if (map < 0) {
+ dev_dbg(priv->dev, "IPU_DISP: No MAP\n");
+ return map;
+ }
+
+ if (interlaced) {
+ dc_link_event(dc, DC_EVT_NL, 0, 3);
+ dc_link_event(dc, DC_EVT_EOL, 0, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
+
+ /* Init template microcode */
+ dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1);
+ } else {
+ if (dc->di) {
+ dc_link_event(dc, DC_EVT_NL, 2, 3);
+ dc_link_event(dc, DC_EVT_EOL, 3, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 1, 1);
+ /* Init template microcode */
+ dc_write_tmpl(dc, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1);
+ dc_write_tmpl(dc, 3, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0);
+ dc_write_tmpl(dc, 4, WRG, 0, map, NULL_WAVE, 0, 0, 1);
+ dc_write_tmpl(dc, 1, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+ } else {
+ dc_link_event(dc, DC_EVT_NL, 5, 3);
+ dc_link_event(dc, DC_EVT_EOL, 6, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 8, 1);
+ /* Init template microcode */
+ dc_write_tmpl(dc, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1);
+ dc_write_tmpl(dc, 6, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0);
+ dc_write_tmpl(dc, 7, WRG, 0, map, NULL_WAVE, 0, 0, 1);
+ dc_write_tmpl(dc, 8, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1);
+ }
+ }
+ dc_link_event(dc, DC_EVT_NF, 0, 0);
+ dc_link_event(dc, DC_EVT_NFIELD, 0, 0);
+ dc_link_event(dc, DC_EVT_EOF, 0, 0);
+ dc_link_event(dc, DC_EVT_EOFIELD, 0, 0);
+ dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0);
+ dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0);
+
+ reg = readl(dc->base + DC_WR_CH_CONF);
+ if (interlaced)
+ reg |= DC_WR_CH_CONF_FIELD_MODE;
+ else
+ reg &= ~DC_WR_CH_CONF_FIELD_MODE;
+ writel(reg, dc->base + DC_WR_CH_CONF);
+
+ writel(0x0, dc->base + DC_WR_CH_ADDR);
+ writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dc_init_sync);
+
+void ipu_dc_enable(struct ipu_soc *ipu)
+{
+ ipu_module_enable(ipu, IPU_CONF_DC_EN);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_enable);
+
+void ipu_dc_enable_channel(struct ipu_dc *dc)
+{
+ int di;
+ u32 reg;
+
+ di = dc->di;
+
+ reg = readl(dc->base + DC_WR_CH_CONF);
+ reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL;
+ writel(reg, dc->base + DC_WR_CH_CONF);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_enable_channel);
+
+static irqreturn_t dc_irq_handler(int irq, void *dev_id)
+{
+ struct ipu_dc *dc = dev_id;
+ u32 reg;
+
+ reg = readl(dc->base + DC_WR_CH_CONF);
+ reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+ writel(reg, dc->base + DC_WR_CH_CONF);
+
+ /* The Freescale BSP kernel clears DIx_COUNTER_RELEASE here */
+
+ complete(&dc->priv->comp);
+ return IRQ_HANDLED;
+}
+
+void ipu_dc_disable_channel(struct ipu_dc *dc)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ int irq, ret;
+ u32 val;
+
+ /* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */
+ if (dc->chno == 1)
+ irq = priv->dc_irq;
+ else if (dc->chno == 5)
+ irq = priv->dp_irq;
+ else
+ return;
+
+ init_completion(&priv->comp);
+ enable_irq(irq);
+ ret = wait_for_completion_timeout(&priv->comp, msecs_to_jiffies(50));
+ disable_irq(irq);
+ if (ret <= 0) {
+ dev_warn(priv->dev, "DC stop timeout after 50 ms\n");
+
+ val = readl(dc->base + DC_WR_CH_CONF);
+ val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+ writel(val, dc->base + DC_WR_CH_CONF);
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
+
+void ipu_dc_disable(struct ipu_soc *ipu)
+{
+ ipu_module_disable(ipu, IPU_CONF_DC_EN);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_disable);
+
+static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map,
+ int byte_num, int offset, int mask)
+{
+ int ptr = map * 3 + byte_num;
+ u32 reg;
+
+ reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr));
+ reg &= ~(0xffff << (16 * (ptr & 0x1)));
+ reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
+ writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr));
+
+ reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
+ reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num)));
+ reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
+ writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map));
+}
+
+static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map)
+{
+ u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
+
+ writel(reg & ~(0xffff << (16 * (map & 0x1))),
+ priv->dc_reg + DC_MAP_CONF_PTR(map));
+}
+
+struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel)
+{
+ struct ipu_dc_priv *priv = ipu->dc_priv;
+ struct ipu_dc *dc;
+
+ if (channel >= IPU_DC_NUM_CHANNELS)
+ return ERR_PTR(-ENODEV);
+
+ dc = &priv->channels[channel];
+
+ mutex_lock(&priv->mutex);
+
+ if (dc->in_use) {
+ mutex_unlock(&priv->mutex);
+ return ERR_PTR(-EBUSY);
+ }
+
+ dc->in_use = true;
+
+ mutex_unlock(&priv->mutex);
+
+ return dc;
+}
+EXPORT_SYMBOL_GPL(ipu_dc_get);
+
+void ipu_dc_put(struct ipu_dc *dc)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+
+ mutex_lock(&priv->mutex);
+ dc->in_use = false;
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_put);
+
+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
+ unsigned long base, unsigned long template_base)
+{
+ struct ipu_dc_priv *priv;
+ static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c,
+ 0x78, 0, 0x94, 0xb4};
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->mutex);
+
+ priv->dev = dev;
+ priv->ipu = ipu;
+ priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE);
+ priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE);
+ if (!priv->dc_reg || !priv->dc_tmpl_reg)
+ return -ENOMEM;
+
+ for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) {
+ priv->channels[i].chno = i;
+ priv->channels[i].priv = priv;
+ priv->channels[i].base = priv->dc_reg + channel_offsets[i];
+ }
+
+ priv->dc_irq = ipu_map_irq(ipu, IPU_IRQ_DC_FC_1);
+ if (!priv->dc_irq)
+ return -EINVAL;
+ ret = devm_request_irq(dev, priv->dc_irq, dc_irq_handler, 0, NULL,
+ &priv->channels[1]);
+ if (ret < 0)
+ return ret;
+ disable_irq(priv->dc_irq);
+ priv->dp_irq = ipu_map_irq(ipu, IPU_IRQ_DP_SF_END);
+ if (!priv->dp_irq)
+ return -EINVAL;
+ ret = devm_request_irq(dev, priv->dp_irq, dc_irq_handler, 0, NULL,
+ &priv->channels[5]);
+ if (ret < 0)
+ return ret;
+ disable_irq(priv->dp_irq);
+
+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) |
+ DC_WR_CH_CONF_PROG_DI_ID,
+ priv->channels[1].base + DC_WR_CH_CONF);
+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0),
+ priv->channels[5].base + DC_WR_CH_CONF);
+
+ writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1,
+ priv->dc_reg + DC_GEN);
+
+ ipu->dc_priv = priv;
+
+ dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n",
+ base, template_base);
+
+ /* rgb24 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24);
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */
+
+ /* rgb565 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565);
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */
+
+ /* gbr24 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24);
+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */
+
+ /* bgr666 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666);
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */
+
+ /* lvds666 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666);
+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */
+
+ /* bgr24 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24);
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */
+
+ return 0;
+}
+
+void ipu_dc_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
new file mode 100644
index 000000000000..c490ba4384fc
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-di.c
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+#include <video/imx-ipu-v3.h>
+#include "ipu-prv.h"
+
+struct ipu_di {
+ void __iomem *base;
+ int id;
+ u32 module;
+ struct clk *clk_di; /* display input clock */
+ struct clk *clk_ipu; /* IPU bus clock */
+ struct clk *clk_di_pixel; /* resulting pixel clock */
+ bool inuse;
+ struct ipu_soc *ipu;
+};
+
+static DEFINE_MUTEX(di_mutex);
+
+struct di_sync_config {
+ int run_count;
+ int run_src;
+ int offset_count;
+ int offset_src;
+ int repeat_count;
+ int cnt_clr_src;
+ int cnt_polarity_gen_en;
+ int cnt_polarity_clr_src;
+ int cnt_polarity_trigger_src;
+ int cnt_up;
+ int cnt_down;
+};
+
+enum di_pins {
+ DI_PIN11 = 0,
+ DI_PIN12 = 1,
+ DI_PIN13 = 2,
+ DI_PIN14 = 3,
+ DI_PIN15 = 4,
+ DI_PIN16 = 5,
+ DI_PIN17 = 6,
+ DI_PIN_CS = 7,
+
+ DI_PIN_SER_CLK = 0,
+ DI_PIN_SER_RS = 1,
+};
+
+enum di_sync_wave {
+ DI_SYNC_NONE = 0,
+ DI_SYNC_CLK = 1,
+ DI_SYNC_INT_HSYNC = 2,
+ DI_SYNC_HSYNC = 3,
+ DI_SYNC_VSYNC = 4,
+ DI_SYNC_DE = 6,
+};
+
+#define SYNC_WAVE 0
+
+#define DI_GENERAL 0x0000
+#define DI_BS_CLKGEN0 0x0004
+#define DI_BS_CLKGEN1 0x0008
+#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1))
+#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1))
+#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2))
+#define DI_SYNC_AS_GEN 0x0054
+#define DI_DW_GEN(gen) (0x0058 + 4 * (gen))
+#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set)))
+#define DI_SER_CONF 0x015c
+#define DI_SSC 0x0160
+#define DI_POL 0x0164
+#define DI_AW0 0x0168
+#define DI_AW1 0x016c
+#define DI_SCR_CONF 0x0170
+#define DI_STAT 0x0174
+
+#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19)
+#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16)
+#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3)
+#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0)
+
+#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29)
+#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25)
+#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12)
+#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9)
+#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16)
+#define DI_SW_GEN1_CNT_UP(x) (x)
+#define DI_SW_GEN1_AUTO_RELOAD (0x10000000)
+
+#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24
+#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16
+
+#define DI_GEN_POLARITY_1 (1 << 0)
+#define DI_GEN_POLARITY_2 (1 << 1)
+#define DI_GEN_POLARITY_3 (1 << 2)
+#define DI_GEN_POLARITY_4 (1 << 3)
+#define DI_GEN_POLARITY_5 (1 << 4)
+#define DI_GEN_POLARITY_6 (1 << 5)
+#define DI_GEN_POLARITY_7 (1 << 6)
+#define DI_GEN_POLARITY_8 (1 << 7)
+#define DI_GEN_POLARITY_DISP_CLK (1 << 17)
+#define DI_GEN_DI_CLK_EXT (1 << 20)
+#define DI_GEN_DI_VSYNC_EXT (1 << 21)
+
+#define DI_POL_DRDY_DATA_POLARITY (1 << 7)
+#define DI_POL_DRDY_POLARITY_15 (1 << 4)
+
+#define DI_VSYNC_SEL_OFFSET 13
+
+static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset)
+{
+ return readl(di->base + offset);
+}
+
+static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)
+{
+ writel(value, di->base + offset);
+}
+
+static void ipu_di_data_wave_config(struct ipu_di *di,
+ int wave_gen,
+ int access_size, int component_size)
+{
+ u32 reg;
+ reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) |
+ (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET);
+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
+}
+
+static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin,
+ int set, int up, int down)
+{
+ u32 reg;
+
+ reg = ipu_di_read(di, DI_DW_GEN(wave_gen));
+ reg &= ~(0x3 << (di_pin * 2));
+ reg |= set << (di_pin * 2);
+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
+
+ ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set));
+}
+
+static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config,
+ int start, int count)
+{
+ u32 reg;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct di_sync_config *c = &config[i];
+ int wave_gen = start + i + 1;
+
+ if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) ||
+ (c->repeat_count >= 0x1000) ||
+ (c->cnt_up >= 0x400) ||
+ (c->cnt_down >= 0x400)) {
+ dev_err(di->ipu->dev, "DI%d counters out of range.\n",
+ di->id);
+ return;
+ }
+
+ reg = DI_SW_GEN0_RUN_COUNT(c->run_count) |
+ DI_SW_GEN0_RUN_SRC(c->run_src) |
+ DI_SW_GEN0_OFFSET_COUNT(c->offset_count) |
+ DI_SW_GEN0_OFFSET_SRC(c->offset_src);
+ ipu_di_write(di, reg, DI_SW_GEN0(wave_gen));
+
+ reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) |
+ DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) |
+ DI_SW_GEN1_CNT_POL_TRIGGER_SRC(
+ c->cnt_polarity_trigger_src) |
+ DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) |
+ DI_SW_GEN1_CNT_DOWN(c->cnt_down) |
+ DI_SW_GEN1_CNT_UP(c->cnt_up);
+
+ /* Enable auto reload */
+ if (c->repeat_count == 0)
+ reg |= DI_SW_GEN1_AUTO_RELOAD;
+
+ ipu_di_write(di, reg, DI_SW_GEN1(wave_gen));
+
+ reg = ipu_di_read(di, DI_STP_REP(wave_gen));
+ reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1)));
+ reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1));
+ ipu_di_write(di, reg, DI_STP_REP(wave_gen));
+ }
+}
+
+static void ipu_di_sync_config_interlaced(struct ipu_di *di,
+ struct ipu_di_signal_cfg *sig)
+{
+ u32 h_total = sig->width + sig->h_sync_width +
+ sig->h_start_width + sig->h_end_width;
+ u32 v_total = sig->height + sig->v_sync_width +
+ sig->v_start_width + sig->v_end_width;
+ u32 reg;
+ struct di_sync_config cfg[] = {
+ {
+ .run_count = h_total / 2 - 1,
+ .run_src = DI_SYNC_CLK,
+ }, {
+ .run_count = h_total - 11,
+ .run_src = DI_SYNC_CLK,
+ .cnt_down = 4,
+ }, {
+ .run_count = v_total * 2 - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = 1,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = 4,
+ }, {
+ .run_count = v_total / 2 - 1,
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = sig->v_start_width,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = 2,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ }, {
+ .run_src = DI_SYNC_HSYNC,
+ .repeat_count = sig->height / 2,
+ .cnt_clr_src = 4,
+ }, {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_HSYNC,
+ }, {
+ .run_count = v_total / 2 - 1,
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = 9,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = 2,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ }, {
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 5,
+ }, {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = v_total / 2,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_clr_src = DI_SYNC_HSYNC,
+ .cnt_down = 4,
+ }
+ };
+
+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
+
+ /* set gentime select and tag sel */
+ reg = ipu_di_read(di, DI_SW_GEN1(9));
+ reg &= 0x1FFFFFFF;
+ reg |= (3 - 1) << 29 | 0x00008000;
+ ipu_di_write(di, reg, DI_SW_GEN1(9));
+
+ ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
+}
+
+static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
+ struct ipu_di_signal_cfg *sig, int div)
+{
+ u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width +
+ sig->h_end_width;
+ u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width +
+ sig->v_end_width;
+ struct di_sync_config cfg[] = {
+ {
+ /* 1: INT_HSYNC */
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ } , {
+ /* PIN2: HSYNC */
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ .offset_count = div * sig->v_to_h_sync,
+ .offset_src = DI_SYNC_CLK,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_CLK,
+ .cnt_down = sig->h_sync_width * 2,
+ } , {
+ /* PIN3: VSYNC */
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = sig->v_sync_width * 2,
+ } , {
+ /* 4: Line Active */
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = sig->v_sync_width + sig->v_start_width,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = sig->height,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ } , {
+ /* 5: Pixel Active, referenced by DC */
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_sync_width + sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 5, /* Line Active */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ },
+ };
+ /* can't use #7 and #8 for line active and pixel active counters */
+ struct di_sync_config cfg_vga[] = {
+ {
+ /* 1: INT_HSYNC */
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ } , {
+ /* 2: VSYNC */
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ } , {
+ /* 3: Line Active */
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = sig->v_sync_width + sig->v_start_width,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .repeat_count = sig->height,
+ .cnt_clr_src = 3 /* VSYNC */,
+ } , {
+ /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */
+ .offset_src = DI_SYNC_CLK,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_CLK,
+ .cnt_down = sig->h_sync_width * 2,
+ } , {
+ /* 5: Pixel Active signal to DC */
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_sync_width + sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 4, /* Line Active */
+ } , {
+ /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = 1, /* magic value from Freescale TVE driver */
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = sig->v_sync_width * 2,
+ } , {
+ /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */
+ .offset_src = DI_SYNC_CLK,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_CLK,
+ .cnt_down = sig->h_sync_width * 2,
+ } , {
+ /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = 1, /* magic value from Freescale TVE driver */
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = sig->v_sync_width * 2,
+ } , {
+ /* unused */
+ },
+ };
+
+ ipu_di_write(di, v_total - 1, DI_SCR_CONF);
+ if (sig->hsync_pin == 2 && sig->vsync_pin == 3)
+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
+ else
+ ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga));
+}
+
+static void ipu_di_config_clock(struct ipu_di *di,
+ const struct ipu_di_signal_cfg *sig)
+{
+ struct clk *clk;
+ unsigned clkgen0;
+ uint32_t val;
+
+ if (sig->clkflags & IPU_DI_CLKMODE_EXT) {
+ /*
+ * CLKMODE_EXT means we must use the DI clock: this is
+ * needed for things like LVDS which needs to feed the
+ * DI and LDB with the same pixel clock.
+ */
+ clk = di->clk_di;
+
+ if (sig->clkflags & IPU_DI_CLKMODE_SYNC) {
+ /*
+ * CLKMODE_SYNC means that we want the DI to be
+ * clocked at the same rate as the parent clock.
+ * This is needed (eg) for LDB which needs to be
+ * fed with the same pixel clock. We assume that
+ * the LDB clock has already been set correctly.
+ */
+ clkgen0 = 1 << 4;
+ } else {
+ /*
+ * We can use the divider. We should really have
+ * a flag here indicating whether the bridge can
+ * cope with a fractional divider or not. For the
+ * time being, let's go for simplicitly and
+ * reliability.
+ */
+ unsigned long in_rate;
+ unsigned div;
+
+ clk_set_rate(clk, sig->pixelclock);
+
+ in_rate = clk_get_rate(clk);
+ div = (in_rate + sig->pixelclock / 2) / sig->pixelclock;
+ if (div == 0)
+ div = 1;
+
+ clkgen0 = div << 4;
+ }
+ } else {
+ /*
+ * For other interfaces, we can arbitarily select between
+ * the DI specific clock and the internal IPU clock. See
+ * DI_GENERAL bit 20. We select the IPU clock if it can
+ * give us a clock rate within 1% of the requested frequency,
+ * otherwise we use the DI clock.
+ */
+ unsigned long rate, clkrate;
+ unsigned div, error;
+
+ clkrate = clk_get_rate(di->clk_ipu);
+ div = (clkrate + sig->pixelclock / 2) / sig->pixelclock;
+ rate = clkrate / div;
+
+ error = rate / (sig->pixelclock / 1000);
+
+ dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n",
+ rate, div, (signed)(error - 1000) / 10, error % 10);
+
+ /* Allow a 1% error */
+ if (error < 1010 && error >= 990) {
+ clk = di->clk_ipu;
+
+ clkgen0 = div << 4;
+ } else {
+ unsigned long in_rate;
+ unsigned div;
+
+ clk = di->clk_di;
+
+ clk_set_rate(clk, sig->pixelclock);
+
+ in_rate = clk_get_rate(clk);
+ div = (in_rate + sig->pixelclock / 2) / sig->pixelclock;
+ if (div == 0)
+ div = 1;
+
+ clkgen0 = div << 4;
+ }
+ }
+
+ di->clk_di_pixel = clk;
+
+ /* Set the divider */
+ ipu_di_write(di, clkgen0, DI_BS_CLKGEN0);
+
+ /*
+ * Set the high/low periods. Bits 24:16 give us the falling edge,
+ * and bits 8:0 give the rising edge. LSB is fraction, and is
+ * based on the divider above. We want a 50% duty cycle, so set
+ * the falling edge to be half the divider.
+ */
+ ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1);
+
+ /* Finally select the input clock */
+ val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT;
+ if (clk == di->clk_di)
+ val |= DI_GEN_DI_CLK_EXT;
+ ipu_di_write(di, val, DI_GENERAL);
+
+ dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n",
+ sig->pixelclock,
+ clk_get_rate(di->clk_ipu),
+ clk_get_rate(di->clk_di),
+ clk == di->clk_di ? "DI" : "IPU",
+ clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4));
+}
+
+int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
+{
+ u32 reg;
+ u32 di_gen, vsync_cnt;
+ u32 div;
+ u32 h_total, v_total;
+
+ dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n",
+ di->id, sig->width, sig->height);
+
+ if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0))
+ return -EINVAL;
+
+ h_total = sig->width + sig->h_sync_width + sig->h_start_width +
+ sig->h_end_width;
+ v_total = sig->height + sig->v_sync_width + sig->v_start_width +
+ sig->v_end_width;
+
+ dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n",
+ clk_get_rate(di->clk_ipu),
+ clk_get_rate(di->clk_di),
+ sig->pixelclock);
+
+ mutex_lock(&di_mutex);
+
+ ipu_di_config_clock(di, sig);
+
+ div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff;
+ div = div / 16; /* Now divider is integer portion */
+
+ /* Setup pixel clock timing */
+ /* Down time is half of period */
+ ipu_di_write(di, (div << 16), DI_BS_CLKGEN1);
+
+ ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1);
+ ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2);
+
+ di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT;
+ di_gen |= DI_GEN_DI_VSYNC_EXT;
+
+ if (sig->interlaced) {
+ ipu_di_sync_config_interlaced(di, sig);
+
+ /* set y_sel = 1 */
+ di_gen |= 0x10000000;
+ di_gen |= DI_GEN_POLARITY_5;
+ di_gen |= DI_GEN_POLARITY_8;
+
+ vsync_cnt = 7;
+
+ if (sig->Hsync_pol)
+ di_gen |= DI_GEN_POLARITY_3;
+ if (sig->Vsync_pol)
+ di_gen |= DI_GEN_POLARITY_2;
+ } else {
+ ipu_di_sync_config_noninterlaced(di, sig, div);
+
+ vsync_cnt = 3;
+ if (di->id == 1)
+ /*
+ * TODO: change only for TVEv2, parallel display
+ * uses pin 2 / 3
+ */
+ if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3))
+ vsync_cnt = 6;
+
+ if (sig->Hsync_pol) {
+ if (sig->hsync_pin == 2)
+ di_gen |= DI_GEN_POLARITY_2;
+ else if (sig->hsync_pin == 4)
+ di_gen |= DI_GEN_POLARITY_4;
+ else if (sig->hsync_pin == 7)
+ di_gen |= DI_GEN_POLARITY_7;
+ }
+ if (sig->Vsync_pol) {
+ if (sig->vsync_pin == 3)
+ di_gen |= DI_GEN_POLARITY_3;
+ else if (sig->vsync_pin == 6)
+ di_gen |= DI_GEN_POLARITY_6;
+ else if (sig->vsync_pin == 8)
+ di_gen |= DI_GEN_POLARITY_8;
+ }
+ }
+
+ if (sig->clk_pol)
+ di_gen |= DI_GEN_POLARITY_DISP_CLK;
+
+ ipu_di_write(di, di_gen, DI_GENERAL);
+
+ ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002,
+ DI_SYNC_AS_GEN);
+
+ reg = ipu_di_read(di, DI_POL);
+ reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15);
+
+ if (sig->enable_pol)
+ reg |= DI_POL_DRDY_POLARITY_15;
+ if (sig->data_pol)
+ reg |= DI_POL_DRDY_DATA_POLARITY;
+
+ ipu_di_write(di, reg, DI_POL);
+
+ mutex_unlock(&di_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel);
+
+int ipu_di_enable(struct ipu_di *di)
+{
+ int ret;
+
+ WARN_ON(IS_ERR(di->clk_di_pixel));
+
+ ret = clk_prepare_enable(di->clk_di_pixel);
+ if (ret)
+ return ret;
+
+ ipu_module_enable(di->ipu, di->module);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_enable);
+
+int ipu_di_disable(struct ipu_di *di)
+{
+ WARN_ON(IS_ERR(di->clk_di_pixel));
+
+ ipu_module_disable(di->ipu, di->module);
+
+ clk_disable_unprepare(di->clk_di_pixel);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_disable);
+
+int ipu_di_get_num(struct ipu_di *di)
+{
+ return di->id;
+}
+EXPORT_SYMBOL_GPL(ipu_di_get_num);
+
+static DEFINE_MUTEX(ipu_di_lock);
+
+struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp)
+{
+ struct ipu_di *di;
+
+ if (disp > 1)
+ return ERR_PTR(-EINVAL);
+
+ di = ipu->di_priv[disp];
+
+ mutex_lock(&ipu_di_lock);
+
+ if (di->inuse) {
+ di = ERR_PTR(-EBUSY);
+ goto out;
+ }
+
+ di->inuse = true;
+out:
+ mutex_unlock(&ipu_di_lock);
+
+ return di;
+}
+EXPORT_SYMBOL_GPL(ipu_di_get);
+
+void ipu_di_put(struct ipu_di *di)
+{
+ mutex_lock(&ipu_di_lock);
+
+ di->inuse = false;
+
+ mutex_unlock(&ipu_di_lock);
+}
+EXPORT_SYMBOL_GPL(ipu_di_put);
+
+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
+ unsigned long base,
+ u32 module, struct clk *clk_ipu)
+{
+ struct ipu_di *di;
+
+ if (id > 1)
+ return -ENODEV;
+
+ di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ ipu->di_priv[id] = di;
+
+ di->clk_di = devm_clk_get(dev, id ? "di1" : "di0");
+ if (IS_ERR(di->clk_di))
+ return PTR_ERR(di->clk_di);
+
+ di->module = module;
+ di->id = id;
+ di->clk_ipu = clk_ipu;
+ di->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!di->base)
+ return -ENOMEM;
+
+ ipu_di_write(di, 0x10, DI_BS_CLKGEN0);
+
+ dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n",
+ id, base, di->base);
+ di->inuse = false;
+ di->ipu = ipu;
+
+ return 0;
+}
+
+void ipu_di_exit(struct ipu_soc *ipu, int id)
+{
+}
diff --git a/drivers/gpu/ipu-v3/ipu-dmfc.c b/drivers/gpu/ipu-v3/ipu-dmfc.c
new file mode 100644
index 000000000000..042c3958e2a0
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-dmfc.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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/types.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+#include <video/imx-ipu-v3.h>
+#include "ipu-prv.h"
+
+#define DMFC_RD_CHAN 0x0000
+#define DMFC_WR_CHAN 0x0004
+#define DMFC_WR_CHAN_DEF 0x0008
+#define DMFC_DP_CHAN 0x000c
+#define DMFC_DP_CHAN_DEF 0x0010
+#define DMFC_GENERAL1 0x0014
+#define DMFC_GENERAL2 0x0018
+#define DMFC_IC_CTRL 0x001c
+#define DMFC_WR_CHAN_ALT 0x0020
+#define DMFC_WR_CHAN_DEF_ALT 0x0024
+#define DMFC_DP_CHAN_ALT 0x0028
+#define DMFC_DP_CHAN_DEF_ALT 0x002c
+#define DMFC_GENERAL1_ALT 0x0030
+#define DMFC_STAT 0x0034
+
+#define DMFC_WR_CHAN_1_28 0
+#define DMFC_WR_CHAN_2_41 8
+#define DMFC_WR_CHAN_1C_42 16
+#define DMFC_WR_CHAN_2C_43 24
+
+#define DMFC_DP_CHAN_5B_23 0
+#define DMFC_DP_CHAN_5F_27 8
+#define DMFC_DP_CHAN_6B_24 16
+#define DMFC_DP_CHAN_6F_29 24
+
+#define DMFC_FIFO_SIZE_64 (3 << 3)
+#define DMFC_FIFO_SIZE_128 (2 << 3)
+#define DMFC_FIFO_SIZE_256 (1 << 3)
+#define DMFC_FIFO_SIZE_512 (0 << 3)
+
+#define DMFC_SEGMENT(x) ((x & 0x7) << 0)
+#define DMFC_BURSTSIZE_128 (0 << 6)
+#define DMFC_BURSTSIZE_64 (1 << 6)
+#define DMFC_BURSTSIZE_32 (2 << 6)
+#define DMFC_BURSTSIZE_16 (3 << 6)
+
+struct dmfc_channel_data {
+ int ipu_channel;
+ unsigned long channel_reg;
+ unsigned long shift;
+ unsigned eot_shift;
+ unsigned max_fifo_lines;
+};
+
+static const struct dmfc_channel_data dmfcdata[] = {
+ {
+ .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_5B_23,
+ .eot_shift = 20,
+ .max_fifo_lines = 3,
+ }, {
+ .ipu_channel = 24,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_6B_24,
+ .eot_shift = 22,
+ .max_fifo_lines = 1,
+ }, {
+ .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_5F_27,
+ .eot_shift = 21,
+ .max_fifo_lines = 2,
+ }, {
+ .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC,
+ .channel_reg = DMFC_WR_CHAN,
+ .shift = DMFC_WR_CHAN_1_28,
+ .eot_shift = 16,
+ .max_fifo_lines = 2,
+ }, {
+ .ipu_channel = 29,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_6F_29,
+ .eot_shift = 23,
+ .max_fifo_lines = 1,
+ },
+};
+
+#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata)
+
+struct ipu_dmfc_priv;
+
+struct dmfc_channel {
+ unsigned slots;
+ unsigned slotmask;
+ unsigned segment;
+ int burstsize;
+ struct ipu_soc *ipu;
+ struct ipu_dmfc_priv *priv;
+ const struct dmfc_channel_data *data;
+};
+
+struct ipu_dmfc_priv {
+ struct ipu_soc *ipu;
+ struct device *dev;
+ struct dmfc_channel channels[DMFC_NUM_CHANNELS];
+ struct mutex mutex;
+ unsigned long bandwidth_per_slot;
+ void __iomem *base;
+ int use_count;
+};
+
+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ mutex_lock(&priv->mutex);
+
+ if (!priv->use_count)
+ ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN);
+
+ priv->use_count++;
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel);
+
+static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(priv->dev,
+ "Timeout waiting for DMFC FIFOs to clear\n");
+ break;
+ }
+ cpu_relax();
+ }
+}
+
+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+
+ mutex_lock(&priv->mutex);
+
+ priv->use_count--;
+
+ if (!priv->use_count) {
+ ipu_dmfc_wait_fifos(priv);
+ ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN);
+ }
+
+ if (priv->use_count < 0)
+ priv->use_count = 0;
+
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel);
+
+static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots,
+ int segment, int burstsize)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ u32 val, field;
+
+ dev_dbg(priv->dev,
+ "dmfc: using %d slots starting from segment %d for IPU channel %d\n",
+ slots, segment, dmfc->data->ipu_channel);
+
+ switch (slots) {
+ case 1:
+ field = DMFC_FIFO_SIZE_64;
+ break;
+ case 2:
+ field = DMFC_FIFO_SIZE_128;
+ break;
+ case 4:
+ field = DMFC_FIFO_SIZE_256;
+ break;
+ case 8:
+ field = DMFC_FIFO_SIZE_512;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (burstsize) {
+ case 16:
+ field |= DMFC_BURSTSIZE_16;
+ break;
+ case 32:
+ field |= DMFC_BURSTSIZE_32;
+ break;
+ case 64:
+ field |= DMFC_BURSTSIZE_64;
+ break;
+ case 128:
+ field |= DMFC_BURSTSIZE_128;
+ break;
+ }
+
+ field |= DMFC_SEGMENT(segment);
+
+ val = readl(priv->base + dmfc->data->channel_reg);
+
+ val &= ~(0xff << dmfc->data->shift);
+ val |= field << dmfc->data->shift;
+
+ writel(val, priv->base + dmfc->data->channel_reg);
+
+ dmfc->slots = slots;
+ dmfc->segment = segment;
+ dmfc->burstsize = burstsize;
+ dmfc->slotmask = ((1 << slots) - 1) << segment;
+
+ return 0;
+}
+
+static int dmfc_bandwidth_to_slots(struct ipu_dmfc_priv *priv,
+ unsigned long bandwidth)
+{
+ int slots = 1;
+
+ while (slots * priv->bandwidth_per_slot < bandwidth)
+ slots *= 2;
+
+ return slots;
+}
+
+static int dmfc_find_slots(struct ipu_dmfc_priv *priv, int slots)
+{
+ unsigned slotmask_need, slotmask_used = 0;
+ int i, segment = 0;
+
+ slotmask_need = (1 << slots) - 1;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ slotmask_used |= priv->channels[i].slotmask;
+
+ while (slotmask_need <= 0xff) {
+ if (!(slotmask_used & slotmask_need))
+ return segment;
+
+ slotmask_need <<= 1;
+ segment++;
+ }
+
+ return -EBUSY;
+}
+
+void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ int i;
+
+ dev_dbg(priv->dev, "dmfc: freeing %d slots starting from segment %d\n",
+ dmfc->slots, dmfc->segment);
+
+ mutex_lock(&priv->mutex);
+
+ if (!dmfc->slots)
+ goto out;
+
+ dmfc->slotmask = 0;
+ dmfc->slots = 0;
+ dmfc->segment = 0;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ priv->channels[i].slotmask = 0;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ if (priv->channels[i].slots > 0) {
+ priv->channels[i].segment =
+ dmfc_find_slots(priv, priv->channels[i].slots);
+ priv->channels[i].slotmask =
+ ((1 << priv->channels[i].slots) - 1) <<
+ priv->channels[i].segment;
+ }
+ }
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ if (priv->channels[i].slots > 0)
+ ipu_dmfc_setup_channel(&priv->channels[i],
+ priv->channels[i].slots,
+ priv->channels[i].segment,
+ priv->channels[i].burstsize);
+ }
+out:
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_free_bandwidth);
+
+int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
+ unsigned long bandwidth_pixel_per_second, int burstsize)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ int slots = dmfc_bandwidth_to_slots(priv, bandwidth_pixel_per_second);
+ int segment = -1, ret = 0;
+
+ dev_dbg(priv->dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n",
+ bandwidth_pixel_per_second / 1000000,
+ dmfc->data->ipu_channel);
+
+ ipu_dmfc_free_bandwidth(dmfc);
+
+ mutex_lock(&priv->mutex);
+
+ if (slots > 8) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* For the MEM_BG channel, first try to allocate twice the slots */
+ if (dmfc->data->ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC)
+ segment = dmfc_find_slots(priv, slots * 2);
+ else if (slots < 2)
+ /* Always allocate at least 128*4 bytes (2 slots) */
+ slots = 2;
+
+ if (segment >= 0)
+ slots *= 2;
+ else
+ segment = dmfc_find_slots(priv, slots);
+ if (segment < 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ipu_dmfc_setup_channel(dmfc, slots, segment, burstsize);
+
+out:
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_alloc_bandwidth);
+
+int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ u32 dmfc_gen1;
+
+ dmfc_gen1 = readl(priv->base + DMFC_GENERAL1);
+
+ if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines)
+ dmfc_gen1 |= 1 << dmfc->data->eot_shift;
+ else
+ dmfc_gen1 &= ~(1 << dmfc->data->eot_shift);
+
+ writel(dmfc_gen1, priv->base + DMFC_GENERAL1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_init_channel);
+
+struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel)
+{
+ struct ipu_dmfc_priv *priv = ipu->dmfc_priv;
+ int i;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ if (dmfcdata[i].ipu_channel == ipu_channel)
+ return &priv->channels[i];
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_get);
+
+void ipu_dmfc_put(struct dmfc_channel *dmfc)
+{
+ ipu_dmfc_free_bandwidth(dmfc);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_put);
+
+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
+ struct clk *ipu_clk)
+{
+ struct ipu_dmfc_priv *priv;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!priv->base)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->ipu = ipu;
+ mutex_init(&priv->mutex);
+
+ ipu->dmfc_priv = priv;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ priv->channels[i].priv = priv;
+ priv->channels[i].ipu = ipu;
+ priv->channels[i].data = &dmfcdata[i];
+ }
+
+ writel(0x0, priv->base + DMFC_WR_CHAN);
+ writel(0x0, priv->base + DMFC_DP_CHAN);
+
+ /*
+ * We have a total bandwidth of clkrate * 4pixel divided
+ * into 8 slots.
+ */
+ priv->bandwidth_per_slot = clk_get_rate(ipu_clk) * 4 / 8;
+
+ dev_dbg(dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n",
+ priv->bandwidth_per_slot / 1000000);
+
+ writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF);
+ writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF);
+ writel(0x00000003, priv->base + DMFC_GENERAL1);
+
+ return 0;
+}
+
+void ipu_dmfc_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/gpu/ipu-v3/ipu-dp.c b/drivers/gpu/ipu-v3/ipu-dp.c
new file mode 100644
index 000000000000..98686edbcdbb
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-dp.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include <video/imx-ipu-v3.h>
+#include "ipu-prv.h"
+
+#define DP_SYNC 0
+#define DP_ASYNC0 0x60
+#define DP_ASYNC1 0xBC
+
+#define DP_COM_CONF 0x0
+#define DP_GRAPH_WIND_CTRL 0x0004
+#define DP_FG_POS 0x0008
+#define DP_CSC_A_0 0x0044
+#define DP_CSC_A_1 0x0048
+#define DP_CSC_A_2 0x004C
+#define DP_CSC_A_3 0x0050
+#define DP_CSC_0 0x0054
+#define DP_CSC_1 0x0058
+
+#define DP_COM_CONF_FG_EN (1 << 0)
+#define DP_COM_CONF_GWSEL (1 << 1)
+#define DP_COM_CONF_GWAM (1 << 2)
+#define DP_COM_CONF_GWCKE (1 << 3)
+#define DP_COM_CONF_CSC_DEF_MASK (3 << 8)
+#define DP_COM_CONF_CSC_DEF_OFFSET 8
+#define DP_COM_CONF_CSC_DEF_FG (3 << 8)
+#define DP_COM_CONF_CSC_DEF_BG (2 << 8)
+#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8)
+
+#define IPUV3_NUM_FLOWS 3
+
+struct ipu_dp_priv;
+
+struct ipu_dp {
+ u32 flow;
+ bool in_use;
+ bool foreground;
+ enum ipu_color_space in_cs;
+};
+
+struct ipu_flow {
+ struct ipu_dp foreground;
+ struct ipu_dp background;
+ enum ipu_color_space out_cs;
+ void __iomem *base;
+ struct ipu_dp_priv *priv;
+};
+
+struct ipu_dp_priv {
+ struct ipu_soc *ipu;
+ struct device *dev;
+ void __iomem *base;
+ struct ipu_flow flow[IPUV3_NUM_FLOWS];
+ struct mutex mutex;
+ int use_count;
+};
+
+static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
+
+static inline struct ipu_flow *to_flow(struct ipu_dp *dp)
+{
+ if (dp->foreground)
+ return container_of(dp, struct ipu_flow, foreground);
+ else
+ return container_of(dp, struct ipu_flow, background);
+}
+
+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable,
+ u8 alpha, bool bg_chan)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+ u32 reg;
+
+ mutex_lock(&priv->mutex);
+
+ reg = readl(flow->base + DP_COM_CONF);
+ if (bg_chan)
+ reg &= ~DP_COM_CONF_GWSEL;
+ else
+ reg |= DP_COM_CONF_GWSEL;
+ writel(reg, flow->base + DP_COM_CONF);
+
+ if (enable) {
+ reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL;
+ writel(reg | ((u32) alpha << 24),
+ flow->base + DP_GRAPH_WIND_CTRL);
+
+ reg = readl(flow->base + DP_COM_CONF);
+ writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
+ } else {
+ reg = readl(flow->base + DP_COM_CONF);
+ writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
+ }
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha);
+
+int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS);
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos);
+
+static void ipu_dp_csc_init(struct ipu_flow *flow,
+ enum ipu_color_space in,
+ enum ipu_color_space out,
+ u32 place)
+{
+ u32 reg;
+
+ reg = readl(flow->base + DP_COM_CONF);
+ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+
+ if (in == out) {
+ writel(reg, flow->base + DP_COM_CONF);
+ return;
+ }
+
+ if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) {
+ writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0);
+ writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1);
+ writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2);
+ writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3);
+ writel(0x3d6 | (0x0000 << 16) | (2 << 30),
+ flow->base + DP_CSC_0);
+ writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
+ flow->base + DP_CSC_1);
+ } else {
+ writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
+ writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1);
+ writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2);
+ writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3);
+ writel(0x000 | (0x3e42 << 16) | (1 << 30),
+ flow->base + DP_CSC_0);
+ writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
+ flow->base + DP_CSC_1);
+ }
+
+ reg |= place;
+
+ writel(reg, flow->base + DP_COM_CONF);
+}
+
+int ipu_dp_setup_channel(struct ipu_dp *dp,
+ enum ipu_color_space in,
+ enum ipu_color_space out)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ mutex_lock(&priv->mutex);
+
+ dp->in_cs = in;
+
+ if (!dp->foreground)
+ flow->out_cs = out;
+
+ if (flow->foreground.in_cs == flow->background.in_cs) {
+ /*
+ * foreground and background are of same colorspace, put
+ * colorspace converter after combining unit.
+ */
+ ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs,
+ DP_COM_CONF_CSC_DEF_BOTH);
+ } else {
+ if (flow->foreground.in_cs == flow->out_cs)
+ /*
+ * foreground identical to output, apply color
+ * conversion on background
+ */
+ ipu_dp_csc_init(flow, flow->background.in_cs,
+ flow->out_cs, DP_COM_CONF_CSC_DEF_BG);
+ else
+ ipu_dp_csc_init(flow, flow->foreground.in_cs,
+ flow->out_cs, DP_COM_CONF_CSC_DEF_FG);
+ }
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
+
+int ipu_dp_enable(struct ipu_soc *ipu)
+{
+ struct ipu_dp_priv *priv = ipu->dp_priv;
+
+ mutex_lock(&priv->mutex);
+
+ if (!priv->use_count)
+ ipu_module_enable(priv->ipu, IPU_CONF_DP_EN);
+
+ priv->use_count++;
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_enable);
+
+int ipu_dp_enable_channel(struct ipu_dp *dp)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+ u32 reg;
+
+ if (!dp->foreground)
+ return 0;
+
+ mutex_lock(&priv->mutex);
+
+ reg = readl(flow->base + DP_COM_CONF);
+ reg |= DP_COM_CONF_FG_EN;
+ writel(reg, flow->base + DP_COM_CONF);
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
+
+void ipu_dp_disable_channel(struct ipu_dp *dp)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+ u32 reg, csc;
+
+ if (!dp->foreground)
+ return;
+
+ mutex_lock(&priv->mutex);
+
+ reg = readl(flow->base + DP_COM_CONF);
+ csc = reg & DP_COM_CONF_CSC_DEF_MASK;
+ if (csc == DP_COM_CONF_CSC_DEF_FG)
+ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+
+ reg &= ~DP_COM_CONF_FG_EN;
+ writel(reg, flow->base + DP_COM_CONF);
+
+ writel(0, flow->base + DP_FG_POS);
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ if (ipu_idmac_channel_busy(priv->ipu, IPUV3_CHANNEL_MEM_BG_SYNC))
+ ipu_wait_interrupt(priv->ipu, IPU_IRQ_DP_SF_END, 50);
+
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
+
+void ipu_dp_disable(struct ipu_soc *ipu)
+{
+ struct ipu_dp_priv *priv = ipu->dp_priv;
+
+ mutex_lock(&priv->mutex);
+
+ priv->use_count--;
+
+ if (!priv->use_count)
+ ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
+
+ if (priv->use_count < 0)
+ priv->use_count = 0;
+
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dp_disable);
+
+struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
+{
+ struct ipu_dp_priv *priv = ipu->dp_priv;
+ struct ipu_dp *dp;
+
+ if ((flow >> 1) >= IPUV3_NUM_FLOWS)
+ return ERR_PTR(-EINVAL);
+
+ if (flow & 1)
+ dp = &priv->flow[flow >> 1].foreground;
+ else
+ dp = &priv->flow[flow >> 1].background;
+
+ if (dp->in_use)
+ return ERR_PTR(-EBUSY);
+
+ dp->in_use = true;
+
+ return dp;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_get);
+
+void ipu_dp_put(struct ipu_dp *dp)
+{
+ dp->in_use = false;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_put);
+
+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base)
+{
+ struct ipu_dp_priv *priv;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->dev = dev;
+ priv->ipu = ipu;
+
+ ipu->dp_priv = priv;
+
+ priv->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!priv->base)
+ return -ENOMEM;
+
+ mutex_init(&priv->mutex);
+
+ for (i = 0; i < IPUV3_NUM_FLOWS; i++) {
+ priv->flow[i].foreground.foreground = true;
+ priv->flow[i].base = priv->base + ipu_dp_flow_base[i];
+ priv->flow[i].priv = priv;
+ }
+
+ return 0;
+}
+
+void ipu_dp_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
new file mode 100644
index 000000000000..c93f50ec04f7
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * 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.
+ */
+#ifndef __IPU_PRV_H__
+#define __IPU_PRV_H__
+
+struct ipu_soc;
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#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
+#define IPU_CM_IRT_REG_OFS 0x00028000
+#define IPU_CM_CSI0_REG_OFS 0x00030000
+#define IPU_CM_CSI1_REG_OFS 0x00038000
+#define IPU_CM_SMFC_REG_OFS 0x00050000
+#define IPU_CM_DC_REG_OFS 0x00058000
+#define IPU_CM_DMFC_REG_OFS 0x00060000
+
+/* Register addresses */
+/* IPU Common registers */
+#define IPU_CM_REG(offset) (offset)
+
+#define IPU_CONF IPU_CM_REG(0)
+
+#define IPU_SRM_PRI1 IPU_CM_REG(0x00a0)
+#define IPU_SRM_PRI2 IPU_CM_REG(0x00a4)
+#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00a8)
+#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00ac)
+#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00b0)
+#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00b4)
+#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00b8)
+#define IPU_SKIP IPU_CM_REG(0x00bc)
+#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00c0)
+#define IPU_DISP_GEN IPU_CM_REG(0x00c4)
+#define IPU_DISP_ALT1 IPU_CM_REG(0x00c8)
+#define IPU_DISP_ALT2 IPU_CM_REG(0x00cc)
+#define IPU_DISP_ALT3 IPU_CM_REG(0x00d0)
+#define IPU_DISP_ALT4 IPU_CM_REG(0x00d4)
+#define IPU_SNOOP IPU_CM_REG(0x00d8)
+#define IPU_MEM_RST IPU_CM_REG(0x00dc)
+#define IPU_PM IPU_CM_REG(0x00e0)
+#define IPU_GPR IPU_CM_REG(0x00e4)
+#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32))
+#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32))
+#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244)
+#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248)
+#define IPU_SRM_STAT IPU_CM_REG(0x024C)
+#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250)
+#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_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))
+
+#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n))
+#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n))
+
+#define IPU_DI0_COUNTER_RELEASE (1 << 24)
+#define IPU_DI1_COUNTER_RELEASE (1 << 25)
+
+#define IPU_IDMAC_REG(offset) (offset)
+
+#define IDMAC_CONF IPU_IDMAC_REG(0x0000)
+#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32))
+#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000c)
+#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010)
+#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32))
+#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001c + 4 * ((ch) / 32))
+#define IDMAC_CH_LOCK_EN_1 IPU_IDMAC_REG(0x0024)
+#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028)
+#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c)
+#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030)
+#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034)
+#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32))
+#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32))
+
+#define IPU_NUM_IRQS (32 * 15)
+
+enum ipu_modules {
+ IPU_CONF_CSI0_EN = (1 << 0),
+ IPU_CONF_CSI1_EN = (1 << 1),
+ IPU_CONF_IC_EN = (1 << 2),
+ IPU_CONF_ROT_EN = (1 << 3),
+ IPU_CONF_ISP_EN = (1 << 4),
+ IPU_CONF_DP_EN = (1 << 5),
+ IPU_CONF_DI0_EN = (1 << 6),
+ IPU_CONF_DI1_EN = (1 << 7),
+ IPU_CONF_SMFC_EN = (1 << 8),
+ IPU_CONF_DC_EN = (1 << 9),
+ IPU_CONF_DMFC_EN = (1 << 10),
+
+ IPU_CONF_VDI_EN = (1 << 12),
+
+ IPU_CONF_IDMAC_DIS = (1 << 22),
+
+ IPU_CONF_IC_DMFC_SEL = (1 << 25),
+ IPU_CONF_IC_DMFC_SYNC = (1 << 26),
+ IPU_CONF_VDI_DMFC_SYNC = (1 << 27),
+
+ IPU_CONF_CSI0_DATA_SOURCE = (1 << 28),
+ IPU_CONF_CSI1_DATA_SOURCE = (1 << 29),
+ IPU_CONF_IC_INPUT = (1 << 30),
+ IPU_CONF_CSI_SEL = (1 << 31),
+};
+
+struct ipuv3_channel {
+ unsigned int num;
+
+ bool enabled;
+ bool busy;
+
+ struct ipu_soc *ipu;
+};
+
+struct ipu_dc_priv;
+struct ipu_dmfc_priv;
+struct ipu_di;
+struct ipu_smfc_priv;
+
+struct ipu_devtype;
+
+struct ipu_soc {
+ struct device *dev;
+ const struct ipu_devtype *devtype;
+ enum ipuv3_type ipu_type;
+ spinlock_t lock;
+ struct mutex channel_lock;
+
+ void __iomem *cm_reg;
+ void __iomem *idmac_reg;
+ struct ipu_ch_param __iomem *cpmem_base;
+
+ int usecount;
+
+ struct clk *clk;
+
+ struct ipuv3_channel channel[64];
+
+ int irq_sync;
+ int irq_err;
+ struct irq_domain *domain;
+
+ 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_smfc_priv *smfc_priv;
+};
+
+void ipu_srm_dp_sync_update(struct ipu_soc *ipu);
+
+int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
+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_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);
+
+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
+ struct clk *ipu_clk);
+void ipu_dmfc_exit(struct ipu_soc *ipu);
+
+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base);
+void ipu_dp_exit(struct ipu_soc *ipu);
+
+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
+ unsigned long template_base);
+void ipu_dc_exit(struct ipu_soc *ipu);
+
+int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base);
+void ipu_cpmem_exit(struct ipu_soc *ipu);
+
+int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base);
+void ipu_smfc_exit(struct ipu_soc *ipu);
+
+#endif /* __IPU_PRV_H__ */
diff --git a/drivers/gpu/ipu-v3/ipu-smfc.c b/drivers/gpu/ipu-v3/ipu-smfc.c
new file mode 100644
index 000000000000..e4f85ad286fc
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-smfc.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2008-2010 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
+ */
+#define DEBUG
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <video/imx-ipu-v3.h>
+
+#include "ipu-prv.h"
+
+struct ipu_smfc_priv {
+ void __iomem *base;
+ spinlock_t lock;
+};
+
+/*SMFC Registers */
+#define SMFC_MAP 0x0000
+#define SMFC_WMC 0x0004
+#define SMFC_BS 0x0008
+
+int ipu_smfc_set_burstsize(struct ipu_soc *ipu, int channel, int burstsize)
+{
+ struct ipu_smfc_priv *smfc = ipu->smfc_priv;
+ unsigned long flags;
+ u32 val, shift;
+
+ spin_lock_irqsave(&smfc->lock, flags);
+
+ shift = channel * 4;
+ val = readl(smfc->base + SMFC_BS);
+ val &= ~(0xf << shift);
+ val |= burstsize << shift;
+ writel(val, smfc->base + SMFC_BS);
+
+ spin_unlock_irqrestore(&smfc->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)
+{
+ struct ipu_smfc_priv *smfc = ipu->smfc_priv;
+ unsigned long flags;
+ u32 val, shift;
+
+ spin_lock_irqsave(&smfc->lock, flags);
+
+ shift = channel * 3;
+ val = readl(smfc->base + SMFC_MAP);
+ val &= ~(0x7 << shift);
+ val |= ((csi_id << 2) | mipi_id) << shift;
+ writel(val, smfc->base + SMFC_MAP);
+
+ spin_unlock_irqrestore(&smfc->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_smfc_map_channel);
+
+int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev,
+ unsigned long base)
+{
+ struct ipu_smfc_priv *smfc;
+
+ smfc = devm_kzalloc(dev, sizeof(*smfc), GFP_KERNEL);
+ if (!smfc)
+ return -ENOMEM;
+
+ ipu->smfc_priv = smfc;
+ spin_lock_init(&smfc->lock);
+
+ smfc->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!smfc->base)
+ return -ENOMEM;
+
+ pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, smfc->base);
+
+ return 0;
+}
+
+void ipu_smfc_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index ec0ae2d1686a..6866448083b2 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -623,7 +623,8 @@ static int vga_switcheroo_runtime_suspend(struct device *dev)
ret = dev->bus->pm->runtime_suspend(dev);
if (ret)
return ret;
-
+ if (vgasr_priv.handler->switchto)
+ vgasr_priv.handler->switchto(VGA_SWITCHEROO_IGD);
vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_OFF);
return 0;
}