diff options
Diffstat (limited to 'drivers/gpu/drm/msm/msm_atomic.c')
-rw-r--r-- | drivers/gpu/drm/msm/msm_atomic.c | 69 |
1 files changed, 68 insertions, 1 deletions
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index f0de412e13dc..191968256c58 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c | |||
@@ -23,10 +23,41 @@ struct msm_commit { | |||
23 | struct drm_atomic_state *state; | 23 | struct drm_atomic_state *state; |
24 | uint32_t fence; | 24 | uint32_t fence; |
25 | struct msm_fence_cb fence_cb; | 25 | struct msm_fence_cb fence_cb; |
26 | uint32_t crtc_mask; | ||
26 | }; | 27 | }; |
27 | 28 | ||
28 | static void fence_cb(struct msm_fence_cb *cb); | 29 | static void fence_cb(struct msm_fence_cb *cb); |
29 | 30 | ||
31 | /* block until specified crtcs are no longer pending update, and | ||
32 | * atomically mark them as pending update | ||
33 | */ | ||
34 | static int start_atomic(struct msm_drm_private *priv, uint32_t crtc_mask) | ||
35 | { | ||
36 | int ret; | ||
37 | |||
38 | spin_lock(&priv->pending_crtcs_event.lock); | ||
39 | ret = wait_event_interruptible_locked(priv->pending_crtcs_event, | ||
40 | !(priv->pending_crtcs & crtc_mask)); | ||
41 | if (ret == 0) { | ||
42 | DBG("start: %08x", crtc_mask); | ||
43 | priv->pending_crtcs |= crtc_mask; | ||
44 | } | ||
45 | spin_unlock(&priv->pending_crtcs_event.lock); | ||
46 | |||
47 | return ret; | ||
48 | } | ||
49 | |||
50 | /* clear specified crtcs (no longer pending update) | ||
51 | */ | ||
52 | static void end_atomic(struct msm_drm_private *priv, uint32_t crtc_mask) | ||
53 | { | ||
54 | spin_lock(&priv->pending_crtcs_event.lock); | ||
55 | DBG("end: %08x", crtc_mask); | ||
56 | priv->pending_crtcs &= ~crtc_mask; | ||
57 | wake_up_all_locked(&priv->pending_crtcs_event); | ||
58 | spin_unlock(&priv->pending_crtcs_event.lock); | ||
59 | } | ||
60 | |||
30 | static struct msm_commit *new_commit(struct drm_atomic_state *state) | 61 | static struct msm_commit *new_commit(struct drm_atomic_state *state) |
31 | { | 62 | { |
32 | struct msm_commit *c = kzalloc(sizeof(*c), GFP_KERNEL); | 63 | struct msm_commit *c = kzalloc(sizeof(*c), GFP_KERNEL); |
@@ -58,12 +89,27 @@ static void complete_commit(struct msm_commit *c) | |||
58 | 89 | ||
59 | drm_atomic_helper_commit_post_planes(dev, state); | 90 | drm_atomic_helper_commit_post_planes(dev, state); |
60 | 91 | ||
92 | /* NOTE: _wait_for_vblanks() only waits for vblank on | ||
93 | * enabled CRTCs. So we end up faulting when disabling | ||
94 | * due to (potentially) unref'ing the outgoing fb's | ||
95 | * before the vblank when the disable has latched. | ||
96 | * | ||
97 | * But if it did wait on disabled (or newly disabled) | ||
98 | * CRTCs, that would be racy (ie. we could have missed | ||
99 | * the irq. We need some way to poll for pipe shut | ||
100 | * down. Or just live with occasionally hitting the | ||
101 | * timeout in the CRTC disable path (which really should | ||
102 | * not be critical path) | ||
103 | */ | ||
104 | |||
61 | drm_atomic_helper_wait_for_vblanks(dev, state); | 105 | drm_atomic_helper_wait_for_vblanks(dev, state); |
62 | 106 | ||
63 | drm_atomic_helper_cleanup_planes(dev, state); | 107 | drm_atomic_helper_cleanup_planes(dev, state); |
64 | 108 | ||
65 | drm_atomic_state_free(state); | 109 | drm_atomic_state_free(state); |
66 | 110 | ||
111 | end_atomic(dev->dev_private, c->crtc_mask); | ||
112 | |||
67 | kfree(c); | 113 | kfree(c); |
68 | } | 114 | } |
69 | 115 | ||
@@ -97,8 +143,9 @@ static void add_fb(struct msm_commit *c, struct drm_framebuffer *fb) | |||
97 | int msm_atomic_commit(struct drm_device *dev, | 143 | int msm_atomic_commit(struct drm_device *dev, |
98 | struct drm_atomic_state *state, bool async) | 144 | struct drm_atomic_state *state, bool async) |
99 | { | 145 | { |
100 | struct msm_commit *c; | ||
101 | int nplanes = dev->mode_config.num_total_plane; | 146 | int nplanes = dev->mode_config.num_total_plane; |
147 | int ncrtcs = dev->mode_config.num_crtc; | ||
148 | struct msm_commit *c; | ||
102 | int i, ret; | 149 | int i, ret; |
103 | 150 | ||
104 | ret = drm_atomic_helper_prepare_planes(dev, state); | 151 | ret = drm_atomic_helper_prepare_planes(dev, state); |
@@ -106,6 +153,18 @@ int msm_atomic_commit(struct drm_device *dev, | |||
106 | return ret; | 153 | return ret; |
107 | 154 | ||
108 | c = new_commit(state); | 155 | c = new_commit(state); |
156 | if (!c) | ||
157 | return -ENOMEM; | ||
158 | |||
159 | /* | ||
160 | * Figure out what crtcs we have: | ||
161 | */ | ||
162 | for (i = 0; i < ncrtcs; i++) { | ||
163 | struct drm_crtc *crtc = state->crtcs[i]; | ||
164 | if (!crtc) | ||
165 | continue; | ||
166 | c->crtc_mask |= (1 << drm_crtc_index(crtc)); | ||
167 | } | ||
109 | 168 | ||
110 | /* | 169 | /* |
111 | * Figure out what fence to wait for: | 170 | * Figure out what fence to wait for: |
@@ -122,6 +181,14 @@ int msm_atomic_commit(struct drm_device *dev, | |||
122 | } | 181 | } |
123 | 182 | ||
124 | /* | 183 | /* |
184 | * Wait for pending updates on any of the same crtc's and then | ||
185 | * mark our set of crtc's as busy: | ||
186 | */ | ||
187 | ret = start_atomic(dev->dev_private, c->crtc_mask); | ||
188 | if (ret) | ||
189 | return ret; | ||
190 | |||
191 | /* | ||
125 | * This is the point of no return - everything below never fails except | 192 | * This is the point of no return - everything below never fails except |
126 | * when the hw goes bonghits. Which means we can commit the new state on | 193 | * when the hw goes bonghits. Which means we can commit the new state on |
127 | * the software side now. | 194 | * the software side now. |