X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=drivers%2Fgpu%2Fdrm%2Farmada%2Farmada_crtc.c;h=0566739118187b95ee217ec5692b1de90c4314a2;hb=9c41467c9aa5b3916ff559b8c2b3725d6290e5ec;hp=42a40daff13265c968f442b4dafd2ec081bbafd7;hpb=47f7dc4b845a9fe60c53b84b8c88cf14efd0de7f;p=uclinux-h8%2Flinux.git diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 42a40daff132..056673911818 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -294,19 +295,6 @@ static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc, spin_unlock_irqrestore(&dcrtc->irq_lock, flags); } -static void armada_drm_crtc_complete_disable_work(struct armada_crtc *dcrtc, - struct armada_plane_work *work) -{ - unsigned long flags; - - if (dcrtc->plane == work->plane) - dcrtc->plane = NULL; - - spin_lock_irqsave(&dcrtc->irq_lock, flags); - armada_drm_crtc_update_regs(dcrtc, work->regs); - spin_unlock_irqrestore(&dcrtc->irq_lock, flags); -} - static struct armada_plane_work * armada_drm_crtc_alloc_plane_work(struct drm_plane *plane) { @@ -325,38 +313,6 @@ armada_drm_crtc_alloc_plane_work(struct drm_plane *plane) return work; } -static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc, - struct drm_framebuffer *fb, bool force) -{ - struct armada_plane_work *work; - - if (!fb) - return; - - if (force) { - /* Display is disabled, so just drop the old fb */ - drm_framebuffer_put(fb); - return; - } - - work = armada_drm_crtc_alloc_plane_work(dcrtc->crtc.primary); - if (work) { - work->old_fb = fb; - - if (armada_drm_plane_work_queue(dcrtc, work) == 0) - return; - - kfree(work); - } - - /* - * Oops - just drop the reference immediately and hope for - * the best. The worst that will happen is the buffer gets - * reused before it has finished being displayed. - */ - drm_framebuffer_put(fb); -} - static void armada_drm_vblank_off(struct armada_crtc *dcrtc) { /* @@ -399,6 +355,7 @@ static void armada_drm_crtc_prepare(struct drm_crtc *crtc) { struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); struct drm_plane *plane; + u32 val; /* * If we have an overlay plane associated with this CRTC, disable @@ -411,6 +368,18 @@ static void armada_drm_crtc_prepare(struct drm_crtc *crtc) WARN_ON(!armada_drm_plane_work_wait(drm_to_armada_plane(plane), HZ)); } + + /* Wait for pending flips to complete */ + armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary), + MAX_SCHEDULE_TIMEOUT); + + drm_crtc_vblank_off(crtc); + + val = dcrtc->dumb_ctrl & ~CFG_DUMB_ENA; + if (val != dcrtc->dumb_ctrl) { + dcrtc->dumb_ctrl = val; + writel_relaxed(val, dcrtc->base + LCD_SPU_DUMB_CTRL); + } } /* The mode_config.mutex will be held for this call */ @@ -418,10 +387,9 @@ static void armada_drm_crtc_commit(struct drm_crtc *crtc) { struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - if (dcrtc->dpms != DRM_MODE_DPMS_ON) { - dcrtc->dpms = DRM_MODE_DPMS_ON; - armada_drm_crtc_update(dcrtc); - } + dcrtc->dpms = DRM_MODE_DPMS_ON; + armada_drm_crtc_update(dcrtc); + drm_crtc_vblank_on(crtc); } /* The mode_config.mutex will be held for this call */ @@ -573,71 +541,16 @@ static uint32_t armada_drm_crtc_calculate_csc(struct armada_crtc *dcrtc) return val; } -static void armada_drm_gra_plane_regs(struct armada_regs *regs, - struct drm_framebuffer *fb, struct armada_plane_state *state, - int x, int y, bool interlaced) -{ - unsigned int i; - u32 ctrl0; - - i = armada_drm_crtc_calc_fb(fb, x, y, regs, interlaced); - armada_reg_queue_set(regs, i, state->dst_yx, LCD_SPU_GRA_OVSA_HPXL_VLN); - armada_reg_queue_set(regs, i, state->src_hw, LCD_SPU_GRA_HPXL_VLN); - armada_reg_queue_set(regs, i, state->dst_hw, LCD_SPU_GZM_HPXL_VLN); - - ctrl0 = state->ctrl0; - if (interlaced) - ctrl0 |= CFG_GRA_FTOGGLE; - - armada_reg_queue_mod(regs, i, ctrl0, CFG_GRAFORMAT | - CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV | - CFG_SWAPYU | CFG_YUV2RGB) | - CFG_PALETTE_ENA | CFG_GRA_FTOGGLE | - CFG_GRA_HSMOOTH | CFG_GRA_ENA, - LCD_SPU_DMA_CTRL0); - armada_reg_queue_end(regs, i); -} - -static void armada_drm_primary_set(struct drm_crtc *crtc, - struct drm_plane *plane, int x, int y) -{ - struct armada_plane_state *state = &drm_to_armada_plane(plane)->state; - struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - struct armada_regs regs[8]; - bool interlaced = dcrtc->interlaced; - - armada_drm_gra_plane_regs(regs, plane->fb, state, x, y, interlaced); - armada_drm_crtc_update_regs(dcrtc, regs); -} - /* The mode_config.mutex will be held for this call */ -static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, struct drm_display_mode *adj, - int x, int y, struct drm_framebuffer *old_fb) +static void armada_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) { + struct drm_display_mode *adj = &crtc->state->adjusted_mode; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); struct armada_regs regs[17]; uint32_t lm, rm, tm, bm, val, sclk; unsigned long flags; unsigned i; - bool interlaced; - - drm_framebuffer_get(crtc->primary->fb); - - interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE); - - val = CFG_GRA_ENA; - val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->fmt); - val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->mod); - - if (drm_fb_to_armada_fb(dcrtc->crtc.primary->fb)->fmt > CFG_420) - val |= CFG_PALETTE_ENA; - - drm_to_armada_plane(crtc->primary)->state.ctrl0 = val; - drm_to_armada_plane(crtc->primary)->state.src_hw = - drm_to_armada_plane(crtc->primary)->state.dst_hw = - adj->crtc_vdisplay << 16 | adj->crtc_hdisplay; - drm_to_armada_plane(crtc->primary)->state.dst_yx = 0; + bool interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE); i = 0; rm = adj->crtc_hsync_start - adj->crtc_hdisplay; @@ -656,18 +569,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, adj->crtc_vsync_end, adj->crtc_vtotal, tm, bm); - /* Wait for pending flips to complete */ - armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary), - MAX_SCHEDULE_TIMEOUT); - - drm_crtc_vblank_off(crtc); - - val = dcrtc->dumb_ctrl & ~CFG_DUMB_ENA; - if (val != dcrtc->dumb_ctrl) { - dcrtc->dumb_ctrl = val; - writel_relaxed(val, dcrtc->base + LCD_SPU_DUMB_CTRL); - } - /* * If we are blanked, we would have disabled the clock. Re-enable * it so that compute_clock() does the right thing. @@ -690,9 +591,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, spin_lock_irqsave(&dcrtc->irq_lock, flags); - /* Ensure graphic fifo is enabled */ - armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1); - /* Even interlaced/progressive frame */ dcrtc->v[1].spu_v_h_total = adj->crtc_vtotal << 16 | adj->crtc_htotal; @@ -735,53 +633,48 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, armada_reg_queue_end(regs, i); armada_drm_crtc_update_regs(dcrtc, regs); - - armada_drm_primary_set(crtc, crtc->primary, x, y); spin_unlock_irqrestore(&dcrtc->irq_lock, flags); - - armada_drm_crtc_update(dcrtc); - - drm_crtc_vblank_on(crtc); - armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms)); - - return 0; } /* The mode_config.mutex will be held for this call */ -static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) +static void armada_drm_crtc_disable(struct drm_crtc *crtc) { - struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - struct armada_regs regs[4]; - unsigned i; - - i = armada_drm_crtc_calc_fb(crtc->primary->fb, crtc->x, crtc->y, regs, - dcrtc->interlaced); - armada_reg_queue_end(regs, i); + armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); - /* Wait for pending flips to complete */ - armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary), - MAX_SCHEDULE_TIMEOUT); + /* Disable our primary plane when we disable the CRTC. */ + crtc->primary->funcs->disable_plane(crtc->primary, NULL); +} - /* Take a reference to the new fb as we're using it */ - drm_framebuffer_get(crtc->primary->fb); +static void armada_drm_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); + struct armada_plane *dplane; - /* Update the base in the CRTC */ - armada_drm_crtc_update_regs(dcrtc, regs); + DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); - /* Drop our previously held reference */ - armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms)); + /* Wait 100ms for any plane works to complete */ + dplane = drm_to_armada_plane(crtc->primary); + if (WARN_ON(armada_drm_plane_work_wait(dplane, HZ / 10) == 0)) + armada_drm_plane_work_cancel(dcrtc, dplane); - return 0; + dcrtc->regs_idx = 0; + dcrtc->regs = dcrtc->atomic_regs; } -/* The mode_config.mutex will be held for this call */ -static void armada_drm_crtc_disable(struct drm_crtc *crtc) +static void armada_drm_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) { - armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); + unsigned long flags; - /* Disable our primary plane when we disable the CRTC. */ - crtc->primary->funcs->disable_plane(crtc->primary, NULL); + DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); + + armada_reg_queue_end(dcrtc->regs, dcrtc->regs_idx); + + spin_lock_irqsave(&dcrtc->irq_lock, flags); + armada_drm_crtc_update_regs(dcrtc, dcrtc->regs); + spin_unlock_irqrestore(&dcrtc->irq_lock, flags); } static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = { @@ -789,9 +682,12 @@ static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = { .prepare = armada_drm_crtc_prepare, .commit = armada_drm_crtc_commit, .mode_fixup = armada_drm_crtc_mode_fixup, - .mode_set = armada_drm_crtc_mode_set, - .mode_set_base = armada_drm_crtc_mode_set_base, + .mode_set = drm_helper_crtc_mode_set, + .mode_set_nofb = armada_drm_crtc_mode_set_nofb, + .mode_set_base = drm_helper_crtc_mode_set_base, .disable = armada_drm_crtc_disable, + .atomic_begin = armada_drm_crtc_atomic_begin, + .atomic_flush = armada_drm_crtc_atomic_flush, }; static void armada_load_cursor_argb(void __iomem *base, uint32_t *pix, @@ -1031,59 +927,81 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) * and a mode_set. */ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t page_flip_flags, - struct drm_modeset_acquire_ctx *ctx) + struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, + uint32_t page_flip_flags, struct drm_modeset_acquire_ctx *ctx) { struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); + struct drm_plane *plane = crtc->primary; + const struct drm_plane_helper_funcs *plane_funcs; + struct drm_plane_state *state; struct armada_plane_work *work; - unsigned i; int ret; - /* We don't support changing the pixel format */ - if (fb->format != crtc->primary->fb->format) - return -EINVAL; - - work = armada_drm_crtc_alloc_plane_work(dcrtc->crtc.primary); - if (!work) + /* Construct new state for the primary plane */ + state = drm_atomic_helper_plane_duplicate_state(plane); + if (!state) return -ENOMEM; - work->event = event; - work->old_fb = dcrtc->crtc.primary->fb; + drm_atomic_set_fb_for_plane(state, fb); - i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs, - dcrtc->interlaced); - armada_reg_queue_end(work->regs, i); + work = armada_drm_crtc_alloc_plane_work(plane); + if (!work) { + ret = -ENOMEM; + goto put_state; + } + + /* Make sure we can get vblank interrupts */ + ret = drm_crtc_vblank_get(crtc); + if (ret) + goto put_work; /* - * Ensure that we hold a reference on the new framebuffer. - * This has to match the behaviour in mode_set. + * If we have another work pending, we can't process this flip. + * The modeset locks protect us from another user queuing a work + * while we're setting up. */ - drm_framebuffer_get(fb); - - ret = armada_drm_plane_work_queue(dcrtc, work); - if (ret) { - /* Undo our reference above */ - drm_framebuffer_put(fb); - kfree(work); - return ret; + if (drm_to_armada_plane(plane)->work) { + ret = -EBUSY; + goto put_vblank; } + work->event = event; + work->old_fb = plane->state->fb; + /* - * Don't take a reference on the new framebuffer; - * drm_mode_page_flip_ioctl() has already grabbed a reference and - * will _not_ drop that reference on successful return from this - * function. Simply mark this new framebuffer as the current one. + * Hold a ref on the new fb while it's being displayed by the + * hardware. The old fb refcount will be released in the worker. */ - dcrtc->crtc.primary->fb = fb; + drm_framebuffer_get(state->fb); + + /* Point of no return */ + swap(plane->state, state); + + dcrtc->regs_idx = 0; + dcrtc->regs = work->regs; + + plane_funcs = plane->helper_private; + plane_funcs->atomic_update(plane, state); + armada_reg_queue_end(dcrtc->regs, dcrtc->regs_idx); + + /* Queue the work - this should never fail */ + WARN_ON(armada_drm_plane_work_queue(dcrtc, work)); + work = NULL; /* * Finally, if the display is blanked, we won't receive an * interrupt, so complete it now. */ if (dpms_blanked(dcrtc->dpms)) - armada_drm_plane_work_run(dcrtc, dcrtc->crtc.primary); - - return 0; + armada_drm_plane_work_run(dcrtc, plane); + +put_vblank: + drm_crtc_vblank_put(crtc); +put_work: + kfree(work); +put_state: + drm_atomic_helper_plane_destroy_state(plane, state); + return ret; } static int @@ -1136,18 +1054,68 @@ static void armada_drm_crtc_disable_vblank(struct drm_crtc *crtc) } static const struct drm_crtc_funcs armada_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, .cursor_set = armada_drm_crtc_cursor_set, .cursor_move = armada_drm_crtc_cursor_move, .destroy = armada_drm_crtc_destroy, .set_config = drm_crtc_helper_set_config, .page_flip = armada_drm_crtc_page_flip, .set_property = armada_drm_crtc_set_property, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .enable_vblank = armada_drm_crtc_enable_vblank, .disable_vblank = armada_drm_crtc_disable_vblank, }; -static void armada_drm_primary_update_state(struct drm_plane_state *state, - struct armada_regs *regs) +int armada_drm_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *state) +{ + DRM_DEBUG_KMS("[PLANE:%d:%s] [FB:%d]\n", + plane->base.id, plane->name, + state->fb ? state->fb->base.id : 0); + + /* + * Take a reference on the new framebuffer - we want to + * hold on to it while the hardware is displaying it. + */ + if (state->fb) + drm_framebuffer_get(state->fb); + return 0; +} + +void armada_drm_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + DRM_DEBUG_KMS("[PLANE:%d:%s] [FB:%d]\n", + plane->base.id, plane->name, + old_state->fb ? old_state->fb->base.id : 0); + + if (old_state->fb) + drm_framebuffer_put(old_state->fb); +} + +int armada_drm_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + if (state->fb && !WARN_ON(!state->crtc)) { + struct drm_crtc *crtc = state->crtc; + struct drm_crtc_state *crtc_state; + + if (state->state) + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + else + crtc_state = crtc->state; + return drm_atomic_helper_check_plane_state(state, crtc_state, + 0, INT_MAX, + true, false); + } else { + state->visible = false; + } + return 0; +} + +static unsigned int armada_drm_primary_update_state( + struct drm_plane_state *state, struct armada_regs *regs) { struct armada_plane *dplane = drm_to_armada_plane(state->plane); struct armada_crtc *dcrtc = drm_to_armada_crtc(state->crtc); @@ -1163,6 +1131,8 @@ static void armada_drm_primary_update_state(struct drm_plane_state *state, val |= CFG_GRA_ENA; if (drm_rect_width(&state->src) >> 16 != drm_rect_width(&state->dst)) val |= CFG_GRA_HSMOOTH; + if (dcrtc->interlaced) + val |= CFG_GRA_FTOGGLE; was_disabled = !(dplane->state.ctrl0 & CFG_GRA_ENA); if (was_disabled) @@ -1170,163 +1140,103 @@ static void armada_drm_primary_update_state(struct drm_plane_state *state, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1); dplane->state.ctrl0 = val; - dplane->state.src_hw = (drm_rect_height(&state->src) & 0xffff0000) | - drm_rect_width(&state->src) >> 16; - dplane->state.dst_hw = drm_rect_height(&state->dst) << 16 | - drm_rect_width(&state->dst); - dplane->state.dst_yx = state->dst.y1 << 16 | state->dst.x1; - - armada_drm_gra_plane_regs(regs + idx, &dfb->fb, &dplane->state, - state->src.x1 >> 16, state->src.y1 >> 16, - dcrtc->interlaced); + dplane->state.src_hw = armada_rect_hw_fp(&state->src); + dplane->state.dst_hw = armada_rect_hw(&state->dst); + dplane->state.dst_yx = armada_rect_yx(&state->dst); + + idx += armada_drm_crtc_calc_fb(&dfb->fb, state->src.x1 >> 16, + state->src.y1 >> 16, regs + idx, + dcrtc->interlaced); + armada_reg_queue_set(regs, idx, dplane->state.dst_yx, + LCD_SPU_GRA_OVSA_HPXL_VLN); + armada_reg_queue_set(regs, idx, dplane->state.src_hw, + LCD_SPU_GRA_HPXL_VLN); + armada_reg_queue_set(regs, idx, dplane->state.dst_hw, + LCD_SPU_GZM_HPXL_VLN); + armada_reg_queue_mod(regs, idx, dplane->state.ctrl0, CFG_GRAFORMAT | + CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV | + CFG_SWAPYU | CFG_YUV2RGB) | + CFG_PALETTE_ENA | CFG_GRA_FTOGGLE | + CFG_GRA_HSMOOTH | CFG_GRA_ENA, + LCD_SPU_DMA_CTRL0); dplane->state.vsync_update = !was_disabled; dplane->state.changed = true; + + return idx; } -static int armada_drm_primary_update(struct drm_plane *plane, - struct drm_crtc *crtc, struct drm_framebuffer *fb, - int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h, - struct drm_modeset_acquire_ctx *ctx) +static void armada_drm_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) { - struct armada_plane *dplane = drm_to_armada_plane(plane); - struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - struct armada_plane_work *work; - struct drm_plane_state state = { - .plane = plane, - .crtc = crtc, - .fb = fb, - .src_x = src_x, - .src_y = src_y, - .src_w = src_w, - .src_h = src_h, - .crtc_x = crtc_x, - .crtc_y = crtc_y, - .crtc_w = crtc_w, - .crtc_h = crtc_h, - .rotation = DRM_MODE_ROTATE_0, - }; - struct drm_crtc_state crtc_state = { - .crtc = crtc, - .enable = crtc->enabled, - .mode = crtc->mode, - }; - int ret; - - ret = drm_atomic_helper_check_plane_state(&state, &crtc_state, 0, - INT_MAX, true, false); - if (ret) - return ret; - - work = &dplane->works[dplane->next_work]; - work->fn = armada_drm_crtc_complete_frame_work; - - if (plane->fb != fb) { - /* - * Take a reference on the new framebuffer - we want to - * hold on to it while the hardware is displaying it. - */ - drm_framebuffer_reference(fb); - - work->old_fb = plane->fb; - } else { - work->old_fb = NULL; - } - - armada_drm_primary_update_state(&state, work->regs); - - if (!dplane->state.changed) - return 0; + struct drm_plane_state *state = plane->state; + struct armada_crtc *dcrtc; + struct armada_regs *regs; - /* Wait for pending work to complete */ - if (armada_drm_plane_work_wait(dplane, HZ / 10) == 0) - armada_drm_plane_work_cancel(dcrtc, dplane); + DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name); - if (!dplane->state.vsync_update) { - work->fn(dcrtc, work); - if (work->old_fb) - drm_framebuffer_unreference(work->old_fb); - return 0; - } + if (!state->fb || WARN_ON(!state->crtc)) + return; - /* Queue it for update on the next interrupt if we are enabled */ - ret = armada_drm_plane_work_queue(dcrtc, work); - if (ret) { - work->fn(dcrtc, work); - if (work->old_fb) - drm_framebuffer_unreference(work->old_fb); - } + DRM_DEBUG_KMS("[PLANE:%d:%s] is on [CRTC:%d:%s] with [FB:%d] visible %u->%u\n", + plane->base.id, plane->name, + state->crtc->base.id, state->crtc->name, + state->fb->base.id, + old_state->visible, state->visible); - dplane->next_work = !dplane->next_work; + dcrtc = drm_to_armada_crtc(state->crtc); + regs = dcrtc->regs + dcrtc->regs_idx; - return 0; + dcrtc->regs_idx += armada_drm_primary_update_state(state, regs); } -int armada_drm_plane_disable(struct drm_plane *plane, - struct drm_modeset_acquire_ctx *ctx) +static void armada_drm_primary_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) { struct armada_plane *dplane = drm_to_armada_plane(plane); struct armada_crtc *dcrtc; - struct armada_plane_work *work; + struct armada_regs *regs; unsigned int idx = 0; - u32 sram_para1, enable_mask; - if (!plane->crtc) - return 0; + DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name); - /* - * Arrange to power down most RAMs and FIFOs if this is the primary - * plane, otherwise just the YUV FIFOs for the overlay plane. - */ - if (plane->type == DRM_PLANE_TYPE_PRIMARY) { - sram_para1 = CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 | - CFG_PDWN32x32 | CFG_PDWN64x66; - enable_mask = CFG_GRA_ENA; - } else { - sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66; - enable_mask = CFG_DMA_ENA; - } - - dplane->state.ctrl0 &= ~enable_mask; + if (!old_state->crtc) + return; - dcrtc = drm_to_armada_crtc(plane->crtc); + DRM_DEBUG_KMS("[PLANE:%d:%s] was on [CRTC:%d:%s] with [FB:%d]\n", + plane->base.id, plane->name, + old_state->crtc->base.id, old_state->crtc->name, + old_state->fb->base.id); - /* - * Try to disable the plane and drop our ref on the framebuffer - * at the next frame update. If we fail for any reason, disable - * the plane immediately. - */ - work = &dplane->works[dplane->next_work]; - work->fn = armada_drm_crtc_complete_disable_work; - work->cancel = armada_drm_crtc_complete_disable_work; - work->old_fb = plane->fb; - - armada_reg_queue_mod(work->regs, idx, - 0, enable_mask, LCD_SPU_DMA_CTRL0); - armada_reg_queue_mod(work->regs, idx, - sram_para1, 0, LCD_SPU_SRAM_PARA1); - armada_reg_queue_end(work->regs, idx); - - /* Wait for any preceding work to complete, but don't wedge */ - if (WARN_ON(!armada_drm_plane_work_wait(dplane, HZ))) - armada_drm_plane_work_cancel(dcrtc, dplane); + dplane->state.ctrl0 &= ~CFG_GRA_ENA; - if (armada_drm_plane_work_queue(dcrtc, work)) { - work->fn(dcrtc, work); - if (work->old_fb) - drm_framebuffer_unreference(work->old_fb); - } + dcrtc = drm_to_armada_crtc(old_state->crtc); + regs = dcrtc->regs + dcrtc->regs_idx; - dplane->next_work = !dplane->next_work; + /* Disable plane and power down most RAMs and FIFOs */ + armada_reg_queue_mod(regs, idx, 0, CFG_GRA_ENA, LCD_SPU_DMA_CTRL0); + armada_reg_queue_mod(regs, idx, CFG_PDWN256x32 | CFG_PDWN256x24 | + CFG_PDWN256x8 | CFG_PDWN32x32 | CFG_PDWN64x66, + 0, LCD_SPU_SRAM_PARA1); - return 0; + dcrtc->regs_idx += idx; } +static const struct drm_plane_helper_funcs armada_primary_plane_helper_funcs = { + .prepare_fb = armada_drm_plane_prepare_fb, + .cleanup_fb = armada_drm_plane_cleanup_fb, + .atomic_check = armada_drm_plane_atomic_check, + .atomic_update = armada_drm_primary_plane_atomic_update, + .atomic_disable = armada_drm_primary_plane_atomic_disable, +}; + static const struct drm_plane_funcs armada_primary_plane_funcs = { - .update_plane = armada_drm_primary_update, - .disable_plane = armada_drm_plane_disable, + .update_plane = drm_plane_helper_update, + .disable_plane = drm_plane_helper_disable, .destroy = drm_primary_helper_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; int armada_drm_plane_init(struct armada_plane *plane) @@ -1455,6 +1365,9 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, goto err_crtc; } + drm_plane_helper_add(&primary->base, + &armada_primary_plane_helper_funcs); + ret = drm_universal_plane_init(drm, &primary->base, 0, &armada_primary_plane_funcs, armada_primary_formats,