249 lines
7.7 KiB
Diff
249 lines
7.7 KiB
Diff
|
From 85d92a47f1a200de3ea7cd0a3d790b2a286ade40 Mon Sep 17 00:00:00 2001
|
||
|
From: Eric Anholt <eric@anholt.net>
|
||
|
Date: Mon, 28 Dec 2015 13:25:41 -0800
|
||
|
Subject: [PATCH 282/304] 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>
|
||
|
(cherry picked from commit d8dbf44f13b91185c618219d912b246817a8d132)
|
||
|
---
|
||
|
drivers/gpu/drm/vc4/vc4_crtc.c | 115 +++++++++++++++++++++++------------------
|
||
|
drivers/gpu/drm/vc4/vc4_drv.h | 8 ++-
|
||
|
drivers/gpu/drm/vc4/vc4_hvs.c | 13 +++++
|
||
|
3 files changed, 84 insertions(+), 52 deletions(-)
|
||
|
|
||
|
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
|
||
|
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
|
||
|
@@ -49,22 +49,27 @@ struct vc4_crtc {
|
||
|
/* Which HVS channel we're using for our CRTC. */
|
||
|
int channel;
|
||
|
|
||
|
- /* Pointer to the actual hardware display list memory for the
|
||
|
- * crtc.
|
||
|
- */
|
||
|
- u32 __iomem *dlist;
|
||
|
-
|
||
|
- u32 dlist_size; /* in dwords */
|
||
|
-
|
||
|
struct drm_pending_vblank_event *event;
|
||
|
};
|
||
|
|
||
|
+struct vc4_crtc_state {
|
||
|
+ struct drm_crtc_state base;
|
||
|
+ /* Dlist area for this CRTC configuration. */
|
||
|
+ struct drm_mm_node mm;
|
||
|
+};
|
||
|
+
|
||
|
static inline struct vc4_crtc *
|
||
|
to_vc4_crtc(struct drm_crtc *crtc)
|
||
|
{
|
||
|
return (struct vc4_crtc *)crtc;
|
||
|
}
|
||
|
|
||
|
+static inline struct vc4_crtc_state *
|
||
|
+to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
|
||
|
+{
|
||
|
+ return (struct vc4_crtc_state *)crtc_state;
|
||
|
+}
|
||
|
+
|
||
|
struct vc4_crtc_data {
|
||
|
/* Which channel of the HVS this pixelvalve sources from. */
|
||
|
int hvs_channel;
|
||
|
@@ -319,11 +324,13 @@ static void vc4_crtc_enable(struct drm_c
|
||
|
static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
|
||
|
struct drm_crtc_state *state)
|
||
|
{
|
||
|
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
|
||
|
struct drm_device *dev = crtc->dev;
|
||
|
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||
|
struct drm_plane *plane;
|
||
|
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
||
|
+ unsigned long flags;
|
||
|
u32 dlist_count = 0;
|
||
|
+ int ret;
|
||
|
|
||
|
/* The pixelvalve can only feed one encoder (and encoders are
|
||
|
* 1:1 with connectors.)
|
||
|
@@ -346,18 +353,12 @@ static int vc4_crtc_atomic_check(struct
|
||
|
|
||
|
dlist_count++; /* Account for SCALER_CTL0_END. */
|
||
|
|
||
|
- if (!vc4_crtc->dlist || dlist_count > vc4_crtc->dlist_size) {
|
||
|
- vc4_crtc->dlist = ((u32 __iomem *)vc4->hvs->dlist +
|
||
|
- HVS_BOOTLOADER_DLIST_END);
|
||
|
- vc4_crtc->dlist_size = ((SCALER_DLIST_SIZE >> 2) -
|
||
|
- HVS_BOOTLOADER_DLIST_END);
|
||
|
-
|
||
|
- if (dlist_count > vc4_crtc->dlist_size) {
|
||
|
- DRM_DEBUG_KMS("dlist too large for CRTC (%d > %d).\n",
|
||
|
- dlist_count, vc4_crtc->dlist_size);
|
||
|
- return -EINVAL;
|
||
|
- }
|
||
|
- }
|
||
|
+ spin_lock_irqsave(&vc4->hvs->mm_lock, flags);
|
||
|
+ ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm,
|
||
|
+ dlist_count, 1, 0);
|
||
|
+ spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
@@ -368,47 +369,29 @@ static void vc4_crtc_atomic_flush(struct
|
||
|
struct drm_device *dev = crtc->dev;
|
||
|
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||
|
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
||
|
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
|
||
|
struct drm_plane *plane;
|
||
|
bool debug_dump_regs = false;
|
||
|
- u32 __iomem *dlist_next = vc4_crtc->dlist;
|
||
|
+ u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start;
|
||
|
+ u32 __iomem *dlist_next = dlist_start;
|
||
|
|
||
|
if (debug_dump_regs) {
|
||
|
DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
|
||
|
vc4_hvs_dump_state(dev);
|
||
|
}
|
||
|
|
||
|
- /* Copy all the active planes' dlist contents to the hardware dlist.
|
||
|
- *
|
||
|
- * XXX: If the new display list was large enough that it
|
||
|
- * overlapped a currently-read display list, we need to do
|
||
|
- * something like disable scanout before putting in the new
|
||
|
- * list. For now, we're safe because we only have the two
|
||
|
- * planes.
|
||
|
- */
|
||
|
+ /* Copy all the active planes' dlist contents to the hardware dlist. */
|
||
|
drm_atomic_crtc_for_each_plane(plane, crtc) {
|
||
|
dlist_next += vc4_plane_write_dlist(plane, dlist_next);
|
||
|
}
|
||
|
|
||
|
- if (dlist_next == vc4_crtc->dlist) {
|
||
|
- /* If no planes were enabled, use the SCALER_CTL0_END
|
||
|
- * at the start of the display list memory (in the
|
||
|
- * bootloader section). We'll rewrite that
|
||
|
- * SCALER_CTL0_END, just in case, though.
|
||
|
- */
|
||
|
- writel(SCALER_CTL0_END, vc4->hvs->dlist);
|
||
|
- HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), 0);
|
||
|
- } else {
|
||
|
- writel(SCALER_CTL0_END, dlist_next);
|
||
|
- dlist_next++;
|
||
|
-
|
||
|
- HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
|
||
|
- (u32 __iomem *)vc4_crtc->dlist -
|
||
|
- (u32 __iomem *)vc4->hvs->dlist);
|
||
|
-
|
||
|
- /* Make the next display list start after ours. */
|
||
|
- vc4_crtc->dlist_size -= (dlist_next - vc4_crtc->dlist);
|
||
|
- vc4_crtc->dlist = dlist_next;
|
||
|
- }
|
||
|
+ writel(SCALER_CTL0_END, dlist_next);
|
||
|
+ dlist_next++;
|
||
|
+
|
||
|
+ WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size);
|
||
|
+
|
||
|
+ HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
|
||
|
+ vc4_state->mm.start);
|
||
|
|
||
|
if (debug_dump_regs) {
|
||
|
DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
|
||
|
@@ -573,6 +556,36 @@ static int vc4_page_flip(struct drm_crtc
|
||
|
return drm_atomic_helper_page_flip(crtc, fb, event, flags);
|
||
|
}
|
||
|
|
||
|
+static struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc)
|
||
|
+{
|
||
|
+ struct vc4_crtc_state *vc4_state;
|
||
|
+
|
||
|
+ vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
|
||
|
+ if (!vc4_state)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base);
|
||
|
+ return &vc4_state->base;
|
||
|
+}
|
||
|
+
|
||
|
+static void vc4_crtc_destroy_state(struct drm_crtc *crtc,
|
||
|
+ struct drm_crtc_state *state)
|
||
|
+{
|
||
|
+ struct vc4_dev *vc4 = to_vc4_dev(crtc->dev);
|
||
|
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
|
||
|
+
|
||
|
+ if (vc4_state->mm.allocated) {
|
||
|
+ unsigned long flags;
|
||
|
+
|
||
|
+ spin_lock_irqsave(&vc4->hvs->mm_lock, flags);
|
||
|
+ drm_mm_remove_node(&vc4_state->mm);
|
||
|
+ spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags);
|
||
|
+
|
||
|
+ }
|
||
|
+
|
||
|
+ __drm_atomic_helper_crtc_destroy_state(crtc, state);
|
||
|
+}
|
||
|
+
|
||
|
static const struct drm_crtc_funcs vc4_crtc_funcs = {
|
||
|
.set_config = drm_atomic_helper_set_config,
|
||
|
.destroy = vc4_crtc_destroy,
|
||
|
@@ -581,8 +594,8 @@ static const struct drm_crtc_funcs vc4_c
|
||
|
.cursor_set = NULL, /* handled by drm_mode_cursor_universal */
|
||
|
.cursor_move = NULL, /* handled by drm_mode_cursor_universal */
|
||
|
.reset = drm_atomic_helper_crtc_reset,
|
||
|
- .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||
|
- .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||
|
+ .atomic_duplicate_state = vc4_crtc_duplicate_state,
|
||
|
+ .atomic_destroy_state = vc4_crtc_destroy_state,
|
||
|
};
|
||
|
|
||
|
static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
|
||
|
--- a/drivers/gpu/drm/vc4/vc4_drv.h
|
||
|
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
|
||
|
@@ -150,7 +150,13 @@ struct vc4_v3d {
|
||
|
struct vc4_hvs {
|
||
|
struct platform_device *pdev;
|
||
|
void __iomem *regs;
|
||
|
- void __iomem *dlist;
|
||
|
+ u32 __iomem *dlist;
|
||
|
+
|
||
|
+ /* Memory manager for CRTCs to allocate space in the display
|
||
|
+ * list. Units are dwords.
|
||
|
+ */
|
||
|
+ struct drm_mm dlist_mm;
|
||
|
+ spinlock_t mm_lock;
|
||
|
};
|
||
|
|
||
|
struct vc4_plane {
|
||
|
--- 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 *d
|
||
|
|
||
|
hvs->dlist = hvs->regs + SCALER_DLIST_START;
|
||
|
|
||
|
+ spin_lock_init(&hvs->mm_lock);
|
||
|
+
|
||
|
+ /* Set up the HVS display list memory manager. We never
|
||
|
+ * overwrite the setup from the bootloader (just 128b out of
|
||
|
+ * our 16K), since we don't want to scramble the screen when
|
||
|
+ * transitioning from the firmware's boot setup to runtime.
|
||
|
+ */
|
||
|
+ drm_mm_init(&hvs->dlist_mm,
|
||
|
+ HVS_BOOTLOADER_DLIST_END,
|
||
|
+ (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END);
|
||
|
+
|
||
|
vc4->hvs = hvs;
|
||
|
return 0;
|
||
|
}
|
||
|
@@ -129,6 +140,8 @@ static void vc4_hvs_unbind(struct device
|
||
|
struct drm_device *drm = dev_get_drvdata(master);
|
||
|
struct vc4_dev *vc4 = drm->dev_private;
|
||
|
|
||
|
+ drm_mm_takedown(&vc4->hvs->dlist_mm);
|
||
|
+
|
||
|
vc4->hvs = NULL;
|
||
|
}
|
||
|
|