]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/armada/armada_crtc.c
Merge remote-tracking branch 'arm-soc/for-next'
[karo-tx-linux.git] / drivers / gpu / drm / armada / armada_crtc.c
index 5d627646601e4d34d7f6e61043cc9dcb4ec87503..cebcab5606268f76aa1f9161315ef17ba82981d7 100644 (file)
@@ -20,6 +20,7 @@
 #include "armada_hw.h"
 
 struct armada_frame_work {
+       struct armada_plane_work work;
        struct drm_pending_vblank_event *event;
        struct armada_regs regs[4];
        struct drm_framebuffer *old_fb;
@@ -33,6 +34,23 @@ enum csc_mode {
        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:
@@ -173,85 +191,82 @@ static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb,
        return i;
 }
 
-void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
-       struct armada_vbl_event *evt)
+static void armada_drm_plane_work_run(struct armada_crtc *dcrtc,
+       struct armada_plane *plane)
 {
-       unsigned long flags;
-       bool not_on_list;
+       struct armada_plane_work *work = xchg(&plane->work, NULL);
 
-       WARN_ON(drm_vblank_get(dcrtc->crtc.dev, dcrtc->num));
+       /* Handle any pending frame work. */
+       if (work) {
+               work->fn(dcrtc, plane, work);
+               drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+       }
 
-       spin_lock_irqsave(&dcrtc->irq_lock, flags);
-       not_on_list = list_empty(&evt->node);
-       if (not_on_list)
-               list_add_tail(&evt->node, &dcrtc->vbl_list);
-       spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
+       wake_up(&plane->frame_wait);
+}
+
+int armada_drm_plane_work_queue(struct armada_crtc *dcrtc,
+       struct armada_plane *plane, struct armada_plane_work *work)
+{
+       int ret;
+
+       ret = drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
+       if (ret) {
+               DRM_ERROR("failed to acquire vblank counter\n");
+               return ret;
+       }
 
-       if (!not_on_list)
+       ret = cmpxchg(&plane->work, NULL, work) ? -EBUSY : 0;
+       if (ret)
                drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
+
+       return ret;
 }
 
-void armada_drm_vbl_event_remove(struct armada_crtc *dcrtc,
-       struct armada_vbl_event *evt)
+int armada_drm_plane_work_wait(struct armada_plane *plane, long timeout)
 {
-       if (!list_empty(&evt->node)) {
-               list_del_init(&evt->node);
-               drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
-       }
+       return wait_event_timeout(plane->frame_wait, !plane->work, timeout);
 }
 
-static void armada_drm_vbl_event_run(struct armada_crtc *dcrtc)
+struct armada_plane_work *armada_drm_plane_work_cancel(
+       struct armada_crtc *dcrtc, struct armada_plane *plane)
 {
-       struct armada_vbl_event *e, *n;
+       struct armada_plane_work *work = xchg(&plane->work, NULL);
 
-       list_for_each_entry_safe(e, n, &dcrtc->vbl_list, node) {
-               list_del_init(&e->node);
+       if (work)
                drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
-               e->fn(dcrtc, e->data);
-       }
+
+       return work;
 }
 
 static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc,
        struct armada_frame_work *work)
 {
-       struct drm_device *dev = dcrtc->crtc.dev;
-       int ret;
+       struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
 
-       ret = drm_vblank_get(dev, dcrtc->num);
-       if (ret) {
-               DRM_ERROR("failed to acquire vblank counter\n");
-               return ret;
-       }
-
-       if (cmpxchg(&dcrtc->frame_work, NULL, work)) {
-               drm_vblank_put(dev, dcrtc->num);
-               ret = -EBUSY;
-       }
-
-       return ret;
+       return armada_drm_plane_work_queue(dcrtc, plane, &work->work);
 }
 
 static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc,
-       struct armada_frame_work *work)
+       struct armada_plane *plane, struct armada_plane_work *work)
 {
+       struct armada_frame_work *fwork = container_of(work, struct armada_frame_work, work);
        struct drm_device *dev = dcrtc->crtc.dev;
        unsigned long flags;
 
        spin_lock_irqsave(&dcrtc->irq_lock, flags);
-       armada_drm_crtc_update_regs(dcrtc, work->regs);
+       armada_drm_crtc_update_regs(dcrtc, fwork->regs);
        spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
 
-       if (work->event) {
+       if (fwork->event) {
                spin_lock_irqsave(&dev->event_lock, flags);
-               drm_send_vblank_event(dev, dcrtc->num, work->event);
+               drm_send_vblank_event(dev, dcrtc->num, fwork->event);
                spin_unlock_irqrestore(&dev->event_lock, flags);
        }
 
-       drm_vblank_put(dev, dcrtc->num);
-
        /* Finally, queue the process-half of the cleanup. */
-       __armada_drm_queue_unref_work(dcrtc->crtc.dev, work->old_fb);
-       kfree(work);
+       __armada_drm_queue_unref_work(dcrtc->crtc.dev, fwork->old_fb);
+       kfree(fwork);
 }
 
 static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
@@ -271,6 +286,7 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
        work = kmalloc(sizeof(*work), GFP_KERNEL);
        if (work) {
                int i = 0;
+               work->work.fn = armada_drm_crtc_complete_frame_work;
                work->event = NULL;
                work->old_fb = fb;
                armada_reg_queue_end(work->regs, i);
@@ -291,18 +307,14 @@ static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc,
 
 static void armada_drm_vblank_off(struct armada_crtc *dcrtc)
 {
-       struct armada_frame_work *work;
+       struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
 
        /*
         * 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);
-
-       /* Handle any pending flip event. */
-       work = xchg(&dcrtc->frame_work, NULL);
-       if (work)
-               armada_drm_crtc_complete_frame_work(dcrtc, work);
+       armada_drm_plane_work_run(dcrtc, plane);
 }
 
 void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b,
@@ -390,6 +402,7 @@ static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
 static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 {
        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);
@@ -400,7 +413,11 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
                drm_handle_vblank(dcrtc->crtc.dev, dcrtc->num);
 
        spin_lock(&dcrtc->irq_lock);
-       armada_drm_vbl_event_run(dcrtc);
+       ovl_plane = dcrtc->plane;
+       if (ovl_plane) {
+               struct armada_plane *plane = drm_to_armada_plane(ovl_plane);
+               armada_drm_plane_work_run(dcrtc, plane);
+       }
 
        if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) {
                int i = stat & GRA_FRAME_IRQ0 ? 0 : 1;
@@ -431,12 +448,8 @@ static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
        spin_unlock(&dcrtc->irq_lock);
 
        if (stat & GRA_FRAME_IRQ) {
-               struct armada_frame_work *work = xchg(&dcrtc->frame_work, NULL);
-
-               if (work)
-                       armada_drm_crtc_complete_frame_work(dcrtc, work);
-
-               wake_up(&dcrtc->frame_wait);
+               struct armada_plane *plane = drm_to_armada_plane(dcrtc->crtc.primary);
+               armada_drm_plane_work_run(dcrtc, plane);
        }
 }
 
@@ -552,7 +565,8 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
                adj->crtc_vtotal, tm, bm);
 
        /* Wait for pending flips to complete */
-       wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
+       armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary),
+                                  MAX_SCHEDULE_TIMEOUT);
 
        drm_crtc_vblank_off(crtc);
 
@@ -669,7 +683,8 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
        armada_reg_queue_end(regs, i);
 
        /* Wait for pending flips to complete */
-       wait_event(dcrtc->frame_wait, !dcrtc->frame_work);
+       armada_drm_plane_work_wait(drm_to_armada_plane(dcrtc->crtc.primary),
+                                  MAX_SCHEDULE_TIMEOUT);
 
        /* Take a reference to the new fb as we're using it */
        drm_framebuffer_reference(crtc->primary->fb);
@@ -683,18 +698,47 @@ static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
        return 0;
 }
 
+void armada_drm_crtc_plane_disable(struct armada_crtc *dcrtc,
+       struct drm_plane *plane)
+{
+       u32 sram_para1, dma_ctrl0_mask;
+
+       /*
+        * Drop our reference on any framebuffer attached to this plane.
+        * We don't need to NULL this out as drm_plane_force_disable(),
+        * and __setplane_internal() will do so for an overlay plane, and
+        * __drm_helper_disable_unused_functions() will do so for the
+        * primary plane.
+        */
+       if (plane->fb)
+               drm_framebuffer_unreference(plane->fb);
+
+       /* Power down the Y/U/V FIFOs */
+       sram_para1 = CFG_PDWN16x66 | CFG_PDWN32x66;
+
+       /* Power down most RAMs and FIFOs if this is the primary plane */
+       if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
+               sram_para1 |= CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
+                             CFG_PDWN32x32 | CFG_PDWN64x66;
+               dma_ctrl0_mask = CFG_GRA_ENA;
+       } else {
+               dma_ctrl0_mask = CFG_DMA_ENA;
+       }
+
+       spin_lock_irq(&dcrtc->irq_lock);
+       armada_updatel(0, dma_ctrl0_mask, dcrtc->base + LCD_SPU_DMA_CTRL0);
+       spin_unlock_irq(&dcrtc->irq_lock);
+
+       armada_updatel(sram_para1, 0, dcrtc->base + LCD_SPU_SRAM_PARA1);
+}
+
 /* The mode_config.mutex will be held for this call */
 static void armada_drm_crtc_disable(struct drm_crtc *crtc)
 {
        struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 
        armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
-       armada_drm_crtc_finish_fb(dcrtc, crtc->primary->fb, true);
-
-       /* Power down most RAMs and FIFOs */
-       writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 |
-                      CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 |
-                      CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
+       armada_drm_crtc_plane_disable(dcrtc, crtc->primary);
 }
 
 static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = {
@@ -963,6 +1007,7 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
        if (!work)
                return -ENOMEM;
 
+       work->work.fn = armada_drm_crtc_complete_frame_work;
        work->event = event;
        work->old_fb = dcrtc->crtc.primary->fb;
 
@@ -996,12 +1041,8 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
         * Finally, if the display is blanked, we won't receive an
         * interrupt, so complete it now.
         */
-       if (dpms_blanked(dcrtc->dpms)) {
-               struct armada_frame_work *work = xchg(&dcrtc->frame_work, NULL);
-
-               if (work)
-                       armada_drm_crtc_complete_frame_work(dcrtc, work);
-       }
+       if (dpms_blanked(dcrtc->dpms))
+               armada_drm_plane_work_run(dcrtc, drm_to_armada_plane(dcrtc->crtc.primary));
 
        return 0;
 }
@@ -1042,6 +1083,19 @@ static struct drm_crtc_funcs armada_crtc_funcs = {
        .set_property   = armada_drm_crtc_set_property,
 };
 
+static const struct drm_plane_funcs armada_primary_plane_funcs = {
+       .update_plane   = drm_primary_helper_update,
+       .disable_plane  = drm_primary_helper_disable,
+       .destroy        = drm_primary_helper_destroy,
+};
+
+int armada_drm_plane_init(struct armada_plane *plane)
+{
+       init_waitqueue_head(&plane->frame_wait);
+
+       return 0;
+}
+
 static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = {
        { CSC_AUTO,        "Auto" },
        { CSC_YUV_CCIR601, "CCIR601" },
@@ -1080,6 +1134,7 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
 {
        struct armada_private *priv = drm->dev_private;
        struct armada_crtc *dcrtc;
+       struct armada_plane *primary;
        void __iomem *base;
        int ret;
 
@@ -1110,8 +1165,6 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
        dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24;
        spin_lock_init(&dcrtc->irq_lock);
        dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR;
-       INIT_LIST_HEAD(&dcrtc->vbl_list);
-       init_waitqueue_head(&dcrtc->frame_wait);
 
        /* Initialize some registers which we don't otherwise set */
        writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV);
@@ -1148,7 +1201,32 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
        priv->dcrtc[dcrtc->num] = dcrtc;
 
        dcrtc->crtc.port = port;
-       drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs);
+
+       primary = kzalloc(sizeof(*primary), GFP_KERNEL);
+       if (!primary)
+               return -ENOMEM;
+
+       ret = armada_drm_plane_init(primary);
+       if (ret) {
+               kfree(primary);
+               return ret;
+       }
+
+       ret = drm_universal_plane_init(drm, &primary->base, 0,
+                                      &armada_primary_plane_funcs,
+                                      armada_primary_formats,
+                                      ARRAY_SIZE(armada_primary_formats),
+                                      DRM_PLANE_TYPE_PRIMARY);
+       if (ret) {
+               kfree(primary);
+               return ret;
+       }
+
+       ret = drm_crtc_init_with_planes(drm, &dcrtc->crtc, &primary->base, NULL,
+                                       &armada_crtc_funcs);
+       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,
@@ -1157,6 +1235,10 @@ static int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
                                   dcrtc->csc_rgb_mode);
 
        return armada_overlay_plane_create(drm, 1 << dcrtc->num);
+
+err_crtc_init:
+       primary->base.funcs->destroy(&primary->base);
+       return ret;
 }
 
 static int