aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2015-12-28 16:25:41 -0500
committerEric Anholt <eric@anholt.net>2016-02-16 14:24:08 -0500
commitd8dbf44f13b91185c618219d912b246817a8d132 (patch)
treea8af3bb3b1e608514f93cd76b921c9e3d7ace76f
parent6674a904d68041d982ffb284d2827410765a097a (diff)
drm/vc4: Make the CRTCs cooperate on allocating display lists.
So far, we've only ever lit up one CRTC, so this has been fine. To extend to more displays or more planes, we need to make sure we don't run our display lists into each other. Signed-off-by: Eric Anholt <eric@anholt.net>
-rw-r--r--drivers/gpu/drm/vc4/vc4_crtc.c115
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.h8
-rw-r--r--drivers/gpu/drm/vc4/vc4_hvs.c13
3 files changed, 84 insertions, 52 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 937409792b97..6cf931557e97 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -49,22 +49,27 @@ struct vc4_crtc {
49 /* Which HVS channel we're using for our CRTC. */ 49 /* Which HVS channel we're using for our CRTC. */
50 int channel; 50 int channel;
51 51
52 /* Pointer to the actual hardware display list memory for the
53 * crtc.
54 */
55 u32 __iomem *dlist;
56
57 u32 dlist_size; /* in dwords */
58
59 struct drm_pending_vblank_event *event; 52 struct drm_pending_vblank_event *event;
60}; 53};
61 54
55struct vc4_crtc_state {
56 struct drm_crtc_state base;
57 /* Dlist area for this CRTC configuration. */
58 struct drm_mm_node mm;
59};
60
62static inline struct vc4_crtc * 61static inline struct vc4_crtc *
63to_vc4_crtc(struct drm_crtc *crtc) 62to_vc4_crtc(struct drm_crtc *crtc)
64{ 63{
65 return (struct vc4_crtc *)crtc; 64 return (struct vc4_crtc *)crtc;
66} 65}
67 66
67static inline struct vc4_crtc_state *
68to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
69{
70 return (struct vc4_crtc_state *)crtc_state;
71}
72
68struct vc4_crtc_data { 73struct vc4_crtc_data {
69 /* Which channel of the HVS this pixelvalve sources from. */ 74 /* Which channel of the HVS this pixelvalve sources from. */
70 int hvs_channel; 75 int hvs_channel;
@@ -319,11 +324,13 @@ static void vc4_crtc_enable(struct drm_crtc *crtc)
319static int vc4_crtc_atomic_check(struct drm_crtc *crtc, 324static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
320 struct drm_crtc_state *state) 325 struct drm_crtc_state *state)
321{ 326{
327 struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
322 struct drm_device *dev = crtc->dev; 328 struct drm_device *dev = crtc->dev;
323 struct vc4_dev *vc4 = to_vc4_dev(dev); 329 struct vc4_dev *vc4 = to_vc4_dev(dev);
324 struct drm_plane *plane; 330 struct drm_plane *plane;
325 struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); 331 unsigned long flags;
326 u32 dlist_count = 0; 332 u32 dlist_count = 0;
333 int ret;
327 334
328 /* The pixelvalve can only feed one encoder (and encoders are 335 /* The pixelvalve can only feed one encoder (and encoders are
329 * 1:1 with connectors.) 336 * 1:1 with connectors.)
@@ -346,18 +353,12 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
346 353
347 dlist_count++; /* Account for SCALER_CTL0_END. */ 354 dlist_count++; /* Account for SCALER_CTL0_END. */
348 355
349 if (!vc4_crtc->dlist || dlist_count > vc4_crtc->dlist_size) { 356 spin_lock_irqsave(&vc4->hvs->mm_lock, flags);
350 vc4_crtc->dlist = ((u32 __iomem *)vc4->hvs->dlist + 357 ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm,
351 HVS_BOOTLOADER_DLIST_END); 358 dlist_count, 1, 0);
352 vc4_crtc->dlist_size = ((SCALER_DLIST_SIZE >> 2) - 359 spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags);
353 HVS_BOOTLOADER_DLIST_END); 360 if (ret)
354 361 return ret;
355 if (dlist_count > vc4_crtc->dlist_size) {
356 DRM_DEBUG_KMS("dlist too large for CRTC (%d > %d).\n",
357 dlist_count, vc4_crtc->dlist_size);
358 return -EINVAL;
359 }
360 }
361 362
362 return 0; 363 return 0;
363} 364}
@@ -368,47 +369,29 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
368 struct drm_device *dev = crtc->dev; 369 struct drm_device *dev = crtc->dev;
369 struct vc4_dev *vc4 = to_vc4_dev(dev); 370 struct vc4_dev *vc4 = to_vc4_dev(dev);
370 struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); 371 struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
372 struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
371 struct drm_plane *plane; 373 struct drm_plane *plane;
372 bool debug_dump_regs = false; 374 bool debug_dump_regs = false;
373 u32 __iomem *dlist_next = vc4_crtc->dlist; 375 u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start;
376 u32 __iomem *dlist_next = dlist_start;
374 377
375 if (debug_dump_regs) { 378 if (debug_dump_regs) {
376 DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc)); 379 DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
377 vc4_hvs_dump_state(dev); 380 vc4_hvs_dump_state(dev);
378 } 381 }
379 382
380 /* Copy all the active planes' dlist contents to the hardware dlist. 383 /* Copy all the active planes' dlist contents to the hardware dlist. */
381 *
382 * XXX: If the new display list was large enough that it
383 * overlapped a currently-read display list, we need to do
384 * something like disable scanout before putting in the new
385 * list. For now, we're safe because we only have the two
386 * planes.
387 */
388 drm_atomic_crtc_for_each_plane(plane, crtc) { 384 drm_atomic_crtc_for_each_plane(plane, crtc) {
389 dlist_next += vc4_plane_write_dlist(plane, dlist_next); 385 dlist_next += vc4_plane_write_dlist(plane, dlist_next);
390 } 386 }
391 387
392 if (dlist_next == vc4_crtc->dlist) { 388 writel(SCALER_CTL0_END, dlist_next);
393 /* If no planes were enabled, use the SCALER_CTL0_END 389 dlist_next++;
394 * at the start of the display list memory (in the 390
395 * bootloader section). We'll rewrite that 391 WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size);
396 * SCALER_CTL0_END, just in case, though. 392
397 */ 393 HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
398 writel(SCALER_CTL0_END, vc4->hvs->dlist); 394 vc4_state->mm.start);
399 HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), 0);
400 } else {
401 writel(SCALER_CTL0_END, dlist_next);
402 dlist_next++;
403
404 HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
405 (u32 __iomem *)vc4_crtc->dlist -
406 (u32 __iomem *)vc4->hvs->dlist);
407
408 /* Make the next display list start after ours. */
409 vc4_crtc->dlist_size -= (dlist_next - vc4_crtc->dlist);
410 vc4_crtc->dlist = dlist_next;
411 }
412 395
413 if (debug_dump_regs) { 396 if (debug_dump_regs) {
414 DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc)); 397 DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
@@ -573,6 +556,36 @@ static int vc4_page_flip(struct drm_crtc *crtc,
573 return drm_atomic_helper_page_flip(crtc, fb, event, flags); 556 return drm_atomic_helper_page_flip(crtc, fb, event, flags);
574} 557}
575 558
559static struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc)
560{
561 struct vc4_crtc_state *vc4_state;
562
563 vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
564 if (!vc4_state)
565 return NULL;
566
567 __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
568 return &vc4_state->base;
569}
570
571static void vc4_crtc_destroy_state(struct drm_crtc *crtc,
572 struct drm_crtc_state *state)
573{
574 struct vc4_dev *vc4 = to_vc4_dev(crtc->dev);
575 struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
576
577 if (vc4_state->mm.allocated) {
578 unsigned long flags;
579
580 spin_lock_irqsave(&vc4->hvs->mm_lock, flags);
581 drm_mm_remove_node(&vc4_state->mm);
582 spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags);
583
584 }
585
586 __drm_atomic_helper_crtc_destroy_state(crtc, state);
587}
588
576static const struct drm_crtc_funcs vc4_crtc_funcs = { 589static const struct drm_crtc_funcs vc4_crtc_funcs = {
577 .set_config = drm_atomic_helper_set_config, 590 .set_config = drm_atomic_helper_set_config,
578 .destroy = vc4_crtc_destroy, 591 .destroy = vc4_crtc_destroy,
@@ -581,8 +594,8 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = {
581 .cursor_set = NULL, /* handled by drm_mode_cursor_universal */ 594 .cursor_set = NULL, /* handled by drm_mode_cursor_universal */
582 .cursor_move = NULL, /* handled by drm_mode_cursor_universal */ 595 .cursor_move = NULL, /* handled by drm_mode_cursor_universal */
583 .reset = drm_atomic_helper_crtc_reset, 596 .reset = drm_atomic_helper_crtc_reset,
584 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 597 .atomic_duplicate_state = vc4_crtc_duplicate_state,
585 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 598 .atomic_destroy_state = vc4_crtc_destroy_state,
586}; 599};
587 600
588static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { 601static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 4c734d087d7f..ae9802486080 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -149,7 +149,13 @@ struct vc4_v3d {
149struct vc4_hvs { 149struct vc4_hvs {
150 struct platform_device *pdev; 150 struct platform_device *pdev;
151 void __iomem *regs; 151 void __iomem *regs;
152 void __iomem *dlist; 152 u32 __iomem *dlist;
153
154 /* Memory manager for CRTCs to allocate space in the display
155 * list. Units are dwords.
156 */
157 struct drm_mm dlist_mm;
158 spinlock_t mm_lock;
153}; 159};
154 160
155struct vc4_plane { 161struct vc4_plane {
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 8098c5b21ba4..9e435545b3b6 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -119,6 +119,17 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
119 119
120 hvs->dlist = hvs->regs + SCALER_DLIST_START; 120 hvs->dlist = hvs->regs + SCALER_DLIST_START;
121 121
122 spin_lock_init(&hvs->mm_lock);
123
124 /* Set up the HVS display list memory manager. We never
125 * overwrite the setup from the bootloader (just 128b out of
126 * our 16K), since we don't want to scramble the screen when
127 * transitioning from the firmware's boot setup to runtime.
128 */
129 drm_mm_init(&hvs->dlist_mm,
130 HVS_BOOTLOADER_DLIST_END,
131 (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END);
132
122 vc4->hvs = hvs; 133 vc4->hvs = hvs;
123 return 0; 134 return 0;
124} 135}
@@ -129,6 +140,8 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master,
129 struct drm_device *drm = dev_get_drvdata(master); 140 struct drm_device *drm = dev_get_drvdata(master);
130 struct vc4_dev *vc4 = drm->dev_private; 141 struct vc4_dev *vc4 = drm->dev_private;
131 142
143 drm_mm_takedown(&vc4->hvs->dlist_mm);
144
132 vc4->hvs = NULL; 145 vc4->hvs = NULL;
133} 146}
134 147