aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2014-11-24 11:41:23 -0500
committerThierry Reding <treding@nvidia.com>2015-01-27 04:14:55 -0500
commit1503ca47d76e184eaeabe7cfa31de97b5ec36a04 (patch)
tree8a97feb074e208da908f0b606b413174bc529f38 /drivers
parent74f48791ad69789bc70eb4d99233d83e016f5f14 (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')
-rw-r--r--drivers/gpu/drm/tegra/dc.c129
-rw-r--r--drivers/gpu/drm/tegra/drm.c84
-rw-r--r--drivers/gpu/drm/tegra/drm.h6
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
823static 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
916void tegra_dc_enable_vblank(struct tegra_dc *dc) 823void 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
994static 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
1018static void tegra_dc_destroy(struct drm_crtc *crtc) 901static 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
1058static const struct drm_crtc_funcs tegra_crtc_funcs = { 941static 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
1327static void tegra_crtc_atomic_begin(struct drm_crtc *crtc) 1210static 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
1331static void tegra_crtc_atomic_flush(struct drm_crtc *crtc) 1224static 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
30static 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
37static 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
68static 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
76static 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
29static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { 107static 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
38static int tegra_drm_load(struct drm_device *drm, unsigned long flags) 116static 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
55struct tegra_drm_client; 61struct tegra_drm_client;