diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_crtc.c')
-rw-r--r-- | drivers/gpu/drm/omapdrm/omap_crtc.c | 541 |
1 files changed, 162 insertions, 379 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index f456544bf300..23d9c928cdc9 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c | |||
@@ -17,12 +17,14 @@ | |||
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | 17 | * this program. If not, see <http://www.gnu.org/licenses/>. |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include "omap_drv.h" | 20 | #include <drm/drm_atomic.h> |
21 | 21 | #include <drm/drm_atomic_helper.h> | |
22 | #include <drm/drm_crtc.h> | ||
23 | #include <drm/drm_crtc_helper.h> | ||
22 | #include <drm/drm_mode.h> | 24 | #include <drm/drm_mode.h> |
23 | #include <drm/drm_plane_helper.h> | 25 | #include <drm/drm_plane_helper.h> |
24 | #include "drm_crtc.h" | 26 | |
25 | #include "drm_crtc_helper.h" | 27 | #include "omap_drv.h" |
26 | 28 | ||
27 | #define to_omap_crtc(x) container_of(x, struct omap_crtc, base) | 29 | #define to_omap_crtc(x) container_of(x, struct omap_crtc, base) |
28 | 30 | ||
@@ -30,10 +32,7 @@ struct omap_crtc { | |||
30 | struct drm_crtc base; | 32 | struct drm_crtc base; |
31 | 33 | ||
32 | const char *name; | 34 | const char *name; |
33 | int pipe; | ||
34 | enum omap_channel channel; | 35 | enum omap_channel channel; |
35 | struct omap_overlay_manager_info info; | ||
36 | struct drm_encoder *current_encoder; | ||
37 | 36 | ||
38 | /* | 37 | /* |
39 | * Temporary: eventually this will go away, but it is needed | 38 | * Temporary: eventually this will go away, but it is needed |
@@ -44,36 +43,14 @@ struct omap_crtc { | |||
44 | struct omap_overlay_manager *mgr; | 43 | struct omap_overlay_manager *mgr; |
45 | 44 | ||
46 | struct omap_video_timings timings; | 45 | struct omap_video_timings timings; |
47 | bool enabled; | ||
48 | 46 | ||
49 | struct omap_drm_apply apply; | 47 | struct omap_drm_irq vblank_irq; |
50 | |||
51 | struct omap_drm_irq apply_irq; | ||
52 | struct omap_drm_irq error_irq; | 48 | struct omap_drm_irq error_irq; |
53 | 49 | ||
54 | /* list of in-progress apply's: */ | ||
55 | struct list_head pending_applies; | ||
56 | |||
57 | /* list of queued apply's: */ | ||
58 | struct list_head queued_applies; | ||
59 | |||
60 | /* for handling queued and in-progress applies: */ | ||
61 | struct work_struct apply_work; | ||
62 | |||
63 | /* if there is a pending flip, these will be non-null: */ | ||
64 | struct drm_pending_vblank_event *event; | ||
65 | struct drm_framebuffer *old_fb; | ||
66 | |||
67 | /* for handling page flips without caring about what | ||
68 | * the callback is called from. Possibly we should just | ||
69 | * make omap_gem always call the cb from the worker so | ||
70 | * we don't have to care about this.. | ||
71 | * | ||
72 | * XXX maybe fold into apply_work?? | ||
73 | */ | ||
74 | struct work_struct page_flip_work; | ||
75 | |||
76 | bool ignore_digit_sync_lost; | 50 | bool ignore_digit_sync_lost; |
51 | |||
52 | bool pending; | ||
53 | wait_queue_head_t pending_wait; | ||
77 | }; | 54 | }; |
78 | 55 | ||
79 | /* ----------------------------------------------------------------------------- | 56 | /* ----------------------------------------------------------------------------- |
@@ -87,7 +64,7 @@ uint32_t pipe2vbl(struct drm_crtc *crtc) | |||
87 | return dispc_mgr_get_vsync_irq(omap_crtc->channel); | 64 | return dispc_mgr_get_vsync_irq(omap_crtc->channel); |
88 | } | 65 | } |
89 | 66 | ||
90 | const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc) | 67 | struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc) |
91 | { | 68 | { |
92 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 69 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
93 | return &omap_crtc->timings; | 70 | return &omap_crtc->timings; |
@@ -99,6 +76,15 @@ enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) | |||
99 | return omap_crtc->channel; | 76 | return omap_crtc->channel; |
100 | } | 77 | } |
101 | 78 | ||
79 | int omap_crtc_wait_pending(struct drm_crtc *crtc) | ||
80 | { | ||
81 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | ||
82 | |||
83 | return wait_event_timeout(omap_crtc->pending_wait, | ||
84 | !omap_crtc->pending, | ||
85 | msecs_to_jiffies(50)); | ||
86 | } | ||
87 | |||
102 | /* ----------------------------------------------------------------------------- | 88 | /* ----------------------------------------------------------------------------- |
103 | * DSS Manager Functions | 89 | * DSS Manager Functions |
104 | */ | 90 | */ |
@@ -116,7 +102,7 @@ enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) | |||
116 | static struct omap_crtc *omap_crtcs[8]; | 102 | static struct omap_crtc *omap_crtcs[8]; |
117 | 103 | ||
118 | /* we can probably ignore these until we support command-mode panels: */ | 104 | /* we can probably ignore these until we support command-mode panels: */ |
119 | static int omap_crtc_connect(struct omap_overlay_manager *mgr, | 105 | static int omap_crtc_dss_connect(struct omap_overlay_manager *mgr, |
120 | struct omap_dss_device *dst) | 106 | struct omap_dss_device *dst) |
121 | { | 107 | { |
122 | if (mgr->output) | 108 | if (mgr->output) |
@@ -131,18 +117,18 @@ static int omap_crtc_connect(struct omap_overlay_manager *mgr, | |||
131 | return 0; | 117 | return 0; |
132 | } | 118 | } |
133 | 119 | ||
134 | static void omap_crtc_disconnect(struct omap_overlay_manager *mgr, | 120 | static void omap_crtc_dss_disconnect(struct omap_overlay_manager *mgr, |
135 | struct omap_dss_device *dst) | 121 | struct omap_dss_device *dst) |
136 | { | 122 | { |
137 | mgr->output->manager = NULL; | 123 | mgr->output->manager = NULL; |
138 | mgr->output = NULL; | 124 | mgr->output = NULL; |
139 | } | 125 | } |
140 | 126 | ||
141 | static void omap_crtc_start_update(struct omap_overlay_manager *mgr) | 127 | static void omap_crtc_dss_start_update(struct omap_overlay_manager *mgr) |
142 | { | 128 | { |
143 | } | 129 | } |
144 | 130 | ||
145 | /* Called only from CRTC pre_apply and suspend/resume handlers. */ | 131 | /* Called only from the encoder enable/disable and suspend/resume handlers. */ |
146 | static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) | 132 | static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) |
147 | { | 133 | { |
148 | struct drm_device *dev = crtc->dev; | 134 | struct drm_device *dev = crtc->dev; |
@@ -200,11 +186,18 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) | |||
200 | } | 186 | } |
201 | 187 | ||
202 | 188 | ||
203 | static int omap_crtc_enable(struct omap_overlay_manager *mgr) | 189 | static int omap_crtc_dss_enable(struct omap_overlay_manager *mgr) |
204 | { | 190 | { |
205 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; | 191 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
192 | struct omap_overlay_manager_info info; | ||
193 | |||
194 | memset(&info, 0, sizeof(info)); | ||
195 | info.default_color = 0x00000000; | ||
196 | info.trans_key = 0x00000000; | ||
197 | info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; | ||
198 | info.trans_enabled = false; | ||
206 | 199 | ||
207 | dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info); | 200 | dispc_mgr_setup(omap_crtc->channel, &info); |
208 | dispc_mgr_set_timings(omap_crtc->channel, | 201 | dispc_mgr_set_timings(omap_crtc->channel, |
209 | &omap_crtc->timings); | 202 | &omap_crtc->timings); |
210 | omap_crtc_set_enabled(&omap_crtc->base, true); | 203 | omap_crtc_set_enabled(&omap_crtc->base, true); |
@@ -212,14 +205,14 @@ static int omap_crtc_enable(struct omap_overlay_manager *mgr) | |||
212 | return 0; | 205 | return 0; |
213 | } | 206 | } |
214 | 207 | ||
215 | static void omap_crtc_disable(struct omap_overlay_manager *mgr) | 208 | static void omap_crtc_dss_disable(struct omap_overlay_manager *mgr) |
216 | { | 209 | { |
217 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; | 210 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
218 | 211 | ||
219 | omap_crtc_set_enabled(&omap_crtc->base, false); | 212 | omap_crtc_set_enabled(&omap_crtc->base, false); |
220 | } | 213 | } |
221 | 214 | ||
222 | static void omap_crtc_set_timings(struct omap_overlay_manager *mgr, | 215 | static void omap_crtc_dss_set_timings(struct omap_overlay_manager *mgr, |
223 | const struct omap_video_timings *timings) | 216 | const struct omap_video_timings *timings) |
224 | { | 217 | { |
225 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; | 218 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
@@ -227,7 +220,7 @@ static void omap_crtc_set_timings(struct omap_overlay_manager *mgr, | |||
227 | omap_crtc->timings = *timings; | 220 | omap_crtc->timings = *timings; |
228 | } | 221 | } |
229 | 222 | ||
230 | static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr, | 223 | static void omap_crtc_dss_set_lcd_config(struct omap_overlay_manager *mgr, |
231 | const struct dss_lcd_mgr_config *config) | 224 | const struct dss_lcd_mgr_config *config) |
232 | { | 225 | { |
233 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; | 226 | struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; |
@@ -235,211 +228,99 @@ static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr, | |||
235 | dispc_mgr_set_lcd_config(omap_crtc->channel, config); | 228 | dispc_mgr_set_lcd_config(omap_crtc->channel, config); |
236 | } | 229 | } |
237 | 230 | ||
238 | static int omap_crtc_register_framedone_handler( | 231 | static int omap_crtc_dss_register_framedone( |
239 | struct omap_overlay_manager *mgr, | 232 | struct omap_overlay_manager *mgr, |
240 | void (*handler)(void *), void *data) | 233 | void (*handler)(void *), void *data) |
241 | { | 234 | { |
242 | return 0; | 235 | return 0; |
243 | } | 236 | } |
244 | 237 | ||
245 | static void omap_crtc_unregister_framedone_handler( | 238 | static void omap_crtc_dss_unregister_framedone( |
246 | struct omap_overlay_manager *mgr, | 239 | struct omap_overlay_manager *mgr, |
247 | void (*handler)(void *), void *data) | 240 | void (*handler)(void *), void *data) |
248 | { | 241 | { |
249 | } | 242 | } |
250 | 243 | ||
251 | static const struct dss_mgr_ops mgr_ops = { | 244 | static const struct dss_mgr_ops mgr_ops = { |
252 | .connect = omap_crtc_connect, | 245 | .connect = omap_crtc_dss_connect, |
253 | .disconnect = omap_crtc_disconnect, | 246 | .disconnect = omap_crtc_dss_disconnect, |
254 | .start_update = omap_crtc_start_update, | 247 | .start_update = omap_crtc_dss_start_update, |
255 | .enable = omap_crtc_enable, | 248 | .enable = omap_crtc_dss_enable, |
256 | .disable = omap_crtc_disable, | 249 | .disable = omap_crtc_dss_disable, |
257 | .set_timings = omap_crtc_set_timings, | 250 | .set_timings = omap_crtc_dss_set_timings, |
258 | .set_lcd_config = omap_crtc_set_lcd_config, | 251 | .set_lcd_config = omap_crtc_dss_set_lcd_config, |
259 | .register_framedone_handler = omap_crtc_register_framedone_handler, | 252 | .register_framedone_handler = omap_crtc_dss_register_framedone, |
260 | .unregister_framedone_handler = omap_crtc_unregister_framedone_handler, | 253 | .unregister_framedone_handler = omap_crtc_dss_unregister_framedone, |
261 | }; | 254 | }; |
262 | 255 | ||
263 | /* ----------------------------------------------------------------------------- | 256 | /* ----------------------------------------------------------------------------- |
264 | * Apply Logic | 257 | * Setup, Flush and Page Flip |
265 | */ | 258 | */ |
266 | 259 | ||
267 | static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) | 260 | static void omap_crtc_complete_page_flip(struct drm_crtc *crtc) |
268 | { | 261 | { |
269 | struct omap_crtc *omap_crtc = | 262 | struct drm_pending_vblank_event *event; |
270 | container_of(irq, struct omap_crtc, error_irq); | 263 | struct drm_device *dev = crtc->dev; |
271 | 264 | unsigned long flags; | |
272 | if (omap_crtc->ignore_digit_sync_lost) { | ||
273 | irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT; | ||
274 | if (!irqstatus) | ||
275 | return; | ||
276 | } | ||
277 | 265 | ||
278 | DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus); | 266 | event = crtc->state->event; |
279 | } | ||
280 | 267 | ||
281 | static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus) | 268 | if (!event) |
282 | { | 269 | return; |
283 | struct omap_crtc *omap_crtc = | ||
284 | container_of(irq, struct omap_crtc, apply_irq); | ||
285 | struct drm_crtc *crtc = &omap_crtc->base; | ||
286 | |||
287 | if (!dispc_mgr_go_busy(omap_crtc->channel)) { | ||
288 | struct omap_drm_private *priv = | ||
289 | crtc->dev->dev_private; | ||
290 | DBG("%s: apply done", omap_crtc->name); | ||
291 | __omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq); | ||
292 | queue_work(priv->wq, &omap_crtc->apply_work); | ||
293 | } | ||
294 | } | ||
295 | 270 | ||
296 | static void apply_worker(struct work_struct *work) | 271 | spin_lock_irqsave(&dev->event_lock, flags); |
297 | { | ||
298 | struct omap_crtc *omap_crtc = | ||
299 | container_of(work, struct omap_crtc, apply_work); | ||
300 | struct drm_crtc *crtc = &omap_crtc->base; | ||
301 | struct drm_device *dev = crtc->dev; | ||
302 | struct omap_drm_apply *apply, *n; | ||
303 | bool need_apply; | ||
304 | 272 | ||
305 | /* | 273 | list_del(&event->base.link); |
306 | * Synchronize everything on mode_config.mutex, to keep | ||
307 | * the callbacks and list modification all serialized | ||
308 | * with respect to modesetting ioctls from userspace. | ||
309 | */ | ||
310 | drm_modeset_lock(&crtc->mutex, NULL); | ||
311 | dispc_runtime_get(); | ||
312 | 274 | ||
313 | /* | 275 | /* |
314 | * If we are still pending a previous update, wait.. when the | 276 | * Queue the event for delivery if it's still linked to a file |
315 | * pending update completes, we get kicked again. | 277 | * handle, otherwise just destroy it. |
316 | */ | 278 | */ |
317 | if (omap_crtc->apply_irq.registered) | 279 | if (event->base.file_priv) |
318 | goto out; | 280 | drm_crtc_send_vblank_event(crtc, event); |
319 | 281 | else | |
320 | /* finish up previous apply's: */ | 282 | event->base.destroy(&event->base); |
321 | list_for_each_entry_safe(apply, n, | ||
322 | &omap_crtc->pending_applies, pending_node) { | ||
323 | apply->post_apply(apply); | ||
324 | list_del(&apply->pending_node); | ||
325 | } | ||
326 | |||
327 | need_apply = !list_empty(&omap_crtc->queued_applies); | ||
328 | 283 | ||
329 | /* then handle the next round of of queued apply's: */ | 284 | spin_unlock_irqrestore(&dev->event_lock, flags); |
330 | list_for_each_entry_safe(apply, n, | ||
331 | &omap_crtc->queued_applies, queued_node) { | ||
332 | apply->pre_apply(apply); | ||
333 | list_del(&apply->queued_node); | ||
334 | apply->queued = false; | ||
335 | list_add_tail(&apply->pending_node, | ||
336 | &omap_crtc->pending_applies); | ||
337 | } | ||
338 | |||
339 | if (need_apply) { | ||
340 | enum omap_channel channel = omap_crtc->channel; | ||
341 | |||
342 | DBG("%s: GO", omap_crtc->name); | ||
343 | |||
344 | if (dispc_mgr_is_enabled(channel)) { | ||
345 | dispc_mgr_go(channel); | ||
346 | omap_irq_register(dev, &omap_crtc->apply_irq); | ||
347 | } else { | ||
348 | struct omap_drm_private *priv = dev->dev_private; | ||
349 | queue_work(priv->wq, &omap_crtc->apply_work); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | out: | ||
354 | dispc_runtime_put(); | ||
355 | drm_modeset_unlock(&crtc->mutex); | ||
356 | } | 285 | } |
357 | 286 | ||
358 | int omap_crtc_apply(struct drm_crtc *crtc, | 287 | static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) |
359 | struct omap_drm_apply *apply) | ||
360 | { | 288 | { |
361 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 289 | struct omap_crtc *omap_crtc = |
362 | 290 | container_of(irq, struct omap_crtc, error_irq); | |
363 | WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); | ||
364 | |||
365 | /* no need to queue it again if it is already queued: */ | ||
366 | if (apply->queued) | ||
367 | return 0; | ||
368 | |||
369 | apply->queued = true; | ||
370 | list_add_tail(&apply->queued_node, &omap_crtc->queued_applies); | ||
371 | 291 | ||
372 | /* | 292 | if (omap_crtc->ignore_digit_sync_lost) { |
373 | * If there are no currently pending updates, then go ahead and | 293 | irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT; |
374 | * kick the worker immediately, otherwise it will run again when | 294 | if (!irqstatus) |
375 | * the current update finishes. | 295 | return; |
376 | */ | ||
377 | if (list_empty(&omap_crtc->pending_applies)) { | ||
378 | struct omap_drm_private *priv = crtc->dev->dev_private; | ||
379 | queue_work(priv->wq, &omap_crtc->apply_work); | ||
380 | } | 296 | } |
381 | 297 | ||
382 | return 0; | 298 | DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus); |
383 | } | 299 | } |
384 | 300 | ||
385 | static void omap_crtc_pre_apply(struct omap_drm_apply *apply) | 301 | static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus) |
386 | { | 302 | { |
387 | struct omap_crtc *omap_crtc = | 303 | struct omap_crtc *omap_crtc = |
388 | container_of(apply, struct omap_crtc, apply); | 304 | container_of(irq, struct omap_crtc, vblank_irq); |
389 | struct drm_crtc *crtc = &omap_crtc->base; | 305 | struct drm_device *dev = omap_crtc->base.dev; |
390 | struct omap_drm_private *priv = crtc->dev->dev_private; | ||
391 | struct drm_encoder *encoder = NULL; | ||
392 | unsigned int i; | ||
393 | |||
394 | DBG("%s: enabled=%d", omap_crtc->name, omap_crtc->enabled); | ||
395 | |||
396 | for (i = 0; i < priv->num_encoders; i++) { | ||
397 | if (priv->encoders[i]->crtc == crtc) { | ||
398 | encoder = priv->encoders[i]; | ||
399 | break; | ||
400 | } | ||
401 | } | ||
402 | |||
403 | if (omap_crtc->current_encoder && encoder != omap_crtc->current_encoder) | ||
404 | omap_encoder_set_enabled(omap_crtc->current_encoder, false); | ||
405 | |||
406 | omap_crtc->current_encoder = encoder; | ||
407 | 306 | ||
408 | if (!omap_crtc->enabled) { | 307 | if (dispc_mgr_go_busy(omap_crtc->channel)) |
409 | if (encoder) | 308 | return; |
410 | omap_encoder_set_enabled(encoder, false); | ||
411 | } else { | ||
412 | if (encoder) { | ||
413 | omap_encoder_set_enabled(encoder, false); | ||
414 | omap_encoder_update(encoder, omap_crtc->mgr, | ||
415 | &omap_crtc->timings); | ||
416 | omap_encoder_set_enabled(encoder, true); | ||
417 | } | ||
418 | } | ||
419 | } | ||
420 | 309 | ||
421 | static void omap_crtc_post_apply(struct omap_drm_apply *apply) | 310 | DBG("%s: apply done", omap_crtc->name); |
422 | { | ||
423 | /* nothing needed for post-apply */ | ||
424 | } | ||
425 | 311 | ||
426 | void omap_crtc_flush(struct drm_crtc *crtc) | 312 | __omap_irq_unregister(dev, &omap_crtc->vblank_irq); |
427 | { | ||
428 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | ||
429 | int loops = 0; | ||
430 | 313 | ||
431 | while (!list_empty(&omap_crtc->pending_applies) || | 314 | rmb(); |
432 | !list_empty(&omap_crtc->queued_applies) || | 315 | WARN_ON(!omap_crtc->pending); |
433 | omap_crtc->event || omap_crtc->old_fb) { | 316 | omap_crtc->pending = false; |
317 | wmb(); | ||
434 | 318 | ||
435 | if (++loops > 10) { | 319 | /* wake up userspace */ |
436 | dev_err(crtc->dev->dev, | 320 | omap_crtc_complete_page_flip(&omap_crtc->base); |
437 | "omap_crtc_flush() timeout\n"); | ||
438 | break; | ||
439 | } | ||
440 | 321 | ||
441 | schedule_timeout_uninterruptible(msecs_to_jiffies(20)); | 322 | /* wake up omap_atomic_complete */ |
442 | } | 323 | wake_up(&omap_crtc->pending_wait); |
443 | } | 324 | } |
444 | 325 | ||
445 | /* ----------------------------------------------------------------------------- | 326 | /* ----------------------------------------------------------------------------- |
@@ -452,7 +333,7 @@ static void omap_crtc_destroy(struct drm_crtc *crtc) | |||
452 | 333 | ||
453 | DBG("%s", omap_crtc->name); | 334 | DBG("%s", omap_crtc->name); |
454 | 335 | ||
455 | WARN_ON(omap_crtc->apply_irq.registered); | 336 | WARN_ON(omap_crtc->vblank_irq.registered); |
456 | omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); | 337 | omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); |
457 | 338 | ||
458 | drm_crtc_cleanup(crtc); | 339 | drm_crtc_cleanup(crtc); |
@@ -460,28 +341,6 @@ static void omap_crtc_destroy(struct drm_crtc *crtc) | |||
460 | kfree(omap_crtc); | 341 | kfree(omap_crtc); |
461 | } | 342 | } |
462 | 343 | ||
463 | static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
464 | { | ||
465 | struct omap_drm_private *priv = crtc->dev->dev_private; | ||
466 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | ||
467 | bool enabled = (mode == DRM_MODE_DPMS_ON); | ||
468 | int i; | ||
469 | |||
470 | DBG("%s: %d", omap_crtc->name, mode); | ||
471 | |||
472 | if (enabled != omap_crtc->enabled) { | ||
473 | omap_crtc->enabled = enabled; | ||
474 | omap_crtc_apply(crtc, &omap_crtc->apply); | ||
475 | |||
476 | /* Enable/disable all planes associated with the CRTC. */ | ||
477 | for (i = 0; i < priv->num_planes; i++) { | ||
478 | struct drm_plane *plane = priv->planes[i]; | ||
479 | if (plane->crtc == crtc) | ||
480 | WARN_ON(omap_plane_set_enable(plane, enabled)); | ||
481 | } | ||
482 | } | ||
483 | } | ||
484 | |||
485 | static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, | 344 | static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, |
486 | const struct drm_display_mode *mode, | 345 | const struct drm_display_mode *mode, |
487 | struct drm_display_mode *adjusted_mode) | 346 | struct drm_display_mode *adjusted_mode) |
@@ -489,187 +348,127 @@ static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, | |||
489 | return true; | 348 | return true; |
490 | } | 349 | } |
491 | 350 | ||
492 | static int omap_crtc_mode_set(struct drm_crtc *crtc, | 351 | static void omap_crtc_enable(struct drm_crtc *crtc) |
493 | struct drm_display_mode *mode, | ||
494 | struct drm_display_mode *adjusted_mode, | ||
495 | int x, int y, | ||
496 | struct drm_framebuffer *old_fb) | ||
497 | { | 352 | { |
498 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 353 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
499 | 354 | ||
500 | mode = adjusted_mode; | 355 | DBG("%s", omap_crtc->name); |
501 | 356 | ||
502 | DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", | 357 | rmb(); |
503 | omap_crtc->name, mode->base.id, mode->name, | 358 | WARN_ON(omap_crtc->pending); |
504 | mode->vrefresh, mode->clock, | 359 | omap_crtc->pending = true; |
505 | mode->hdisplay, mode->hsync_start, | 360 | wmb(); |
506 | mode->hsync_end, mode->htotal, | ||
507 | mode->vdisplay, mode->vsync_start, | ||
508 | mode->vsync_end, mode->vtotal, | ||
509 | mode->type, mode->flags); | ||
510 | 361 | ||
511 | copy_timings_drm_to_omap(&omap_crtc->timings, mode); | 362 | omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); |
512 | 363 | ||
513 | /* | 364 | drm_crtc_vblank_on(crtc); |
514 | * The primary plane CRTC can be reset if the plane is disabled directly | ||
515 | * through the universal plane API. Set it again here. | ||
516 | */ | ||
517 | crtc->primary->crtc = crtc; | ||
518 | |||
519 | return omap_plane_mode_set(crtc->primary, crtc, crtc->primary->fb, | ||
520 | 0, 0, mode->hdisplay, mode->vdisplay, | ||
521 | x, y, mode->hdisplay, mode->vdisplay, | ||
522 | NULL, NULL); | ||
523 | } | 365 | } |
524 | 366 | ||
525 | static void omap_crtc_prepare(struct drm_crtc *crtc) | 367 | static void omap_crtc_disable(struct drm_crtc *crtc) |
526 | { | 368 | { |
527 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 369 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
528 | DBG("%s", omap_crtc->name); | ||
529 | omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); | ||
530 | } | ||
531 | 370 | ||
532 | static void omap_crtc_commit(struct drm_crtc *crtc) | ||
533 | { | ||
534 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | ||
535 | DBG("%s", omap_crtc->name); | 371 | DBG("%s", omap_crtc->name); |
536 | omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); | ||
537 | } | ||
538 | |||
539 | static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
540 | struct drm_framebuffer *old_fb) | ||
541 | { | ||
542 | struct drm_plane *plane = crtc->primary; | ||
543 | struct drm_display_mode *mode = &crtc->mode; | ||
544 | 372 | ||
545 | return omap_plane_mode_set(plane, crtc, crtc->primary->fb, | 373 | drm_crtc_vblank_off(crtc); |
546 | 0, 0, mode->hdisplay, mode->vdisplay, | ||
547 | x, y, mode->hdisplay, mode->vdisplay, | ||
548 | NULL, NULL); | ||
549 | } | 374 | } |
550 | 375 | ||
551 | static void vblank_cb(void *arg) | 376 | static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) |
552 | { | 377 | { |
553 | struct drm_crtc *crtc = arg; | ||
554 | struct drm_device *dev = crtc->dev; | ||
555 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 378 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
556 | unsigned long flags; | 379 | struct drm_display_mode *mode = &crtc->state->adjusted_mode; |
557 | struct drm_framebuffer *fb; | ||
558 | |||
559 | spin_lock_irqsave(&dev->event_lock, flags); | ||
560 | 380 | ||
561 | /* wakeup userspace */ | 381 | DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", |
562 | if (omap_crtc->event) | 382 | omap_crtc->name, mode->base.id, mode->name, |
563 | drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event); | 383 | mode->vrefresh, mode->clock, |
564 | 384 | mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, | |
565 | fb = omap_crtc->old_fb; | 385 | mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, |
566 | 386 | mode->type, mode->flags); | |
567 | omap_crtc->event = NULL; | ||
568 | omap_crtc->old_fb = NULL; | ||
569 | |||
570 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
571 | 387 | ||
572 | if (fb) | 388 | copy_timings_drm_to_omap(&omap_crtc->timings, mode); |
573 | drm_framebuffer_unreference(fb); | ||
574 | } | 389 | } |
575 | 390 | ||
576 | static void page_flip_worker(struct work_struct *work) | 391 | static void omap_crtc_atomic_begin(struct drm_crtc *crtc) |
577 | { | 392 | { |
578 | struct omap_crtc *omap_crtc = | ||
579 | container_of(work, struct omap_crtc, page_flip_work); | ||
580 | struct drm_crtc *crtc = &omap_crtc->base; | ||
581 | struct drm_display_mode *mode = &crtc->mode; | ||
582 | struct drm_gem_object *bo; | ||
583 | |||
584 | drm_modeset_lock(&crtc->mutex, NULL); | ||
585 | omap_plane_mode_set(crtc->primary, crtc, crtc->primary->fb, | ||
586 | 0, 0, mode->hdisplay, mode->vdisplay, | ||
587 | crtc->x, crtc->y, mode->hdisplay, mode->vdisplay, | ||
588 | vblank_cb, crtc); | ||
589 | drm_modeset_unlock(&crtc->mutex); | ||
590 | |||
591 | bo = omap_framebuffer_bo(crtc->primary->fb, 0); | ||
592 | drm_gem_object_unreference_unlocked(bo); | ||
593 | } | 393 | } |
594 | 394 | ||
595 | static void page_flip_cb(void *arg) | 395 | static void omap_crtc_atomic_flush(struct drm_crtc *crtc) |
596 | { | 396 | { |
597 | struct drm_crtc *crtc = arg; | ||
598 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | 397 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); |
599 | struct omap_drm_private *priv = crtc->dev->dev_private; | ||
600 | 398 | ||
601 | /* avoid assumptions about what ctxt we are called from: */ | 399 | WARN_ON(omap_crtc->vblank_irq.registered); |
602 | queue_work(priv->wq, &omap_crtc->page_flip_work); | ||
603 | } | ||
604 | 400 | ||
605 | static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, | 401 | if (dispc_mgr_is_enabled(omap_crtc->channel)) { |
606 | struct drm_framebuffer *fb, | ||
607 | struct drm_pending_vblank_event *event, | ||
608 | uint32_t page_flip_flags) | ||
609 | { | ||
610 | struct drm_device *dev = crtc->dev; | ||
611 | struct omap_crtc *omap_crtc = to_omap_crtc(crtc); | ||
612 | struct drm_plane *primary = crtc->primary; | ||
613 | struct drm_gem_object *bo; | ||
614 | unsigned long flags; | ||
615 | 402 | ||
616 | DBG("%d -> %d (event=%p)", primary->fb ? primary->fb->base.id : -1, | 403 | DBG("%s: GO", omap_crtc->name); |
617 | fb->base.id, event); | ||
618 | 404 | ||
619 | spin_lock_irqsave(&dev->event_lock, flags); | 405 | rmb(); |
406 | WARN_ON(omap_crtc->pending); | ||
407 | omap_crtc->pending = true; | ||
408 | wmb(); | ||
620 | 409 | ||
621 | if (omap_crtc->old_fb) { | 410 | dispc_mgr_go(omap_crtc->channel); |
622 | spin_unlock_irqrestore(&dev->event_lock, flags); | 411 | omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); |
623 | dev_err(dev->dev, "already a pending flip\n"); | ||
624 | return -EBUSY; | ||
625 | } | 412 | } |
626 | 413 | ||
627 | omap_crtc->event = event; | 414 | crtc->invert_dimensions = !!(crtc->primary->state->rotation & |
628 | omap_crtc->old_fb = primary->fb = fb; | 415 | (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))); |
629 | drm_framebuffer_reference(omap_crtc->old_fb); | 416 | } |
630 | 417 | ||
631 | spin_unlock_irqrestore(&dev->event_lock, flags); | 418 | static int omap_crtc_atomic_set_property(struct drm_crtc *crtc, |
419 | struct drm_crtc_state *state, | ||
420 | struct drm_property *property, | ||
421 | uint64_t val) | ||
422 | { | ||
423 | struct drm_plane_state *plane_state; | ||
424 | struct drm_plane *plane = crtc->primary; | ||
632 | 425 | ||
633 | /* | 426 | /* |
634 | * Hold a reference temporarily until the crtc is updated | 427 | * Delegate property set to the primary plane. Get the plane state and |
635 | * and takes the reference to the bo. This avoids it | 428 | * set the property directly. |
636 | * getting freed from under us: | ||
637 | */ | 429 | */ |
638 | bo = omap_framebuffer_bo(fb, 0); | ||
639 | drm_gem_object_reference(bo); | ||
640 | 430 | ||
641 | omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc); | 431 | plane_state = drm_atomic_get_plane_state(state->state, plane); |
432 | if (!plane_state) | ||
433 | return -EINVAL; | ||
642 | 434 | ||
643 | return 0; | 435 | return drm_atomic_plane_set_property(plane, plane_state, property, val); |
644 | } | 436 | } |
645 | 437 | ||
646 | static int omap_crtc_set_property(struct drm_crtc *crtc, | 438 | static int omap_crtc_atomic_get_property(struct drm_crtc *crtc, |
647 | struct drm_property *property, uint64_t val) | 439 | const struct drm_crtc_state *state, |
440 | struct drm_property *property, | ||
441 | uint64_t *val) | ||
648 | { | 442 | { |
649 | struct omap_drm_private *priv = crtc->dev->dev_private; | 443 | /* |
650 | 444 | * Delegate property get to the primary plane. The | |
651 | if (property == priv->rotation_prop) { | 445 | * drm_atomic_plane_get_property() function isn't exported, but can be |
652 | crtc->invert_dimensions = | 446 | * called through drm_object_property_get_value() as that will call |
653 | !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270))); | 447 | * drm_atomic_get_property() for atomic drivers. |
654 | } | 448 | */ |
655 | 449 | return drm_object_property_get_value(&crtc->primary->base, property, | |
656 | return omap_plane_set_property(crtc->primary, property, val); | 450 | val); |
657 | } | 451 | } |
658 | 452 | ||
659 | static const struct drm_crtc_funcs omap_crtc_funcs = { | 453 | static const struct drm_crtc_funcs omap_crtc_funcs = { |
660 | .set_config = drm_crtc_helper_set_config, | 454 | .reset = drm_atomic_helper_crtc_reset, |
455 | .set_config = drm_atomic_helper_set_config, | ||
661 | .destroy = omap_crtc_destroy, | 456 | .destroy = omap_crtc_destroy, |
662 | .page_flip = omap_crtc_page_flip_locked, | 457 | .page_flip = drm_atomic_helper_page_flip, |
663 | .set_property = omap_crtc_set_property, | 458 | .set_property = drm_atomic_helper_crtc_set_property, |
459 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | ||
460 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | ||
461 | .atomic_set_property = omap_crtc_atomic_set_property, | ||
462 | .atomic_get_property = omap_crtc_atomic_get_property, | ||
664 | }; | 463 | }; |
665 | 464 | ||
666 | static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { | 465 | static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = { |
667 | .dpms = omap_crtc_dpms, | ||
668 | .mode_fixup = omap_crtc_mode_fixup, | 466 | .mode_fixup = omap_crtc_mode_fixup, |
669 | .mode_set = omap_crtc_mode_set, | 467 | .mode_set_nofb = omap_crtc_mode_set_nofb, |
670 | .prepare = omap_crtc_prepare, | 468 | .disable = omap_crtc_disable, |
671 | .commit = omap_crtc_commit, | 469 | .enable = omap_crtc_enable, |
672 | .mode_set_base = omap_crtc_mode_set_base, | 470 | .atomic_begin = omap_crtc_atomic_begin, |
471 | .atomic_flush = omap_crtc_atomic_flush, | ||
673 | }; | 472 | }; |
674 | 473 | ||
675 | /* ----------------------------------------------------------------------------- | 474 | /* ----------------------------------------------------------------------------- |
@@ -699,7 +498,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, | |||
699 | { | 498 | { |
700 | struct drm_crtc *crtc = NULL; | 499 | struct drm_crtc *crtc = NULL; |
701 | struct omap_crtc *omap_crtc; | 500 | struct omap_crtc *omap_crtc; |
702 | struct omap_overlay_manager_info *info; | ||
703 | int ret; | 501 | int ret; |
704 | 502 | ||
705 | DBG("%s", channel_names[channel]); | 503 | DBG("%s", channel_names[channel]); |
@@ -710,21 +508,13 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, | |||
710 | 508 | ||
711 | crtc = &omap_crtc->base; | 509 | crtc = &omap_crtc->base; |
712 | 510 | ||
713 | INIT_WORK(&omap_crtc->page_flip_work, page_flip_worker); | 511 | init_waitqueue_head(&omap_crtc->pending_wait); |
714 | INIT_WORK(&omap_crtc->apply_work, apply_worker); | ||
715 | |||
716 | INIT_LIST_HEAD(&omap_crtc->pending_applies); | ||
717 | INIT_LIST_HEAD(&omap_crtc->queued_applies); | ||
718 | |||
719 | omap_crtc->apply.pre_apply = omap_crtc_pre_apply; | ||
720 | omap_crtc->apply.post_apply = omap_crtc_post_apply; | ||
721 | 512 | ||
722 | omap_crtc->channel = channel; | 513 | omap_crtc->channel = channel; |
723 | omap_crtc->name = channel_names[channel]; | 514 | omap_crtc->name = channel_names[channel]; |
724 | omap_crtc->pipe = id; | ||
725 | 515 | ||
726 | omap_crtc->apply_irq.irqmask = pipe2vbl(crtc); | 516 | omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc); |
727 | omap_crtc->apply_irq.irq = omap_crtc_apply_irq; | 517 | omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq; |
728 | 518 | ||
729 | omap_crtc->error_irq.irqmask = | 519 | omap_crtc->error_irq.irqmask = |
730 | dispc_mgr_get_sync_lost_irq(channel); | 520 | dispc_mgr_get_sync_lost_irq(channel); |
@@ -734,13 +524,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, | |||
734 | /* temporary: */ | 524 | /* temporary: */ |
735 | omap_crtc->mgr = omap_dss_get_overlay_manager(channel); | 525 | omap_crtc->mgr = omap_dss_get_overlay_manager(channel); |
736 | 526 | ||
737 | /* TODO: fix hard-coded setup.. add properties! */ | ||
738 | info = &omap_crtc->info; | ||
739 | info->default_color = 0x00000000; | ||
740 | info->trans_key = 0x00000000; | ||
741 | info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST; | ||
742 | info->trans_enabled = false; | ||
743 | |||
744 | ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, | 527 | ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, |
745 | &omap_crtc_funcs); | 528 | &omap_crtc_funcs); |
746 | if (ret < 0) { | 529 | if (ret < 0) { |