diff options
author | Thierry Reding <treding@nvidia.com> | 2014-11-24 11:41:23 -0500 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2015-01-27 04:14:55 -0500 |
commit | 1503ca47d76e184eaeabe7cfa31de97b5ec36a04 (patch) | |
tree | 8a97feb074e208da908f0b606b413174bc529f38 /drivers/gpu | |
parent | 74f48791ad69789bc70eb4d99233d83e016f5f14 (diff) |
drm/tegra: Atomic conversion, phase 3, step 3
Provide a custom ->atomic_commit() implementation which supports async
commits. The generic atomic page-flip helper can use this to implement
page-flipping.
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/tegra/dc.c | 129 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.c | 84 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.h | 6 |
3 files changed, 100 insertions, 119 deletions
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index aa66abb57b1c..2f9af13905da 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c | |||
@@ -820,99 +820,6 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) | |||
820 | return 0; | 820 | return 0; |
821 | } | 821 | } |
822 | 822 | ||
823 | static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, | ||
824 | struct drm_framebuffer *fb) | ||
825 | { | ||
826 | struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); | ||
827 | unsigned int h_offset = 0, v_offset = 0; | ||
828 | struct tegra_bo_tiling tiling; | ||
829 | unsigned long value, flags; | ||
830 | unsigned int format, swap; | ||
831 | int err; | ||
832 | |||
833 | err = tegra_fb_get_tiling(fb, &tiling); | ||
834 | if (err < 0) | ||
835 | return err; | ||
836 | |||
837 | spin_lock_irqsave(&dc->lock, flags); | ||
838 | |||
839 | tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); | ||
840 | |||
841 | value = fb->offsets[0] + y * fb->pitches[0] + | ||
842 | x * fb->bits_per_pixel / 8; | ||
843 | |||
844 | tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR); | ||
845 | tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE); | ||
846 | |||
847 | format = tegra_dc_format(fb->pixel_format, &swap); | ||
848 | tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH); | ||
849 | tegra_dc_writel(dc, swap, DC_WIN_BYTE_SWAP); | ||
850 | |||
851 | if (dc->soc->supports_block_linear) { | ||
852 | unsigned long height = tiling.value; | ||
853 | |||
854 | switch (tiling.mode) { | ||
855 | case TEGRA_BO_TILING_MODE_PITCH: | ||
856 | value = DC_WINBUF_SURFACE_KIND_PITCH; | ||
857 | break; | ||
858 | |||
859 | case TEGRA_BO_TILING_MODE_TILED: | ||
860 | value = DC_WINBUF_SURFACE_KIND_TILED; | ||
861 | break; | ||
862 | |||
863 | case TEGRA_BO_TILING_MODE_BLOCK: | ||
864 | value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) | | ||
865 | DC_WINBUF_SURFACE_KIND_BLOCK; | ||
866 | break; | ||
867 | } | ||
868 | |||
869 | tegra_dc_writel(dc, value, DC_WINBUF_SURFACE_KIND); | ||
870 | } else { | ||
871 | switch (tiling.mode) { | ||
872 | case TEGRA_BO_TILING_MODE_PITCH: | ||
873 | value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV | | ||
874 | DC_WIN_BUFFER_ADDR_MODE_LINEAR; | ||
875 | break; | ||
876 | |||
877 | case TEGRA_BO_TILING_MODE_TILED: | ||
878 | value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV | | ||
879 | DC_WIN_BUFFER_ADDR_MODE_TILE; | ||
880 | break; | ||
881 | |||
882 | case TEGRA_BO_TILING_MODE_BLOCK: | ||
883 | DRM_ERROR("hardware doesn't support block linear mode\n"); | ||
884 | spin_unlock_irqrestore(&dc->lock, flags); | ||
885 | return -EINVAL; | ||
886 | } | ||
887 | |||
888 | tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE); | ||
889 | } | ||
890 | |||
891 | /* make sure bottom-up buffers are properly displayed */ | ||
892 | if (tegra_fb_is_bottom_up(fb)) { | ||
893 | value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); | ||
894 | value |= V_DIRECTION; | ||
895 | tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); | ||
896 | |||
897 | v_offset += fb->height - 1; | ||
898 | } else { | ||
899 | value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); | ||
900 | value &= ~V_DIRECTION; | ||
901 | tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); | ||
902 | } | ||
903 | |||
904 | tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); | ||
905 | tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); | ||
906 | |||
907 | value = GENERAL_ACT_REQ | WIN_A_ACT_REQ; | ||
908 | tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL); | ||
909 | tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); | ||
910 | |||
911 | spin_unlock_irqrestore(&dc->lock, flags); | ||
912 | |||
913 | return 0; | ||
914 | } | ||
915 | |||
916 | void tegra_dc_enable_vblank(struct tegra_dc *dc) | 823 | void tegra_dc_enable_vblank(struct tegra_dc *dc) |
917 | { | 824 | { |
918 | unsigned long value, flags; | 825 | unsigned long value, flags; |
@@ -991,30 +898,6 @@ void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) | |||
991 | spin_unlock_irqrestore(&drm->event_lock, flags); | 898 | spin_unlock_irqrestore(&drm->event_lock, flags); |
992 | } | 899 | } |
993 | 900 | ||
994 | static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, | ||
995 | struct drm_pending_vblank_event *event, uint32_t page_flip_flags) | ||
996 | { | ||
997 | unsigned int pipe = drm_crtc_index(crtc); | ||
998 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
999 | |||
1000 | if (dc->event) | ||
1001 | return -EBUSY; | ||
1002 | |||
1003 | if (event) { | ||
1004 | event->pipe = pipe; | ||
1005 | dc->event = event; | ||
1006 | drm_crtc_vblank_get(crtc); | ||
1007 | } | ||
1008 | |||
1009 | if (crtc->primary->state) | ||
1010 | drm_atomic_set_fb_for_plane(crtc->primary->state, fb); | ||
1011 | |||
1012 | tegra_dc_set_base(dc, 0, 0, fb); | ||
1013 | crtc->primary->fb = fb; | ||
1014 | |||
1015 | return 0; | ||
1016 | } | ||
1017 | |||
1018 | static void tegra_dc_destroy(struct drm_crtc *crtc) | 901 | static void tegra_dc_destroy(struct drm_crtc *crtc) |
1019 | { | 902 | { |
1020 | drm_crtc_cleanup(crtc); | 903 | drm_crtc_cleanup(crtc); |
@@ -1056,7 +939,7 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, | |||
1056 | } | 939 | } |
1057 | 940 | ||
1058 | static const struct drm_crtc_funcs tegra_crtc_funcs = { | 941 | static const struct drm_crtc_funcs tegra_crtc_funcs = { |
1059 | .page_flip = tegra_dc_page_flip, | 942 | .page_flip = drm_atomic_helper_page_flip, |
1060 | .set_config = drm_atomic_helper_set_config, | 943 | .set_config = drm_atomic_helper_set_config, |
1061 | .destroy = tegra_dc_destroy, | 944 | .destroy = tegra_dc_destroy, |
1062 | .reset = tegra_crtc_reset, | 945 | .reset = tegra_crtc_reset, |
@@ -1326,6 +1209,16 @@ static int tegra_crtc_atomic_check(struct drm_crtc *crtc, | |||
1326 | 1209 | ||
1327 | static void tegra_crtc_atomic_begin(struct drm_crtc *crtc) | 1210 | static void tegra_crtc_atomic_begin(struct drm_crtc *crtc) |
1328 | { | 1211 | { |
1212 | struct tegra_dc *dc = to_tegra_dc(crtc); | ||
1213 | |||
1214 | if (crtc->state->event) { | ||
1215 | crtc->state->event->pipe = drm_crtc_index(crtc); | ||
1216 | |||
1217 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); | ||
1218 | |||
1219 | dc->event = crtc->state->event; | ||
1220 | crtc->state->event = NULL; | ||
1221 | } | ||
1329 | } | 1222 | } |
1330 | 1223 | ||
1331 | static void tegra_crtc_atomic_flush(struct drm_crtc *crtc) | 1224 | static void tegra_crtc_atomic_flush(struct drm_crtc *crtc) |
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index d01484cf3432..0edfb5e0b374 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/host1x.h> | 10 | #include <linux/host1x.h> |
11 | #include <linux/iommu.h> | 11 | #include <linux/iommu.h> |
12 | 12 | ||
13 | #include <drm/drm_atomic.h> | ||
13 | #include <drm/drm_atomic_helper.h> | 14 | #include <drm/drm_atomic_helper.h> |
14 | 15 | ||
15 | #include "drm.h" | 16 | #include "drm.h" |
@@ -26,13 +27,90 @@ struct tegra_drm_file { | |||
26 | struct list_head contexts; | 27 | struct list_head contexts; |
27 | }; | 28 | }; |
28 | 29 | ||
30 | static void tegra_atomic_schedule(struct tegra_drm *tegra, | ||
31 | struct drm_atomic_state *state) | ||
32 | { | ||
33 | tegra->commit.state = state; | ||
34 | schedule_work(&tegra->commit.work); | ||
35 | } | ||
36 | |||
37 | static void tegra_atomic_complete(struct tegra_drm *tegra, | ||
38 | struct drm_atomic_state *state) | ||
39 | { | ||
40 | struct drm_device *drm = tegra->drm; | ||
41 | |||
42 | /* | ||
43 | * Everything below can be run asynchronously without the need to grab | ||
44 | * any modeset locks at all under one condition: It must be guaranteed | ||
45 | * that the asynchronous work has either been cancelled (if the driver | ||
46 | * supports it, which at least requires that the framebuffers get | ||
47 | * cleaned up with drm_atomic_helper_cleanup_planes()) or completed | ||
48 | * before the new state gets committed on the software side with | ||
49 | * drm_atomic_helper_swap_state(). | ||
50 | * | ||
51 | * This scheme allows new atomic state updates to be prepared and | ||
52 | * checked in parallel to the asynchronous completion of the previous | ||
53 | * update. Which is important since compositors need to figure out the | ||
54 | * composition of the next frame right after having submitted the | ||
55 | * current layout. | ||
56 | */ | ||
57 | |||
58 | drm_atomic_helper_commit_pre_planes(drm, state); | ||
59 | drm_atomic_helper_commit_planes(drm, state); | ||
60 | drm_atomic_helper_commit_post_planes(drm, state); | ||
61 | |||
62 | drm_atomic_helper_wait_for_vblanks(drm, state); | ||
63 | |||
64 | drm_atomic_helper_cleanup_planes(drm, state); | ||
65 | drm_atomic_state_free(state); | ||
66 | } | ||
67 | |||
68 | static void tegra_atomic_work(struct work_struct *work) | ||
69 | { | ||
70 | struct tegra_drm *tegra = container_of(work, struct tegra_drm, | ||
71 | commit.work); | ||
72 | |||
73 | tegra_atomic_complete(tegra, tegra->commit.state); | ||
74 | } | ||
75 | |||
76 | static int tegra_atomic_commit(struct drm_device *drm, | ||
77 | struct drm_atomic_state *state, bool async) | ||
78 | { | ||
79 | struct tegra_drm *tegra = drm->dev_private; | ||
80 | int err; | ||
81 | |||
82 | err = drm_atomic_helper_prepare_planes(drm, state); | ||
83 | if (err) | ||
84 | return err; | ||
85 | |||
86 | /* serialize outstanding asynchronous commits */ | ||
87 | mutex_lock(&tegra->commit.lock); | ||
88 | flush_work(&tegra->commit.work); | ||
89 | |||
90 | /* | ||
91 | * This is the point of no return - everything below never fails except | ||
92 | * when the hw goes bonghits. Which means we can commit the new state on | ||
93 | * the software side now. | ||
94 | */ | ||
95 | |||
96 | drm_atomic_helper_swap_state(drm, state); | ||
97 | |||
98 | if (async) | ||
99 | tegra_atomic_schedule(tegra, state); | ||
100 | else | ||
101 | tegra_atomic_complete(tegra, state); | ||
102 | |||
103 | mutex_unlock(&tegra->commit.lock); | ||
104 | return 0; | ||
105 | } | ||
106 | |||
29 | static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { | 107 | static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { |
30 | .fb_create = tegra_fb_create, | 108 | .fb_create = tegra_fb_create, |
31 | #ifdef CONFIG_DRM_TEGRA_FBDEV | 109 | #ifdef CONFIG_DRM_TEGRA_FBDEV |
32 | .output_poll_changed = tegra_fb_output_poll_changed, | 110 | .output_poll_changed = tegra_fb_output_poll_changed, |
33 | #endif | 111 | #endif |
34 | .atomic_check = drm_atomic_helper_check, | 112 | .atomic_check = drm_atomic_helper_check, |
35 | .atomic_commit = drm_atomic_helper_commit, | 113 | .atomic_commit = tegra_atomic_commit, |
36 | }; | 114 | }; |
37 | 115 | ||
38 | static int tegra_drm_load(struct drm_device *drm, unsigned long flags) | 116 | static int tegra_drm_load(struct drm_device *drm, unsigned long flags) |
@@ -58,6 +136,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) | |||
58 | 136 | ||
59 | mutex_init(&tegra->clients_lock); | 137 | mutex_init(&tegra->clients_lock); |
60 | INIT_LIST_HEAD(&tegra->clients); | 138 | INIT_LIST_HEAD(&tegra->clients); |
139 | |||
140 | mutex_init(&tegra->commit.lock); | ||
141 | INIT_WORK(&tegra->commit.work, tegra_atomic_work); | ||
142 | |||
61 | drm->dev_private = tegra; | 143 | drm->dev_private = tegra; |
62 | tegra->drm = drm; | 144 | tegra->drm = drm; |
63 | 145 | ||
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 3db719de312f..b1c7027b26e7 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h | |||
@@ -50,6 +50,12 @@ struct tegra_drm { | |||
50 | #endif | 50 | #endif |
51 | 51 | ||
52 | unsigned int pitch_align; | 52 | unsigned int pitch_align; |
53 | |||
54 | struct { | ||
55 | struct drm_atomic_state *state; | ||
56 | struct work_struct work; | ||
57 | struct mutex lock; | ||
58 | } commit; | ||
53 | }; | 59 | }; |
54 | 60 | ||
55 | struct tegra_drm_client; | 61 | struct tegra_drm_client; |