diff options
Diffstat (limited to 'drivers/gpu/drm/armada')
-rw-r--r-- | drivers/gpu/drm/armada/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_510.c | 24 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_crtc.c | 1008 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_crtc.h | 56 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_drm.h | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_drv.c | 54 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_fb.c | 30 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_fb.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_fbdev.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_gem.c | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_hw.h | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_overlay.c | 665 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_plane.c | 289 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_plane.h | 15 |
14 files changed, 926 insertions, 1273 deletions
diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile index ecf25cf9f9f5..9bc3c3213724 100644 --- a/drivers/gpu/drm/armada/Makefile +++ b/drivers/gpu/drm/armada/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 armada-y := armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \ - armada_gem.o armada_overlay.o armada_trace.o + armada_gem.o armada_overlay.o armada_plane.o armada_trace.o armada-y += armada_510.o armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c index 41a784f5a5e6..2f7c048c5361 100644 --- a/drivers/gpu/drm/armada/armada_510.c +++ b/drivers/gpu/drm/armada/armada_510.c @@ -27,6 +27,10 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev) /* Lower the watermark so to eliminate jitter at higher bandwidths */ armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F); + /* Initialise SPU register */ + writel_relaxed(ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, + dcrtc->base + LCD_SPU_ADV_REG); + return 0; } @@ -75,9 +79,27 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, return 0; } +static void armada510_crtc_disable(struct armada_crtc *dcrtc) +{ + if (!IS_ERR(dcrtc->clk)) { + clk_disable_unprepare(dcrtc->clk); + dcrtc->clk = ERR_PTR(-EINVAL); + } +} + +static void armada510_crtc_enable(struct armada_crtc *dcrtc, + const struct drm_display_mode *mode) +{ + if (IS_ERR(dcrtc->clk)) { + dcrtc->clk = dcrtc->extclk[0]; + WARN_ON(clk_prepare_enable(dcrtc->clk)); + } +} + const struct armada_variant armada510_ops = { .has_spu_adv_reg = true, - .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, .init = armada510_crtc_init, .compute_clock = armada510_crtc_compute_clock, + .disable = armada510_crtc_disable, + .enable = armada510_crtc_enable, }; diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 42a40daff132..da9360688b55 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -11,6 +11,7 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_plane_helper.h> #include <drm/drm_atomic_helper.h> @@ -19,33 +20,9 @@ #include "armada_fb.h" #include "armada_gem.h" #include "armada_hw.h" +#include "armada_plane.h" #include "armada_trace.h" -enum csc_mode { - CSC_AUTO = 0, - CSC_YUV_CCIR601 = 1, - CSC_YUV_CCIR709 = 2, - CSC_RGB_COMPUTER = 1, - CSC_RGB_STUDIO = 2, -}; - -static const uint32_t armada_primary_formats[] = { - DRM_FORMAT_UYVY, - DRM_FORMAT_YUYV, - DRM_FORMAT_VYUY, - DRM_FORMAT_YVYU, - DRM_FORMAT_ARGB8888, - DRM_FORMAT_ABGR8888, - DRM_FORMAT_XRGB8888, - DRM_FORMAT_XBGR8888, - DRM_FORMAT_RGB888, - DRM_FORMAT_BGR888, - DRM_FORMAT_ARGB1555, - DRM_FORMAT_ABGR1555, - DRM_FORMAT_RGB565, - DRM_FORMAT_BGR565, -}; - /* * A note about interlacing. Let's consider HDMI 1920x1080i. * The timing parameters we have from X are: @@ -115,15 +92,13 @@ armada_drm_crtc_update_regs(struct armada_crtc *dcrtc, struct armada_regs *regs) } } -#define dpms_blanked(dpms) ((dpms) != DRM_MODE_DPMS_ON) - -static void armada_drm_crtc_update(struct armada_crtc *dcrtc) +static void armada_drm_crtc_update(struct armada_crtc *dcrtc, bool enable) { uint32_t dumb_ctrl; dumb_ctrl = dcrtc->cfg_dumb_ctrl; - if (!dpms_blanked(dcrtc->dpms)) + if (enable) dumb_ctrl |= CFG_DUMB_ENA; /* @@ -132,295 +107,26 @@ static void armada_drm_crtc_update(struct armada_crtc *dcrtc) * force LCD_D[23:0] to output blank color, overriding the GPIO or * SPI usage. So leave it as-is unless in DUMB24_RGB888_0 mode. */ - if (dpms_blanked(dcrtc->dpms) && - (dumb_ctrl & DUMB_MASK) == DUMB24_RGB888_0) { + if (!enable && (dumb_ctrl & DUMB_MASK) == DUMB24_RGB888_0) { dumb_ctrl &= ~DUMB_MASK; dumb_ctrl |= DUMB_BLANK; } - /* - * The documentation doesn't indicate what the normal state of - * the sync signals are. Sebastian Hesselbart kindly probed - * these signals on his board to determine their state. - * - * The non-inverted state of the sync signals is active high. - * Setting these bits makes the appropriate signal active low. - */ - if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NCSYNC) - dumb_ctrl |= CFG_INV_CSYNC; - if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NHSYNC) - dumb_ctrl |= CFG_INV_HSYNC; - if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NVSYNC) - dumb_ctrl |= CFG_INV_VSYNC; - - if (dcrtc->dumb_ctrl != dumb_ctrl) { - dcrtc->dumb_ctrl = dumb_ctrl; - writel_relaxed(dumb_ctrl, dcrtc->base + LCD_SPU_DUMB_CTRL); - } -} - -void armada_drm_plane_calc_addrs(u32 *addrs, struct drm_framebuffer *fb, - int x, int y) -{ - const struct drm_format_info *format = fb->format; - unsigned int num_planes = format->num_planes; - u32 addr = drm_fb_obj(fb)->dev_addr; - int i; - - if (num_planes > 3) - num_planes = 3; - - addrs[0] = addr + fb->offsets[0] + y * fb->pitches[0] + - x * format->cpp[0]; - - y /= format->vsub; - x /= format->hsub; - - for (i = 1; i < num_planes; i++) - addrs[i] = addr + fb->offsets[i] + y * fb->pitches[i] + - x * format->cpp[i]; - for (; i < 3; i++) - addrs[i] = 0; -} - -static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb, - int x, int y, struct armada_regs *regs, bool interlaced) -{ - unsigned pitch = fb->pitches[0]; - u32 addrs[3], addr_odd, addr_even; - unsigned i = 0; - - DRM_DEBUG_DRIVER("pitch %u x %d y %d bpp %d\n", - pitch, x, y, fb->format->cpp[0] * 8); - - armada_drm_plane_calc_addrs(addrs, fb, x, y); - - addr_odd = addr_even = addrs[0]; - - if (interlaced) { - addr_even += pitch; - pitch *= 2; - } - - /* write offset, base, and pitch */ - armada_reg_queue_set(regs, i, addr_odd, LCD_CFG_GRA_START_ADDR0); - armada_reg_queue_set(regs, i, addr_even, LCD_CFG_GRA_START_ADDR1); - armada_reg_queue_mod(regs, i, pitch, 0xffff, LCD_CFG_GRA_PITCH); - - return i; -} - -static void armada_drm_plane_work_call(struct armada_crtc *dcrtc, - struct armada_plane_work *work, - void (*fn)(struct armada_crtc *, struct armada_plane_work *)) -{ - struct armada_plane *dplane = drm_to_armada_plane(work->plane); - struct drm_pending_vblank_event *event; - struct drm_framebuffer *fb; - - if (fn) - fn(dcrtc, work); - drm_crtc_vblank_put(&dcrtc->crtc); - - event = work->event; - fb = work->old_fb; - if (event || fb) { - struct drm_device *dev = dcrtc->crtc.dev; - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - if (event) - drm_crtc_send_vblank_event(&dcrtc->crtc, event); - if (fb) - __armada_drm_queue_unref_work(dev, fb); - spin_unlock_irqrestore(&dev->event_lock, flags); - } - - if (work->need_kfree) - kfree(work); - - wake_up(&dplane->frame_wait); -} - -static void armada_drm_plane_work_run(struct armada_crtc *dcrtc, - struct drm_plane *plane) -{ - struct armada_plane *dplane = drm_to_armada_plane(plane); - struct armada_plane_work *work = xchg(&dplane->work, NULL); - - /* Handle any pending frame work. */ - if (work) - armada_drm_plane_work_call(dcrtc, work, work->fn); -} - -int armada_drm_plane_work_queue(struct armada_crtc *dcrtc, - struct armada_plane_work *work) -{ - struct armada_plane *plane = drm_to_armada_plane(work->plane); - int ret; - - ret = drm_crtc_vblank_get(&dcrtc->crtc); - if (ret) - return ret; - - ret = cmpxchg(&plane->work, NULL, work) ? -EBUSY : 0; - if (ret) - drm_crtc_vblank_put(&dcrtc->crtc); - - return ret; -} - -int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout) -{ - return wait_event_timeout(plane->frame_wait, !plane->work, timeout); -} - -void armada_drm_plane_work_cancel(struct armada_crtc *dcrtc, - struct armada_plane *dplane) -{ - struct armada_plane_work *work = xchg(&dplane->work, NULL); - - if (work) - armada_drm_plane_work_call(dcrtc, work, work->cancel); -} - -static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc, - struct armada_plane_work *work) -{ - unsigned long flags; - - spin_lock_irqsave(&dcrtc->irq_lock, flags); - armada_drm_crtc_update_regs(dcrtc, work->regs); - 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) -{ - struct armada_plane_work *work; - int i = 0; - - work = kzalloc(sizeof(*work), GFP_KERNEL); - if (!work) - return NULL; - - work->plane = plane; - work->fn = armada_drm_crtc_complete_frame_work; - work->need_kfree = true; - armada_reg_queue_end(work->regs, i); - - 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); + armada_updatel(dumb_ctrl, + ~(CFG_INV_CSYNC | CFG_INV_HSYNC | CFG_INV_VSYNC), + dcrtc->base + LCD_SPU_DUMB_CTRL); } -static void armada_drm_vblank_off(struct armada_crtc *dcrtc) -{ - /* - * Tell the DRM core that vblank IRQs aren't going to happen for - * a while. This cleans up any pending vblank events for us. - */ - drm_crtc_vblank_off(&dcrtc->crtc); - armada_drm_plane_work_run(dcrtc, dcrtc->crtc.primary); -} - -/* The mode_config.mutex will be held for this call */ -static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms) -{ - struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - - if (dpms_blanked(dcrtc->dpms) != dpms_blanked(dpms)) { - if (dpms_blanked(dpms)) - armada_drm_vblank_off(dcrtc); - else if (!IS_ERR(dcrtc->clk)) - WARN_ON(clk_prepare_enable(dcrtc->clk)); - dcrtc->dpms = dpms; - armada_drm_crtc_update(dcrtc); - if (!dpms_blanked(dpms)) - drm_crtc_vblank_on(&dcrtc->crtc); - else if (!IS_ERR(dcrtc->clk)) - clk_disable_unprepare(dcrtc->clk); - } else if (dcrtc->dpms != dpms) { - dcrtc->dpms = dpms; - } -} - -/* - * Prepare for a mode set. Turn off overlay to ensure that we don't end - * up with the overlay size being bigger than the active screen size. - * We rely upon X refreshing this state after the mode set has completed. - * - * The mode_config.mutex will be held for this call - */ -static void armada_drm_crtc_prepare(struct drm_crtc *crtc) -{ - struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - struct drm_plane *plane; - - /* - * If we have an overlay plane associated with this CRTC, disable - * it before the modeset to avoid its coordinates being outside - * the new mode parameters. - */ - plane = dcrtc->plane; - if (plane) { - drm_plane_force_disable(plane); - WARN_ON(!armada_drm_plane_work_wait(drm_to_armada_plane(plane), - HZ)); - } -} - -/* The mode_config.mutex will be held for this call */ -static void armada_drm_crtc_commit(struct drm_crtc *crtc) +static void armada_drm_crtc_queue_state_event(struct drm_crtc *crtc) { struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); + struct drm_pending_vblank_event *event; - if (dcrtc->dpms != DRM_MODE_DPMS_ON) { - dcrtc->dpms = DRM_MODE_DPMS_ON; - armada_drm_crtc_update(dcrtc); + /* If we have an event, we need vblank events enabled */ + event = xchg(&crtc->state->event, NULL); + if (event) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + dcrtc->event = event; } } @@ -465,8 +171,8 @@ static void armada_drm_crtc_enable_irq(struct armada_crtc *dcrtc, u32 mask) static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) { + struct drm_pending_vblank_event *event; void __iomem *base = dcrtc->base; - struct drm_plane *ovl_plane; if (stat & DMA_FF_UNDERFLOW) DRM_ERROR("video underflow on crtc %u\n", dcrtc->num); @@ -476,10 +182,6 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) if (stat & VSYNC_IRQ) drm_crtc_handle_vblank(&dcrtc->crtc); - ovl_plane = dcrtc->plane; - if (ovl_plane) - armada_drm_plane_work_run(dcrtc, ovl_plane); - spin_lock(&dcrtc->irq_lock); if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) { int i = stat & GRA_FRAME_IRQ0 ? 0 : 1; @@ -495,22 +197,35 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) writel_relaxed(val, base + LCD_SPU_ADV_REG); } - if (stat & DUMB_FRAMEDONE && dcrtc->cursor_update) { - writel_relaxed(dcrtc->cursor_hw_pos, - base + LCD_SPU_HWC_OVSA_HPXL_VLN); - writel_relaxed(dcrtc->cursor_hw_sz, - base + LCD_SPU_HWC_HPXL_VLN); - armada_updatel(CFG_HWC_ENA, - CFG_HWC_ENA | CFG_HWC_1BITMOD | CFG_HWC_1BITENA, - base + LCD_SPU_DMA_CTRL0); - dcrtc->cursor_update = false; + if (stat & dcrtc->irq_ena & DUMB_FRAMEDONE) { + if (dcrtc->update_pending) { + armada_drm_crtc_update_regs(dcrtc, dcrtc->regs); + dcrtc->update_pending = false; + } + if (dcrtc->cursor_update) { + writel_relaxed(dcrtc->cursor_hw_pos, + base + LCD_SPU_HWC_OVSA_HPXL_VLN); + writel_relaxed(dcrtc->cursor_hw_sz, + base + LCD_SPU_HWC_HPXL_VLN); + armada_updatel(CFG_HWC_ENA, + CFG_HWC_ENA | CFG_HWC_1BITMOD | + CFG_HWC_1BITENA, + base + LCD_SPU_DMA_CTRL0); + dcrtc->cursor_update = false; + } armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA); } - spin_unlock(&dcrtc->irq_lock); - if (stat & GRA_FRAME_IRQ) - armada_drm_plane_work_run(dcrtc, dcrtc->crtc.primary); + if (stat & VSYNC_IRQ && !dcrtc->update_pending) { + event = xchg(&dcrtc->event, NULL); + if (event) { + spin_lock(&dcrtc->crtc.dev->event_lock); + drm_crtc_send_vblank_event(&dcrtc->crtc, event); + spin_unlock(&dcrtc->crtc.dev->event_lock); + drm_crtc_vblank_put(&dcrtc->crtc); + } + } } static irqreturn_t armada_drm_irq(int irq, void *arg) @@ -537,107 +252,16 @@ static irqreturn_t armada_drm_irq(int irq, void *arg) return IRQ_NONE; } -static uint32_t armada_drm_crtc_calculate_csc(struct armada_crtc *dcrtc) -{ - struct drm_display_mode *adj = &dcrtc->crtc.mode; - uint32_t val = 0; - - if (dcrtc->csc_yuv_mode == CSC_YUV_CCIR709) - val |= CFG_CSC_YUV_CCIR709; - if (dcrtc->csc_rgb_mode == CSC_RGB_STUDIO) - val |= CFG_CSC_RGB_STUDIO; - - /* - * In auto mode, set the colorimetry, based upon the HDMI spec. - * 1280x720p, 1920x1080p and 1920x1080i use ITU709, others use - * ITU601. It may be more appropriate to set this depending on - * the source - but what if the graphic frame is YUV and the - * video frame is RGB? - */ - if ((adj->hdisplay == 1280 && adj->vdisplay == 720 && - !(adj->flags & DRM_MODE_FLAG_INTERLACE)) || - (adj->hdisplay == 1920 && adj->vdisplay == 1080)) { - if (dcrtc->csc_yuv_mode == CSC_AUTO) - val |= CFG_CSC_YUV_CCIR709; - } - - /* - * We assume we're connected to a TV-like device, so the YUV->RGB - * conversion should produce a limited range. We should set this - * depending on the connectors attached to this CRTC, and what - * kind of device they report being connected. - */ - if (dcrtc->csc_rgb_mode == CSC_AUTO) - val |= CFG_CSC_RGB_STUDIO; - - 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; @@ -645,35 +269,15 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, bm = adj->crtc_vsync_start - adj->crtc_vdisplay; tm = adj->crtc_vtotal - adj->crtc_vsync_end; - DRM_DEBUG_DRIVER("H: %d %d %d %d lm %d rm %d\n", - adj->crtc_hdisplay, - adj->crtc_hsync_start, - adj->crtc_hsync_end, - adj->crtc_htotal, lm, rm); - DRM_DEBUG_DRIVER("V: %d %d %d %d tm %d bm %d\n", - adj->crtc_vdisplay, - adj->crtc_vsync_start, - 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. - */ - if (!IS_ERR(dcrtc->clk) && dpms_blanked(dcrtc->dpms)) - WARN_ON(clk_prepare_enable(dcrtc->clk)); + DRM_DEBUG_KMS("[CRTC:%d:%s] mode " DRM_MODE_FMT "\n", + crtc->base.id, crtc->name, + adj->base.id, adj->name, adj->vrefresh, adj->clock, + adj->crtc_hdisplay, adj->crtc_hsync_start, + adj->crtc_hsync_end, adj->crtc_htotal, + adj->crtc_vdisplay, adj->crtc_vsync_start, + adj->crtc_vsync_end, adj->crtc_vtotal, + adj->type, adj->flags); + DRM_DEBUG_KMS("lm %d rm %d tm %d bm %d\n", lm, rm, tm, bm); /* Now compute the divider for real */ dcrtc->variant->compute_clock(dcrtc, adj, &sclk); @@ -690,25 +294,20 @@ 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; dcrtc->v[1].spu_v_porch = tm << 16 | bm; val = adj->crtc_hsync_start; - dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | - dcrtc->variant->spu_adv_reg; + dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN; if (interlaced) { /* Odd interlaced frame */ + val -= adj->crtc_htotal / 2; + dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN; dcrtc->v[0].spu_v_h_total = dcrtc->v[1].spu_v_h_total + (1 << 16); dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1; - val = adj->crtc_hsync_start - adj->crtc_htotal / 2; - dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | - dcrtc->variant->spu_adv_reg; } else { dcrtc->v[0] = dcrtc->v[1]; } @@ -721,77 +320,136 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total, LCD_SPUT_V_H_TOTAL); - if (dcrtc->variant->has_spu_adv_reg) { + if (dcrtc->variant->has_spu_adv_reg) armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg, ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN, LCD_SPU_ADV_REG); - } val = adj->flags & DRM_MODE_FLAG_NVSYNC ? CFG_VSYNC_INV : 0; armada_reg_queue_mod(regs, i, val, CFG_VSYNC_INV, LCD_SPU_DMA_CTRL1); - val = dcrtc->spu_iopad_ctrl | armada_drm_crtc_calculate_csc(dcrtc); - armada_reg_queue_set(regs, i, val, LCD_SPU_IOPAD_CONTROL); + /* + * The documentation doesn't indicate what the normal state of + * the sync signals are. Sebastian Hesselbart kindly probed + * these signals on his board to determine their state. + * + * The non-inverted state of the sync signals is active high. + * Setting these bits makes the appropriate signal active low. + */ + val = 0; + if (adj->flags & DRM_MODE_FLAG_NCSYNC) + val |= CFG_INV_CSYNC; + if (adj->flags & DRM_MODE_FLAG_NHSYNC) + val |= CFG_INV_HSYNC; + if (adj->flags & DRM_MODE_FLAG_NVSYNC) + val |= CFG_INV_VSYNC; + armada_reg_queue_mod(regs, i, val, CFG_INV_CSYNC | CFG_INV_HSYNC | + CFG_INV_VSYNC, LCD_SPU_DUMB_CTRL); 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); +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); - drm_crtc_vblank_on(crtc); - armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms)); + DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); - return 0; + dcrtc->regs_idx = 0; + dcrtc->regs = dcrtc->atomic_regs; } -/* 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_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) { 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); + DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); - /* Wait for pending flips to complete */ - armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary), - MAX_SCHEDULE_TIMEOUT); + armada_reg_queue_end(dcrtc->regs, dcrtc->regs_idx); - /* Take a reference to the new fb as we're using it */ - drm_framebuffer_get(crtc->primary->fb); + /* + * If we aren't doing a full modeset, then we need to queue + * the event here. + */ + if (!drm_atomic_crtc_needs_modeset(crtc->state)) { + dcrtc->update_pending = true; + armada_drm_crtc_queue_state_event(crtc); + spin_lock_irq(&dcrtc->irq_lock); + armada_drm_crtc_enable_irq(dcrtc, DUMB_FRAMEDONE_ENA); + spin_unlock_irq(&dcrtc->irq_lock); + } else { + spin_lock_irq(&dcrtc->irq_lock); + armada_drm_crtc_update_regs(dcrtc, dcrtc->regs); + spin_unlock_irq(&dcrtc->irq_lock); + } +} - /* Update the base in the CRTC */ - armada_drm_crtc_update_regs(dcrtc, regs); +static void armada_drm_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) +{ + struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); + struct drm_pending_vblank_event *event; - /* Drop our previously held reference */ - armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms)); + DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); - return 0; + drm_crtc_vblank_off(crtc); + armada_drm_crtc_update(dcrtc, false); + + if (!crtc->state->active) { + /* + * This modeset will be leaving the CRTC disabled, so + * call the backend to disable upstream clocks etc. + */ + if (dcrtc->variant->disable) + dcrtc->variant->disable(dcrtc); + + /* + * We will not receive any further vblank events. + * Send the flip_done event manually. + */ + event = crtc->state->event; + crtc->state->event = NULL; + if (event) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } + } } -/* 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_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) { - armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - /* 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); + + if (!old_state->active) { + /* + * This modeset is enabling the CRTC after it having + * been disabled. Reverse the call to ->disable in + * the atomic_disable(). + */ + if (dcrtc->variant->enable) + dcrtc->variant->enable(dcrtc, &crtc->state->adjusted_mode); + } + armada_drm_crtc_update(dcrtc, true); + drm_crtc_vblank_on(crtc); + + armada_drm_crtc_queue_state_event(crtc); } static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = { - .dpms = armada_drm_crtc_dpms, - .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, - .disable = armada_drm_crtc_disable, + .mode_set_nofb = armada_drm_crtc_mode_set_nofb, + .atomic_begin = armada_drm_crtc_atomic_begin, + .atomic_flush = armada_drm_crtc_atomic_flush, + .atomic_disable = armada_drm_crtc_atomic_disable, + .atomic_enable = armada_drm_crtc_atomic_enable, }; static void armada_load_cursor_argb(void __iomem *base, uint32_t *pix, @@ -884,7 +542,6 @@ static int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload) if (!dcrtc->cursor_obj || !h || !w) { spin_lock_irq(&dcrtc->irq_lock); - armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA); dcrtc->cursor_update = false; armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0); spin_unlock_irq(&dcrtc->irq_lock); @@ -908,7 +565,6 @@ static int armada_drm_crtc_cursor_update(struct armada_crtc *dcrtc, bool reload) if (dcrtc->cursor_hw_sz != (h << 16 | w)) { spin_lock_irq(&dcrtc->irq_lock); - armada_drm_crtc_disable_irq(dcrtc, DUMB_FRAMEDONE_ENA); dcrtc->cursor_update = false; armada_updatel(0, CFG_HWC_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0); spin_unlock_irq(&dcrtc->irq_lock); @@ -1016,8 +672,8 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) priv->dcrtc[dcrtc->num] = NULL; drm_crtc_cleanup(&dcrtc->crtc); - if (!IS_ERR(dcrtc->clk)) - clk_disable_unprepare(dcrtc->clk); + if (dcrtc->variant->disable) + dcrtc->variant->disable(dcrtc); writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA); @@ -1026,93 +682,6 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) kfree(dcrtc); } -/* - * The mode_config lock is held here, to prevent races between this - * 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 armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - 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) - return -ENOMEM; - - work->event = event; - work->old_fb = dcrtc->crtc.primary->fb; - - i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs, - dcrtc->interlaced); - armada_reg_queue_end(work->regs, i); - - /* - * Ensure that we hold a reference on the new framebuffer. - * This has to match the behaviour in mode_set. - */ - 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; - } - - /* - * 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. - */ - dcrtc->crtc.primary->fb = fb; - - /* - * 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; -} - -static int -armada_drm_crtc_set_property(struct drm_crtc *crtc, - struct drm_property *property, uint64_t val) -{ - struct armada_private *priv = crtc->dev->dev_private; - struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - bool update_csc = false; - - if (property == priv->csc_yuv_prop) { - dcrtc->csc_yuv_mode = val; - update_csc = true; - } else if (property == priv->csc_rgb_prop) { - dcrtc->csc_rgb_mode = val; - update_csc = true; - } - - if (update_csc) { - uint32_t val; - - val = dcrtc->spu_iopad_ctrl | - armada_drm_crtc_calculate_csc(dcrtc); - writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL); - } - - return 0; -} - /* These are called under the vbl_lock. */ static int armada_drm_crtc_enable_vblank(struct drm_crtc *crtc) { @@ -1136,257 +705,28 @@ 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, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .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) -{ - struct armada_plane *dplane = drm_to_armada_plane(state->plane); - struct armada_crtc *dcrtc = drm_to_armada_crtc(state->crtc); - struct armada_framebuffer *dfb = drm_fb_to_armada_fb(state->fb); - bool was_disabled; - unsigned int idx = 0; - u32 val; - - val = CFG_GRA_FMT(dfb->fmt) | CFG_GRA_MOD(dfb->mod); - if (dfb->fmt > CFG_420) - val |= CFG_PALETTE_ENA; - if (state->visible) - val |= CFG_GRA_ENA; - if (drm_rect_width(&state->src) >> 16 != drm_rect_width(&state->dst)) - val |= CFG_GRA_HSMOOTH; - - was_disabled = !(dplane->state.ctrl0 & CFG_GRA_ENA); - if (was_disabled) - armada_reg_queue_mod(regs, idx, - 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.vsync_update = !was_disabled; - dplane->state.changed = true; -} - -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) -{ - 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; - - /* Wait for pending work to complete */ - if (armada_drm_plane_work_wait(dplane, HZ / 10) == 0) - armada_drm_plane_work_cancel(dcrtc, dplane); - - if (!dplane->state.vsync_update) { - work->fn(dcrtc, work); - if (work->old_fb) - drm_framebuffer_unreference(work->old_fb); - return 0; - } - - /* 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); - } - - dplane->next_work = !dplane->next_work; - - return 0; -} - -int armada_drm_plane_disable(struct drm_plane *plane, - struct drm_modeset_acquire_ctx *ctx) -{ - struct armada_plane *dplane = drm_to_armada_plane(plane); - struct armada_crtc *dcrtc; - struct armada_plane_work *work; - unsigned int idx = 0; - u32 sram_para1, enable_mask; - - if (!plane->crtc) - return 0; - - /* - * 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; - - dcrtc = drm_to_armada_crtc(plane->crtc); - - /* - * 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); - - if (armada_drm_plane_work_queue(dcrtc, work)) { - work->fn(dcrtc, work); - if (work->old_fb) - drm_framebuffer_unreference(work->old_fb); - } - - dplane->next_work = !dplane->next_work; - - return 0; -} - -static const struct drm_plane_funcs armada_primary_plane_funcs = { - .update_plane = armada_drm_primary_update, - .disable_plane = armada_drm_plane_disable, - .destroy = drm_primary_helper_destroy, -}; - -int armada_drm_plane_init(struct armada_plane *plane) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(plane->works); i++) - plane->works[i].plane = &plane->base; - - init_waitqueue_head(&plane->frame_wait); - - return 0; -} - -static const struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = { - { CSC_AUTO, "Auto" }, - { CSC_YUV_CCIR601, "CCIR601" }, - { CSC_YUV_CCIR709, "CCIR709" }, -}; - -static const struct drm_prop_enum_list armada_drm_csc_rgb_enum_list[] = { - { CSC_AUTO, "Auto" }, - { CSC_RGB_COMPUTER, "Computer system" }, - { CSC_RGB_STUDIO, "Studio" }, -}; - -static int armada_drm_crtc_create_properties(struct drm_device *dev) -{ - struct armada_private *priv = dev->dev_private; - - if (priv->csc_yuv_prop) - return 0; - - priv->csc_yuv_prop = drm_property_create_enum(dev, 0, - "CSC_YUV", armada_drm_csc_yuv_enum_list, - ARRAY_SIZE(armada_drm_csc_yuv_enum_list)); - priv->csc_rgb_prop = drm_property_create_enum(dev, 0, - "CSC_RGB", armada_drm_csc_rgb_enum_list, - ARRAY_SIZE(armada_drm_csc_rgb_enum_list)); - - if (!priv->csc_yuv_prop || !priv->csc_rgb_prop) - return -ENOMEM; - - return 0; -} - static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, struct resource *res, int irq, const struct armada_variant *variant, struct device_node *port) { struct armada_private *priv = drm->dev_private; struct armada_crtc *dcrtc; - struct armada_plane *primary; + struct drm_plane *primary; void __iomem *base; int ret; - ret = armada_drm_crtc_create_properties(drm); - if (ret) - return ret; - base = devm_ioremap_resource(dev, res); if (IS_ERR(base)) return PTR_ERR(base); @@ -1404,8 +744,6 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, dcrtc->base = base; dcrtc->num = drm->mode_config.num_crtc; dcrtc->clk = ERR_PTR(-EINVAL); - dcrtc->csc_yuv_mode = CSC_AUTO; - dcrtc->csc_rgb_mode = CSC_AUTO; dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0; dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24; spin_lock_init(&dcrtc->irq_lock); @@ -1449,39 +787,23 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, goto err_crtc; } - ret = armada_drm_plane_init(primary); - if (ret) { - kfree(primary); - goto err_crtc; - } - - ret = drm_universal_plane_init(drm, &primary->base, 0, - &armada_primary_plane_funcs, - armada_primary_formats, - ARRAY_SIZE(armada_primary_formats), - NULL, - DRM_PLANE_TYPE_PRIMARY, NULL); + ret = armada_drm_primary_plane_init(drm, primary); if (ret) { kfree(primary); goto err_crtc; } - ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, &primary->base, NULL, + ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, primary, NULL, &armada_crtc_funcs, NULL); if (ret) goto err_crtc_init; drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); - drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop, - dcrtc->csc_yuv_mode); - drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop, - dcrtc->csc_rgb_mode); - return armada_overlay_plane_create(drm, 1 << dcrtc->num); err_crtc_init: - primary->base.funcs->destroy(&primary->base); + primary->funcs->destroy(primary); err_crtc: kfree(dcrtc); diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 445829b8877a..7ebd337b60af 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -32,49 +32,8 @@ struct armada_regs { armada_reg_queue_mod(_r, _i, 0, 0, ~0) struct armada_crtc; -struct armada_plane; struct armada_variant; -struct armada_plane_work { - void (*fn)(struct armada_crtc *, struct armada_plane_work *); - void (*cancel)(struct armada_crtc *, struct armada_plane_work *); - bool need_kfree; - struct drm_plane *plane; - struct drm_framebuffer *old_fb; - struct drm_pending_vblank_event *event; - struct armada_regs regs[14]; -}; - -struct armada_plane_state { - u16 src_x; - u16 src_y; - u32 src_hw; - u32 dst_hw; - u32 dst_yx; - u32 ctrl0; - bool changed; - bool vsync_update; -}; - -struct armada_plane { - struct drm_plane base; - wait_queue_head_t frame_wait; - bool next_work; - struct armada_plane_work works[2]; - struct armada_plane_work *work; - struct armada_plane_state state; -}; -#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base) - -int armada_drm_plane_init(struct armada_plane *plane); -int armada_drm_plane_work_queue(struct armada_crtc *dcrtc, - struct armada_plane_work *work); -int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout); -void armada_drm_plane_work_cancel(struct armada_crtc *dcrtc, - struct armada_plane *plane); -void armada_drm_plane_calc_addrs(u32 *addrs, struct drm_framebuffer *fb, - int x, int y); - struct armada_crtc { struct drm_crtc crtc; const struct armada_variant *variant; @@ -89,10 +48,6 @@ struct armada_crtc { } v[2]; bool interlaced; bool cursor_update; - uint8_t csc_yuv_mode; - uint8_t csc_rgb_mode; - - struct drm_plane *plane; struct armada_gem_object *cursor_obj; int cursor_x; @@ -102,21 +57,22 @@ struct armada_crtc { uint32_t cursor_w; uint32_t cursor_h; - int dpms; uint32_t cfg_dumb_ctrl; - uint32_t dumb_ctrl; uint32_t spu_iopad_ctrl; spinlock_t irq_lock; uint32_t irq_ena; + + bool update_pending; + struct drm_pending_vblank_event *event; + struct armada_regs atomic_regs[32]; + struct armada_regs *regs; + unsigned int regs_idx; }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); -int armada_drm_plane_disable(struct drm_plane *plane, - struct drm_modeset_acquire_ctx *ctx); - extern struct platform_driver armada_lcd_platform_driver; #endif diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h index cc4c557c9f66..f09083ff15d3 100644 --- a/drivers/gpu/drm/armada/armada_drm.h +++ b/drivers/gpu/drm/armada/armada_drm.h @@ -42,11 +42,12 @@ struct armada_private; struct armada_variant { bool has_spu_adv_reg; - uint32_t spu_adv_reg; int (*init)(struct armada_crtc *, struct device *); int (*compute_clock)(struct armada_crtc *, const struct drm_display_mode *, uint32_t *); + void (*disable)(struct armada_crtc *); + void (*enable)(struct armada_crtc *, const struct drm_display_mode *); }; /* Variant ops */ @@ -54,14 +55,10 @@ extern const struct armada_variant armada510_ops; struct armada_private { struct drm_device drm; - struct work_struct fb_unref_work; - DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8); struct drm_fb_helper *fbdev; struct armada_crtc *dcrtc[2]; struct drm_mm linear; /* protected by linear_lock */ struct mutex linear_lock; - struct drm_property *csc_yuv_prop; - struct drm_property *csc_rgb_prop; struct drm_property *colorkey_prop; struct drm_property *colorkey_min_prop; struct drm_property *colorkey_max_prop; @@ -76,13 +73,6 @@ struct armada_private { #endif }; -void __armada_drm_queue_unref_work(struct drm_device *, - struct drm_framebuffer *); -void armada_drm_queue_unref_work(struct drm_device *, - struct drm_framebuffer *); - -extern const struct drm_mode_config_funcs armada_drm_mode_config_funcs; - int armada_fbdev_init(struct drm_device *); void armada_fbdev_fini(struct drm_device *); diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 4b11b6b52f1d..fa31589b4fc0 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -9,46 +9,18 @@ #include <linux/component.h> #include <linux/module.h> #include <linux/of_graph.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_of.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_gem.h" +#include "armada_fb.h" #include "armada_hw.h" #include <drm/armada_drm.h> #include "armada_ioctlP.h" -static void armada_drm_unref_work(struct work_struct *work) -{ - struct armada_private *priv = - container_of(work, struct armada_private, fb_unref_work); - struct drm_framebuffer *fb; - - while (kfifo_get(&priv->fb_unref, &fb)) - drm_framebuffer_put(fb); -} - -/* Must be called with dev->event_lock held */ -void __armada_drm_queue_unref_work(struct drm_device *dev, - struct drm_framebuffer *fb) -{ - struct armada_private *priv = dev->dev_private; - - WARN_ON(!kfifo_put(&priv->fb_unref, fb)); - schedule_work(&priv->fb_unref_work); -} - -void armada_drm_queue_unref_work(struct drm_device *dev, - struct drm_framebuffer *fb) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - __armada_drm_queue_unref_work(dev, fb); - spin_unlock_irqrestore(&dev->event_lock, flags); -} - static struct drm_ioctl_desc armada_ioctls[] = { DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,0), DRM_IOCTL_DEF_DRV(ARMADA_GEM_MMAP, armada_gem_mmap_ioctl, 0), @@ -72,11 +44,18 @@ static struct drm_driver armada_drm_driver = { .desc = "Armada SoC DRM", .date = "20120730", .driver_features = DRIVER_GEM | DRIVER_MODESET | - DRIVER_PRIME, + DRIVER_PRIME | DRIVER_ATOMIC, .ioctls = armada_ioctls, .fops = &armada_drm_fops, }; +static const struct drm_mode_config_funcs armada_drm_mode_config_funcs = { + .fb_create = armada_fb_create, + .output_poll_changed = drm_fb_helper_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + static int armada_drm_bind(struct device *dev) { struct armada_private *priv; @@ -109,7 +88,7 @@ static int armada_drm_bind(struct device *dev) /* * The drm_device structure must be at the start of - * armada_private for drm_dev_unref() to work correctly. + * armada_private for drm_dev_put() to work correctly. */ BUILD_BUG_ON(offsetof(struct armada_private, drm) != 0); @@ -125,9 +104,6 @@ static int armada_drm_bind(struct device *dev) dev_set_drvdata(dev, &priv->drm); - INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); - INIT_KFIFO(priv->fb_unref); - /* Mode setting support */ drm_mode_config_init(&priv->drm); priv->drm.mode_config.min_width = 320; @@ -155,6 +131,8 @@ static int armada_drm_bind(struct device *dev) priv->drm.irq_enabled = true; + drm_mode_config_reset(&priv->drm); + ret = armada_fbdev_init(&priv->drm); if (ret) goto err_comp; @@ -179,8 +157,7 @@ static int armada_drm_bind(struct device *dev) err_kms: drm_mode_config_cleanup(&priv->drm); drm_mm_takedown(&priv->linear); - flush_work(&priv->fb_unref_work); - drm_dev_unref(&priv->drm); + drm_dev_put(&priv->drm); return ret; } @@ -198,9 +175,8 @@ static void armada_drm_unbind(struct device *dev) drm_mode_config_cleanup(&priv->drm); drm_mm_takedown(&priv->linear); - flush_work(&priv->fb_unref_work); - drm_dev_unref(&priv->drm); + drm_dev_put(&priv->drm); } static int compare_of(struct device *dev, void *data) diff --git a/drivers/gpu/drm/armada/armada_fb.c b/drivers/gpu/drm/armada/armada_fb.c index ac92bce07ecd..6bd638a54579 100644 --- a/drivers/gpu/drm/armada/armada_fb.c +++ b/drivers/gpu/drm/armada/armada_fb.c @@ -7,30 +7,15 @@ */ #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> #include "armada_drm.h" #include "armada_fb.h" #include "armada_gem.h" #include "armada_hw.h" -static void armada_fb_destroy(struct drm_framebuffer *fb) -{ - struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb); - - drm_framebuffer_cleanup(&dfb->fb); - drm_gem_object_put_unlocked(&dfb->obj->obj); - kfree(dfb); -} - -static int armada_fb_create_handle(struct drm_framebuffer *fb, - struct drm_file *dfile, unsigned int *handle) -{ - struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb); - return drm_gem_handle_create(dfile, &dfb->obj->obj, handle); -} - static const struct drm_framebuffer_funcs armada_fb_funcs = { - .destroy = armada_fb_destroy, - .create_handle = armada_fb_create_handle, + .destroy = drm_gem_fb_destroy, + .create_handle = drm_gem_fb_create_handle, }; struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev, @@ -78,7 +63,7 @@ struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev, dfb->fmt = format; dfb->mod = config; - dfb->obj = obj; + dfb->fb.obj[0] = &obj->obj; drm_helper_mode_fill_fb_struct(dev, &dfb->fb, mode); @@ -99,7 +84,7 @@ struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev, return dfb; } -static struct drm_framebuffer *armada_fb_create(struct drm_device *dev, +struct drm_framebuffer *armada_fb_create(struct drm_device *dev, struct drm_file *dfile, const struct drm_mode_fb_cmd2 *mode) { struct armada_gem_object *obj; @@ -153,8 +138,3 @@ static struct drm_framebuffer *armada_fb_create(struct drm_device *dev, DRM_ERROR("failed to initialize framebuffer: %d\n", ret); return ERR_PTR(ret); } - -const struct drm_mode_config_funcs armada_drm_mode_config_funcs = { - .fb_create = armada_fb_create, - .output_poll_changed = drm_fb_helper_output_poll_changed, -}; diff --git a/drivers/gpu/drm/armada/armada_fb.h b/drivers/gpu/drm/armada/armada_fb.h index 48073c4f54d8..476daad0a36a 100644 --- a/drivers/gpu/drm/armada/armada_fb.h +++ b/drivers/gpu/drm/armada/armada_fb.h @@ -10,15 +10,15 @@ struct armada_framebuffer { struct drm_framebuffer fb; - struct armada_gem_object *obj; uint8_t fmt; uint8_t mod; }; #define drm_fb_to_armada_fb(dfb) \ container_of(dfb, struct armada_framebuffer, fb) -#define drm_fb_obj(fb) drm_fb_to_armada_fb(fb)->obj +#define drm_fb_obj(fb) drm_to_armada_gem((fb)->obj[0]) struct armada_framebuffer *armada_framebuffer_create(struct drm_device *, const struct drm_mode_fb_cmd2 *, struct armada_gem_object *); - +struct drm_framebuffer *armada_fb_create(struct drm_device *dev, + struct drm_file *dfile, const struct drm_mode_fb_cmd2 *mode); #endif diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index 2a59db0994b2..8d23700848df 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -24,7 +24,7 @@ static /*const*/ struct fb_ops armada_fb_ops = { .fb_imageblit = drm_fb_helper_cfb_imageblit, }; -static int armada_fb_create(struct drm_fb_helper *fbh, +static int armada_fbdev_create(struct drm_fb_helper *fbh, struct drm_fb_helper_surface_size *sizes) { struct drm_device *dev = fbh->dev; @@ -108,7 +108,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh, int ret = 0; if (!fbh->fb) { - ret = armada_fb_create(fbh, sizes); + ret = armada_fbdev_create(fbh, sizes); if (ret == 0) ret = 1; } diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c index a97f509743a5..892c1d9304bb 100644 --- a/drivers/gpu/drm/armada/armada_gem.c +++ b/drivers/gpu/drm/armada/armada_gem.c @@ -13,25 +13,14 @@ #include <drm/armada_drm.h> #include "armada_ioctlP.h" -static int armada_gem_vm_fault(struct vm_fault *vmf) +static vm_fault_t armada_gem_vm_fault(struct vm_fault *vmf) { struct drm_gem_object *gobj = vmf->vma->vm_private_data; struct armada_gem_object *obj = drm_to_armada_gem(gobj); unsigned long pfn = obj->phys_addr >> PAGE_SHIFT; - int ret; pfn += (vmf->address - vmf->vma->vm_start) >> PAGE_SHIFT; - ret = vm_insert_pfn(vmf->vma, vmf->address, pfn); - - switch (ret) { - case 0: - case -EBUSY: - return VM_FAULT_NOPAGE; - case -ENOMEM: - return VM_FAULT_OOM; - default: - return VM_FAULT_SIGBUS; - } + return vmf_insert_pfn(vmf->vma, vmf->address, pfn); } const struct vm_operations_struct armada_gem_vm_ops = { @@ -490,8 +479,6 @@ static const struct dma_buf_ops armada_gem_prime_dmabuf_ops = { .map_dma_buf = armada_gem_prime_map_dma_buf, .unmap_dma_buf = armada_gem_prime_unmap_dma_buf, .release = drm_gem_dmabuf_release, - .map_atomic = armada_gem_dmabuf_no_kmap, - .unmap_atomic = armada_gem_dmabuf_no_kunmap, .map = armada_gem_dmabuf_no_kmap, .unmap = armada_gem_dmabuf_no_kunmap, .mmap = armada_gem_dmabuf_mmap, diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h index 345dc4d0851e..277580b36758 100644 --- a/drivers/gpu/drm/armada/armada_hw.h +++ b/drivers/gpu/drm/armada/armada_hw.h @@ -316,4 +316,19 @@ enum { PWRDN_IRQ_LEVEL = 1 << 0, }; +static inline u32 armada_rect_hw_fp(struct drm_rect *r) +{ + return (drm_rect_height(r) & 0xffff0000) | drm_rect_width(r) >> 16; +} + +static inline u32 armada_rect_hw(struct drm_rect *r) +{ + return drm_rect_height(r) << 16 | (drm_rect_width(r) & 0x0000ffff); +} + +static inline u32 armada_rect_yx(struct drm_rect *r) +{ + return (r)->y1 << 16 | ((r)->x1 & 0x0000ffff); +} + #endif diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index afa7ded3ae31..eb7dfb65ef47 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -7,358 +7,468 @@ * published by the Free Software Foundation. */ #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/armada_drm.h> #include "armada_crtc.h" #include "armada_drm.h" #include "armada_fb.h" #include "armada_gem.h" #include "armada_hw.h" -#include <drm/armada_drm.h> #include "armada_ioctlP.h" +#include "armada_plane.h" #include "armada_trace.h" -struct armada_ovl_plane_properties { - uint32_t colorkey_yr; - uint32_t colorkey_ug; - uint32_t colorkey_vb; -#define K2R(val) (((val) >> 0) & 0xff) -#define K2G(val) (((val) >> 8) & 0xff) -#define K2B(val) (((val) >> 16) & 0xff) - int16_t brightness; - uint16_t contrast; - uint16_t saturation; - uint32_t colorkey_mode; - uint32_t colorkey_enable; -}; - -struct armada_ovl_plane { - struct armada_plane base; - struct armada_ovl_plane_properties prop; +#define DEFAULT_BRIGHTNESS 0 +#define DEFAULT_CONTRAST 0x4000 +#define DEFAULT_SATURATION 0x4000 +#define DEFAULT_ENCODING DRM_COLOR_YCBCR_BT601 + +struct armada_overlay_state { + struct drm_plane_state base; + u32 colorkey_yr; + u32 colorkey_ug; + u32 colorkey_vb; + u32 colorkey_mode; + u32 colorkey_enable; + s16 brightness; + u16 contrast; + u16 saturation; }; -#define drm_to_armada_ovl_plane(p) \ - container_of(p, struct armada_ovl_plane, base.base) - +#define drm_to_overlay_state(s) \ + container_of(s, struct armada_overlay_state, base) -static void -armada_ovl_update_attr(struct armada_ovl_plane_properties *prop, - struct armada_crtc *dcrtc) +static inline u32 armada_spu_contrast(struct drm_plane_state *state) { - writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y); - writel_relaxed(prop->colorkey_ug, dcrtc->base + LCD_SPU_COLORKEY_U); - writel_relaxed(prop->colorkey_vb, dcrtc->base + LCD_SPU_COLORKEY_V); + return drm_to_overlay_state(state)->brightness << 16 | + drm_to_overlay_state(state)->contrast; +} - writel_relaxed(prop->brightness << 16 | prop->contrast, - dcrtc->base + LCD_SPU_CONTRAST); +static inline u32 armada_spu_saturation(struct drm_plane_state *state) +{ /* Docs say 15:0, but it seems to actually be 31:16 on Armada 510 */ - writel_relaxed(prop->saturation << 16, - dcrtc->base + LCD_SPU_SATURATION); - writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE); - - spin_lock_irq(&dcrtc->irq_lock); - armada_updatel(prop->colorkey_mode, - CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK, - dcrtc->base + LCD_SPU_DMA_CTRL1); - if (dcrtc->variant->has_spu_adv_reg) - armada_updatel(prop->colorkey_enable, - ADV_GRACOLORKEY | ADV_VIDCOLORKEY, - dcrtc->base + LCD_SPU_ADV_REG); - spin_unlock_irq(&dcrtc->irq_lock); + return drm_to_overlay_state(state)->saturation << 16; } -/* === Plane support === */ -static void armada_ovl_plane_work(struct armada_crtc *dcrtc, - struct armada_plane_work *work) +static inline u32 armada_csc(struct drm_plane_state *state) { - unsigned long flags; - - trace_armada_ovl_plane_work(&dcrtc->crtc, work->plane); - - spin_lock_irqsave(&dcrtc->irq_lock, flags); - armada_drm_crtc_update_regs(dcrtc, work->regs); - spin_unlock_irqrestore(&dcrtc->irq_lock, flags); + /* + * The CFG_CSC_RGB_* settings control the output of the colour space + * converter, setting the range of output values it produces. Since + * we will be blending with the full-range graphics, we need to + * produce full-range RGB output from the conversion. + */ + return CFG_CSC_RGB_COMPUTER | + (state->color_encoding == DRM_COLOR_YCBCR_BT709 ? + CFG_CSC_YUV_CCIR709 : CFG_CSC_YUV_CCIR601); } -static void armada_ovl_plane_update_state(struct drm_plane_state *state, - struct armada_regs *regs) +/* === Plane support === */ +static void armada_drm_overlay_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) { - struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(state->plane); - struct armada_framebuffer *dfb = drm_fb_to_armada_fb(state->fb); - const struct drm_format_info *format; - unsigned int idx = 0; - bool fb_changed; - u32 val, ctrl0; - u16 src_x, src_y; + struct drm_plane_state *state = plane->state; + struct armada_crtc *dcrtc; + struct armada_regs *regs; + unsigned int idx; + u32 cfg, cfg_mask, val; - ctrl0 = CFG_DMA_FMT(dfb->fmt) | CFG_DMA_MOD(dfb->mod) | CFG_CBSH_ENA; - if (state->visible) - ctrl0 |= CFG_DMA_ENA; - if (drm_rect_width(&state->src) >> 16 != drm_rect_width(&state->dst)) - ctrl0 |= CFG_DMA_HSMOOTH; + DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name); - /* - * Shifting a YUV packed format image by one pixel causes the U/V - * planes to swap. Compensate for it by also toggling the UV swap. - */ - format = dfb->fb.format; - if (format->num_planes == 1 && state->src.x1 >> 16 & (format->hsub - 1)) - ctrl0 ^= CFG_DMA_MOD(CFG_SWAPUV); + if (!state->fb || WARN_ON(!state->crtc)) + return; + + 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); + + dcrtc = drm_to_armada_crtc(state->crtc); + regs = dcrtc->regs + dcrtc->regs_idx; - if (~dplane->base.state.ctrl0 & ctrl0 & CFG_DMA_ENA) { - /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */ + idx = 0; + if (!old_state->visible && state->visible) armada_reg_queue_mod(regs, idx, 0, CFG_PDWN16x66 | CFG_PDWN32x66, LCD_SPU_SRAM_PARA1); - } - - fb_changed = dplane->base.base.fb != &dfb->fb || - dplane->base.state.src_x != state->src.x1 >> 16 || - dplane->base.state.src_y != state->src.y1 >> 16; - - dplane->base.state.vsync_update = fb_changed; - + val = armada_rect_hw_fp(&state->src); + if (armada_rect_hw_fp(&old_state->src) != val) + armada_reg_queue_set(regs, idx, val, LCD_SPU_DMA_HPXL_VLN); + val = armada_rect_yx(&state->dst); + if (armada_rect_yx(&old_state->dst) != val) + armada_reg_queue_set(regs, idx, val, LCD_SPU_DMA_OVSA_HPXL_VLN); + val = armada_rect_hw(&state->dst); + if (armada_rect_hw(&old_state->dst) != val) + armada_reg_queue_set(regs, idx, val, LCD_SPU_DZM_HPXL_VLN); /* FIXME: overlay on an interlaced display */ - if (fb_changed) { - u32 addrs[3]; - - dplane->base.state.src_y = src_y = state->src.y1 >> 16; - dplane->base.state.src_x = src_x = state->src.x1 >> 16; + if (old_state->src.x1 != state->src.x1 || + old_state->src.y1 != state->src.y1 || + old_state->fb != state->fb) { + const struct drm_format_info *format; + u16 src_x, pitches[3]; + u32 addrs[2][3]; - armada_drm_plane_calc_addrs(addrs, &dfb->fb, src_x, src_y); + armada_drm_plane_calc(state, addrs, pitches, false); - armada_reg_queue_set(regs, idx, addrs[0], + armada_reg_queue_set(regs, idx, addrs[0][0], LCD_SPU_DMA_START_ADDR_Y0); - armada_reg_queue_set(regs, idx, addrs[1], + armada_reg_queue_set(regs, idx, addrs[0][1], LCD_SPU_DMA_START_ADDR_U0); - armada_reg_queue_set(regs, idx, addrs[2], + armada_reg_queue_set(regs, idx, addrs[0][2], LCD_SPU_DMA_START_ADDR_V0); - armada_reg_queue_set(regs, idx, addrs[0], + armada_reg_queue_set(regs, idx, addrs[1][0], LCD_SPU_DMA_START_ADDR_Y1); - armada_reg_queue_set(regs, idx, addrs[1], + armada_reg_queue_set(regs, idx, addrs[1][1], LCD_SPU_DMA_START_ADDR_U1); - armada_reg_queue_set(regs, idx, addrs[2], + armada_reg_queue_set(regs, idx, addrs[1][2], LCD_SPU_DMA_START_ADDR_V1); - val = dfb->fb.pitches[0] << 16 | dfb->fb.pitches[0]; - armada_reg_queue_set(regs, idx, val, - LCD_SPU_DMA_PITCH_YC); - val = dfb->fb.pitches[1] << 16 | dfb->fb.pitches[2]; - armada_reg_queue_set(regs, idx, val, - LCD_SPU_DMA_PITCH_UV); - } + val = pitches[0] << 16 | pitches[0]; + armada_reg_queue_set(regs, idx, val, LCD_SPU_DMA_PITCH_YC); + val = pitches[1] << 16 | pitches[2]; + armada_reg_queue_set(regs, idx, val, LCD_SPU_DMA_PITCH_UV); - val = (drm_rect_height(&state->src) & 0xffff0000) | - drm_rect_width(&state->src) >> 16; - if (dplane->base.state.src_hw != val) { - dplane->base.state.src_hw = val; - armada_reg_queue_set(regs, idx, val, - LCD_SPU_DMA_HPXL_VLN); - } + cfg = CFG_DMA_FMT(drm_fb_to_armada_fb(state->fb)->fmt) | + CFG_DMA_MOD(drm_fb_to_armada_fb(state->fb)->mod) | + CFG_CBSH_ENA; + if (state->visible) + cfg |= CFG_DMA_ENA; - val = drm_rect_height(&state->dst) << 16 | drm_rect_width(&state->dst); - if (dplane->base.state.dst_hw != val) { - dplane->base.state.dst_hw = val; - armada_reg_queue_set(regs, idx, val, - LCD_SPU_DZM_HPXL_VLN); + /* + * Shifting a YUV packed format image by one pixel causes the + * U/V planes to swap. Compensate for it by also toggling + * the UV swap. + */ + format = state->fb->format; + src_x = state->src.x1 >> 16; + if (format->num_planes == 1 && src_x & (format->hsub - 1)) + cfg ^= CFG_DMA_MOD(CFG_SWAPUV); + cfg_mask = CFG_CBSH_ENA | CFG_DMAFORMAT | + CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | + CFG_SWAPYU | CFG_YUV2RGB) | + CFG_DMA_FTOGGLE | CFG_DMA_TSTMODE | + CFG_DMA_ENA; + } else if (old_state->visible != state->visible) { + cfg = state->visible ? CFG_DMA_ENA : 0; + cfg_mask = CFG_DMA_ENA; + } else { + cfg = cfg_mask = 0; } - - val = state->dst.y1 << 16 | state->dst.x1; - if (dplane->base.state.dst_yx != val) { - dplane->base.state.dst_yx = val; - armada_reg_queue_set(regs, idx, val, - LCD_SPU_DMA_OVSA_HPXL_VLN); + if (drm_rect_width(&old_state->src) != drm_rect_width(&state->src) || + drm_rect_width(&old_state->dst) != drm_rect_width(&state->dst)) { + cfg_mask |= CFG_DMA_HSMOOTH; + if (drm_rect_width(&state->src) >> 16 != + drm_rect_width(&state->dst)) + cfg |= CFG_DMA_HSMOOTH; } - if (dplane->base.state.ctrl0 != ctrl0) { - dplane->base.state.ctrl0 = ctrl0; - armada_reg_queue_mod(regs, idx, ctrl0, - CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE | - CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE | - CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU | - CFG_YUV2RGB) | CFG_DMA_ENA, - LCD_SPU_DMA_CTRL0); - dplane->base.state.vsync_update = true; - } + if (cfg_mask) + armada_reg_queue_mod(regs, idx, cfg, cfg_mask, + LCD_SPU_DMA_CTRL0); + + val = armada_spu_contrast(state); + if ((!old_state->visible && state->visible) || + armada_spu_contrast(old_state) != val) + armada_reg_queue_set(regs, idx, val, LCD_SPU_CONTRAST); + val = armada_spu_saturation(state); + if ((!old_state->visible && state->visible) || + armada_spu_saturation(old_state) != val) + armada_reg_queue_set(regs, idx, val, LCD_SPU_SATURATION); + if (!old_state->visible && state->visible) + armada_reg_queue_set(regs, idx, 0x00002000, LCD_SPU_CBSH_HUE); + val = armada_csc(state); + if ((!old_state->visible && state->visible) || + armada_csc(old_state) != val) + armada_reg_queue_mod(regs, idx, val, CFG_CSC_MASK, + LCD_SPU_IOPAD_CONTROL); + val = drm_to_overlay_state(state)->colorkey_yr; + if ((!old_state->visible && state->visible) || + drm_to_overlay_state(old_state)->colorkey_yr != val) + armada_reg_queue_set(regs, idx, val, LCD_SPU_COLORKEY_Y); + val = drm_to_overlay_state(state)->colorkey_ug; + if ((!old_state->visible && state->visible) || + drm_to_overlay_state(old_state)->colorkey_ug != val) + armada_reg_queue_set(regs, idx, val, LCD_SPU_COLORKEY_U); + val = drm_to_overlay_state(state)->colorkey_vb; + if ((!old_state->visible && state->visible) || + drm_to_overlay_state(old_state)->colorkey_vb != val) + armada_reg_queue_set(regs, idx, val, LCD_SPU_COLORKEY_V); + val = drm_to_overlay_state(state)->colorkey_mode; + if ((!old_state->visible && state->visible) || + drm_to_overlay_state(old_state)->colorkey_mode != val) + armada_reg_queue_mod(regs, idx, val, CFG_CKMODE_MASK | + CFG_ALPHAM_MASK | CFG_ALPHA_MASK, + LCD_SPU_DMA_CTRL1); + val = drm_to_overlay_state(state)->colorkey_enable; + if (((!old_state->visible && state->visible) || + drm_to_overlay_state(old_state)->colorkey_enable != val) && + dcrtc->variant->has_spu_adv_reg) + armada_reg_queue_mod(regs, idx, val, ADV_GRACOLORKEY | + ADV_VIDCOLORKEY, LCD_SPU_ADV_REG); + + dcrtc->regs_idx += idx; +} + +static void armada_drm_overlay_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct armada_crtc *dcrtc; + struct armada_regs *regs; + unsigned int idx = 0; + + DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name); + + if (!old_state->crtc) + return; - dplane->base.state.changed = idx != 0; + 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); - armada_reg_queue_end(regs, idx); + dcrtc = drm_to_armada_crtc(old_state->crtc); + regs = dcrtc->regs + dcrtc->regs_idx; + + /* Disable plane and power down the YUV FIFOs */ + armada_reg_queue_mod(regs, idx, 0, CFG_DMA_ENA, LCD_SPU_DMA_CTRL0); + armada_reg_queue_mod(regs, idx, CFG_PDWN16x66 | CFG_PDWN32x66, 0, + LCD_SPU_SRAM_PARA1); + + dcrtc->regs_idx += idx; } +static const struct drm_plane_helper_funcs armada_overlay_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_overlay_plane_atomic_update, + .atomic_disable = armada_drm_overlay_plane_atomic_disable, +}; + static int -armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, +armada_overlay_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h, struct drm_modeset_acquire_ctx *ctx) { - struct armada_ovl_plane *dplane = drm_to_armada_ovl_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; + struct drm_atomic_state *state; + struct drm_plane_state *plane_state; + int ret = 0; trace_armada_ovl_plane_update(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h); - ret = drm_atomic_helper_check_plane_state(&state, &crtc_state, 0, - INT_MAX, true, false); - if (ret) - return ret; - - work = &dplane->base.works[dplane->base.next_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); + state = drm_atomic_state_alloc(plane->dev); + if (!state) + return -ENOMEM; - work->old_fb = plane->fb; - } else { - work->old_fb = NULL; + state->acquire_ctx = ctx; + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto fail; } - armada_ovl_plane_update_state(&state, work->regs); - - if (!dplane->base.state.changed) - return 0; - - /* Wait for pending work to complete */ - if (armada_drm_plane_work_wait(&dplane->base, HZ / 25) == 0) - armada_drm_plane_work_cancel(dcrtc, &dplane->base); + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); + if (ret != 0) + goto fail; + + drm_atomic_set_fb_for_plane(plane_state, fb); + plane_state->crtc_x = crtc_x; + plane_state->crtc_y = crtc_y; + plane_state->crtc_h = crtc_h; + plane_state->crtc_w = crtc_w; + plane_state->src_x = src_x; + plane_state->src_y = src_y; + plane_state->src_h = src_h; + plane_state->src_w = src_w; + + ret = drm_atomic_nonblocking_commit(state); +fail: + drm_atomic_state_put(state); + return ret; +} - /* Just updating the position/size? */ - if (!dplane->base.state.vsync_update) { - armada_ovl_plane_work(dcrtc, work); - return 0; - } +static void armada_ovl_plane_destroy(struct drm_plane *plane) +{ + drm_plane_cleanup(plane); + kfree(plane); +} - if (!dcrtc->plane) { - dcrtc->plane = plane; - armada_ovl_update_attr(&dplane->prop, dcrtc); +static void armada_overlay_reset(struct drm_plane *plane) +{ + struct armada_overlay_state *state; + + if (plane->state) + __drm_atomic_helper_plane_destroy_state(plane->state); + kfree(plane->state); + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) { + state->base.plane = plane; + state->base.color_encoding = DEFAULT_ENCODING; + state->base.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE; + state->base.rotation = DRM_MODE_ROTATE_0; + state->colorkey_yr = 0xfefefe00; + state->colorkey_ug = 0x01010100; + state->colorkey_vb = 0x01010100; + state->colorkey_mode = CFG_CKMODE(CKMODE_RGB) | + CFG_ALPHAM_GRA | CFG_ALPHA(0); + state->colorkey_enable = ADV_GRACOLORKEY; + state->brightness = DEFAULT_BRIGHTNESS; + state->contrast = DEFAULT_CONTRAST; + state->saturation = DEFAULT_SATURATION; } - - /* Queue it for update on the next interrupt if we are enabled */ - ret = armada_drm_plane_work_queue(dcrtc, work); - if (ret) - DRM_ERROR("failed to queue plane work: %d\n", ret); - - dplane->base.next_work = !dplane->base.next_work; - - return 0; + plane->state = &state->base; } -static void armada_ovl_plane_destroy(struct drm_plane *plane) +struct drm_plane_state * +armada_overlay_duplicate_state(struct drm_plane *plane) { - struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); + struct armada_overlay_state *state; - drm_plane_cleanup(plane); + if (WARN_ON(!plane->state)) + return NULL; - kfree(dplane); + state = kmemdup(plane->state, sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_plane_duplicate_state(plane, &state->base); + return &state->base; } -static int armada_ovl_plane_set_property(struct drm_plane *plane, - struct drm_property *property, uint64_t val) +static int armada_overlay_set_property(struct drm_plane *plane, + struct drm_plane_state *state, struct drm_property *property, + uint64_t val) { struct armada_private *priv = plane->dev->dev_private; - struct armada_ovl_plane *dplane = drm_to_armada_ovl_plane(plane); - bool update_attr = false; +#define K2R(val) (((val) >> 0) & 0xff) +#define K2G(val) (((val) >> 8) & 0xff) +#define K2B(val) (((val) >> 16) & 0xff) if (property == priv->colorkey_prop) { #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8) - dplane->prop.colorkey_yr = CCC(K2R(val)); - dplane->prop.colorkey_ug = CCC(K2G(val)); - dplane->prop.colorkey_vb = CCC(K2B(val)); + drm_to_overlay_state(state)->colorkey_yr = CCC(K2R(val)); + drm_to_overlay_state(state)->colorkey_ug = CCC(K2G(val)); + drm_to_overlay_state(state)->colorkey_vb = CCC(K2B(val)); #undef CCC - update_attr = true; } else if (property == priv->colorkey_min_prop) { - dplane->prop.colorkey_yr &= ~0x00ff0000; - dplane->prop.colorkey_yr |= K2R(val) << 16; - dplane->prop.colorkey_ug &= ~0x00ff0000; - dplane->prop.colorkey_ug |= K2G(val) << 16; - dplane->prop.colorkey_vb &= ~0x00ff0000; - dplane->prop.colorkey_vb |= K2B(val) << 16; - update_attr = true; + drm_to_overlay_state(state)->colorkey_yr &= ~0x00ff0000; + drm_to_overlay_state(state)->colorkey_yr |= K2R(val) << 16; + drm_to_overlay_state(state)->colorkey_ug &= ~0x00ff0000; + drm_to_overlay_state(state)->colorkey_ug |= K2G(val) << 16; + drm_to_overlay_state(state)->colorkey_vb &= ~0x00ff0000; + drm_to_overlay_state(state)->colorkey_vb |= K2B(val) << 16; } else if (property == priv->colorkey_max_prop) { - dplane->prop.colorkey_yr &= ~0xff000000; - dplane->prop.colorkey_yr |= K2R(val) << 24; - dplane->prop.colorkey_ug &= ~0xff000000; - dplane->prop.colorkey_ug |= K2G(val) << 24; - dplane->prop.colorkey_vb &= ~0xff000000; - dplane->prop.colorkey_vb |= K2B(val) << 24; - update_attr = true; + drm_to_overlay_state(state)->colorkey_yr &= ~0xff000000; + drm_to_overlay_state(state)->colorkey_yr |= K2R(val) << 24; + drm_to_overlay_state(state)->colorkey_ug &= ~0xff000000; + drm_to_overlay_state(state)->colorkey_ug |= K2G(val) << 24; + drm_to_overlay_state(state)->colorkey_vb &= ~0xff000000; + drm_to_overlay_state(state)->colorkey_vb |= K2B(val) << 24; } else if (property == priv->colorkey_val_prop) { - dplane->prop.colorkey_yr &= ~0x0000ff00; - dplane->prop.colorkey_yr |= K2R(val) << 8; - dplane->prop.colorkey_ug &= ~0x0000ff00; - dplane->prop.colorkey_ug |= K2G(val) << 8; - dplane->prop.colorkey_vb &= ~0x0000ff00; - dplane->prop.colorkey_vb |= K2B(val) << 8; - update_attr = true; + drm_to_overlay_state(state)->colorkey_yr &= ~0x0000ff00; + drm_to_overlay_state(state)->colorkey_yr |= K2R(val) << 8; + drm_to_overlay_state(state)->colorkey_ug &= ~0x0000ff00; + drm_to_overlay_state(state)->colorkey_ug |= K2G(val) << 8; + drm_to_overlay_state(state)->colorkey_vb &= ~0x0000ff00; + drm_to_overlay_state(state)->colorkey_vb |= K2B(val) << 8; } else if (property == priv->colorkey_alpha_prop) { - dplane->prop.colorkey_yr &= ~0x000000ff; - dplane->prop.colorkey_yr |= K2R(val); - dplane->prop.colorkey_ug &= ~0x000000ff; - dplane->prop.colorkey_ug |= K2G(val); - dplane->prop.colorkey_vb &= ~0x000000ff; - dplane->prop.colorkey_vb |= K2B(val); - update_attr = true; + drm_to_overlay_state(state)->colorkey_yr &= ~0x000000ff; + drm_to_overlay_state(state)->colorkey_yr |= K2R(val); + drm_to_overlay_state(state)->colorkey_ug &= ~0x000000ff; + drm_to_overlay_state(state)->colorkey_ug |= K2G(val); + drm_to_overlay_state(state)->colorkey_vb &= ~0x000000ff; + drm_to_overlay_state(state)->colorkey_vb |= K2B(val); } else if (property == priv->colorkey_mode_prop) { if (val == CKMODE_DISABLE) { - dplane->prop.colorkey_mode = + drm_to_overlay_state(state)->colorkey_mode = CFG_CKMODE(CKMODE_DISABLE) | CFG_ALPHAM_CFG | CFG_ALPHA(255); - dplane->prop.colorkey_enable = 0; + drm_to_overlay_state(state)->colorkey_enable = 0; } else { - dplane->prop.colorkey_mode = + drm_to_overlay_state(state)->colorkey_mode = CFG_CKMODE(val) | CFG_ALPHAM_GRA | CFG_ALPHA(0); - dplane->prop.colorkey_enable = ADV_GRACOLORKEY; + drm_to_overlay_state(state)->colorkey_enable = + ADV_GRACOLORKEY; } - update_attr = true; } else if (property == priv->brightness_prop) { - dplane->prop.brightness = val - 256; - update_attr = true; + drm_to_overlay_state(state)->brightness = val - 256; } else if (property == priv->contrast_prop) { - dplane->prop.contrast = val; - update_attr = true; + drm_to_overlay_state(state)->contrast = val; } else if (property == priv->saturation_prop) { - dplane->prop.saturation = val; - update_attr = true; + drm_to_overlay_state(state)->saturation = val; + } else { + return -EINVAL; } + return 0; +} - if (update_attr && dplane->base.base.crtc) - armada_ovl_update_attr(&dplane->prop, - drm_to_armada_crtc(dplane->base.base.crtc)); +static int armada_overlay_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, struct drm_property *property, + uint64_t *val) +{ + struct armada_private *priv = plane->dev->dev_private; +#define C2K(c,s) (((c) >> (s)) & 0xff) +#define R2BGR(r,g,b,s) (C2K(r,s) << 0 | C2K(g,s) << 8 | C2K(b,s) << 16) + if (property == priv->colorkey_prop) { + /* Do best-efforts here for this property */ + *val = R2BGR(drm_to_overlay_state(state)->colorkey_yr, + drm_to_overlay_state(state)->colorkey_ug, + drm_to_overlay_state(state)->colorkey_vb, 16); + /* If min != max, or min != val, error out */ + if (*val != R2BGR(drm_to_overlay_state(state)->colorkey_yr, + drm_to_overlay_state(state)->colorkey_ug, + drm_to_overlay_state(state)->colorkey_vb, 24) || + *val != R2BGR(drm_to_overlay_state(state)->colorkey_yr, + drm_to_overlay_state(state)->colorkey_ug, + drm_to_overlay_state(state)->colorkey_vb, 8)) + return -EINVAL; + } else if (property == priv->colorkey_min_prop) { + *val = R2BGR(drm_to_overlay_state(state)->colorkey_yr, + drm_to_overlay_state(state)->colorkey_ug, + drm_to_overlay_state(state)->colorkey_vb, 16); + } else if (property == priv->colorkey_max_prop) { + *val = R2BGR(drm_to_overlay_state(state)->colorkey_yr, + drm_to_overlay_state(state)->colorkey_ug, + drm_to_overlay_state(state)->colorkey_vb, 24); + } else if (property == priv->colorkey_val_prop) { + *val = R2BGR(drm_to_overlay_state(state)->colorkey_yr, + drm_to_overlay_state(state)->colorkey_ug, + drm_to_overlay_state(state)->colorkey_vb, 8); + } else if (property == priv->colorkey_alpha_prop) { + *val = R2BGR(drm_to_overlay_state(state)->colorkey_yr, + drm_to_overlay_state(state)->colorkey_ug, + drm_to_overlay_state(state)->colorkey_vb, 0); + } else if (property == priv->colorkey_mode_prop) { + *val = (drm_to_overlay_state(state)->colorkey_mode & + CFG_CKMODE_MASK) >> ffs(CFG_CKMODE_MASK); + } else if (property == priv->brightness_prop) { + *val = drm_to_overlay_state(state)->brightness + 256; + } else if (property == priv->contrast_prop) { + *val = drm_to_overlay_state(state)->contrast; + } else if (property == priv->saturation_prop) { + *val = drm_to_overlay_state(state)->saturation; + } else { + return -EINVAL; + } return 0; } static const struct drm_plane_funcs armada_ovl_plane_funcs = { - .update_plane = armada_ovl_plane_update, - .disable_plane = armada_drm_plane_disable, + .update_plane = armada_overlay_plane_update, + .disable_plane = drm_atomic_helper_disable_plane, .destroy = armada_ovl_plane_destroy, - .set_property = armada_ovl_plane_set_property, + .reset = armada_overlay_reset, + .atomic_duplicate_state = armada_overlay_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_set_property = armada_overlay_set_property, + .atomic_get_property = armada_overlay_get_property, }; static const uint32_t armada_ovl_formats[] = { @@ -431,48 +541,31 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) { struct armada_private *priv = dev->dev_private; struct drm_mode_object *mobj; - struct armada_ovl_plane *dplane; + struct drm_plane *overlay; int ret; ret = armada_overlay_create_properties(dev); if (ret) return ret; - dplane = kzalloc(sizeof(*dplane), GFP_KERNEL); - if (!dplane) + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); + if (!overlay) return -ENOMEM; - ret = armada_drm_plane_init(&dplane->base); - if (ret) { - kfree(dplane); - return ret; - } - - dplane->base.works[0].fn = armada_ovl_plane_work; - dplane->base.works[1].fn = armada_ovl_plane_work; + drm_plane_helper_add(overlay, &armada_overlay_plane_helper_funcs); - ret = drm_universal_plane_init(dev, &dplane->base.base, crtcs, + ret = drm_universal_plane_init(dev, overlay, crtcs, &armada_ovl_plane_funcs, armada_ovl_formats, ARRAY_SIZE(armada_ovl_formats), NULL, DRM_PLANE_TYPE_OVERLAY, NULL); if (ret) { - kfree(dplane); + kfree(overlay); return ret; } - dplane->prop.colorkey_yr = 0xfefefe00; - dplane->prop.colorkey_ug = 0x01010100; - dplane->prop.colorkey_vb = 0x01010100; - dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB) | - CFG_ALPHAM_GRA | CFG_ALPHA(0); - dplane->prop.colorkey_enable = ADV_GRACOLORKEY; - dplane->prop.brightness = 0; - dplane->prop.contrast = 0x4000; - dplane->prop.saturation = 0x4000; - - mobj = &dplane->base.base.base; + mobj = &overlay->base; drm_object_attach_property(mobj, priv->colorkey_prop, 0x0101fe); drm_object_attach_property(mobj, priv->colorkey_min_prop, @@ -485,11 +578,19 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) 0x000000); drm_object_attach_property(mobj, priv->colorkey_mode_prop, CKMODE_RGB); - drm_object_attach_property(mobj, priv->brightness_prop, 256); + drm_object_attach_property(mobj, priv->brightness_prop, + 256 + DEFAULT_BRIGHTNESS); drm_object_attach_property(mobj, priv->contrast_prop, - dplane->prop.contrast); + DEFAULT_CONTRAST); drm_object_attach_property(mobj, priv->saturation_prop, - dplane->prop.saturation); + DEFAULT_SATURATION); - return 0; + ret = drm_plane_create_color_properties(overlay, + BIT(DRM_COLOR_YCBCR_BT601) | + BIT(DRM_COLOR_YCBCR_BT709), + BIT(DRM_COLOR_YCBCR_LIMITED_RANGE), + DEFAULT_ENCODING, + DRM_COLOR_YCBCR_LIMITED_RANGE); + + return ret; } diff --git a/drivers/gpu/drm/armada/armada_plane.c b/drivers/gpu/drm/armada/armada_plane.c new file mode 100644 index 000000000000..9f36423dd394 --- /dev/null +++ b/drivers/gpu/drm/armada/armada_plane.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2012 Russell King + * Rewritten from the dovefb driver, and Armada510 manuals. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_plane_helper.h> +#include "armada_crtc.h" +#include "armada_drm.h" +#include "armada_fb.h" +#include "armada_gem.h" +#include "armada_hw.h" +#include "armada_plane.h" +#include "armada_trace.h" + +static const uint32_t armada_primary_formats[] = { + DRM_FORMAT_UYVY, + DRM_FORMAT_YUYV, + DRM_FORMAT_VYUY, + DRM_FORMAT_YVYU, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, +}; + +void armada_drm_plane_calc(struct drm_plane_state *state, u32 addrs[2][3], + u16 pitches[3], bool interlaced) +{ + struct drm_framebuffer *fb = state->fb; + const struct drm_format_info *format = fb->format; + unsigned int num_planes = format->num_planes; + unsigned int x = state->src.x1 >> 16; + unsigned int y = state->src.y1 >> 16; + u32 addr = drm_fb_obj(fb)->dev_addr; + int i; + + DRM_DEBUG_KMS("pitch %u x %d y %d bpp %d\n", + fb->pitches[0], x, y, format->cpp[0] * 8); + + if (num_planes > 3) + num_planes = 3; + + addrs[0][0] = addr + fb->offsets[0] + y * fb->pitches[0] + + x * format->cpp[0]; + pitches[0] = fb->pitches[0]; + + y /= format->vsub; + x /= format->hsub; + + for (i = 1; i < num_planes; i++) { + addrs[0][i] = addr + fb->offsets[i] + y * fb->pitches[i] + + x * format->cpp[i]; + pitches[i] = fb->pitches[i]; + } + for (; i < 3; i++) { + addrs[0][i] = 0; + pitches[i] = 0; + } + if (interlaced) { + for (i = 0; i < 3; i++) { + addrs[1][i] = addrs[0][i] + pitches[i]; + pitches[i] *= 2; + } + } else { + for (i = 0; i < 3; i++) + addrs[1][i] = addrs[0][i]; + } +} + +static unsigned armada_drm_crtc_calc_fb(struct drm_plane_state *state, + struct armada_regs *regs, bool interlaced) +{ + u16 pitches[3]; + u32 addrs[2][3]; + unsigned i = 0; + + armada_drm_plane_calc(state, addrs, pitches, interlaced); + + /* write offset, base, and pitch */ + armada_reg_queue_set(regs, i, addrs[0][0], LCD_CFG_GRA_START_ADDR0); + armada_reg_queue_set(regs, i, addrs[1][0], LCD_CFG_GRA_START_ADDR1); + armada_reg_queue_mod(regs, i, pitches[0], 0xffff, LCD_CFG_GRA_PITCH); + + return i; +} + +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 void armada_drm_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_plane_state *state = plane->state; + struct armada_crtc *dcrtc; + struct armada_regs *regs; + u32 cfg, cfg_mask, val; + unsigned int idx; + + DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name); + + if (!state->fb || WARN_ON(!state->crtc)) + return; + + 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); + + dcrtc = drm_to_armada_crtc(state->crtc); + regs = dcrtc->regs + dcrtc->regs_idx; + + idx = 0; + if (!old_state->visible && state->visible) { + val = CFG_PDWN64x66; + if (drm_fb_to_armada_fb(state->fb)->fmt > CFG_420) + val |= CFG_PDWN256x24; + armada_reg_queue_mod(regs, idx, 0, val, LCD_SPU_SRAM_PARA1); + } + val = armada_rect_hw_fp(&state->src); + if (armada_rect_hw_fp(&old_state->src) != val) + armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_HPXL_VLN); + val = armada_rect_yx(&state->dst); + if (armada_rect_yx(&old_state->dst) != val) + armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_OVSA_HPXL_VLN); + val = armada_rect_hw(&state->dst); + if (armada_rect_hw(&old_state->dst) != val) + armada_reg_queue_set(regs, idx, val, LCD_SPU_GZM_HPXL_VLN); + if (old_state->src.x1 != state->src.x1 || + old_state->src.y1 != state->src.y1 || + old_state->fb != state->fb || + state->crtc->state->mode_changed) { + idx += armada_drm_crtc_calc_fb(state, regs + idx, + dcrtc->interlaced); + } + if (old_state->fb != state->fb || + state->crtc->state->mode_changed) { + cfg = CFG_GRA_FMT(drm_fb_to_armada_fb(state->fb)->fmt) | + CFG_GRA_MOD(drm_fb_to_armada_fb(state->fb)->mod); + if (drm_fb_to_armada_fb(state->fb)->fmt > CFG_420) + cfg |= CFG_PALETTE_ENA; + if (state->visible) + cfg |= CFG_GRA_ENA; + if (dcrtc->interlaced) + cfg |= CFG_GRA_FTOGGLE; + cfg_mask = CFG_GRAFORMAT | + CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV | + CFG_SWAPYU | CFG_YUV2RGB) | + CFG_PALETTE_ENA | CFG_GRA_FTOGGLE | + CFG_GRA_ENA; + } else if (old_state->visible != state->visible) { + cfg = state->visible ? CFG_GRA_ENA : 0; + cfg_mask = CFG_GRA_ENA; + } else { + cfg = cfg_mask = 0; + } + if (drm_rect_width(&old_state->src) != drm_rect_width(&state->src) || + drm_rect_width(&old_state->dst) != drm_rect_width(&state->dst)) { + cfg_mask |= CFG_GRA_HSMOOTH; + if (drm_rect_width(&state->src) >> 16 != + drm_rect_width(&state->dst)) + cfg |= CFG_GRA_HSMOOTH; + } + + if (cfg_mask) + armada_reg_queue_mod(regs, idx, cfg, cfg_mask, + LCD_SPU_DMA_CTRL0); + + dcrtc->regs_idx += idx; +} + +static void armada_drm_primary_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct armada_crtc *dcrtc; + struct armada_regs *regs; + unsigned int idx = 0; + + DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name); + + if (!old_state->crtc) + return; + + 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); + + dcrtc = drm_to_armada_crtc(old_state->crtc); + regs = dcrtc->regs + dcrtc->regs_idx; + + /* 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); + + 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 = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .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_primary_plane_init(struct drm_device *drm, + struct drm_plane *primary) +{ + int ret; + + drm_plane_helper_add(primary, &armada_primary_plane_helper_funcs); + + ret = drm_universal_plane_init(drm, primary, 0, + &armada_primary_plane_funcs, + armada_primary_formats, + ARRAY_SIZE(armada_primary_formats), + NULL, + DRM_PLANE_TYPE_PRIMARY, NULL); + + return ret; +} diff --git a/drivers/gpu/drm/armada/armada_plane.h b/drivers/gpu/drm/armada/armada_plane.h new file mode 100644 index 000000000000..ff4281ba7fad --- /dev/null +++ b/drivers/gpu/drm/armada/armada_plane.h @@ -0,0 +1,15 @@ +#ifndef ARMADA_PLANE_H +#define ARMADA_PLANE_H + +void armada_drm_plane_calc(struct drm_plane_state *state, u32 addrs[2][3], + u16 pitches[3], bool interlaced); +int armada_drm_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *state); +void armada_drm_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state); +int armada_drm_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state); +int armada_drm_primary_plane_init(struct drm_device *drm, + struct drm_plane *primary); + +#endif |