Skip to content

Commit 9864fd7

Browse files
committed
Merge tag 'drm-vc4-next-2016-02-17' of github.com:anholt/linux into drm-next
This pull request brings in overlay plane support for vc4. * tag 'drm-vc4-next-2016-02-17' of github.com:anholt/linux: drm/vc4: Add support for YUV planes. drm/vc4: Add support a few more RGB display plane formats. drm/vc4: Add support for scaling of display planes. drm/vc4: Fix which value is being used for source image size. drm/vc4: Add more display planes to each CRTC. drm/vc4: Make the CRTCs cooperate on allocating display lists. drm/vc4: Add a proper short-circut path for legacy cursor updates. drm/vc4: Move the plane clipping/scaling setup to a separate function. drm/vc4: Add missing __iomem annotation to hw_dlist. drm/vc4: Improve comments on vc4_plane_state members.
2 parents 5263925 + fc04023 commit 9864fd7

File tree

6 files changed

+872
-122
lines changed

6 files changed

+872
-122
lines changed

drivers/gpu/drm/vc4/vc4_crtc.c

Lines changed: 104 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,27 @@ struct vc4_crtc {
4949
/* Which HVS channel we're using for our CRTC. */
5050
int channel;
5151

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-
5952
struct drm_pending_vblank_event *event;
6053
};
6154

55+
struct vc4_crtc_state {
56+
struct drm_crtc_state base;
57+
/* Dlist area for this CRTC configuration. */
58+
struct drm_mm_node mm;
59+
};
60+
6261
static inline struct vc4_crtc *
6362
to_vc4_crtc(struct drm_crtc *crtc)
6463
{
6564
return (struct vc4_crtc *)crtc;
6665
}
6766

67+
static inline struct vc4_crtc_state *
68+
to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
69+
{
70+
return (struct vc4_crtc_state *)crtc_state;
71+
}
72+
6873
struct vc4_crtc_data {
6974
/* Which channel of the HVS this pixelvalve sources from. */
7075
int hvs_channel;
@@ -319,11 +324,13 @@ static void vc4_crtc_enable(struct drm_crtc *crtc)
319324
static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
320325
struct drm_crtc_state *state)
321326
{
327+
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
322328
struct drm_device *dev = crtc->dev;
323329
struct vc4_dev *vc4 = to_vc4_dev(dev);
324330
struct drm_plane *plane;
325-
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
331+
unsigned long flags;
326332
u32 dlist_count = 0;
333+
int ret;
327334

328335
/* The pixelvalve can only feed one encoder (and encoders are
329336
* 1:1 with connectors.)
@@ -346,18 +353,12 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
346353

347354
dlist_count++; /* Account for SCALER_CTL0_END. */
348355

349-
if (!vc4_crtc->dlist || dlist_count > vc4_crtc->dlist_size) {
350-
vc4_crtc->dlist = ((u32 __iomem *)vc4->hvs->dlist +
351-
HVS_BOOTLOADER_DLIST_END);
352-
vc4_crtc->dlist_size = ((SCALER_DLIST_SIZE >> 2) -
353-
HVS_BOOTLOADER_DLIST_END);
354-
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-
}
356+
spin_lock_irqsave(&vc4->hvs->mm_lock, flags);
357+
ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm,
358+
dlist_count, 1, 0);
359+
spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags);
360+
if (ret)
361+
return ret;
361362

362363
return 0;
363364
}
@@ -368,47 +369,29 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
368369
struct drm_device *dev = crtc->dev;
369370
struct vc4_dev *vc4 = to_vc4_dev(dev);
370371
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
372+
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
371373
struct drm_plane *plane;
372374
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;
374377

375378
if (debug_dump_regs) {
376379
DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
377380
vc4_hvs_dump_state(dev);
378381
}
379382

380-
/* 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-
*/
383+
/* Copy all the active planes' dlist contents to the hardware dlist. */
388384
drm_atomic_crtc_for_each_plane(plane, crtc) {
389385
dlist_next += vc4_plane_write_dlist(plane, dlist_next);
390386
}
391387

392-
if (dlist_next == vc4_crtc->dlist) {
393-
/* If no planes were enabled, use the SCALER_CTL0_END
394-
* at the start of the display list memory (in the
395-
* bootloader section). We'll rewrite that
396-
* SCALER_CTL0_END, just in case, though.
397-
*/
398-
writel(SCALER_CTL0_END, vc4->hvs->dlist);
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-
}
388+
writel(SCALER_CTL0_END, dlist_next);
389+
dlist_next++;
390+
391+
WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size);
392+
393+
HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
394+
vc4_state->mm.start);
412395

413396
if (debug_dump_regs) {
414397
DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
@@ -573,6 +556,36 @@ static int vc4_page_flip(struct drm_crtc *crtc,
573556
return drm_atomic_helper_page_flip(crtc, fb, event, flags);
574557
}
575558

559+
static 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+
571+
static 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+
576589
static const struct drm_crtc_funcs vc4_crtc_funcs = {
577590
.set_config = drm_atomic_helper_set_config,
578591
.destroy = vc4_crtc_destroy,
@@ -581,8 +594,8 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = {
581594
.cursor_set = NULL, /* handled by drm_mode_cursor_universal */
582595
.cursor_move = NULL, /* handled by drm_mode_cursor_universal */
583596
.reset = drm_atomic_helper_crtc_reset,
584-
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
585-
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
597+
.atomic_duplicate_state = vc4_crtc_duplicate_state,
598+
.atomic_destroy_state = vc4_crtc_destroy_state,
586599
};
587600

588601
static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
@@ -644,9 +657,9 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
644657
struct vc4_dev *vc4 = to_vc4_dev(drm);
645658
struct vc4_crtc *vc4_crtc;
646659
struct drm_crtc *crtc;
647-
struct drm_plane *primary_plane, *cursor_plane;
660+
struct drm_plane *primary_plane, *cursor_plane, *destroy_plane, *temp;
648661
const struct of_device_id *match;
649-
int ret;
662+
int ret, i;
650663

651664
vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
652665
if (!vc4_crtc)
@@ -675,38 +688,62 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
675688
goto err;
676689
}
677690

678-
cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
679-
if (IS_ERR(cursor_plane)) {
680-
dev_err(dev, "failed to construct cursor plane\n");
681-
ret = PTR_ERR(cursor_plane);
682-
goto err_primary;
683-
}
684-
685-
drm_crtc_init_with_planes(drm, crtc, primary_plane, cursor_plane,
691+
drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
686692
&vc4_crtc_funcs, NULL);
687693
drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
688694
primary_plane->crtc = crtc;
689-
cursor_plane->crtc = crtc;
690695
vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc;
691696
vc4_crtc->channel = vc4_crtc->data->hvs_channel;
692697

698+
/* Set up some arbitrary number of planes. We're not limited
699+
* by a set number of physical registers, just the space in
700+
* the HVS (16k) and how small an plane can be (28 bytes).
701+
* However, each plane we set up takes up some memory, and
702+
* increases the cost of looping over planes, which atomic
703+
* modesetting does quite a bit. As a result, we pick a
704+
* modest number of planes to expose, that should hopefully
705+
* still cover any sane usecase.
706+
*/
707+
for (i = 0; i < 8; i++) {
708+
struct drm_plane *plane =
709+
vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY);
710+
711+
if (IS_ERR(plane))
712+
continue;
713+
714+
plane->possible_crtcs = 1 << drm_crtc_index(crtc);
715+
}
716+
717+
/* Set up the legacy cursor after overlay initialization,
718+
* since we overlay planes on the CRTC in the order they were
719+
* initialized.
720+
*/
721+
cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
722+
if (!IS_ERR(cursor_plane)) {
723+
cursor_plane->possible_crtcs = 1 << drm_crtc_index(crtc);
724+
cursor_plane->crtc = crtc;
725+
crtc->cursor = cursor_plane;
726+
}
727+
693728
CRTC_WRITE(PV_INTEN, 0);
694729
CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
695730
ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
696731
vc4_crtc_irq_handler, 0, "vc4 crtc", vc4_crtc);
697732
if (ret)
698-
goto err_cursor;
733+
goto err_destroy_planes;
699734

700735
vc4_set_crtc_possible_masks(drm, crtc);
701736

702737
platform_set_drvdata(pdev, vc4_crtc);
703738

704739
return 0;
705740

706-
err_cursor:
707-
cursor_plane->funcs->destroy(cursor_plane);
708-
err_primary:
709-
primary_plane->funcs->destroy(primary_plane);
741+
err_destroy_planes:
742+
list_for_each_entry_safe(destroy_plane, temp,
743+
&drm->mode_config.plane_list, head) {
744+
if (destroy_plane->possible_crtcs == 1 << drm_crtc_index(crtc))
745+
destroy_plane->funcs->destroy(destroy_plane);
746+
}
710747
err:
711748
return ret;
712749
}

drivers/gpu/drm/vc4/vc4_drv.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,17 @@ struct vc4_v3d {
149149
struct vc4_hvs {
150150
struct platform_device *pdev;
151151
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+
/* Memory manager for the LBM memory used by HVS scaling. */
159+
struct drm_mm lbm_mm;
160+
spinlock_t mm_lock;
161+
162+
struct drm_mm_node mitchell_netravali_filter;
153163
};
154164

155165
struct vc4_plane {

0 commit comments

Comments
 (0)