diff options
Diffstat (limited to 'target/linux/brcm2708/patches-4.4/0278-drm-vc4-Make-the-CRTCs-cooperate-on-allocating-displ.patch')
-rw-r--r-- | target/linux/brcm2708/patches-4.4/0278-drm-vc4-Make-the-CRTCs-cooperate-on-allocating-displ.patch | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.4/0278-drm-vc4-Make-the-CRTCs-cooperate-on-allocating-displ.patch b/target/linux/brcm2708/patches-4.4/0278-drm-vc4-Make-the-CRTCs-cooperate-on-allocating-displ.patch new file mode 100644 index 0000000000..f235996659 --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0278-drm-vc4-Make-the-CRTCs-cooperate-on-allocating-displ.patch @@ -0,0 +1,248 @@ +From 417b8c7dcc6cad23f255a4307dda97d04f9c9b5b Mon Sep 17 00:00:00 2001 +From: Eric Anholt <eric@anholt.net> +Date: Mon, 28 Dec 2015 13:25:41 -0800 +Subject: [PATCH 278/423] 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; + } + |