123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- From 4bfd9c177d92b59723ee6323e9ed81fbc331675c Mon Sep 17 00:00:00 2001
- From: Eric Anholt <eric@anholt.net>
- Date: Mon, 28 Dec 2015 13:25:41 -0800
- Subject: [PATCH] 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;
- }
-
|