diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_display.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_display.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index f8987bcb7f51..505c6bfb4d75 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c | |||
@@ -30,6 +30,8 @@ | |||
30 | #include "nouveau_fb.h" | 30 | #include "nouveau_fb.h" |
31 | #include "nouveau_fbcon.h" | 31 | #include "nouveau_fbcon.h" |
32 | #include "nouveau_hw.h" | 32 | #include "nouveau_hw.h" |
33 | #include "nouveau_crtc.h" | ||
34 | #include "nouveau_dma.h" | ||
33 | 35 | ||
34 | static void | 36 | static void |
35 | nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) | 37 | nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) |
@@ -131,3 +133,181 @@ nouveau_vblank_disable(struct drm_device *dev, int crtc) | |||
131 | else | 133 | else |
132 | NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, 0); | 134 | NVWriteCRTC(dev, crtc, NV_PCRTC_INTR_EN_0, 0); |
133 | } | 135 | } |
136 | |||
137 | static int | ||
138 | nouveau_page_flip_reserve(struct nouveau_bo *old_bo, | ||
139 | struct nouveau_bo *new_bo) | ||
140 | { | ||
141 | int ret; | ||
142 | |||
143 | ret = nouveau_bo_pin(new_bo, TTM_PL_FLAG_VRAM); | ||
144 | if (ret) | ||
145 | return ret; | ||
146 | |||
147 | ret = ttm_bo_reserve(&new_bo->bo, false, false, false, 0); | ||
148 | if (ret) | ||
149 | goto fail; | ||
150 | |||
151 | ret = ttm_bo_reserve(&old_bo->bo, false, false, false, 0); | ||
152 | if (ret) | ||
153 | goto fail_unreserve; | ||
154 | |||
155 | return 0; | ||
156 | |||
157 | fail_unreserve: | ||
158 | ttm_bo_unreserve(&new_bo->bo); | ||
159 | fail: | ||
160 | nouveau_bo_unpin(new_bo); | ||
161 | return ret; | ||
162 | } | ||
163 | |||
164 | static void | ||
165 | nouveau_page_flip_unreserve(struct nouveau_bo *old_bo, | ||
166 | struct nouveau_bo *new_bo, | ||
167 | struct nouveau_fence *fence) | ||
168 | { | ||
169 | nouveau_bo_fence(new_bo, fence); | ||
170 | ttm_bo_unreserve(&new_bo->bo); | ||
171 | |||
172 | nouveau_bo_fence(old_bo, fence); | ||
173 | ttm_bo_unreserve(&old_bo->bo); | ||
174 | |||
175 | nouveau_bo_unpin(old_bo); | ||
176 | } | ||
177 | |||
178 | static int | ||
179 | nouveau_page_flip_emit(struct nouveau_channel *chan, | ||
180 | struct nouveau_bo *old_bo, | ||
181 | struct nouveau_bo *new_bo, | ||
182 | struct nouveau_page_flip_state *s, | ||
183 | struct nouveau_fence **pfence) | ||
184 | { | ||
185 | struct drm_device *dev = chan->dev; | ||
186 | unsigned long flags; | ||
187 | int ret; | ||
188 | |||
189 | /* Queue it to the pending list */ | ||
190 | spin_lock_irqsave(&dev->event_lock, flags); | ||
191 | list_add_tail(&s->head, &chan->nvsw.flip); | ||
192 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
193 | |||
194 | /* Synchronize with the old framebuffer */ | ||
195 | ret = nouveau_fence_sync(old_bo->bo.sync_obj, chan); | ||
196 | if (ret) | ||
197 | goto fail; | ||
198 | |||
199 | /* Emit the pageflip */ | ||
200 | ret = RING_SPACE(chan, 2); | ||
201 | if (ret) | ||
202 | goto fail; | ||
203 | |||
204 | BEGIN_RING(chan, NvSubSw, NV_SW_PAGE_FLIP, 1); | ||
205 | OUT_RING(chan, 0); | ||
206 | FIRE_RING(chan); | ||
207 | |||
208 | ret = nouveau_fence_new(chan, pfence, true); | ||
209 | if (ret) | ||
210 | goto fail; | ||
211 | |||
212 | return 0; | ||
213 | fail: | ||
214 | spin_lock_irqsave(&dev->event_lock, flags); | ||
215 | list_del(&s->head); | ||
216 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | int | ||
221 | nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, | ||
222 | struct drm_pending_vblank_event *event) | ||
223 | { | ||
224 | struct drm_device *dev = crtc->dev; | ||
225 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
226 | struct nouveau_bo *old_bo = nouveau_framebuffer(crtc->fb)->nvbo; | ||
227 | struct nouveau_bo *new_bo = nouveau_framebuffer(fb)->nvbo; | ||
228 | struct nouveau_page_flip_state *s; | ||
229 | struct nouveau_channel *chan; | ||
230 | struct nouveau_fence *fence; | ||
231 | int ret; | ||
232 | |||
233 | if (dev_priv->engine.graph.accel_blocked) | ||
234 | return -ENODEV; | ||
235 | |||
236 | s = kzalloc(sizeof(*s), GFP_KERNEL); | ||
237 | if (!s) | ||
238 | return -ENOMEM; | ||
239 | |||
240 | /* Don't let the buffers go away while we flip */ | ||
241 | ret = nouveau_page_flip_reserve(old_bo, new_bo); | ||
242 | if (ret) | ||
243 | goto fail_free; | ||
244 | |||
245 | /* Initialize a page flip struct */ | ||
246 | *s = (struct nouveau_page_flip_state) | ||
247 | { { }, s->event, nouveau_crtc(crtc)->index, | ||
248 | fb->bits_per_pixel, fb->pitch, crtc->x, crtc->y, | ||
249 | new_bo->bo.offset }; | ||
250 | |||
251 | /* Choose the channel the flip will be handled in */ | ||
252 | chan = nouveau_fence_channel(new_bo->bo.sync_obj); | ||
253 | if (!chan) | ||
254 | chan = nouveau_channel_get_unlocked(dev_priv->channel); | ||
255 | mutex_lock(&chan->mutex); | ||
256 | |||
257 | /* Emit a page flip */ | ||
258 | ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence); | ||
259 | nouveau_channel_put(&chan); | ||
260 | if (ret) | ||
261 | goto fail_unreserve; | ||
262 | |||
263 | /* Update the crtc struct and cleanup */ | ||
264 | crtc->fb = fb; | ||
265 | |||
266 | nouveau_page_flip_unreserve(old_bo, new_bo, fence); | ||
267 | nouveau_fence_unref(&fence); | ||
268 | return 0; | ||
269 | |||
270 | fail_unreserve: | ||
271 | nouveau_page_flip_unreserve(old_bo, new_bo, NULL); | ||
272 | fail_free: | ||
273 | kfree(s); | ||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | int | ||
278 | nouveau_finish_page_flip(struct nouveau_channel *chan, | ||
279 | struct nouveau_page_flip_state *ps) | ||
280 | { | ||
281 | struct drm_device *dev = chan->dev; | ||
282 | struct nouveau_page_flip_state *s; | ||
283 | unsigned long flags; | ||
284 | |||
285 | spin_lock_irqsave(&dev->event_lock, flags); | ||
286 | |||
287 | if (list_empty(&chan->nvsw.flip)) { | ||
288 | NV_ERROR(dev, "Unexpected pageflip in channel %d.\n", chan->id); | ||
289 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
290 | return -EINVAL; | ||
291 | } | ||
292 | |||
293 | s = list_first_entry(&chan->nvsw.flip, | ||
294 | struct nouveau_page_flip_state, head); | ||
295 | if (s->event) { | ||
296 | struct drm_pending_vblank_event *e = s->event; | ||
297 | struct timeval now; | ||
298 | |||
299 | do_gettimeofday(&now); | ||
300 | e->event.sequence = 0; | ||
301 | e->event.tv_sec = now.tv_sec; | ||
302 | e->event.tv_usec = now.tv_usec; | ||
303 | list_add_tail(&e->base.link, &e->base.file_priv->event_list); | ||
304 | wake_up_interruptible(&e->base.file_priv->event_wait); | ||
305 | } | ||
306 | |||
307 | list_del(&s->head); | ||
308 | *ps = *s; | ||
309 | kfree(s); | ||
310 | |||
311 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
312 | return 0; | ||
313 | } | ||